diff --git a/.reflection-state.json b/.reflection-state.json index 40defce..fdf3d7b 100644 --- a/.reflection-state.json +++ b/.reflection-state.json @@ -1,10 +1,13 @@ { "version": 1, - "last_run": "2026-03-24T10:36:46Z", + "last_run": "2026-05-11T09:48:44Z", "processed": { "log/2026-03-17.095436.md": "253f75f7ae43fb2547cd3377afb8d3b2", "log/2026-03-17.105314.md": "9c81013e9ab545527a363f525eff93e3", "log/2026-03-17.110414.md": "5ad1a211737b604f33e31e9ce754be74", - "log/2026-03-23.123551.md": "6c9560f5348b0625abb5c4e52b1e0464" + "log/2026-03-23.123551.md": "6c9560f5348b0625abb5c4e52b1e0464", + "log/2026-04-12.002212.md": "7bc1c971149fdd04f528e432fd8a1e16", + "log/2026-04-12.003747.md": "17fafffbfc6735c777b2fc71fdd463d2", + "log/2026-04-24.075643.md": "894d58fc7e390bb6af39e71d5219cc3f" } } diff --git a/scripts/__pycache__/idle-draftcpython-312.pyc b/scripts/__pycache__/idle-draftcpython-312.pyc new file mode 100644 index 0000000..fd3b322 Binary files /dev/null and b/scripts/__pycache__/idle-draftcpython-312.pyc differ diff --git a/scripts/mm-worktree-task b/scripts/mm-worktree-task index 77785ca..e9f061a 100755 --- a/scripts/mm-worktree-task +++ b/scripts/mm-worktree-task @@ -31,11 +31,21 @@ Options: --docs all|recent|none Which item documents to bundle (default: recent) --tools "" Extra --allowedTools entries (comma-separated) --timeout Sub-session timeout (default: 1800) + --context cp-cli planner context (default: the ~/.cpconfig context + whose project_id equals the repo name, else cp-cli's own + current_context) --upload After --collect, upload the report + mark impl-complete --collect Don't launch; print status for an already-run slug --dryrun, -n Assemble everything, print it, create nothing --help, -h Show this help +Environment: + MM_WORKTREE_TASK_CP_CLI_DIR= checkout holding lib/cp_cli (default: the + current repo if it has one, else + ~/dev/claude/projects/agent-runtimes) + MM_WORKTREE_TASK_CP_CONTEXT= same as --context + MM_WORKTREE_TASK_CPCONFIG= cpconfig to resolve the context from + (default: ~/.cpconfig) Env overrides (for testing): MM_WORKTREE_TASK_FAKE_ITEM= fake `planner items get --json` output MM_WORKTREE_TASK_FAKE_DOCS_DIR= fake docs: /list.json + / @@ -52,6 +62,7 @@ TIMEOUT=1800 UPLOAD=0 COLLECT=0 DRYRUN=0 +CP_CONTEXT="${MM_WORKTREE_TASK_CP_CONTEXT:-}" while [[ $# -gt 0 ]]; do case "$1" in @@ -61,6 +72,7 @@ while [[ $# -gt 0 ]]; do --docs) DOCS="$2"; shift 2 ;; --tools) EXTRA_TOOLS="$2"; shift 2 ;; --timeout) TIMEOUT="$2"; shift 2 ;; + --context) CP_CONTEXT="$2"; shift 2 ;; --upload) UPLOAD=1; shift ;; --collect) COLLECT=1; shift ;; --dryrun|-n) DRYRUN=1; shift ;; @@ -97,6 +109,50 @@ if ! REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)"; then fi REPO_NAME="$(basename "$REPO_ROOT")" +# --- planner CLI: where it runs from, and which context it talks to --- +# The script is used from any planner-backed project; cp_cli itself lives in +# agent-runtimes. Resolve the checkout once and the context once, then every +# planner call goes through cp_cli(). +resolve_cp_cli_dir() { + local d + for d in "${MM_WORKTREE_TASK_CP_CLI_DIR:-}" "$REPO_ROOT" "$HOME/dev/claude/projects/agent-runtimes"; do + [[ -n "$d" && ( -f "$d/lib/cp_cli.py" || -d "$d/lib/cp_cli" ) ]] && { echo "$d"; return 0; } + done + return 1 +} +CP_CLI_DIR="$(resolve_cp_cli_dir || true)" + +if [[ -z "$CP_CONTEXT" ]]; then + CPCONFIG="${MM_WORKTREE_TASK_CPCONFIG:-$HOME/.cpconfig}" + if [[ -f "$CPCONFIG" ]]; then + CP_CONTEXT="$(python3 - "$CPCONFIG" "$REPO_NAME" <<'PY' 2>/dev/null || true +import sys +try: + import yaml + cfg = yaml.safe_load(open(sys.argv[1])) or {} +except Exception: + sys.exit(0) +for ctx in cfg.get("contexts") or []: + if ctx.get("project_id") == sys.argv[2]: + print(ctx.get("name", "")) + break +PY +)" + fi +fi + +cp_cli() { + if [[ -z "$CP_CLI_DIR" ]]; then + err "cannot find lib/cp_cli.py (set MM_WORKTREE_TASK_CP_CLI_DIR or check out agent-runtimes)" + return 1 + fi + if [[ -n "$CP_CONTEXT" ]]; then + ( cd "$CP_CLI_DIR" && python3 -m lib.cp_cli --context "$CP_CONTEXT" "$@" ) + else + ( cd "$CP_CLI_DIR" && python3 -m lib.cp_cli "$@" ) + fi +} + REPORT_DIR="${XDG_RUNTIME_DIR:-/tmp}/mm-worktree-task/${REPO_NAME}" WORKTREE="${REPO_ROOT}/.claude/worktrees/mm-${SLUG}" BRANCH="mm/${SLUG}" @@ -140,9 +196,9 @@ if [[ $COLLECT -eq 1 ]]; then DATE="$(date +%Y-%m-%d)" DOC_NAME="mm-${SLUG}-${DATE}.md" info "uploading ${DOC_NAME} to item ${UUID}" - ( cd "$REPO_ROOT" && python3 -m lib.cp_cli planner items documents put "$UUID" "$DOC_NAME" --file "$RESULT_FILE" ) - ( cd "$REPO_ROOT" && python3 -m lib.cp_cli planner items patch "$UUID" --flow-state impl-complete \ - --description "mm-worktree-task: ${SLUG} committed on mm/${SLUG}" ) + cp_cli planner items documents put "$UUID" "$DOC_NAME" --file "$RESULT_FILE" + cp_cli planner items patch "$UUID" --flow-state impl-complete \ + --description "mm-worktree-task: ${SLUG} committed on mm/${SLUG}" ok "uploaded and marked impl-complete" fi exit 0 @@ -160,7 +216,7 @@ fetch_item() { if [[ -n "${MM_WORKTREE_TASK_FAKE_ITEM:-}" ]]; then ITEM_JSON="$(cat "$MM_WORKTREE_TASK_FAKE_ITEM")" else - ITEM_JSON="$(cd "$REPO_ROOT" && python3 -m lib.cp_cli planner items get "$UUID" --json)" + ITEM_JSON="$(cp_cli planner items get "$UUID" --json)" fi TITLE="$(echo "$ITEM_JSON" | python3 -c 'import json,sys;print(json.load(sys.stdin).get("title",""))')" fi @@ -173,7 +229,7 @@ fetch_docs() { [[ -f "${MM_WORKTREE_TASK_FAKE_DOCS_DIR}/list.json" ]] || return list_json="$(cat "${MM_WORKTREE_TASK_FAKE_DOCS_DIR}/list.json")" elif [[ -n "$UUID" ]]; then - list_json="$(cd "$REPO_ROOT" && python3 -m lib.cp_cli planner items documents list "$UUID" --json 2>/dev/null || echo '{"documents":[]}')" + list_json="$(cp_cli planner items documents list "$UUID" --json 2>/dev/null || echo '{"documents":[]}')" else return fi @@ -201,7 +257,7 @@ fetch_docs() { if [[ -n "${MM_WORKTREE_TASK_FAKE_DOCS_DIR:-}" ]]; then DOC_BODY["$name"]="$(cat "${MM_WORKTREE_TASK_FAKE_DOCS_DIR}/${name}" 2>/dev/null || echo "")" else - DOC_BODY["$name"]="$(cd "$REPO_ROOT" && python3 -m lib.cp_cli planner items documents get "$UUID" "$name" 2>/dev/null || echo "")" + DOC_BODY["$name"]="$(cp_cli planner items documents get "$UUID" "$name" 2>/dev/null || echo "")" fi fi done < <(echo "$list_json" | python3 -c ' @@ -223,7 +279,7 @@ if [[ -n "$ITEM_JSON" ]]; then PARENT_ID="$(echo "$ITEM_JSON" | python3 -c 'import json,sys;print(json.load(sys.stdin).get("parent_id") or "")')" fi if [[ -n "$PARENT_ID" && -z "${MM_WORKTREE_TASK_FAKE_ITEM:-}" ]]; then - PARENT_JSON="$(cd "$REPO_ROOT" && python3 -m lib.cp_cli planner items get "$PARENT_ID" --json 2>/dev/null || echo "")" + PARENT_JSON="$(cp_cli planner items get "$PARENT_ID" --json 2>/dev/null || echo "")" fi # --- build bundle content --- @@ -256,6 +312,7 @@ build_bundle() { fi echo for n in "${DOC_BUNDLED[@]:-}"; do + [[ -z "$n" ]] && continue echo "## Document: ${n}" echo '```' echo "${DOC_BODY[$n]:-}" @@ -308,6 +365,8 @@ CMD="cd ${WORKTREE} && setsid nohup timeout ${TIMEOUT} ask-minimax-run --model \ if [[ $DRYRUN -eq 1 ]]; then echo "[dryrun] mm-worktree-task — no changes will be made" echo " Repo: ${REPO_ROOT}" + echo " cp-cli from: ${CP_CLI_DIR:-}" + echo " cp-cli ctx: ${CP_CONTEXT:-}" echo " Slug: ${SLUG}" echo " UUID: ${UUID:-(none, --brief only)}" echo " Worktree: ${WORKTREE}" diff --git a/specs/bg-model-call.spec.md b/specs/bg-model-call.spec.md new file mode 100644 index 0000000..8871379 --- /dev/null +++ b/specs/bg-model-call.spec.md @@ -0,0 +1,158 @@ +# bg-model-call + +## Purpose + +Submit a prompt to a third-party model via the agent-runtimes control plane, wait for completion, and write the result to a file — enabling the `ScheduleWakeup` polling pattern for second-opinion consultations without blocking the main Claude Code session. + +## Usage + +``` +bg-model-call [OPTIONS] +``` + +### Required flags + +| Flag | Description | +|------|-------------| +| `--model ` | Model alias: `minimax`, `sonnet`, `haiku`, `opus` | +| `--prompt ` | Path to a file containing the prompt text | +| `--output ` | Path where the result will be written | + +### Optional flags + +| Flag | Default | Description | +|------|---------|-------------| +| `--template ` | (derived from `--model`) | Override the task template name (without `.yaml`). Takes precedence over the `--model` → template mapping. | +| `--project ` | `bg-model-call` | Project ID sent to the CP, used for task grouping in agent-monitor | +| `--url ` | `$CP_URL`, then `http://localhost:8100` | Control plane base URL | +| `--task-name ` | `bg-model-call-` | Short task name shown in agent-monitor | +| `--timeout ` | `1800` | Max seconds to wait for the task before declaring it failed | +| `--poll-interval ` | `10` | Seconds between CP state polls | +| `--dryrun`, `-n` | — | Resolve all values and print what would be submitted; do not call the CP or write any files | +| `--help`, `-h` | — | Show usage information and exit 0 | + +## Model-to-Template Mapping + +A constant defined at the top of the script: + +| Model alias | Template name | +|-------------|---------------| +| `minimax` | `minimax-planning` | +| `sonnet` | `sonnet-code-repo` | +| `haiku` | `haiku-code-repo` | +| `opus` | `opus-code-repo` | + +The `--template` flag overrides this mapping entirely. + +## Behaviour + +1. Parse and validate all flags. Exit 1 with an error message if required flags are missing or values are invalid. +2. Resolve the CP URL: `--url` flag → `$CP_URL` env var → `http://localhost:8100`. +3. Resolve the template name: `--template` flag if given, otherwise look up `--model` in the mapping table. If the model alias is not in the table, exit 1. +4. Read the prompt text from the `--prompt` file. If the file is not readable, exit 1. +5. Locate the `dispatch-task` CLI: + - Check if `dispatch-task` is on PATH. + - Otherwise try `/../../../projects/agent-runtimes/scripts/dispatch-task` as a fallback (for dev environments). + - If neither is found, fall back to direct `curl` POST to `$CP_URL/tasks`. +6. Submit the task to the CP via the resolved `dispatch-task` (or curl fallback): + - `--name ` + - `--project-id ` + - `--prompt-file ` + - `--template