@bstrehl/tix
v0.1.0
Published
Local-first Markdown ticket CLI
Readme
tix
Local-first Markdown PRDs and tickets for coding-agent workflows.
tix stores planning state in .tix/ inside a repository. Markdown files are
the canonical source of truth; there is no generated database or index to keep in
sync.
Install And Run
This repository is a Node 20+ TypeScript CLI.
After publishing, install the scoped npm package:
npm install -g @bstrehl/tixThe package installs the tix binary.
For local development:
npm install
npm run build
npm testDuring development, run the CLI through tsx:
npm run dev -- --help
npm run dev -- init
npm run dev -- listAfter building, the package exposes the tix binary from dist/cli.js. When
installed or linked, use:
tix --help
tix initAvailable project scripts:
npm run dev -- <args>runssrc/cli.ts.npm run buildcompiles TypeScript intodist/.npm testruns the Vitest suite once.npm run test:watchruns Vitest in watch mode.npm run typecheckrunstsc --noEmit.
Quick Start
Initialize .tix/ in a project:
tix config init # optional: create ~/.config/tix/config.md defaults
tix initCreate a PRD and a few tickets:
tix prd create -t "Search v1" --label needs-triage --body "Build local search."
tix create -t "Index files" --prd A --label needs-triage --priority high -b "Build the file index."
tix create -t "Search index" --prd A --depends-on 1Pick the next eligible ticket, read its agent context, and update state:
tix next
tix ticket 1
tix work 1
tix comment 1 "Started implementation."
tix done 1
tix checkRun a configured headless agent:
tix run 1
tix loop ACore Model
tix has two object types:
- PRDs use Excel-style uppercase letter IDs:
A,B,Z,AA. - Tickets use global positive integer IDs:
1,2,3.
Object IDs are intentionally simple:
tix Areads PRDA.tix 19reads ticket19.- Mixed IDs such as
A1are invalid.
The project root is the nearest parent directory containing .tix/. Commands
can be run from nested directories inside that project.
Files
tix init creates:
.tix/
config.md
prompts.md
prds/
tickets/
tmp/
locks/When run inside a Git repository, tix init defaults to adding runtime paths to
.gitignore:
.tix/tmp/
.tix/locks/Use tix init --gitignore all to ignore the whole .tix/ directory for
local-only planning state, or tix init --gitignore none to leave .gitignore
untouched.
Canonical planning state:
.tix/prds/*.mdstores PRDs..tix/tickets/*.mdstores tickets..tix/config.mdstores agent commands..tix/prompts.mdstores reusable prompt sections..tix/allocation.mdstores the last allocated ticket ID and PRD number.
Runtime state:
.tix/tmp/run-*/prompt.mdis the exact prompt sent to an agent..tix/tmp/run-*/stdout.txtcaptures agent stdout..tix/tmp/run-*/stderr.txtcaptures agent stderr..tix/locks/ticket-<id>.jsonprevents concurrent runs for the same ticket.
Command Reference
All examples use the installed binary name, tix. In this source repo, replace
tix with npm run dev -- while developing.
Help And Version
tix --help
tix help
tix <command> --help
tix --versiontix init
Initializes .tix/ in the current directory.
tix init
tix init --gitignore runtime
tix init --gitignore all
tix init --gitignore noneThe command is idempotent. It creates missing paths without overwriting existing
config.md or prompts.md.
Options:
--gitignore runtimeadds.tix/tmp/and.tix/locks/when inside a Git repository. This is the default and keeps PRDs, tickets, config, and prompts shareable in the repo.--gitignore alladds.tix/when inside a Git repository. Use this when tix state should stay local-only.--gitignore nonedoes not create or update.gitignore.
If ~/.config/tix/config.md exists, tix init copies it into a new
.tix/config.md. Existing project config is never overwritten.
tix config init
Initializes the global config template at ~/.config/tix/config.md.
tix config initThe command creates the same starter config format used by project init and is
idempotent. Edit this file to configure default agents and agent commands for
future projects. It seeds new projects only; tix run and tix loop read the
project's .tix/config.md.
tix prd create
Creates a PRD and prints the new file path.
tix prd create -t "Title"
tix prd create --title "Title" --body "Markdown body"
tix prd create -t "Title" -F body.md
tix prd create -t "Title" -F -
tix prd create -t "Title" --label needs-triageOptions:
-t, --title <title>is required.--body <body>uses inline Markdown body text.-F, --body-file <path>reads the body from a file.-F -reads the body from stdin.--label <label>applies an initial label. It is repeatable and accepts comma-separated values.
Use either --body or --body-file, not both.
New PRDs start with status active.
tix prd list
Lists PRDs as tab-separated rows:
<id> <status> <title>Commands:
tix prd list
tix prd list --status active
tix prd list --status done
tix prd list --status archived
tix prd list --label needs-triage--status and --label are literal filters. --status does not validate
against built-in PRD statuses.
tix prd <id>
Reads a PRD Markdown file.
tix prd A
tix AIf the PRD has tickets, read output includes a computed ## Ticket Summary
section:
## Ticket Summary
- 1 [todo] Build index
- 2 [done] Wire searchtix create
Creates a ticket and prints the new file path.
tix create -t "Title"
tix create -t "Title" -b "Markdown body"
tix create -t "Title" -F ticket.md
tix create -t "Title" -F -
tix create -t "Title" --prd A
tix create -t "Title" --label needs-triage
tix create -t "Title" --priority urgent
tix create -t "Title" --depends-on 1,2
tix create -t "Title" --depends-on 1 --depends-on 2
tix create -t "Title" --agent codexOptions:
-t, --title <title>is required.-b, --body <body>uses inline Markdown body text.-F, --body-file <path>reads the body from a file.-F -reads the body from stdin.--label <label>applies an initial label. It is repeatable and accepts comma-separated values.--prd <id>assigns the ticket to an existing PRD.--priority <priority>setsurgent,high,normal, orlow.--depends-on <ids>sets dependencies. It is repeatable and accepts comma-separated values.--agent <agent>sets a ticket-specific agent from.tix/config.md.
New tickets start with status todo. The default priority is normal.
The command validates PRD references, dependency ticket IDs, priority values, and agent names before allocating an ID.
tix edit <id>
Edits ticket metadata or body non-interactively and prints the ticket file path.
tix edit 1 -t "New title"
tix edit 1 -b "Replacement body"
tix edit 1 -F body.md
tix edit 1 --prd B
tix edit 1 --no-prd
tix edit 1 --priority high
tix edit 1 --depends-on 2,3
tix edit 1 --depends-on ""
tix edit 1 --status doing
tix edit 1 --agent claudeOptions:
-t, --title <title>replaces the title.-b, --body <body>replaces the body.-F, --body-file <path>replaces the body from a file or stdin.--prd <id>assigns the ticket to an existing PRD.--no-prdremoves the PRD assignment.--priority <priority>setsurgent,high,normal, orlow.--depends-on <ids>replaces dependencies.--status <status>changes status after validating built-in statuses.--agent <agent>sets a ticket-specific configured agent.
--status through edit appends a status log only when the status changes.
tix status <id> <status>
Changes a ticket or PRD status and prints the file path.
tix status 1 doing
tix status 1 blocked
tix status A archivedTicket statuses are fixed to todo, doing, blocked, review, and done.
PRD statuses are fixed to active, done, and archived.
Status changes append a parseable log entry under ## Log. Reapplying the
current status is a no-op and does not write another status log.
Ticket Status Aliases
Aliases only accept ticket IDs:
tix work 1
tix done 1
tix block 1 "waiting on API details"
tix review 1
tix reopen 1Alias mapping:
worksetsdoing.donesetsdone.blocksetsblockedand optionally writesblocked_reason.reviewsetsreview.reopensetstodo.
When a blocked ticket moves to another status, blocked_reason is removed from
frontmatter and preserved in the status log entry.
tix label
Adds or removes labels on a ticket or PRD.
tix label add 1 needs-triage
tix label add A needs-triage ready-for-agent
tix label remove 1 needs-triageLabels are metadata stored in frontmatter and are separate from workflow status.
Use labels for triage routing such as needs-triage, needs-info,
ready-for-agent, ready-for-human, and wontfix.
tix comment <id> <text...>
Appends a comment log entry to a ticket or PRD and prints the file path.
tix comment 1 "Started the indexer."
tix comment A "Scope clarified with the team."Logs are Markdown bullets under ## Log:
- 2026-05-15T08:30:00.000Z - comment - Started the indexer.tix ticket <id>
Reads assembled ticket working context for an agent.
tix ticket 1
tix ticket 1 --prompt custom-section
tix 1Output can include, in order:
## Repo Promptfrom.tix/prompts.md.## PRD Ticket Promptfrom the ticket PRD's## Ticket Promptsection.## Ticket Promptfrom.tix/prompts.md, or a custom prompt section.## Ticket Frontmatter.## Ticket Body.## Ticket Log.
The ticket body's ## Log section is separated into ## Ticket Log so the log
does not appear twice.
When --prompt <section> is passed, the named section must exist in
.tix/prompts.md; missing explicit prompt sections fail instead of silently
omitting the prompt.
tix run <id>
Runs a configured headless agent for one ticket.
tix run 1
tix run 1 --agent codex
tix run 1 --follow
tix run 1 --prompt custom-sectionOptions:
--agent <agent>is a fallback when the ticket has noagentfrontmatter.--followstreams stdout and stderr while still writing artifacts.--prompt <section>uses a custom agent prompt section from.tix/prompts.md.
Agent resolution order:
- Ticket frontmatter
agent. --agent <agent>.## Default Agentin.tix/config.md.
run writes artifacts under .tix/tmp/run-<ticket>-<timestamp>-*/ and appends a
ticket log entry:
- ... - run - agent=codex exit_code=0 artifacts=.tix/tmp/run-1-...
- ... - run - agent=codex exit_code=143 signal=SIGTERM artifacts=.tix/tmp/run-1-...It does not change ticket status. The agent prompt tells the worker to run
tix work <id> at the start and finish with tix done <id> or
tix block <id> "reason".
Exit behavior:
- A zero agent exit code exits zero.
- A non-zero agent exit code becomes the
tix runexit code. - In quiet mode, failures print the artifact directory and the last stderr lines.
SIGINTandSIGTERMare forwarded to the child process group.
Environment variables provided to agents:
TIX_TICKET_IDTIX_PRD_ID, only when the ticket belongs to a PRDTIX_AGENTTIX_PROMPT_FILE
tix loop
Runs tickets sequentially through tix run.
tix loop
tix loop A
tix loop 1 2 3
tix loop --agent codex
tix loop A --agent codex
tix loop 1 2 3 --agent codexModes:
- No target: repeatedly selects global
tix next. - One PRD ID: repeatedly selects
tix next <prd>. - One or more ticket IDs: runs that explicit ordered list.
Loop stops when:
- there are no eligible tickets,
- a run exits non-zero,
- a successful run leaves the ticket in any status other than
done, - an explicit ticket is not ready.
Explicit ticket readiness requires status todo and all dependencies done.
tix list
Lists tickets as tab-separated rows:
<id> <status> <priority> <prd-or-> <title>Commands:
tix list
tix list --status todo
tix list --status done
tix list --prd A
tix list --no-prd
tix list --priority high
tix list --depends-on 1
tix list --depends-on 1,2
tix list --label needs-triageBy default, done tickets are hidden. Use --status done to show completed
tickets.
tix next [prd]
Selects the highest-priority eligible todo ticket.
tix next
tix next AEligibility:
- status must be
todo, - all dependencies must have status
done, - global selection includes tickets with no PRD and tickets in active PRDs,
- PRD-scoped selection includes tickets in that PRD, even if the PRD is not active.
Priority order:
urgenthighnormallow
Lower ticket ID breaks ties.
Exit codes:
0: selected a ticket.2: no eligible tickets and no dependency-blocked tickets remain.3: no eligible tickets because dependency-blocked tickets remain.1: command error.
tix blocked
Lists blocked tickets with current reasons:
tix blockedRows are tab-separated:
<id> <status> <priority> <prd-or-> <title> <blocked-reason-or->tix check
Validates local Markdown state without modifying files.
tix checkIt reports:
- malformed or missing frontmatter,
- missing required
id,type,title, orstatus, - filename/frontmatter ID mismatches,
- duplicate PRD or ticket IDs,
- unknown ticket or PRD statuses,
- invalid label metadata,
- missing PRD references,
- missing dependency ticket references,
- dependency cycles,
- ticket agents not configured in
.tix/config.md, - malformed
.tix/allocation.md.
It does not check whether agent commands exist on PATH.
.tix/config.md
config.md controls headless agent commands. tix init creates:
# Tix Config
## Default Agent
codex
## Agents
- codex: codex exec -
- claude: claude -pIf ~/.config/tix/config.md exists, tix init copies that file into new
projects instead of using the built-in starter config. Create the global file
with tix config init, then edit it to change the default agent set used for
future projects. Existing .tix/config.md files are not updated from global
config.
Default Agent
## Default Agent selects the fallback agent for tix run and tix loop:
## Default Agent
codexLeave this section empty only if every run will specify an agent through ticket
frontmatter or --agent. If no agent can be resolved, run fails.
Agents
The compact syntax is:
## Agents
- codex: codex exec -
- claude: claude -p
- reviewer: codex exec -The heading syntax is also supported:
## Agents
### codex
- command: codex exec -
### claude
- status: active
- command: claude -p
- prompt: stdinOnly command affects execution. status and prompt are accepted for legacy
configs but are otherwise ignored.
Agent command behavior:
- Commands run from the project root.
- Commands are shell strings.
- By default, the assembled run prompt is sent on stdin.
- If the command contains
{prompt_file}, tix writes the prompt to a file and substitutes the shell-quoted prompt file path. - If the command contains
{prompt}, tix substitutes the shell-quoted prompt text as an argument. - If either placeholder is used, stdin is empty.
- Exact legacy commands
codexandclaudeare normalized tocodex exec -andclaude -p.
Examples:
## Agents
- codex: codex exec -
- claude: claude -p
- file-agent: my-agent --prompt-file {prompt_file}
- arg-agent: my-agent --prompt {prompt}.tix/prompts.md
prompts.md stores reusable prompt sections. tix init creates:
# Tix Prompts
## repo
You are working in this repository. Inspect the code before editing and keep changes scoped to the active ticket.
## ticket
Work only on the requested ticket. Update status and logs with tix commands as you make progress.
## agent
You are a headless coding agent. Start by running `tix work <ticket>` when work begins and finish with `tix done <ticket>` or `tix block <ticket> "reason"`.Prompt section names are normalized by trimming, lowercasing, and collapsing
whitespace. ## Custom Section is selected with --prompt "custom section".
When a custom section is requested explicitly, it must exist.
If any default section is missing, tix omits it instead of failing.
repo
Included in tix ticket <id> output as ## Repo Prompt.
Use it for repository-wide instructions that apply to every ticket:
## repo
Inspect the code before editing. Keep changes focused. Run relevant tests before marking a ticket done.ticket
Included in tix ticket <id> output as ## Ticket Prompt.
Use it for instructions that apply whenever an agent reads a ticket:
## ticket
Work only on this ticket. Update progress with `tix work`, `tix comment`, `tix done`, or `tix block`.Override it per read with:
tix ticket 1 --prompt bugfixagent
Included in tix run <id> prompts as ## Agent Prompt.
Use it for instructions specific to headless agent execution:
## agent
Start by running `tix work <ticket>`. Commit no changes unless explicitly instructed. Finish by running `tix done <ticket>` or `tix block <ticket> "reason"`.Override it per run with:
tix run 1 --prompt cautious-agentCustom Sections
Add any extra ## section and select it with --prompt:
## bugfix
Reproduce the bug first, add a regression test, then implement the smallest fix.
## cautious-agent
Make no broad refactors. Stop and block the ticket if requirements conflict with existing architecture.Custom sections can be used by:
tix ticket <id> --prompt <section>tix run <id> --prompt <section>
PRD-Level Ticket Prompt
A PRD can define a ## Ticket Prompt section. When a ticket belongs to that PRD,
tix ticket <id> includes that section as ## PRD Ticket Prompt.
Example PRD body:
The search v1 work should stay local-first.
## Ticket Prompt
For tickets in this PRD, preserve backwards compatibility and document migration steps.Only the ## Ticket Prompt section is included in ticket read output. The rest
of the PRD body is not embedded.
Markdown Formats
PRD Files
PRDs live in .tix/prds/ and use filenames such as A-search-v1.md.
---
id: "A"
type: "prd"
title: "Search v1"
status: "active"
labels: ["needs-triage"]
---
PRD body.Ticket Files
Tickets live in .tix/tickets/ and use filenames such as 1-index-files.md.
---
id: 1
type: "ticket"
title: "Index files"
status: "todo"
prd: "A"
labels: ["needs-triage","ready-for-agent"]
priority: "high"
dependencies: [2,3]
agent: "codex"
blocked_reason: "waiting on API details"
---
Ticket body.
## Log
- 2026-05-15T08:30:00.000Z - status - todo -> doingRequired fields for valid PRDs and tickets:
idtypetitlestatus
PRD optional fields:
labels
Ticket optional fields:
prdlabelsprioritydependenciesagentblocked_reason
Frontmatter values are serialized as JSON strings for strings and arrays.
Recommended Agent Workflow
- Select work with
tix nextor use an assigned ticket ID. - Read context with
tix ticket <id>. - Start with
tix work <id>. - Leave progress notes with
tix comment <id> "...". - Finish with
tix done <id>ortix block <id> "specific blocker". - Run
tix checkafter changing.tixstate.
Current Scope
Implemented scope:
- local Markdown PRDs and tickets,
- command-based creation, editing, listing, reading, status changes, and logs,
- label metadata and label filters,
- dependency-aware
next, - read-only
check, - sequential
runandlooporchestration, - configurable headless agents.
Intentionally not implemented:
- delete commands,
- assignees, milestones, or external project fields,
- JSON output,
- Git branch, commit, or PR helpers,
- remote issue tracker sync,
- parallel loop execution.
