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:

  1. 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.
  2. Sometimes I had to be overly verbose as well as repetitive in my prompts to get the agent to follow the intended workflow.
  3. 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.