A scope-driven development scaffolder — the keel is the first structural element laid down on a ship, and everything else is built relative to it; scope is laid first here too, separate and load-bearing, with design and decisions built on top.
Concretely: a CLI for managing a personal "projects workspace" — a directory of
design documents, decision records, and code worktrees, one folder per project.
Each project (and optionally each deliverable inside a project) gets its own
scope, design, decisions, and .phase lifecycle marker. A per-unit TOML manifest
records linked source repositories so a fresh clone of the workspace can
reconstruct local worktrees.
This is a personal-tool flavor — it scratches a specific itch (a flat list of
~/projects/<name>/ workspaces) — but it is published in case the structure
or the modules are useful to others.
Requires Python 3.11+. Both packages are on PyPI (alpha — versions 0.0.x,
expect breakage):
Just the CLI:
pip install keel-cli
keel --helpCLI plus the Jira plugin:
pip install "keel-cli[jira]" # same as: pip install keel-cli keel-jira
keel plugin list # confirms the jira provider is registeredThird-party plugins work the same way:
pip install keel-cli some-author-keel-foo
keel plugin list # discovers their plugin via entry pointsDevelop against the source (clone + editable install, useful for contributing):
git clone https://github.com/andmatei/keel.git
cd keel
uv tool install --editable . --with-editable ./plugins/jiraOptionally install shell completion:
keel completion zsh --install # or bash, fish# Create a new project
keel new my-project -d "Build a thing"
# Add a deliverable (mini-project nested under the parent)
keel deliverable add my-project api -d "REST API surface"
# Record a decision without opening an editor
keel decision new "Pick the storage backend" --no-edit
# Advance the phase
keel phase designing
# Export design + decisions to a single markdown document
keel design export| Command | Purpose | Example |
|---|---|---|
new |
Create a new project workspace | keel new my-project -d "..." |
list |
List projects, optionally filtered | keel list --phase implementing |
list --active |
Show only projects with active milestones/tasks | keel list --active |
show |
Show a project's structure and state | keel show my-project |
show --brief |
Skip milestone/task summary (faster) | keel show my-project --brief |
phase |
Show or transition the lifecycle phase | keel phase implementing |
validate |
Check project structure and content | keel validate --content |
archive |
Soft-delete: remove worktrees, move to .archive/ |
keel archive my-project |
restore |
Restore a project from .archive/ |
keel restore my-project |
rename |
Rename directory, worktrees, and branch prefixes | keel rename old-name new-name |
show and most commands auto-detect the project from $PWD:
cd ~/projects/my-project/design && keel showSee keel <cmd> --help for the full flag surface (dry-run, --json, --yes, etc.).
Mini-projects nested under a parent project; each gets its own design, decisions, and code linkage. Deliverables are structurally identical to top-level projects.
| Command | Purpose | Example |
|---|---|---|
deliverable add |
Create a new deliverable | keel deliverable add my-project api -d "..." |
deliverable list |
List deliverables in a project | keel deliverable list my-project |
deliverable rename |
Rename a deliverable | keel deliverable rename my-project api rest-api |
deliverable rm |
Remove a deliverable and its artefacts | keel deliverable rm my-project api |
One file per decision, stored under decisions/ at the project or deliverable scope.
| Command | Purpose | Example |
|---|---|---|
decision new |
Create a decision record | keel decision new "Use Postgres" --no-edit |
decision list |
List decisions at the current scope | keel decision list |
decision show |
Print a decision file | keel decision show 2024-01-15-use-postgres |
decision rm |
Delete a decision file | keel decision rm 2024-01-15-use-postgres |
To supersede an existing decision: keel decision new "Switch to SQLite" --supersedes 2024-01-15-use-postgres.
Manifest-driven worktrees: source repos are declared in project.toml and can be materialized on any machine.
| Command | Purpose | Example |
|---|---|---|
code list |
List repos declared in the manifest | keel code list |
code status |
Show per-repo worktree status | keel code status |
code init |
Materialize worktrees from the manifest | keel code init --clone-missing |
code add |
Add a repo to the manifest and create its worktree | keel code add ~/some-repo |
code rm |
Remove a repo from the manifest and its worktree | keel code rm some-repo |
keel design export # design.md + decisions, stdout
keel design export --include-scope # prepend scope.md
keel design export --no-deliverables # parent project only
keel design export -o exported.md # write to file| Command | Purpose | Example |
|---|---|---|
migrate |
Migrate legacy Bash-CLI projects to manifest format | keel migrate my-project --apply |
completion |
Print or install shell completion | keel completion zsh --install |
Most commands accept the project name in one of three ways:
- Positional argument:
keel show foo,keel validate foo,keel archive foo,keel rename foo bar,keel migrate foo,keel design export foo. --project NAMEflag: works on every command.- CWD auto-detection: when run from inside
~/projects/<name>/..., the project is inferred. Pass--projectto override.
A few commands (phase, deliverable *, decision *, code *) do not have a positional project argument because their positional slot is taken by something else (a target phase, a deliverable name, etc.). Use --project NAME or CWD detection on those.
The CLI manages directories under $PROJECTS_DIR (default: ~/projects).
~/projects/
└── <project-name>/
├── README.md # auto-generated from project.toml + lifecycle
├── project.toml # manifest: source of truth for code linkage
├── scope.md
├── design.md
├── decisions/
│ └── YYYY-MM-DD-slug.md
├── plans/ # implementation plans (optional)
├── specs/ # design specs (optional)
├── .keel/ # tool state (managed by keel)
│ ├── phase # current phase + history
│ └── lifecycle.lock.toml # snapshot of the resolved lifecycle
├── code/ # git worktree (or code-<repo>/ if multiple)
└── deliverables/ # mini-projects nested under the parent
└── <name>/
├── README.md
├── project.toml
├── scope.md
├── design.md
├── decisions/
├── .keel/
└── code/
project.toml is the manifest source of truth for code linkage and deliverable
references. README.md is auto-generated from it.
If you used the older ~/projects/bin/project Bash CLI, keel migrate converts
legacy projects to manifest format:
# Preview what would change (dry-run by default)
keel migrate my-project
# Migrate one project
keel migrate my-project --apply
# Migrate every legacy project at once
keel migrate --all --applyIf you used keel before 0.0.3 (which kept everything under <project>/design/),
run:
keel migrate <project-name> --apply
# or for every project at once:
keel migrate --all --applyThe migrate command moves manifests to the unit root, creates .keel/, and
generates a README.md. It's idempotent — already-migrated projects are
left alone.
keel supports dependency-tracked milestones and tasks via DAG topology and ready-task queries:
keel milestone add m1 --title "Foundation"
keel task add t1 --milestone m1 --title "Set up"
keel task add t2 --milestone m1 --title "Deps" --depends-on t1
keel task next # print the topologically-first ready task
keel task next --start # start it immediately (planned → active, records branch)
keel task start t1 # records branch, planned → active
keel task done t1 # active → done
keel task list --ready # shows t2 (now unblocked)
keel task graph # ASCII tree of the DAG
keel show my-project # shows active tasks + ready-next hints (if milestones.toml exists)For ticketing integration, keel ships a TicketProvider Protocol and
entry-point discovery mechanism. Real providers ship as separate PyPI packages.
The first one is keel-jira (Atlassian Jira Cloud) and lives in this repo at
plugins/jira/. Install it with:
pip install "keel-cli[jira]" # equivalent to: pip install keel-cli keel-jira…then configure project.toml:
[extensions.ticketing]
provider = "jira"
parent_id = "PROJ-123"
[extensions.ticketing.jira]
url = "https://your-workspace.atlassian.net"
project_key = "PROJ"…and provide credentials via env vars (KEEL_JIRA_EMAIL, KEEL_JIRA_TOKEN).
See plugins/jira/README.md for the full setup.
keel plugin list shows the registered provider when installed; keel plugin doctor validates your config end-to-end.
First-party plugins keel maintains live under plugins/<name>/ in this
monorepo, each as its own Python package with its own pyproject.toml and
its own PyPI release. The repo is set up as a uv workspace,
so cross-plugin development works without pip install -e shenanigans.
External plugin authors don't need to live here. Write your-name/keel-foo
in your own GitHub repo, register a keel.ticket_providers (or
keel.commands, keel.event_listeners) entry point in your
pyproject.toml, publish to PyPI, and pip install keel-foo will plug
it in via discovery — no fork or PR to this repo required. See
plugins/jira/ as a working reference
implementation. The plugin contract is documented in
CONTRIBUTING.md and the rationale
for this dual layout (first-party in monorepo, third-party anywhere) is in
design/decisions/2026-05-04-monorepo-for-first-party-plugins.md.
By default, projects use a 6-phase lifecycle:
scoping → designing → poc → implementing → shipping → done. You can define
custom lifecycles as TOML files under ~/projects/.keel/lifecycles/<name>.toml
and pick one at project creation:
keel lifecycle init research # scaffold a starter TOML
$EDITOR ~/projects/.keel/lifecycles/research.toml
keel lifecycle validate ~/projects/.keel/lifecycles/research.toml
keel new my-paper -d "Q3 paper" --lifecycle research
keel phase --list-next # follows the FSM you definedInspect what's available:
keel lifecycle list
keel lifecycle show defaultA lifecycle is modelled as a finite-state machine: a set of named states,
allowed transitions between them, an initial state for new projects, and
terminal states. If a cancelled state is declared, every state with
cancellable = true (the default) gets an implicit <state> → cancelled
edge; lifecycles without a cancelled state simply don't support
cancellation.
Drop executable scripts in ~/projects/.keel/hooks/ (workspace-global)
or ~/projects/<name>/.keel/hooks/ (per-project) to react to keel
events:
keel hooks init # scaffold ~/projects/.keel/hooks/ with a README
cat > ~/projects/.keel/hooks/post-phase <<'EOF'
#!/usr/bin/env bash
# Args: <from_phase> <to_phase>
echo "$KEEL_PROJECT: $1 → $2"
EOF
chmod +x ~/projects/.keel/hooks/post-phaseSupported events (v0): new, phase, deliverable-add, decision-new,
archive (each with pre- and post- variants), plus post-only
restore and rename. Pre-hooks can block their command by exiting
non-zero; --no-verify overrides.
keel hooks list shows everything keel can fire and who's subscribed.
For plugin authors, the keel.event_listeners entry-point group provides
the same surface as a Python API — see CONTRIBUTING.md.
Plans 1–7 have all shipped, plus the first plugin (keel-jira):
- Plan 1 — Foundation: core modules +
new,list,show - Plan 2 — Deliverable, decision, phase:
deliverable,decision,phase - Plan 2.5 — Cleanup: small fixes and refactors
- Plan 3 — Validate, export, code, archive, rename:
validate,design export,code,archive,rename - Plan 4 — Migration, completion, cutover:
migrate,completion, workspace cutover + rename tokeel - Plan 4.5 — Pre-plan-5 cleanup: API surface stabilization, testing fixtures, error-code enum
- Plan 5 — Milestones, tasks, ticketing:
milestone,task, plugin protocol - Plan 5.1 — Simplification: helpers,
jira_id→ticket_id, dead code removal - Plan 5.2 — API consistency:
--dry-run, JSON shapes, help text - Plan 5.3 — Documentation truth: README/scope/plans cleanup, public-repo readiness
- Plan 5.4 — API completion + code health:
manifest/package split,get_milestone/get_task, public-API discipline - Plan 5.5 — Visibility wins:
keel showmilestone summary,task next,list --active,restore - Plan 6 — Extensibility hardening: phase preflights,
phase --list-next,keel plugingroup - Plan 7 — Customizable phase lifecycles: FSM-based lifecycles,
keel lifecyclegroup - Plugin: keel-jira (
plugins/jira/) — first realTicketProvider
See design/plans/ for the implementation plan documents and
design/decisions/ for the rationale behind major choices.
git clone https://github.com/andmatei/keel.git
cd keel
# Run tests
uv run --extra dev pytest
# Run from source without installing globally
uv run python -m keel --helpContributions welcome — see CONTRIBUTING.md for development setup, conventions, and the review process.
To report a security issue, see SECURITY.md.
MIT.