feat(mm-worktree-task): run from any planner-backed repo — cp_cli dir + context resolution (--context, MM_WORKTREE_TASK_CP_*), empty-docs guard, tests + spec

cp_cli lives in agent-runtimes; previously every planner call ran 'python3 -m lib.cp_cli' from the current repo with no --context, so the script only worked inside agent-runtimes. The context now resolves from ~/.cpconfig by matching project_id to the repo directory name. Verified live from hatchmate (item + parent + documents fetched via the hatchmate context).

Claude-Session: https://claude.ai/code/session_01JgJbWzVJioVznVTFyQFEfx
This commit is contained in:
Paul O'Reilly
2026-09-11 16:27:08 +12:00
parent 9488335713
commit f50561ea78
9 changed files with 969 additions and 10 deletions

View File

@@ -26,11 +26,20 @@ mm-worktree-task <item-uuid> [OPTIONS]
| `--docs all\|recent\|none` | `recent` | Which item documents to bundle. `recent` = documents updated in the last 14 days; falls back to `all` if the CLI does not expose update dates. The full list of document names is always included in the prompt regardless of which are bundled. |
| `--tools "<list>"` | — | Extra `--allowedTools` entries merged into the default allowlist (comma-separated). |
| `--timeout <secs>` | `1800` | Max seconds the sub-session may run (`timeout <secs>` wrapper). |
| `--context <name>` | resolved (see §0) | `cp-cli --context` used for every planner call. |
| `--upload` | off | After the sub-session exits, upload its report as a document on the item and patch `flow_state` to `impl-complete`. Implemented only in combination with `--collect` (see Behaviour §6) since launch is detached. |
| `--collect` | off | Don't launch. Print `git log --oneline main..HEAD`, `git diff --stat main...HEAD`, and the report file for an already-run slug's worktree. |
| `--dryrun`, `-n` | off | Assemble everything, print the bundle manifest, the full prompt, and the exact command that would run. Create nothing: no worktree, no branch, no launch, no bundle/prompt files left outside the report dir's dryrun preview. |
| `--help`, `-h` | — | Show usage and exit 0. |
### Environment
| Var | Effect |
|-----|--------|
| `MM_WORKTREE_TASK_CP_CLI_DIR=<dir>` | Checkout holding `lib/cp_cli.py` to run the planner CLI from. Default resolution: this repo if it has `lib/cp_cli.py`, else `~/dev/claude/projects/agent-runtimes`. |
| `MM_WORKTREE_TASK_CP_CONTEXT=<name>` | Same as `--context` (the flag wins). |
| `MM_WORKTREE_TASK_CPCONFIG=<file>` | cpconfig file used for context auto-resolution (default `~/.cpconfig`). |
### Environment overrides (for testing)
| Var | Effect |
@@ -40,10 +49,14 @@ mm-worktree-task <item-uuid> [OPTIONS]
## Behaviour
0. **Planner CLI resolution** — the script runs from *any* planner-backed repo; `cp_cli` lives in agent-runtimes.
- CLI dir: `MM_WORKTREE_TASK_CP_CLI_DIR`, else `<repo>/lib/cp_cli.py` (or the `lib/cp_cli/` package) if present, else `~/dev/claude/projects/agent-runtimes`. Only fails (exit 1, "cannot find lib/cp_cli.py") when a planner call is actually made — fake-item runs and `--brief`-only runs never need it.
- Context: `--context`, else `MM_WORKTREE_TASK_CP_CONTEXT`, else the context in `~/.cpconfig` (or `MM_WORKTREE_TASK_CPCONFIG`) whose `project_id` equals the repo directory name, else none (cp-cli's `current_context`).
- Every planner call is `cd <cli-dir> && python3 -m lib.cp_cli [--context <ctx>] …`. `--dryrun` prints both resolved values (`cp-cli from:`, `cp-cli ctx:`).
1. Determine repo root: `git rev-parse --show-toplevel` from cwd. Fail (exit 1) if not inside a git repository.
2. Resolve `<slug>`: `--slug` if given, else first 8 chars of `<item-uuid>` (or required alongside `--brief` when no uuid is given).
3. Resolve report dir: `${XDG_RUNTIME_DIR:-/tmp}/mm-worktree-task/<repo-name>/`. Create it (unless `--dryrun`, which only prints the path).
4. **Fetch the item** (run from repo root, needs the repo's `lib`), unless `MM_WORKTREE_TASK_FAKE_ITEM` is set:
4. **Fetch the item** (through the §0 wrapper), unless `MM_WORKTREE_TASK_FAKE_ITEM` is set:
- `python3 -m lib.cp_cli planner items get <uuid> --json` → fields used: `id`, `title`, `label`, `state`, `flow_state`, `body`, `tags`, `parent_id`, `related_items`.
- `python3 -m lib.cp_cli planner items documents list <uuid> --json` → document names (+ `updated_at` if present).
- For each document selected by `--docs`: `python3 -m lib.cp_cli planner items documents get <uuid> <name>` (fake dir equivalent: read `<dir>/<name>`).