tl;dr: I built ticketscli - an open source, terminal-based issue tracking system that takes the place of things like JIRA, tracks tickets as Markdown files in Git and can be used as the basis for your spec-driven development workflow. If that sounds interesting and you want to learn more, see the quick demo below or the full docs on GitHub. After the quick demo is the background on how I got here.
Quick Demo of ticketscli
Say we’re starting a new project called “FaceSpace” - the first thing we do is install the ticketscli APM package targeting our particular AI coding agent, in this case OpenCode:
mkdir facespace
cd facespace
git init .
apm install https://github.com/polycloudinc/ticketscli.git --dev --target opencode
This installs the following agent skills:
- tickets-init
- tickets-create
- tickets-list
- tickets-review
- tickets-execution-plan
- tickets-transition
- tickets-rank
Next we’ll initialize the tickets system for this project. This is a one-time step that sets the ticket code prefix:
npx @polycloudinc/ticketscli@latest init --code-prefix FCSP
This means our tickets will get codes FCSP001, FCPC002, etc. This command created the storage directory and s couple of files:
ls .tickets/
settings.yaml statistics.yaml
Next we fire up the AI coding agent and create our first ticket with a prompt such as:
Create a ticket for scaffolding the backend of FaceSpace using express js
The coding agent identifies that it should use the tickets-create agent skills, and uses that skill to help it correctly create the new ticket. The agent then uses it’s model to determine the name for the ticket based on the user prompt, and uses the CLI to create the ticket:
Thought: Using ticket creation skill · 1.4s
→ Skill "tickets-create"
+ Thought: Inspecting command requirements · 953ms
I’ll create the ticket with the requested Express.js backend scaffolding scope, then verify and complete its generated template fields.
Thought · 258ms
$ npx @polycloudinc/ticketscli@latest create -n "Scaffold FaceSpace Backend With Express.js"
Created: /workspaces/facespace/.tickets/FCSP001 - Scaffold FaceSpace Backend With Express.js.md
Thought: Reviewing ticket details · 1.9s
→ Read .tickets/FCSP001 - Scaffold FaceSpace Backend With Express.js.md
✱ Glob "**/Ticket.md" in . (1 match)
Thought: Editing project requirements · 1.1s
The ticket was created as FCSP001; its front matter already matches the template. I’m replacing the generated placeholders with the project purpose, outcome requirements, and the explicit Express.js decision.
Next, following the skill instructions, the agent fills in the Introduction, Requirements and Technical Solution sections based on the initial prompt:
Thought: Revising patch context · 996ms
← Patched /workspaces/facespace/.tickets/FCSP001 - Scaffold FaceSpace Backend With Express.js.md
11 ticket_completed:
12 ---
13 # Introduction
14
15 - TODO: A one to two sentence explanation of the purpose of the ticket without getting into deep requirements and without getting into the solution.
15 + Establish the initial backend foundation for FaceSpace so server-side features can be developed consistently. The scaffold should provide a runnable Express.js application with a clear structure for future API work.
16
17 # Requirements
18
19 - TODO: Detailed requirements captured in a way that focuses on outcomes while keeping the solution negotiable. Note the Requirements section may be organized into subsectiona if appropriate.
19 + - The FaceSpace backend can be installed and started through documented project commands.
20 + - The application exposes a basic health or readiness endpoint that confirms the server is running.
21 + - The backend has a maintainable structure for adding routes, middleware, configuration, and services.
22 + - The scaffold includes appropriate baseline error handling and JSON request/response support.
23 + - The setup provides a foundation that can be extended with FaceSpace domain APIs without restructuring the project.
24
25 # Technical Solution
26
23 - TODO: Capture enough detail on the technical solution for there to be no ambiguities when it comes time to implement the solution. This may mean capturing key technical decisions, architecture outline, project structure layout, interface descriptions or other elements that define the shape of the implementation. The Technical Solution section should never contain large blocks of code.
27 + Use Express.js as the HTTP server framework for the FaceSpace backend. Create a minimal application entry point and separate the server startup from the Express app configuration where practical. Organize the scaffold so routes, middleware, configuration, and service logic have clear locations, and add a simple health endpoint plus baseline JSON parsing and error handling.
Before calling the job done, the agent uses the validate CLI subcommand to check for any structural issues with the ticket:
Thought: Verifying fields and validation · 1.3s
→ Read .tickets/FCSP001 - Scaffold FaceSpace Backend With Express.js.md
$ npx @polycloudinc/ticketscli@latest validate
Error: --all or --ticket is required
Usage: tickets validate [--all | --ticket <code>] [options]
Options:
-a, --all Validate all tickets
-t, --ticket <code> Validate a single ticket by code
-d, --tickets-dir <path> Path to tickets directory (default: _tickets)
-h, --help Show this help message
Thought · 218ms
$ npx @polycloudinc/ticketscli@latest validate --ticket FCSP001
Validating: .tickets/FCSP001 - Scaffold FaceSpace Backend With Express.js.md
Created ticket FCSP001: .tickets/FCSP001 - Scaffold FaceSpace Backend With Express.js.md
Validation passed.
We can see the new ticket in our backlog with the list CLI subcommand:
npx @polycloudinc/ticketscli@latest list
Code Subject Rank Status
-------- ------------------------------------------------------- ----- ------------
FCSP001 Scaffold FaceSpace Backend With Express.js 1 backlog
-------- ------------------------------------------------------- ----- ------------
1 matching from 1 total tickets
From here we typically go into a review and prompt cycle to refine the requirements and factor our decisions into the ticket. Once those sections are to our liking we render the ticket down to an execution plan:
Define execution plan
The agent follows the tickets-execution-plan skill instructions to produce a linear execution plan that is broken into logical phases with verification steps for each phase:
Thought · 231ms
→ Skill "tickets-execution-plan"
+ Thought: Defining an editing plan · 996ms
I’ll replace the placeholder on FCSP001 with phased, checkbox-based implementation tasks and deterministic verification steps for each phase.
→ Read .tickets/FCSP001 - Scaffold FaceSpace Backend With Express.js.md
+ Thought: Planning patch phases · 2.0s
← Patched /workspaces/facespace/.tickets/FCSP001 - Scaffold FaceSpace Backend With Express.js.md
27 Use Express.js as the HTTP server framework for the FaceSpace backend. Create a minimal application entry point and separate the server startup from the Express app configuration where practical. Organize the scaffold so routes, middleware, configuration, and service logic have clear locations, and add a simple health endpoint plus baseline JSON parsing and error handling.
28
29 # Execution Plan
30
31 - TODO
31 + ## Project Foundation
32 +
33 + ### Tasks
34 + - [ ] Define the backend package metadata, dependency installation, and development/start commands.
35 + - [ ] Create the Express application entry point and separate server startup from app configuration.
36 + - [ ] Add environment configuration with a safe default port and documented local setup.
37 +
38 + ### Verification Steps
39 + - [ ] Install dependencies and start the backend using the documented command; confirm the process listens on the configured port.
40 +
41 + ## API Baseline
42 +
43 + ### Tasks
44 + - [ ] Add JSON request parsing and the initial route registration structure.
45 + - [ ] Implement a health or readiness endpoint with a stable JSON response.
46 + - [ ] Add a not-found response and centralized error-handling middleware.
47 +
48 + ### Verification Steps
49 + - [ ] Run a scripted HTTP smoke test that checks the health endpoint, JSON response, unknown-route handling, and malformed-request behavior.
50 +
51 + ## Maintainability And Documentation
52 +
53 + ### Tasks
54 + - [ ] Organize routes, middleware, configuration, and service directories for future FaceSpace APIs.
55 + - [ ] Add or update the backend README with setup, run, endpoint, and configuration instructions.
56 + - [ ] Add automated tests for the app bootstrap and health endpoint using the project test framework.
57 +
58 + ### Verification Steps
59 + - [ ] Run the backend test suite and confirm all tests pass from a clean dependency installation.
Thought · 389ms
$ npx @polycloudinc/ticketscli@latest validate --ticket FCSP001
Validating: .tickets/FCSP001 - Scaffold FaceSpace Backend With Express.js.md
Defined the execution plan for FCSP001 with three phases:
- Project Foundation
- API Baseline
- Maintainability And Documentation
Each phase includes implementation tasks and deterministic verification steps. Ticket validation passed.
From here we review the execution plan and make any necessary adjustments, then ask the agent to execute that plan to deliver the work. We might ask it to execute task-by-task, phase-by-phase or end-to-end depending on the level of oversight we want to give the agent. Regardless of the scope of execution the agent will use the tickets-execute skill for guidance and guardrails.
See the full docs for ticketscli ⁵on GitHub.
Atlassian
I first used Atlassian JIRA and Confluence in 2005 and worked at several shops that used those products over the ensuing years. I also operated my own instances for about a decade, however I moved away from those tools to Git-tracked Markdown by around 2022.
Move to Markdown
I started practicing and promoting the use of Markdown for software specs and design docs in the same repo as code sometime around 2014/2015 with the main driver being versioning design docs alongside the code it pertains to. I even got non-technical users in my team to follow this practice, made easier using the pretty nice WYSIWYG Markdown tool Typora which at the time was in beta and was free to use. Through this practice I was able to move all the content from the Confluence servers I managed out to Git-tracked Markdown by around 2018.
Obsidian
Around 2020 while working at AWS I was introduced to Obsidian and that opened up a whole new set of possibilities with Markdown. Through a combination of Obsidian’s built-in templates system, and the Dataview and Tasks plugins I came up with a ticketing workflow where each ticket is a Markdown file with YAML front matter for the structured metadata about the ticket such as it’s status. By around 2023 I had migrated all the tickets from my JIRA servers to Git-tracked Markdown as well. I still use that Obsidian workflow to this day for my personal projects and life management as well as for professional purposes.
Spec-Driven Development
With the advent of coding agents in early 2024, I started using those Markdown tickets as my way of doing SDD - and once we had Opus 4.5, that started working remarkably well. Not because my system is doing anything special, just because the models are so good now, and you don’t need megabytes of specs to steer them effectively.
For a while I was quite happy with nothing more than a template Ticket.md and some simple instructions in AGENTS.md. I could tell the agent something like “Create a ticket for adding database connection pooling to the FooBar microservice” and it would create the fresh ticket in the designated .tickets/ directory and from there I could iterate with the agent to flesh out the requirements, stipulate technical design decisions and eventually render a flat, linear execution plan which it would then execute for me.
Rough Edges
It worked great but over time a few small issues came up:
- Sometimes the agent would mess up the ticket creation or other tasks, picking the wrong sequence number, using the wrong filename convention or some other little thing that I would have to steer back.
- Sometimes I had to be overly verbose as well as repetitive in my prompts to get the agent to follow the intended workflow.
- Once the number of tickets in
.tickets/gets beyond 20 or so it becomes difficult to understand and plan through the backlog, and to understand what’s done already.
ticketscli
To solve these issues I decided to formalize my tickets system with:
- A CLI that handles the mechanical aspects such as creating tickets and transitioning status.
- A set of agent skills that provides much crisper workflow guidance for the coding agent.
Thus was born ticketscli.
Today it’s very functional for building and curating a backlog and delivering work SDD style, and I have a backlog of improvements coming that should help it scale up beyond a solo developer scenario and handle projects that run for multiple years. I have personally been using this workflow for about two years and using the ticketscli implementation of the workflow since I first built it in roughly March 2026.
See the full docs on GitHub.