- MIT licensed
- Python 3.11+, zero dependencies
- Claude Code + Codex
Project plans that
live in Git.
Projector gives every project a permanent home under docs/projects/, one command for people and coding agents, and review loops that carry a pull request to a clean head. No service, no account, no index to keep in sync.
pipx install git+https://github.com/ninjudd/projector.git$ project listnow: payments in-progress Ledger-backed paymentsnext: payments/invoices ready Issue from the ledger search-relevance draft Rank results by recencylater: audit-log ready Record every mutationcompleted: adopt-projector completed Adopt Projector $ project status payments/invoices in-progressdocs/projects/payments/invoices/readme.md$ project checkProject plans are valid.$ Why Projector
Plans that stay where the code is.
Project trackers drift from the repository they describe. Projector keeps intent next to the code that carries it out, under version control, reviewed in the same pull request.
- 01
Git is the database.
A clone contains every plan and its history. Projector needs no service, account, daemon, or generated index.
- 02
One project, one permanent home.
A status change edits frontmatter. It never moves a file or maintains a second representation of the same plan.
- 03
Concurrent projects change different files.
Listing and prioritization are queries, so two branches working on two projects never contend on a shared now.md, next.md, or later.md.
- 04
Plans grow recursively.
Every project is a directory. It can take on design notes, decisions, and nested projects without changing shape.
- 05
Built for people and agents.
Concise human output, stable JSON with a schema version, and skills that call the same commands you can inspect and run yourself.
- 06
Policy stays local.
Projector ships useful defaults without embedding anyone's GitHub accounts, home-directory layout, or review rules.
How it works
Adopt it in three commands.
Run init from anywhere inside a Git repository. From then on, every project is a directory you can browse on GitHub and a record the CLI can query.
- 01
Adopt the convention
Creates
docs/projects/README.md, the one file that explains the format to everyone who opens the repository. It never replaces an existing convention file.project init - 02
Create a project
One directory, one lowercase
readme.md. The path is the name, sopayments/invoicesis a nested project with its own status and its own plan.project create payments --status ready --priority nextproject create invoices --parent payments - 03
Move it through the lifecycle
Status and priority change in frontmatter, in the same pull request that makes the claim true. The directory never moves, so a citation like
payments/readme.md § 4stays valid for the life of the project.project status payments in-progressproject done payments
docs/projects/
├── README.md
└── payments/
├── readme.md status + priority frontmatter
├── design.md
└── invoices/
└── readme.md nested project: payments/invoices---
status: in-progress
priority: now
---
# Ledger-backed payments
## 1. Outcome
Every invoice is issued from the ledger service, and the
legacy invoice table is read-only.
## 2. Decisions
Backfill runs once, behind a flag, before the cutover.The model
Two fields. Two independent claims.
status says where the work is in its lifecycle. priority says when it should happen. Neither implies the other, so a draft can be the current focus and in-progress work can be set aside without losing its place.
- draft
The plan is still being written. It makes no readiness claim.
- ready
Complete enough to execute. Every question that blocks implementation is answered or deliberately deferred.
- in-progress
Implementation has begun. A blocked project stays here with the blocker explained in its plan.
- completed
No more work needed. The plan records whether it shipped, was abandoned, or was superseded.
- now
Deserves the team's current attention.
- next
Becomes current when capacity opens.
- later
Recorded but not scheduled.
The CLI
One command for the whole lifecycle.
The framework is Projector. The command it installs is project. It finds the Git root from wherever you are, reads the plans under docs/projects/, and never needs the network.
- project init
- Adopt the convention by creating docs/projects/README.md.
- project list
- Group projects by priority. Filter with --status and --priority.
- project show <project>
- Print a plan with its frontmatter.
- project search <query>
- Search names, metadata, plans, and supplemental files.
- project create <project>
- Create one directory and one readme.md. --parent nests it.
- project edit <project>
- Open the plan in $VISUAL or $EDITOR.
- project status <project> <status>
- Change only the status scalar.
- project priority <project> <priority>
- Change only the priority scalar.
- project done <project>
- Shorthand for status completed.
- project check
- Validate every plan: frontmatter, casing, links, symlinks.
- project config get|list|paths
- Read layered .projector.toml settings.
{
"projects": [
{
"name": "payments",
"owner": null,
"path": "docs/projects/payments/readme.md",
"priority": "now",
"status": "in-progress",
"title": "Ledger-backed payments"
}
],
"schema_version": 2
}A schema version in every response
JSON output carries schema_version 2, so a script can refuse a shape it does not recognize instead of guessing.
Clean streams
stdout is JSON only when you ask for it. Diagnostics go to stderr.
Safe writes, no commits
Mutations write through a temporary file, preserve unrelated frontmatter, and refuse the update if the plan changed after Projector read it. Committing is yours.
Exit codes that mean something
65 for a validation failure, 66 for a missing projects directory, 69 when edit has no terminal or editor.
Agent workflows
Skills your agents already know.
Projector packages one canonical skill tree for Claude Code and Codex. Skills express the workflow; the CLI supplies the mechanics. No MCP server required.
/plan-project
Turn a requested outcome into a durable plan that another person or agent can execute without reconstructing the conversation.
/work-project
Implement a project while keeping its plan and status current. The plan is the intent; the repository is the evidence.
/finish-project
Verify the acceptance criteria, record whether the work shipped, was abandoned, or was superseded, and mark it completed in the same change.
/start-review-loop
Review each exact pushed SHA locally and publish verified findings as labeled reviews. A pull request stays in draft until its head is clean.
/start-fix-loop
Watch for review findings, verify and fix each one, then reply with the commit, push, and resolve the thread.
/gh-stack
Create, push, rebase, and navigate stacks of dependent pull requests with the gh-stack extension when work crosses a reviewability boundary.
Equal targets, one source tree
Both plugin manifests point at the same skills/ directory. A host loads the same instructions without a generated copy or a host-specific fork, so a fix lands in both places at once.
Review loops
Draft means changes are needed.
Two background loops carry a pull request from first push to a clean head while you keep working. The draft state is the verdict, so the moment a pull request turns ready, it has actually been reviewed.
- 01
Push
A new SHA lands on a pull request the authenticated account owns.
- 02
Review the exact head
The review loop checks out that SHA locally and inspects it. A review starts only when inspection actually begins.
- 03
Publish verified findings
Findings go up as one labeled COMMENT review on that head. The pull request stays in draft.
- 04
Fix each finding
The fix loop verifies a finding, commits the fix, replies with the commit, pushes, and resolves the thread.
- 05
Clean head, ready for humans
When a review of the head finds nothing, the pull request is marked ready. That transition is the sign-off.
Never mark your own draft ready yourself; a clean review is what clears it.
The loops review as the operator: the account whose pull requests are watched and whose branches carry the fixes. Identity follows the authenticated token, so a loop never scopes itself to pull requests it cannot push to.
A real APPROVE is posted only when review.allow_approve is set and the author is someone else. On your own pull request the loop stays with labeled comments, which GitHub would insist on anyway.
Every finding is verified against the checked-out head before it is published, and every fix is committed, pushed, and resolved in that order, so the thread on GitHub always points at the commit that answered it.
Configuration
Settings that follow the code.
Projector reads .projector.toml from your home directory down to the repository root, nearest last. Put a value in the file closest to the code it governs. Tables merge key by key, so a nearer file overrides one setting without discarding the rest.
# every repository under ~/src
[review]
username = "review-bot"
effort = "high"
model = "sonnet"# this repository only
[review]
model = "fable"
allow_approve = true{
"key": "review.model",
"schema_version": 2,
"source": "/home/you/src/payments/.projector.toml",
"value": "fable"
}| Key | Default | Read by |
|---|---|---|
| projects.dir | docs/projects | every command |
| review.username | the authenticated user | start-review-loop |
| review.allow_approve | false | start-review-loop |
Keys are not validated. Any key a skill or a script agrees on works, so the same command reads settings Projector itself knows nothing about.
get --json reports which file each value came from, which is the fastest way to learn why a setting is not what you expected. get exits 1 when a key is unset and no default is given, so a caller can branch on the exit status instead of parsing output.
Install
Two commands per host.
Install the CLI once, then add the plugin to whichever coding agents you use. The core workflows call the local command and need nothing else.
The CLI
Requires Python 3.11 or newer and has no runtime dependencies. pipx gives you an isolated project executable.
pipx install git+https://github.com/ninjudd/projector.gitproject --helpClaude Code
Adds the repository as a marketplace and installs the plugin at user scope. Invoke a skill as /projector:<skill>.
claude plugin marketplace add ninjudd/projector --scope userclaude plugin install projector@projector --scope userCodex
The same repository and the same skill tree. Invoke a skill as $<skill>.
codex plugin marketplace add ninjudd/projectorcodex plugin add projector@projectorFrom a clone
./install.sh all installs the CLI and both plugins from the checkout. ./install.sh status compares the installed command against the files in the checkout, so it reports a stale command even when nobody bumped a version.
git clone https://github.com/ninjudd/projector.gitcd projector./install.sh all