A way to organize agent work — a git-backed, markdown-first CLI for humans and AI agents.
Project description
Coga
Programming languages are the superstructure over machine opcode. Coga is the superstructure over text — the structure, reuse, and legibility that turn one-off prompts into a system you own.
Most tools say don't think — delegate the work and forget it. Coga's bet is the opposite:
Don't don't think. Think better.
Coga is git-backed Markdown + a small CLI that turns your intent into executable tickets and runs them on the coding agents you already use — Claude Code and Codex today, or any CLI agent (Gemini CLI, Goose, OpenHands, Devin, your own) in a few lines of config. It is not another autonomous agent — it sits above the agents you have and coordinates them across code, research, and operations alike.
The difference you feel: your context and corrections compound. Instead of
a flat CLAUDE.md that bloats and a session that forgets, Coga's prose is
decomposed, scoped, versioned, and reused — and every correction lands as a
pull request you merge, not a note that evaporates when the session ends. The
agent acts; you judge what's worth doing and what was wrong, and grant autonomy
one step at a time. It's all Markdown and Git on your disk — nothing hidden, no
lock-in — so you always see why an agent did what it did.
It's the substrate FastJVM uses to run the company. Try it now ↓ — then read the principles for why it's shaped this way.
Getting Started
1. Install the CLI (once).
pip install coga # or, for an isolated CLI install: pipx install coga
That puts coga on your PATH. Upgrade later with pip install --upgrade coga
(or pipx upgrade coga).
To work against a source checkout instead — for developing coga itself, or testing a PR branch before it lands:
git clone https://github.com/FastJVM/coga
cd coga
python -m pip install -e .
Keep that current with a periodic git pull && pip install -e ., or install a
packaged build straight from a branch or tag with pipx:
pipx install --force "git+https://github.com/FastJVM/coga.git@<branch-or-tag>"
Use main after a PR lands, or the PR branch name while testing it.
2. Set up a project. One coga/ per repo — the repo is the project.
For a fresh project:
mkdir ~/work/myproject && cd ~/work/myproject
git init
coga init --user "<your name>" # creates coga/ and records you as the owner
--user is required on a fresh init; it stamps who owns the work and seeds a
coga-build onboarding ticket.
3. Bootstrap with coga build.
coga build
coga build opens an onboarding chat: it asks one question, talks through what
you're building, writes a product-vision context, and drafts a starter batch of
tickets.
4. Launch your first ticket.
coga status # see the drafted tickets
coga launch <slug> # activate + start work on one
What you end up with: a coga/ in your repo, a product-vision context,
a handful of draft tickets, and one of them in progress with an agent.
From there:
coga chat— drop into an agent already oriented in your repo, no ticket.coga create <new-slug>— create a placeholder ticket you can come back to later.coga ticket— create a new ticket or edit an existing one and immediately begin authoring.coga ticket <new-slug>— create a new task and begin ticket authoring.coga ticket <existing-slug>— begin editing an existing ticket.coga status— every task and its state.coga --help— the full command surface.
First run vs. ongoing authoring.
coga buildis the one-time greenfield path (onboarding chat → a batch of draft tickets). Day-to-day you author tasks one at a time —coga ticketfor guided authoring,coga createfor a quick stub — see Task lifecycle andcoga ticketbelow.
Principles
Everything in Coga is a consequence of that one idea, and every consequence has a receipt — the feature that proves it.
| Principle | What it means | The feature that proves it |
|---|---|---|
| 1. Hackable | change anything, directly — no plugin fence | edit any markdown under coga/ → next coga launch uses it; the ~2-min correction loop (edit → commit → fixed) |
| 2. Agents do, humans think | offload everything mechanizable; humans spend attention on judgment | no webUI — the CLI + files are the whole surface; modes (interactive/auto vs script) and per-step assignee route each step to agent, script, or human |
| 3. Obvious | boring, standard, immediately understandable | the substrate is just markdown + Python + SKILL.md (the Claude Code / Codex format); no DB, no DSL |
| 4. Memory via PR | thinking compounds, human-gated, never opaque | Dream reads execution history and opens proposal PRs — propose, human disposes; the blackboard region (in ticket.md) = working memory, contexts/ = long-term |
| 5. Yours | own the substrate, swap the vendors | git-backed markdown, local, no cloud; claude ↔ codex interchangeable; SKILL.md is an open standard |
| 6. Fail loud | surface every failure | missing context/skill → raise; coga validate errors; failures never swallowed; coga panic hands back to a human |
The full canon is coga/contexts/coga/principles/SKILL.md;
the why essay is docs/vision.md; the market/strategy
read is docs/market-thesis.md. The rest of this
README is the reference for the features above — layout, task lifecycle, and
a one-screen entry per CLI command.
Operating notes
A few things worth knowing once you run more than one project:
-
One
coga/per repo. The repo is the project, so runcoga initin each operational repo. -
Vendored CLI. Each
coga initalso vendors a self-contained copy of the CLI into that repo'scoga/.coga/(its own venv) — there for bootstrap (a fresh clone runs withoutpip install) and as a known-good copy agents can target. -
Your global
cogaruns day-to-day. Coga deliberately doesn't auto-activate the vendored copy per-repo (no PATH munging, no rbenv-style re-exec) — with several repos that turns into a "which.coga/.venv/bin/cogawon the PATH race?" mess. The tradeoff: keep your global install reasonably current —pip install --upgrade coga(orgit pull && pip install -e .from a source checkout).
External CLI Tools
requirements.txt is only for Python packages. Coga also shells out to a few
human-installed command line tools. The canonical list — name, purpose, and
whether it's required — lives in src/coga/dependencies.py,
which is what coga init checks; this section mirrors it:
git— required (checked atcoga init). Coga stores state in the current git repo, vendors its CLI via a clone, and uses working-tree diffs as the review surface.python3.11+ — required for the Coga CLI and the vendored.coga/copy.gh— required (checked atcoga init) for GitHub PR workflows such as opening PRs and the merged-ticket autoclose sweep. Rungh auth loginonce installed.gh2.90.0+ withgh skill— optional, used by Coga-managed skill install/update.gh skillships as a public preview in recentgh; a freshcoga initon an olderghskips managed skills with a warning rather than failing.op— the 1Password CLI. Optional, checked at launch — not at init. Needed only when a ticket'ssecrets:entry has anop://vault/item/fieldreference: Coga resolves it live withop readat launch /coga secret get(runop signinfirst), failing loud naming the reference (never the value) ifopis missing when anop://secret is actually needed. Tickets that use onlyenv:VARreferences never invoke it.
So coga init fails loud (with install hints) if git or gh is missing,
surfacing a broken setup up front rather than at PR time. op is deliberately
left out of that check — forcing the 1Password CLI on everyone would be too
much — and is enforced at the point a ticket actually needs it.
Git/GitHub auth readiness
Coga does not run its own account system or store a GitHub token — it uses the standard tools you already have. PR and git-sync paths need:
- A configured git remote. Coga uses the remote named in
[git].remote(defaultorigin), not a hardcodedorigin— if yours is named differently, set that key incoga.toml. - Push access to that remote through your normal git setup. Transport is your choice:
- For SSH, keep your key loaded in
ssh-agent(ssh-add -l) and authorized on GitHub. - For HTTPS, let a git credential helper hold valid credentials. Coga never reads
GITHUB_TOKENor stores a PAT. ghinstalled and authenticated for the same GitHub host as the remote: rungh auth loginonce, orgh auth login --hostname <host>for GitHub Enterprise.
Check all of this before launching work with the opt-in preflight:
coga validate --check-github
It verifies the configured remote exists, probes push access with a
non-mutating git push --dry-run, and confirms gh is installed and
authenticated for the remote host - turning each failure into a direct setup
hint (set/fix the remote, load your SSH key or credential helper, run
gh auth login) instead of a surprise at PR time. Like --check-slack, it is
the only thing that makes coga validate touch the network; plain
coga validate, coga status, and coga show stay offline and read-only.
Multi-surface companies (e.g. an admin repo + a code repo) run multiple
coga/ side by side — coordinate them by giving each repo a
[notification.slack].webhook = "env:SLACK_WEBHOOK_URL" entry whose env var
resolves to the same channel webhook.
Layout
mycompany/
├── .git/
└── coga/
├── coga.toml # shared config (committed)
├── coga.local.toml # per-machine: user, paths, secrets (gitignored)
├── context.md # company context shared by every task
├── skills/ # reusable process knowledge
├── contexts/ # reusable domain knowledge
├── workflows/ # multi-step recipes
├── recurring/ # cron-like recurring task templates
├── log.md # repo-global append-only audit log (merge=union)
├── tasks/ # one dir per task (named by slug): a single ticket.md (body + blackboard region)
├── bootstrap/ # persistent bootstrap tickets (e.g. bootstrap/ticket)
└── .coga/ # vendored CLI + venv (gitignored)
├── src/coga/ # CLI source
├── .venv/ # self-contained venv
├── bin/coga # wrapper symlink
└── COGA_PIN # upstream commit SHA this .coga/ was vendored from
Task lifecycle
Coga now separates ticket authoring, queue approval, and launched work:
draft -> active -> in_progress -> done
\ /
-> paused
draftis unapproved work. Usecoga ticket "<title>"for the guided authoring interview, orcoga create "<title>"when you only want the raw files. You can also runcoga ticket "<new-slug>"to create a ticket and begin authoring, orcoga ticket "<existing-slug>"to edit a ticket.activeis approved and queued. Humans can still refine active tickets withcoga ticket <slug>before work starts.in_progressis launched work.coga launch <slug>moves an active ticket into this state, andcoga bump <slug>only moves workflow steps from here.pausedpreserves the current workflow step while taking the task out of execution.doneclears the workflow step and closes the task.
The normal path for a new ticket is:
# Opens a guided authoring chat — the agent greets first and writes the draft.
coga ticket "Add retry to webhook handler"
# After you finish authoring and exit, start the work:
coga launch add-retry-to-webhook-handler
# The agent works, writes the blackboard, and bumps workflow steps.
coga mark done add-retry-to-webhook-handler # finish on the final step
Commands
coga init [PATH] [--user <name>]
Create coga/ inside PATH (default: the current dir). A fresh init
requires --user <name> (the name tickets and agents refer to you by), and
PATH must already be a git repo — coga is git-backed, so init commits the
new create into your repo; run git init there first if it isn't one. Copies
templates from the installed Coga package, vendors the CLI into .coga/,
creates a self-contained venv, writes a starter coga.local.toml, and
auto-stages and commits the new create (push is left to you).
coga init --user marc # fresh init in the current dir (PATH defaults to .)
coga init mycompany --user marc # or: create + init a new mycompany/ dir
If ~/.local/bin is on your PATH, init also drops a ~/.local/bin/coga
symlink so the vendored copy is usable from any cwd in a new shell.
coga uninstall [--yes] [--purge]
Remove the Coga footprint from the current repo: coga/, the agent skill
symlinks in .claude/ and .codex/, unmodified Coga orientation guides
(CLAUDE.md / AGENTS.md), the coga-managed .gitignore block, and the
~/.local/bin/coga shim if it points back into this repo.
Uninstall prints a removal plan and asks for confirmation; --yes skips the
prompt for scripts. Edited CLAUDE.md / AGENTS.md files are renamed to
<name>.coga-bak rather than deleted. By default the global coga
package is left installed and the command prints the exact pipx uninstall coga / pip uninstall coga commands. Add --purge to run the global
uninstall too.
Batteries and skill discovery. The installed Coga package carries bundled
skills, contexts, hooks, and bootstrap tickets as package resources. pip install
puts those resources in the wheel; coga init does not
materialize a repo-local coga/bootstrap/ mirror. Runtime resolution reads
the package resources directly after checking project-local coga/skills/,
coga/contexts/, and coga/workflows/, so local overrides still win when
they define the same ref.
Init also builds an ignored coga/.agent-skills/ view that merges
project-local skills with bundled bootstrap skills, then wires that view into
the project-level skill dirs of the agents that follow the SKILL.md standard:
- Claude Code — symlinked into
.claude/skills/coga/. - Codex — symlinked into
.codex/skills/coga/.
That covers our two daily drivers. Other agents (e.g. OpenCode) don't have a
matching project-level skill convention yet — point them at
coga/.agent-skills/ yourself if you use them. If init finds something
non-directory in the way (e.g. an empty .codex sentinel file from an older
setup), it skips that agent and prints what to clear.
coga create "<title>"
Create a new raw draft ticket under coga/tasks/<slug>/ (slug
derived from the title). Does not spawn an agent
and does not run the guided authoring interview. The new ticket is empty
— title, owner, mode, and timestamp set; workflow, contexts, assignee, and
description still need to be filled in. If the slug already exists, the new
task gets -2, -3, … appended.
coga create "Add retry to webhook handler"
coga create "Nightly cleanup" --mode auto
coga ticket [<title-or-slug>] [--agent <type>]
Run the guided ticket-authoring skill. This is the normal path when you want Coga to ask clarifying questions, choose a workflow/context/assignee shape, and create or edit the ticket before work starts.
coga ticket # no target — agent greets and asks: new ticket, or edit an existing one?
coga ticket "Add retry to webhook handler" # new draft, then a guided authoring chat (agent greets first)
coga ticket add-retry # edit an existing ticket (any status)
coga ticket add-retry --agent codex # same, but pick the authoring agent
coga ticket edits a ticket at any lifecycle status. Editing leaves the
status unchanged; for an in_progress (in flight) or done (finished)
ticket it prints a heads-up first, since revising those is unusual.
For the standard claude and codex CLIs, coga ticket passes the
composed authoring prompt as system/developer context. That keeps the first
human exchange available for the agent session title, which makes later
resume lists easier to scan. Set [agents.<type>].discussion to override
the argv template for another agent.
The usual boot sequence is:
coga ticket "<title>"— create and fill the draft.- Review or edit the ticket.
coga launch <slug>— approve, mark itin_progress, and spawn the agent. Launching adraftactivates it inline, so there is no separatecoga mark activestep.
Programmatic callers (e.g. coga recurring) call create_task() in
coga.create directly with the full keyword surface.
coga mark <state> <slug> [--message "..."]
Change a ticket's status. Three subcommands:
coga mark active add-retry # draft / paused → active.
coga mark paused add-retry # active / in_progress → paused. Preserves step.
coga mark done add-retry # active / in_progress → done. Clears step.
coga mark active refuses a ticket with no workflow — a workflow-less
ticket has no steps and can't be moved by coga bump. A bare-string
workflow: ref is frozen into its snapshot on activation. coga validate
backs the same rule, erroring on a workflow-less active/in_progress/paused
ticket: a workflow is mandatory everywhere except draft. (Recurring and
retire tasks create straight to active, but they are not workflow-less:
a template that declares no workflow, and every retire task, creates with
the one-step direct/body workflow that runs the ticket body directly.)
coga launch owns the active → in_progress start transition, and
activates a draft inline as it starts — so coga mark active is not a
required pre-launch step. Reach for it only to approve or queue a ticket you
don't want to launch yet. coga bump no longer marks final-step tickets done.
--message piggy-backs an FYI onto the state-transition Slack
broadcast — one post instead of two.
coga recurring
Scan coga/recurring/ and launch the templates that are due. Coga keeps
one live task per template: a generated task is identified by the stable
group-qualified ref recurring/<name> (coga/tasks/recurring/<name>/),
and if it is already active or orphaned in_progress, that one is
launched/resumed and no duplicate is created. Only when none is live does
coga recurring get-or-create the current run at that stable path and update
the template blackboard's last_serviced_period high-water mark. It prints a
scan table, then launches the due ones sequentially: orphaned in_progress
resumes first (a dead sweep's frozen run, picked back up from its step), then
fresh launches, each group most-overdue first. done and paused tasks are
left alone. A stuck in_progress run therefore defers the next period
until it reaches done/paused — finish the in-flight run before piling
another on, and it stays visible in coga status meanwhile. During a bare
recurring sweep, if a launched task returns still active, in_progress, or
otherwise unfinished, the sweep stops before launching the next due task.
Only the current period is considered; coga recurring never chases missed
periods, so a template runs at most once per period no matter how long since
the last invocation. Dedup after a completed run is deleted comes from
last_serviced_period >= current period_key in the template blackboard. The
the repo-global coga/log.md stays append-only human history; it is not parsed for dedup.
Recurring creating goes through create_task() in coga.create
directly with the template's full frontmatter. Recurring tasks are created
straight to active — they can't go through the coga mark active gate — so
they must carry a workflow to be valid and bumpable: a template that declares
its own keeps it, and a workflow-less one (e.g. Dream) creates with the
one-step direct/body workflow, which runs the template body's ordered phases
directly.
Coga does not ship a scheduler entry point in v1. Naming a command
recurring does not install or schedule anything — coga recurring only
runs when you invoke it directly.
--interactive launches every due task in interactive mode for that run,
even ones whose template says mode: auto — the debug knob for stepping
through a recurring run in an attended terminal. It threads coga launch --mode interactive through and rewrites no ticket files.
coga recurring launch <name>
Create one named recurring template now, ignoring its schedule, and launch
it. The task ref is recurring/<name>, so a manual launch and a bare
coga recurring run converge on one stable task directory; an orphaned
in_progress run is resumed rather than duplicated. This is the on-demand
entry point behind aliases like coga dream. --interactive runs it in
interactive mode even if the template says mode: auto — handy for debugging
one template by hand.
coga dream
Run Coga's generic cleanup pass now. dream is an alias for
coga recurring launch dream: it creates the recurring/dream/
recurring task and launches it. The instantiated task ref is
recurring/dream, shared with the scheduled run — running coga dream
mid-week reuses that task rather than creating a second one.
Dream and REM
Dream is Coga's generic ticket cleanup pass for one coga/. It ships as a
recurring task template, coga/recurring/dream/: a weekly coga recurring run creates and launches it when its schedule is due, and the
coga dream alias creates and launches it on demand. A Dream task scans all tickets, runs fixed Coga
housekeeping skills such as validate-drift and retro/done-ticket, proposes
cleanup, writes results to that run's blackboard, and leaves a human-reviewable
trail. Retro work is batched for done tickets: Dream loads the context/skill
corpus once, processes every eligible done ticket in a single run with a
running knowledge delta, and opens one small PR per coherent theme (at most
five source tickets each) only when durable knowledge changed.
REM is repo/user-specific recurring maintenance. It is opt-in user space: copy
the inert coga/recurring/_rem/ template, give it a schedule and
workflow, and define the operational checks that matter to that repo. Stale
branch cleanup belongs in a dev maintenance loop, not in Dream's generic ticket
cleanup pass.
coga skill
Manage project-local skills under coga/skills/ without inventing a second
package manager. GitHub-backed installs and updates delegate to GitHub CLI's
public preview gh skill command; Coga adds exact removal, URL-backed
provenance, local-adaptation checks, and a PR-ready update summary for Dream.
Bundled bootstrap skills are package-backed batteries: coga skill status
shows them, but coga skill update --all skips them and points you at the
package update path (pip install --upgrade coga).
coga skill install owner/repo skill-name
coga skill install-url https://example.com/skill.zip
coga skill install-local ./downloaded-skill
coga skill update skill-name
coga skill update --all
coga skill update --all --pr --verify "coga validate --json"
coga skill remove skill-name
coga skill status --check
URL-backed installs are downloaded into a temporary directory, validated for a
SKILL.md, installed through gh skill install --from-local, then recorded in
coga/skills/<name>/.coga-source.json with the original URL and content
digests. URL-backed updates re-fetch that source and skip locally adapted
skills instead of overwriting them. Removal is exact-name only and leaves a
normal git delete for review. To customize a bundled skill, copy it to the same
ref under coga/skills/; the local copy shadows the bundled one and becomes
your repo-owned skill.
coga launch <target>
Compose every relevant file for a task — rules, project context, ticket, attached contexts, current workflow step, frozen skills — into a single prompt and start the configured agent against it.
launch accepts status: active or status: in_progress directly. A
draft / paused / done ticket is activated inline first — typing coga launch is the readiness signal, so it activates the ticket for you
(re-activating a done ticket restarts its workflow at step 1) rather
than refusing. A ticket that can't be activated — no workflow, or an empty
required extension field — fails loud with the same remedy mark active
gives. Launching an active ticket then marks it in_progress; launching an
already-in_progress ticket resumes it.
coga launch add-retry-to-webhook-handler # full slug
coga launch add-retry # any unique prefix works
coga launch add-retry --agent codex # one-off agent override
coga launch add-retry --mode interactive # debug: run an auto ticket interactively
coga launch add-retry --prompt-report # show prompt layer sizes, no launch
coga launch bootstrap/orient # stateless launch target → run a skill
coga launch bootstrap/orient --agent codex # choose a bootstrap agent
Tasks are addressed by slug — there is no numeric ID. Pass any unique prefix (git-short-SHA-style) and ambiguous prefixes error out with the matches listed.
The agent type comes from the ticket's assignee (e.g. claude), which
names an [agents.<type>] block in coga.toml directly. Pass
--agent <type> to override for this launch only; normal task launches do
not rewrite the ticket's assignee. Bootstrap tickets use the same flag for
one-off sessions, so coga chat --agent codex can open the orient ticket
with Codex while coga chat --agent claude opens it with Claude.
Discussion tickets (bootstrap/orient, bootstrap/ticket) use built-in
discussion templates for the standard claude and codex CLIs, or the
selected agent's optional discussion = "...{prompt}..." override. In
interactive mode the Coga prompt is context and the first human ask can name
the session. Ordinary task launches keep passing the composed prompt
positionally.
For workflow-bound interactive/auto tasks, one coga launch can run multiple
agent-owned steps. After each clean agent exit, Coga re-reads the ticket and
continues in a fresh agent process only when the task is still in_progress, the step
advanced, the new current step has a skill:, and the concrete assignee:
did not change. It stops at human/no-skill steps, assignee handoffs, done or
paused tasks, no-progress exits, and panic/non-zero exits.
Use --prompt-report to inspect the composed prompt without checking for a
TTY or spawning an agent. The report lists each included layer, exact
context/skill refs, bytes, and approximate token counts. The token estimate is
intentionally dependency-light (characters / 4), so use it to catch prompt
bloat and compare tasks, not to predict exact provider billing.
--mode <interactive|auto> overrides the ticket's mode: for one launch —
the debug knob for stepping through a mode: auto ticket in an attended
terminal (and vice versa). It is ephemeral: the ticket file is never
rewritten, and both the spawned command and the composed mode-specific prompt
block follow the override. It is rejected for mode: script tasks, which
compose no agent prompt. auto is temporarily disabled (until streaming
lands): coga launch --mode auto is refused, so today the override is only
useful for running an auto ticket interactively.
bootstrap/<name> tickets are stateless re-entry points for skills.
Concurrent launches are safe — they have no status, no log of state changes,
and no lock. They are package-backed resources, not files materialized into
the repo. Don't add custom bootstrap tickets under coga/bootstrap/; write
your own launch wrappers elsewhere.
For autonomous runs, an operator can opt an agent into skipping its CLI's
per-command permission/approval prompts with a partial [agents.<name>]
table in coga.local.toml:
[agents.claude]
skip_permissions = "auto"
skip_permissions_argv = "--dangerously-skip-permissions"
The policy is machine-local by design — committing either key to shared
coga.toml fails config load, so a repo can never set a dangerous default
for everyone. It applies only to normal task tickets in effective
mode: auto: interactive launches, bootstrap/discussion tickets (coga chat,
coga ticket), and script tasks keep today's behavior. The argv is one
string (shlex-split) inserted between the session-name argv and the auto
argv/prompt — e.g. codex --dangerously-bypass-approvals-and-sandbox exec <prompt> — and supervised chains re-resolve it per step for whichever agent
the step rotated to. skip_permissions = "auto" with no configured
skip_permissions_argv fails the launch loud before any agent spawns.
Verify the flags against your installed CLIs before enabling.
coga status
Show the live tasks in the repo — draft, active, in_progress, and paused.
One line per task. Bootstrap tickets have no status and don't appear. done
tickets are hidden by default (the listing ends with a (N done tasks hidden — use --all to show) note); add --all (-a) to include them.
coga status
coga status --all
coga show <slug>
Print a task's ticket.md (body + blackboard region) and its history from the repo-global coga/log.md to the
terminal, rendered as markdown. Same prefix matching as bump/launch.
Bootstrap tickets show only ticket.md. For grep/pipe use, read the
files directly — show is for human eyes.
coga show add-retry
coga show bootstrap/orient
coga bump <slug> [--message "..."] [--to N | --backward]
Move a workflow-bound task step. By default this advances one step. A human
outside a supervised launch may rewind to an earlier step number with
--to <step-number> or one step back with --backward. Each move updates
the ticket's step: field and appends a log entry. The workflow itself is
frozen into the ticket at create time, so step semantics don't drift mid-task.
bump no longer finishes tickets. Bumping past the last step is an
error pointing you at coga mark done <slug>. Bumping a ticket
without a workflow is the same error — mark done is how you finish
those.
--message piggy-backs an FYI onto the state-transition Slack
broadcast — useful for "advanced to (pr) — PR opened: " without
firing a second message.
coga bump add-retry # advance one step
coga bump add-retry --to 1 # human rewind to step 1
coga bump add-retry --backward # human rewind one step
coga bump add-retry --message "PR: https://example/142"
coga mark done add-retry # finish (on final step, or no workflow)
Auto-closing merged tickets
The autoclose-merged recurring sweep walks active/in-progress tickets,
finds ones on their final workflow step (or with no workflow) whose
blackboard ## Dev section names a merged PR, and auto-bumps them to
done. It looks the PR up via gh pr view and posts to Slack with a
distinct auto-bumped on merge of PR #<N> line.
This daily sweep is the sole trigger. There is no manual automerge
command and no post-merge git hook, and coga status does not trigger
it (status stays a strictly read-only view — no network, no state mutation
as a side effect of rendering). Tradeoff: a ticket merged today auto-closes
on the next sweep (≤24h). To close one immediately, run coga mark done.
coga delete <slug>
Throw away an abandoned ticket. Removes the whole task directory —
ticket, blackboard, log. Recovery is via git restore; the git
history is the audit trail, no Slack broadcast.
Bootstrap tickets aren't user-deletable — they're package-backed batteries managed by the installed Coga package.
coga delete add-retry
coga retire <slug>
Wrap up a done ticket: create a one-shot retire-<slug> task whose
body invokes the retro/done-ticket skill against the named ticket. retire
keeps the single-ticket path; Dream owns batched Retro runs. The retro skill
always deletes the processed source task in a reviewable PR. When it extracts
new durable knowledge, that PR records the ## Retro marker, edits the
knowledge base, and deletes the source task directory together. When no new
durable knowledge exists, Retro records result: no-new-durable-knowledge and
deletes the ticket in a delete-only prune PR. The retire task creates
straight to active carrying the one-step direct/body workflow (which runs
its body directly) and is launched unless --no-launch is passed.
Refuses if the target task is not status: done. Use coga delete
for an abandoned ticket where retro has nothing to extract. Branch
hygiene (pruning the merged feature branch, sweeping stale branches)
belongs in a Dream worker, not here.
coga retire add-retry # interactive mode (the default)
coga retire add-retry --mode auto # one-shot autonomous Retro run
coga retire add-retry --no-launch # create without launching
retire runs interactively by default so the Retro pass writes live
console output. --mode auto is temporarily disabled (until streaming
lands) and is currently refused; it is intended to run the pass as a one-shot
headless claude -p session whose output is buffered to the task log.
coga panic --task <slug> --reason "..."
The agent gives up. Writes a blocker entry to the ticket and posts a
notification naming the owner so a human (or another agent) can pick it up.
Coga has no task lock to release — the ticket's status is the only signal.
Intended for the agent to call when it's truly stuck — not for routine
handoffs.
coga panic --task add-retry --reason "Auth flow needs prod creds I don't have"
coga slack --task <slug> --message "..."
Manual broadcast escape hatch. Posts a short FYI through the configured
notification channel without changing task state — for events that don't
coincide with a bump/panic/launch transition (e.g. a human announcing they
hand-edited a ticket, or "tests still flaky" mid-step). For FYIs that do
fire alongside a state change, prefer bump --message.
coga slack --task add-retry --message "Reassigned to pierre"
Notifications — the team sync point
Notifications are optional on first run. A fresh coga init selects no
channels ([notification] channels = []), so you can draft, launch, and
bump your first task without configuring anything. Turn them on when you
start coordinating with other people.
Slack is the first backend. Once selected, urgent and manual events post to the
channel configured by [notification.slack].webhook; outcome events may spool
into the daily digest before posting. Relaunching an already-in_progress
ticket does not post — that isn't a new state change. Once Slack is selected,
failures are loud: if Slack is unreachable or the webhook isn't set, the
command exits non-zero rather than silently dropping the message — a missed FYI
becomes a stale mental model on the human side, and that's worse than a noisy
retry.
Opt in (team). Create a Slack incoming webhook for the channel, select the Slack channel, keep the shared config pointed at an env var, and export the URL locally; older or minimal repos add the same block:
[notification]
channels = ["slack"]
[notification.slack]
webhook = "env:SLACK_WEBHOOK_URL"
Then set the env var in your shell rc:
export SLACK_WEBHOOK_URL="https://hooks.slack.com/services/..."
Coga reads the canonical webhook from [notification.slack].webhook. Legacy
[slack].webhook and a bare SLACK_WEBHOOK_URL remain deprecated
compatibility fallbacks. The URL is a bearer token — anyone holding it can post
to that channel as the app. Don't commit a literal URL; don't paste it in
tickets or logs. Rotate via the Slack app's webhook page if it ever leaks. For
multi-user setups, commit the safe env: reference and have each member export
the same URL locally; coga.local.toml may override
[notification.slack].webhook for a machine-specific channel.
Run coga validate --check-slack to probe the webhook (POSTs an
empty-text payload that Slack rejects without posting to the channel)
so a dead URL surfaces at config time, not at first bump. Failures
during runtime posts also append a line (tagged by task ref) to the repo-global coga/log.md so
daemon / cron / launched-script runs leave a recoverable trace.
Temporarily silence a repo that already opted in (solo dev / CI / dry
runs). To run without posts but keep the Slack channel selected, set in
coga.local.toml:
[notification.slack]
enabled = false
With enabled = false, every Slack-channel call is suppressed to stderr and
nothing crashes. Treat this as an exit from the sync loop, not a
default — once you're working with another person, turn it back on. (If you
never opted in, you don't need this — a fresh repo posts nothing already.)
coga digest
Post the spooled outcome events — done tickets and other merged commits — to
the notification channel, then advance the digest state so the same events
aren't posted twice. This is the batch counterpart to the urgent events that
post immediately: outcome events spool here and go out together (a recurring
digest template drives it on a schedule).
coga digest # post the spool; stay silent if it's empty (default)
coga digest --announce-empty # post a one-line "nothing to report" note instead
coga validate
Static diagnostic for the repo and config: it checks task files, frontmatter
schema, workflow refs, and config, and exits non-zero if anything is an error.
Read-only and offline by default — the two --check-* probes are the only
options that touch the network.
coga validate # validate the whole repo
coga validate --json # machine-readable output
coga validate --task add-retry # validate one task (skips Slack + idle checks)
coga validate --fix # conservative safe repairs (add a missing blackboard fence)
coga validate --check-slack # probe the Slack webhook
coga validate --check-github # probe git/GitHub auth readiness
--idle-hours and --max-blackboard-kb tune the thresholds for the idle-task
and blackboard-bloat warnings.
coga --version
Print the coga package version, plus — when run from inside a coga/ —
the upstream commit SHA .coga/ was vendored from. Useful for "is this
fixed in your copy?" questions.
$ coga --version
coga 0.2.0
vendored from upstream 61fa3ddb6571 (full: 61fa3ddb6571339237c701424c5675c2c615bdba)
Aliases
Sugar for the often-used commands. The [aliases] table in coga.toml
maps a one-word name to an expanded coga command; positional args after
the alias name forward to the expansion. Default aliases shipped by
coga init:
[aliases]
chat = "launch bootstrap/orient"
dream = "recurring launch dream"
build = "launch coga-build"
# Add per-agent shortcuts once those types are declared in `[agents.*]`:
# claude = "launch bootstrap/orient --agent claude"
# codex = "launch bootstrap/orient --agent codex"
draft, ticket, and create are built-in commands, not aliases. Add your
own aliases for bootstrap tickets or skills you launch often; running an alias prints the
expansion to stderr —
→ coga launch bootstrap/orient — so the indirection is visible.
Rules, checked at config load — fail loud, not silent:
- Alias names cannot collide with built-in commands.
- The first token of the expansion must be a known built-in.
- Aliases are pass-through only. Arguments and flags after the alias name are forwarded to the expanded command.
Development
Install from source as in Getting Started, then:
python -m pytest # run the test suite
coga validate --json # validate the bundled example/ fixture
coga validate --fix # add a missing blackboard fence only
The dogfood coga/ for this very repo lives at coga/. Tasks tracked
under coga/tasks/ are real — they're how we drive work on coga itself.
License
Coga is open source under AGPL-3.0-or-later. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file coga-0.2.0.tar.gz.
File metadata
- Download URL: coga-0.2.0.tar.gz
- Upload date:
- Size: 1.1 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
20e1951cfbf42a70489580c58be54fbd7f90ca813ece800d53519e05099b44c2
|
|
| MD5 |
6c0abb1ae1b52767e9500abfb2771893
|
|
| BLAKE2b-256 |
1fc3517cd1c2b87970b6dfade2f332b8c3e4c877e0bf708d2bf60ded9da03199
|
Provenance
The following attestation bundles were made for coga-0.2.0.tar.gz:
Publisher:
release.yml on FastJVM/coga
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
coga-0.2.0.tar.gz -
Subject digest:
20e1951cfbf42a70489580c58be54fbd7f90ca813ece800d53519e05099b44c2 - Sigstore transparency entry: 1983729730
- Sigstore integration time:
-
Permalink:
FastJVM/coga@117777752bce0977fd9930fd0c242db824f2da44 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/FastJVM
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@117777752bce0977fd9930fd0c242db824f2da44 -
Trigger Event:
release
-
Statement type:
File details
Details for the file coga-0.2.0-py3-none-any.whl.
File metadata
- Download URL: coga-0.2.0-py3-none-any.whl
- Upload date:
- Size: 438.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f764e3939a7369542eb0395ee298f6d0e683c1c1d6d83dd19e3b1852c9721c0c
|
|
| MD5 |
39130d591fd88cc66cf982b552ea31a4
|
|
| BLAKE2b-256 |
38d962761664b28fa1ad76d01d237f9068fddce7598d8ad681c170dd9a0b3fc4
|
Provenance
The following attestation bundles were made for coga-0.2.0-py3-none-any.whl:
Publisher:
release.yml on FastJVM/coga
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
coga-0.2.0-py3-none-any.whl -
Subject digest:
f764e3939a7369542eb0395ee298f6d0e683c1c1d6d83dd19e3b1852c9721c0c - Sigstore transparency entry: 1983729847
- Sigstore integration time:
-
Permalink:
FastJVM/coga@117777752bce0977fd9930fd0c242db824f2da44 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/FastJVM
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@117777752bce0977fd9930fd0c242db824f2da44 -
Trigger Event:
release
-
Statement type: