headlesscode 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ATTRIBUTION.md +53 -0
- package/CODE_OF_CONDUCT.md +130 -0
- package/CONTRIBUTING.md +107 -0
- package/LICENSE +202 -0
- package/README.md +486 -0
- package/SECURITY.md +211 -0
- package/bin/headlesscode.mjs +83 -0
- package/package.json +63 -0
- package/shared/prompts/review-mode-prompt-short.md +93 -0
- package/shared/prompts/review-mode-prompt.md +281 -0
- package/shared/rules-code/rules.md +22 -0
- package/shared/stacks/cpp/rules.md +30 -0
- package/shared/stacks/fastapi/rules.md +30 -0
- package/shared/stacks/javascript/rules.md +37 -0
- package/shared/stacks/postgresql/rules.md +31 -0
- package/shared/stacks/python/rules.md +35 -0
- package/shared/stacks/react/rules.md +11 -0
- package/shared/stacks/typescript/rules.md +10 -0
- package/src/budget/budget.ts +221 -0
- package/src/budget/concurrency.ts +126 -0
- package/src/budget/cost.ts +309 -0
- package/src/budget/index.ts +8 -0
- package/src/checkpoints/cli.ts +256 -0
- package/src/checkpoints/service.ts +227 -0
- package/src/cli.ts +1535 -0
- package/src/cloud/docker-provider.ts +334 -0
- package/src/cloud/provider.ts +300 -0
- package/src/codeintel/call-graph.ts +78 -0
- package/src/codeintel/find-references.ts +123 -0
- package/src/codeintel/go-to-definition.ts +193 -0
- package/src/codeintel/handlers.ts +190 -0
- package/src/codeintel/import-graph.ts +173 -0
- package/src/codeintel/outline.ts +180 -0
- package/src/codeintel/position.ts +77 -0
- package/src/codeintel/program.ts +350 -0
- package/src/codeintel/rename-symbol.ts +213 -0
- package/src/codeintel/tools.ts +280 -0
- package/src/codemap/build.ts +135 -0
- package/src/codemap/cli.ts +190 -0
- package/src/codemap/extract.ts +339 -0
- package/src/codemap/files.ts +236 -0
- package/src/codemap/fingerprint.ts +65 -0
- package/src/codemap/flows.ts +62 -0
- package/src/codemap/html.ts +451 -0
- package/src/codemap/lock.ts +80 -0
- package/src/codemap/types.ts +101 -0
- package/src/codesearch/airunner-embedder.ts +185 -0
- package/src/codesearch/chunk.ts +339 -0
- package/src/codesearch/cli.ts +223 -0
- package/src/codesearch/embedder.ts +332 -0
- package/src/codesearch/files.ts +280 -0
- package/src/codesearch/index.ts +469 -0
- package/src/codesearch/ollama-embedder.ts +205 -0
- package/src/codesearch/search.ts +141 -0
- package/src/codesearch/types.ts +100 -0
- package/src/config/mode-models.ts +218 -0
- package/src/dashboard/aggregate.ts +364 -0
- package/src/dashboard/chat-thread.ts +141 -0
- package/src/dashboard/checkpoints.ts +124 -0
- package/src/dashboard/cli.ts +193 -0
- package/src/dashboard/codemap.ts +44 -0
- package/src/dashboard/files.ts +121 -0
- package/src/dashboard/page.ts +2803 -0
- package/src/dashboard/self-improvement-metrics.ts +282 -0
- package/src/dashboard/server.ts +1103 -0
- package/src/dashboard/session-launch.ts +310 -0
- package/src/dashboard/timeline.ts +273 -0
- package/src/dashboard/tool-exec.ts +107 -0
- package/src/dashboard/trend-cli.ts +141 -0
- package/src/dashboard/trend.ts +413 -0
- package/src/decision-proxy/cli.ts +261 -0
- package/src/decision-proxy/proxy.ts +569 -0
- package/src/deploy/gate-cli.ts +147 -0
- package/src/deploy/gate.ts +254 -0
- package/src/engine/condense.ts +512 -0
- package/src/engine/events.ts +428 -0
- package/src/engine/handoff.ts +71 -0
- package/src/engine/lazy-tools.ts +160 -0
- package/src/engine/local-explore.ts +653 -0
- package/src/engine/logger.ts +96 -0
- package/src/engine/loop.ts +5517 -0
- package/src/engine/parser.ts +347 -0
- package/src/engine/prompt.ts +860 -0
- package/src/engine/reports.ts +47 -0
- package/src/engine/stacks.ts +448 -0
- package/src/engine/types.ts +291 -0
- package/src/engine/usage.ts +186 -0
- package/src/github/app-auth.ts +161 -0
- package/src/github/cli.ts +448 -0
- package/src/github/installations.ts +133 -0
- package/src/github/pr.ts +321 -0
- package/src/github/provision.ts +118 -0
- package/src/github/push.ts +122 -0
- package/src/index-util.ts +50 -0
- package/src/index.ts +81 -0
- package/src/init/cli.ts +248 -0
- package/src/init/gitignore.ts +74 -0
- package/src/llm/ollama.ts +308 -0
- package/src/llm/openrouter.ts +868 -0
- package/src/llm/preflight.ts +367 -0
- package/src/llm/transcript-capture.ts +84 -0
- package/src/memory/embed.ts +110 -0
- package/src/memory/index.ts +22 -0
- package/src/memory/local.ts +259 -0
- package/src/memory/summarizer.ts +283 -0
- package/src/memory/types.ts +153 -0
- package/src/memory/uwuchat.ts +157 -0
- package/src/migrate/cli.ts +115 -0
- package/src/orchestrator/analyze-cli.ts +104 -0
- package/src/orchestrator/auto-split.ts +206 -0
- package/src/orchestrator/cleanup.ts +1003 -0
- package/src/orchestrator/cli.ts +3571 -0
- package/src/orchestrator/cost-estimate.ts +564 -0
- package/src/orchestrator/cost-history-cli.ts +242 -0
- package/src/orchestrator/cost-history.ts +397 -0
- package/src/orchestrator/git-sync.ts +250 -0
- package/src/orchestrator/index.ts +153 -0
- package/src/orchestrator/log-analysis.ts +0 -0
- package/src/orchestrator/merge-check.ts +108 -0
- package/src/orchestrator/pipeline.ts +411 -0
- package/src/orchestrator/resume.ts +1940 -0
- package/src/orchestrator/reviewer.ts +503 -0
- package/src/orchestrator/split.ts +296 -0
- package/src/orchestrator/state.ts +542 -0
- package/src/orchestrator/status.ts +697 -0
- package/src/orchestrator/verification-gate.ts +134 -0
- package/src/orchestrator/watch.ts +898 -0
- package/src/permissions/commands.ts +1083 -0
- package/src/permissions/config.ts +241 -0
- package/src/permissions/index.ts +12 -0
- package/src/permissions/protected-files.ts +96 -0
- package/src/permissions/store-protection.ts +272 -0
- package/src/project-store.ts +648 -0
- package/src/projects/cli.ts +382 -0
- package/src/qa/qa.ts +487 -0
- package/src/tools/browser/handler.ts +346 -0
- package/src/tools/browser/service.ts +406 -0
- package/src/tools/browser/smoke.ts +78 -0
- package/src/tools/browser/tool.ts +99 -0
- package/src/tools/executor.ts +2575 -0
- package/src/tools/language-detect.ts +183 -0
- package/src/tools/output-summarizer.ts +369 -0
- package/src/tools/run-tests.ts +302 -0
- package/src/tools/set-indentation-tool.ts +49 -0
- package/src/tools/test-selection.ts +160 -0
- package/src/vendor/tests/smoke.ts +103 -0
- package/src/vendor/zoo-code/VENDOR-NOTES.md +213 -0
- package/src/vendor/zoo-code/shim/anthropic.ts +71 -0
- package/src/vendor/zoo-code/shim/openai.d.ts +60 -0
- package/src/vendor/zoo-code/shim/os-name.ts +18 -0
- package/src/vendor/zoo-code/shim/strip-bom.ts +14 -0
- package/src/vendor/zoo-code/shim/vscode.ts +76 -0
- package/src/vendor/zoo-code/src/core/config/CustomModesManager.ts +1015 -0
- package/src/vendor/zoo-code/src/core/diff/strategies/multi-search-replace.ts +670 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/capabilities.ts +46 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/custom-instructions.ts +559 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/index.ts +10 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/markdown-formatting.ts +7 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/modes.ts +35 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/objective.ts +13 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/rules.ts +95 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/skills.ts +105 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/system-info.ts +30 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/tool-use-guidelines.ts +9 -0
- package/src/vendor/zoo-code/src/core/prompts/sections/tool-use.ts +7 -0
- package/src/vendor/zoo-code/src/core/prompts/system.ts +176 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/access_mcp_resource.ts +41 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/apply_diff.ts +40 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/apply_patch.ts +61 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/ask_followup_question.ts +62 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/attempt_completion.ts +33 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/codebase_search.ts +43 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/converters.ts +109 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/edit.ts +48 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/edit_file.ts +72 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/execute_command.ts +54 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/generate_image.ts +51 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/index.ts +75 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/list_files.ts +41 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/mcp_server.ts +75 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/new_task.ts +39 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/read_command_output.ts +81 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/read_file.ts +169 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/run_slash_command.ts +31 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/search_files.ts +50 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/search_replace.ts +51 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/skill.ts +33 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/switch_mode.ts +31 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/update_todo_list.ts +54 -0
- package/src/vendor/zoo-code/src/core/prompts/tools/native-tools/write_to_file.ts +40 -0
- package/src/vendor/zoo-code/src/core/prompts/types.ts +12 -0
- package/src/vendor/zoo-code/src/i18n/index.ts +19 -0
- package/src/vendor/zoo-code/src/integrations/misc/extract-text.ts +81 -0
- package/src/vendor/zoo-code/src/services/checkpoints/RepoPerTaskCheckpointService.ts +15 -0
- package/src/vendor/zoo-code/src/services/checkpoints/ShadowCheckpointService.ts +553 -0
- package/src/vendor/zoo-code/src/services/checkpoints/excludes.ts +212 -0
- package/src/vendor/zoo-code/src/services/checkpoints/index.ts +3 -0
- package/src/vendor/zoo-code/src/services/checkpoints/types.ts +35 -0
- package/src/vendor/zoo-code/src/services/code-index/manager.ts +19 -0
- package/src/vendor/zoo-code/src/services/mcp/McpHub.ts +36 -0
- package/src/vendor/zoo-code/src/services/roo-config/index.ts +441 -0
- package/src/vendor/zoo-code/src/services/search/file-search.ts +143 -0
- package/src/vendor/zoo-code/src/services/skills/SkillsManager.ts +20 -0
- package/src/vendor/zoo-code/src/shared/globalFileNames.ts +9 -0
- package/src/vendor/zoo-code/src/shared/language.ts +43 -0
- package/src/vendor/zoo-code/src/shared/modes.ts +257 -0
- package/src/vendor/zoo-code/src/shared/tools.ts +385 -0
- package/src/vendor/zoo-code/src/utils/fs.ts +39 -0
- package/src/vendor/zoo-code/src/utils/globalContext.ts +22 -0
- package/src/vendor/zoo-code/src/utils/json-schema.ts +16 -0
- package/src/vendor/zoo-code/src/utils/logging.ts +21 -0
- package/src/vendor/zoo-code/src/utils/mcp-name.ts +190 -0
- package/src/vendor/zoo-code/src/utils/object.ts +18 -0
- package/src/vendor/zoo-code/src/utils/path.ts +94 -0
- package/src/vendor/zoo-code/src/utils/shell.ts +376 -0
- package/src/vendor/zoo-code/src/utils/text-normalization.ts +99 -0
- package/src/vendor/zoo-code/types/global-settings.ts +19 -0
- package/src/vendor/zoo-code/types/index.ts +22 -0
- package/src/vendor/zoo-code/types/message.ts +375 -0
- package/src/vendor/zoo-code/types/mode.ts +241 -0
- package/src/vendor/zoo-code/types/todo.ts +19 -0
- package/src/vendor/zoo-code/types/tool-params.ts +116 -0
- package/src/vendor/zoo-code/types/tool.ts +67 -0
- package/src/vendor/zoo-code/types/vscode.ts +84 -0
- package/src/vision/describe.ts +242 -0
- package/src/vision/tool.ts +91 -0
- package/src/watcher/cli.ts +369 -0
- package/src/watcher/github.ts +304 -0
- package/src/watcher/index.ts +59 -0
- package/src/watcher/state.ts +254 -0
- package/src/watcher/watch.ts +562 -0
- package/tsconfig.json +18 -0
package/README.md
ADDED
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
# headlesscode
|
|
2
|
+
|
|
3
|
+
A small, purpose-built, genuinely headless coding-agent harness. It reuses what
|
|
4
|
+
makes [Zoo Code](https://github.com/Zoo-Code-Org/Zoo-Code) (an Apache-2.0 Roo
|
|
5
|
+
Code fork) good — its system prompts, native tool schemas, and
|
|
6
|
+
mode/rules configuration format — without carrying along the VS Code GUI
|
|
7
|
+
dependency.
|
|
8
|
+
|
|
9
|
+
This repo implements:
|
|
10
|
+
|
|
11
|
+
- The **headless runtime engine** — vendoring the portable Apache-2.0 core and
|
|
12
|
+
the headless runtime (OpenRouter client, tool executor, tool-call parser,
|
|
13
|
+
orchestration loop, CLI).
|
|
14
|
+
- A **headless orchestration layer** — split an issue into worker groups, run
|
|
15
|
+
each as a harness subprocess in its own git worktree, review, and merge
|
|
16
|
+
(see [`docs/phase2-orchestration.md`](./docs/phase2-orchestration.md)).
|
|
17
|
+
- A **memory subsystem** — per-project knowledge facts + rolling session
|
|
18
|
+
summaries, with local semantic recall and a pluggable `MemoryStore`
|
|
19
|
+
contract for a remote backend.
|
|
20
|
+
- **Headless QA** — run the target repo's `qa-agent` mode as a second harness
|
|
21
|
+
session (see [`docs/phase4-qa.md`](./docs/phase4-qa.md)) and a
|
|
22
|
+
human-approval deploy gate in front of the repo's `deploy-production.sh`
|
|
23
|
+
(see [`docs/phase4-deploy-gate.md`](./docs/phase4-deploy-gate.md)).
|
|
24
|
+
- A **GitHub issue watcher** (see
|
|
25
|
+
[`docs/phase5-issue-watcher.md`](./docs/phase5-issue-watcher.md)) — file an
|
|
26
|
+
issue with a target label and a poll loop automatically splits and spawns it
|
|
27
|
+
through the existing orchestration pipeline, with durable idempotency so a
|
|
28
|
+
restart never double-spawns.
|
|
29
|
+
- **Cloud-scaling guardrails** — a hard concurrent-session cap, a per-session
|
|
30
|
+
cost/time/iteration budget, and a `CloudProvider` abstraction (container/VM
|
|
31
|
+
per issue behind the same lifecycle as the local worktree flow) with an
|
|
32
|
+
evaluation-only cloud-provider sketch (see
|
|
33
|
+
[`docs/phase6-cloud.md`](./docs/phase6-cloud.md)). No live cloud resources
|
|
34
|
+
are launched; the caps/budgets land before any scaling.
|
|
35
|
+
|
|
36
|
+
> ⚠️ **Security warning: default-allow arbitrary command execution.**
|
|
37
|
+
> `headlesscode` is a headless coding agent: by default it runs **arbitrary
|
|
38
|
+
> shell commands with your full user privileges** (no sandbox, no approval
|
|
39
|
+
> prompts) and can read and modify your files — including `~/.ssh`, `~/.aws`,
|
|
40
|
+
> and any other credentials your user can access. Run it only on machines and
|
|
41
|
+
> with tasks you trust, and isolate it (container/VM/dedicated user) whenever
|
|
42
|
+
> untrusted content is involved. See [`SECURITY.md`](./SECURITY.md) for the
|
|
43
|
+
> full disclosure and the optional permissions layer that provides defense in
|
|
44
|
+
> depth (not a security boundary).
|
|
45
|
+
|
|
46
|
+
## Purpose
|
|
47
|
+
|
|
48
|
+
Drive a coding agent headlessly against real git repos: read a target repo's
|
|
49
|
+
`.roomodes` and `.roo/rules-<slug>/` files, build a system prompt from mode +
|
|
50
|
+
rules, call an LLM API (OpenRouter in Phase 1) with the tool schema, execute
|
|
51
|
+
tool calls via plain `fs`/`child_process` (no `vscode.*` anywhere), and loop
|
|
52
|
+
until completion. Non-interactive by design.
|
|
53
|
+
|
|
54
|
+
## Quick start
|
|
55
|
+
|
|
56
|
+
`headlesscode` is not yet published to the npm registry, so install it from a
|
|
57
|
+
checkout:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
git clone https://github.com/Capsize-Games/headlesscode.git
|
|
61
|
+
cd headlesscode
|
|
62
|
+
npm install
|
|
63
|
+
|
|
64
|
+
# Required (except for --dry-run):
|
|
65
|
+
export HEADLESSCODE_OPENROUTER_API_KEY=sk-or-...
|
|
66
|
+
|
|
67
|
+
# Optional:
|
|
68
|
+
export OPENROUTER_MODEL=deepseek/deepseek-v4-flash-0731 # default model
|
|
69
|
+
export OPENROUTER_HTTP_REFERER=https://example.com # OpenRouter app header
|
|
70
|
+
export OPENROUTER_APP_TITLE="headlesscode" # OpenRouter X-Title header
|
|
71
|
+
export HEADLESSCODE_WORKSPACE_ROOT=/path/to/target/repo # default workspace root
|
|
72
|
+
|
|
73
|
+
# Run a task against a target repo, straight from the checkout:
|
|
74
|
+
node bin/headlesscode.mjs --task "Fix the bug in src/index.ts" --workspace /path/to/target/repo
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Systemwide `headlesscode` command
|
|
78
|
+
|
|
79
|
+
To avoid re-typing `node <checkout>/bin/headlesscode.mjs` from every project,
|
|
80
|
+
install a `headlesscode` command onto your `PATH` once:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
scripts/install-cli.sh
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
This writes a wrapper to `~/.local/bin/headlesscode` (override with
|
|
87
|
+
`HEADLESSCODE_BIN_DIR`) that runs this checkout's `src/cli.ts` via its local
|
|
88
|
+
`tsx`, without `cd`-ing — so `--repo`/`--workspace` still default to whatever
|
|
89
|
+
directory you're standing in when you invoke it. Re-run the script any time
|
|
90
|
+
after `git pull` to point it at a moved checkout; the wrapper itself doesn't
|
|
91
|
+
need updating for ordinary code changes.
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# from any repo, no HEADLESSCODE_ROOT plumbing needed:
|
|
95
|
+
headlesscode orchestrate --repo . --issue 42
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The rest of this README uses the plain `headlesscode` form for brevity —
|
|
99
|
+
substitute `node <checkout>/bin/headlesscode.mjs` if you haven't run
|
|
100
|
+
`scripts/install-cli.sh` yet.
|
|
101
|
+
|
|
102
|
+
## The `--dry-run` flow (no API key needed)
|
|
103
|
+
|
|
104
|
+
`--dry-run` builds the full system prompt and validates configuration loading
|
|
105
|
+
without calling the LLM — useful for CI and for checking that a target repo's
|
|
106
|
+
`.roomodes` / `.roo/rules-<slug>/` / `AGENTS.md` are picked up:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
headlesscode --dry-run --mode code --workspace /path/to/target/repo
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
It prints the assembled system prompt plus a summary line (mode, custom modes
|
|
113
|
+
loaded, exposed tools, prompt size). Exit code 0 means prompt building + mode /
|
|
114
|
+
rules loading succeeded; non-zero means a config error.
|
|
115
|
+
|
|
116
|
+
## Real-usage example against a target repo
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
export HEADLESSCODE_OPENROUTER_API_KEY=sk-or-...
|
|
120
|
+
export HEADLESSCODE_WORKSPACE_ROOT=~/Projects/some-target-repo
|
|
121
|
+
|
|
122
|
+
# Point the agent at an already-scoped issue, in an isolated worktree:
|
|
123
|
+
headlesscode \
|
|
124
|
+
--mode code \
|
|
125
|
+
--task "Implement issue #29: add retry logic to the HTTP client (see .roo/rules for project conventions)." \
|
|
126
|
+
--workspace ~/Projects/some-target-repo \
|
|
127
|
+
--max-iterations 50 \
|
|
128
|
+
--log-file ./headlesscode-session.log
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The agent reads files, runs commands, writes code, and finishes by calling
|
|
132
|
+
`attempt_completion` (or by giving a final text answer). The final result is
|
|
133
|
+
printed to stdout. Exit code 0 = success; 1 = task failed (max iterations or
|
|
134
|
+
consecutive-mistake limit); 2 = usage/config error (e.g. missing
|
|
135
|
+
`HEADLESSCODE_OPENROUTER_API_KEY`).
|
|
136
|
+
|
|
137
|
+
## Registering a new project
|
|
138
|
+
|
|
139
|
+
To use headlesscode against a project that isn't set up as a headlesscode
|
|
140
|
+
project yet, register it in one step:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
headlesscode init --workspace ~/Projects/your-project
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
This detects the project's stack(s) (which drives per-session instruction
|
|
147
|
+
selection), makes sure the project's `.gitignore` excludes `.headlesscode/`
|
|
148
|
+
session artifacts, and builds the codebase-search index and the codemap — no
|
|
149
|
+
manual `index`/`codemap`/`.gitignore` steps needed. The index step calls the
|
|
150
|
+
embedding API and costs real money unless you pass `--skip-index` or
|
|
151
|
+
`--embedding-backend ollama`. See `headlesscode init --help` for the full
|
|
152
|
+
options.
|
|
153
|
+
|
|
154
|
+
## CLI reference
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
headlesscode --task "<task text>" [options]
|
|
158
|
+
headlesscode --task-file <path> [options]
|
|
159
|
+
headlesscode --dry-run [options]
|
|
160
|
+
headlesscode orchestrate --repo <path> --issue <n> [--issue <n> ...] [options]
|
|
161
|
+
headlesscode watch --owner <o> --repo <path> --label <name> [options]
|
|
162
|
+
|
|
163
|
+
--mode <slug> Mode to run in (built-in or from .roomodes). Default: code
|
|
164
|
+
--task <text> The task description for the agent
|
|
165
|
+
--task-file <path> Read the task from a file (relative to workspace)
|
|
166
|
+
--workspace <root> Workspace root (default: $HEADLESSCODE_WORKSPACE_ROOT or cwd)
|
|
167
|
+
--model <id> OpenRouter model id (default: $OPENROUTER_MODEL or deepseek/deepseek-v4-flash-0731)
|
|
168
|
+
--max-iterations <n> Loop iteration cap (default: 50)
|
|
169
|
+
--consecutive-error-limit <n> Consecutive mistakes before giving up (default: 3)
|
|
170
|
+
--max-cost-usd <n> Phase 6: per-session cost cap in USD (decimal). Default
|
|
171
|
+
$HEADLESSCODE_MAX_COST_USD; off when neither is set
|
|
172
|
+
--max-duration-ms <n> Phase 6: per-session wall-clock cap in ms. Default
|
|
173
|
+
$HEADLESSCODE_MAX_DURATION_MS; off when neither is set.
|
|
174
|
+
A tripped cap aborts with reason "budget"
|
|
175
|
+
--log-file <path> Also append structured logs to this file
|
|
176
|
+
--memory-dir <path> Phase 3 memory: store facts + session summaries under <path>
|
|
177
|
+
(enabled; default $HEADLESSCODE_MEMORY_DIR or
|
|
178
|
+
<workspace>/.headlesscode/memory). Memory is OFF unless set.
|
|
179
|
+
--no-memory Explicitly disable memory even if HEADLESSCODE_MEMORY_DIR is set
|
|
180
|
+
--allowed-commands <list> Comma-separated command prefixes the agent may run.
|
|
181
|
+
Default: $HEADLESSCODE_ALLOWED_COMMANDS, else
|
|
182
|
+
.headlesscode/permissions.json, else empty (=
|
|
183
|
+
allow everything except --denied-commands; see
|
|
184
|
+
SECURITY.md)
|
|
185
|
+
--denied-commands <list> Comma-separated command prefixes that are ALWAYS
|
|
186
|
+
refused (deny wins over allow; dangerous shell
|
|
187
|
+
substitutions are always blocked regardless).
|
|
188
|
+
Default: $HEADLESSCODE_DENIED_COMMANDS, else
|
|
189
|
+
.headlesscode/permissions.json, else empty
|
|
190
|
+
--protected-files <list> Comma-separated glob patterns of files the agent may
|
|
191
|
+
not write. Default: $HEADLESSCODE_PROTECTED_FILES,
|
|
192
|
+
else .headlesscode/permissions.json, else
|
|
193
|
+
".env,.env.*,*.pem,*.key,id_rsa*"
|
|
194
|
+
--allow-protected-writes Escape hatch: permit writes to protected files
|
|
195
|
+
(default: OFF). Also settable via
|
|
196
|
+
"allowProtectedWrites": true in
|
|
197
|
+
.headlesscode/permissions.json
|
|
198
|
+
--dry-run Build the system prompt + validate config, then exit (no API key)
|
|
199
|
+
--version / --help
|
|
200
|
+
|
|
201
|
+
orchestrate subcommand (Phase 2 — parallel worktrees, headless workers):
|
|
202
|
+
--repo <path> Target repo root (required)
|
|
203
|
+
--issue <n> Issue number to include (repeatable)
|
|
204
|
+
--issues-json <file> Read issues from a JSON array of {number,title,body}
|
|
205
|
+
(used when gh is unavailable, or for tests)
|
|
206
|
+
--file-issues With --issues-json: file a REAL GitHub issue for
|
|
207
|
+
each synthetic entry (gh issue create --repo
|
|
208
|
+
<origin-owner>/<origin-repo>), swap in the real
|
|
209
|
+
number returned, and print one confirmation line
|
|
210
|
+
per created issue. A real, visible write to
|
|
211
|
+
GitHub — opt-in, never automatic. Requires
|
|
212
|
+
--issues-json
|
|
213
|
+
--batch <name> Batch id in the state file (default round-<date>)
|
|
214
|
+
--review-mode <slug> Mode slug for review sessions (default deepseek-reviewer)
|
|
215
|
+
--no-review Spawn + watch only; skip the reviewer
|
|
216
|
+
--qa Phase 4: run a headless QA session (--mode qa-agent)
|
|
217
|
+
on each group after its review passes; record
|
|
218
|
+
qa {status,verdict,evidence} in the state file
|
|
219
|
+
--qa-mode <slug> Mode slug for QA sessions (default qa-agent; the
|
|
220
|
+
target repo's .roomodes + .roo/rules-<slug>/ are
|
|
221
|
+
spliced automatically)
|
|
222
|
+
--deploy Phase 4: after all groups done + reviewed + QA passed,
|
|
223
|
+
run the human-approval deploy gate
|
|
224
|
+
(scripts/deploy-gate.sh) — a hard stop that never runs
|
|
225
|
+
the repo's deploy-production.sh without explicit human
|
|
226
|
+
approval (interactive on a TTY, token/file otherwise)
|
|
227
|
+
--deploy-args <str> Deploy args forwarded to the deploy script after the
|
|
228
|
+
gate approves (space-separated flags; also DEPLOY_ARGS env)
|
|
229
|
+
--poll-interval-ms <n> Watcher poll interval (default 5000)
|
|
230
|
+
--max-concurrent-sessions <n> Phase 6 global cap on concurrent sessions across
|
|
231
|
+
processes (default $HEADLESSCODE_MAX_CONCURRENT_
|
|
232
|
+
SESSIONS or 3). At/over the cap this run ABORTS
|
|
233
|
+
with a clear message, exit 1
|
|
234
|
+
--dry-run Print the split plan + spawn commands, spawn nothing
|
|
235
|
+
|
|
236
|
+
watch subcommand (Phase 5 — GitHub issue watcher, poll-based intake):
|
|
237
|
+
--owner <o> GitHub owner (required)
|
|
238
|
+
--repo <path> Local clone of the target repo (required; worktrees
|
|
239
|
+
are spawned under <path>/.worktrees/). The GitHub repo
|
|
240
|
+
name defaults to the directory basename (--gh-repo overrides)
|
|
241
|
+
--label <name> The label that triggers processing, e.g. needs-agent
|
|
242
|
+
--poll-interval-ms <n> Sweep interval in continuous mode (default 60000)
|
|
243
|
+
--run-once One sweep then exit 0 (or 1 if any spawn failed)
|
|
244
|
+
--max-per-sweep <n> Max NEW issues spawned per sweep (default 5); the rest
|
|
245
|
+
stay 'pending' in state and are picked up next sweep
|
|
246
|
+
--max-concurrent-sessions <n> Phase 6 GLOBAL cap on concurrent sessions across
|
|
247
|
+
processes (default $HEADLESSCODE_MAX_CONCURRENT_
|
|
248
|
+
SESSIONS or 3). Interplay: maxPerSweep bounds one
|
|
249
|
+
sweep's burst; this bounds the total fleet — issues
|
|
250
|
+
beyond it stay 'pending' until slots free up
|
|
251
|
+
--state-file <path> Durable idempotency state (default
|
|
252
|
+
<repo>/.worktrees/.watcher-state.json)
|
|
253
|
+
--mode <slug> / --memory-dir <path>
|
|
254
|
+
Forwarded to the spawner (ORCHESTRATOR_MODE /
|
|
255
|
+
HEADLESSCODE_MEMORY_DIR)
|
|
256
|
+
--qa / --deploy Pass-through: recorded per batch for the follow-up
|
|
257
|
+
orchestrate completion run
|
|
258
|
+
--dry-run Sweep + print the spawn plan, spawn nothing, write no
|
|
259
|
+
state (still needs a GitHub token for listIssues)
|
|
260
|
+
--retry-failed Retry previously-failed spawns next sweep
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Architecture
|
|
264
|
+
|
|
265
|
+
- [`src/llm/openrouter.ts`](./src/llm/openrouter.ts) — OpenRouter
|
|
266
|
+
chat-completions client using native `fetch` (no axios/node-fetch/openai
|
|
267
|
+
SDK). Reads `HEADLESSCODE_OPENROUTER_API_KEY`, optional `OPENROUTER_HTTP_REFERER` /
|
|
268
|
+
`OPENROUTER_APP_TITLE`; model default `deepseek/deepseek-v4-flash-0731` (env
|
|
269
|
+
`OPENROUTER_MODEL` overrides). Non-2xx → typed `OpenRouterError` with status +
|
|
270
|
+
body excerpt; supports `AbortSignal` timeouts.
|
|
271
|
+
- [`src/tools/executor.ts`](./src/tools/executor.ts) — headless executor for
|
|
272
|
+
`read_file`, `write_to_file`, `execute_command`, `list_files` (plain
|
|
273
|
+
`fs`/`child_process`), plus `attempt_completion` / `ask_followup_question`
|
|
274
|
+
handlers and "not implemented" stubs for every other vendored tool schema.
|
|
275
|
+
All file operations are resolved relative to the workspace root and rejected
|
|
276
|
+
if they escape it (path-traversal guard via `path.resolve` + containment
|
|
277
|
+
check). Command results are truncated (~30k chars) to keep context bounded.
|
|
278
|
+
- [`src/tools/output-summarizer.ts`](./src/tools/output-summarizer.ts) —
|
|
279
|
+
OPT-IN local summarization of oversized `execute_command` output: when
|
|
280
|
+
`HEADLESSCODE_LOCAL_SUMMARIZATION=1`, a result that would exceed the 30k
|
|
281
|
+
char cap is compressed by a local Ollama chat model before reaching the
|
|
282
|
+
cloud model, with a `[Output summarized by local model…]` transparency
|
|
283
|
+
header. OFF by default; on any failure it falls back to today's exact blunt
|
|
284
|
+
truncation (never an error, never a hang). Endpoint/model configurable via
|
|
285
|
+
`HEADLESSCODE_OLLAMA_URL` (default `http://localhost:11434`) and
|
|
286
|
+
`HEADLESSCODE_SUMMARIZATION_MODEL` (default `qwen3:8b`). Deliberately
|
|
287
|
+
limited to command output — file/diff content always stays verbatim.
|
|
288
|
+
- [`src/engine/parser.ts`](./src/engine/parser.ts) — OpenAI function-calling
|
|
289
|
+
parser: JSON.parses `tool_calls[].function.arguments` with a best-effort
|
|
290
|
+
partial-JSON fallback; parse failures are marked and fed back as errors.
|
|
291
|
+
- [`src/engine/prompt.ts`](./src/engine/prompt.ts) — wraps the vendored
|
|
292
|
+
`SYSTEM_PROMPT` builder: loads project `.roomodes` (same zod schema as Zoo
|
|
293
|
+
Code's `CustomModesManager`), passes the workspace as `cwd` so `.roo/rules-*`
|
|
294
|
+
/ `AGENTS.md` splice in, and selects the mode's exposed tools.
|
|
295
|
+
- [`src/engine/loop.ts`](./src/engine/loop.ts) — `HeadlessSession`, the
|
|
296
|
+
orchestration loop. System + user → LLM → assistant (with tool_calls) → parse
|
|
297
|
+
→ execute → `tool` role message → repeat. Terminates on `attempt_completion`
|
|
298
|
+
(its `args.result` is the final answer) or a text-only reply; fails bounded
|
|
299
|
+
on max iterations or `consecutiveErrorLimit` consecutive mistakes (tool
|
|
300
|
+
errors / parse errors / identical repeated calls). History truncation is a
|
|
301
|
+
Phase 1 placeholder: system + first user always kept, sliding window of the
|
|
302
|
+
last ~40 messages. The loop accepts an injected `llmClient` (DI) so tests use
|
|
303
|
+
a fake; the CLI wires `OpenRouterClient`.
|
|
304
|
+
- [`src/engine/logger.ts`](./src/engine/logger.ts) — structured logger
|
|
305
|
+
(timestamped lines to stdout/stderr, optional file).
|
|
306
|
+
- [`src/memory/`](./src/memory/index.ts) — memory subsystem:
|
|
307
|
+
- [`src/memory/types.ts`](./src/memory/types.ts) — the `MemoryFact` /
|
|
308
|
+
`SessionSummary` schema (per-project scoped, kinds
|
|
309
|
+
`convention|decision|failure|knowledge` where "things that didn't work"
|
|
310
|
+
are `failure`) and the two contracts: `MemoryStore` (the pluggable
|
|
311
|
+
storage boundary) and `Embedder`. Hard data-isolation requirement
|
|
312
|
+
documented: the harness knowledge is a dedicated schema, never reachable
|
|
313
|
+
through any customer tenant route.
|
|
314
|
+
- [`src/memory/local.ts`](./src/memory/local.ts) — `LocalMemoryStore`: the
|
|
315
|
+
fully working file backend (`facts/<project>.jsonl` +
|
|
316
|
+
`sessions/<project>.jsonl`, append-only, idempotent `addFact` by content
|
|
317
|
+
hash, `queryRecall` = keyword matches (high weight) + local-embedder
|
|
318
|
+
cosine similarity, deterministic ordering).
|
|
319
|
+
- [`src/memory/embed.ts`](./src/memory/embed.ts) — `createLocalEmbedder()`:
|
|
320
|
+
zero-dependency, deterministic lexical-hash embedder (lowercase word +
|
|
321
|
+
char-bigram tokens → fixed-dim L2-normalized vector). Placeholder for a
|
|
322
|
+
real local embedding model behind the same `Embedder` interface.
|
|
323
|
+
- [`src/memory/summarizer.ts`](./src/memory/summarizer.ts) —
|
|
324
|
+
`extractSessionSummary` (deterministic; files/commands derived from the
|
|
325
|
+
tool history, facts via keyword heuristics) + `buildRollingSummary`
|
|
326
|
+
(compact markdown recap of the last N sessions, so a session never needs
|
|
327
|
+
the infinite raw history).
|
|
328
|
+
- [`src/memory/uwuchat.ts`](./src/memory/uwuchat.ts) — a remote
|
|
329
|
+
`MemoryStore` implementation stub for a future hosted memory API. Throws
|
|
330
|
+
"not implemented" until its base-URL/token env vars are set.
|
|
331
|
+
- Memory is wired into `HeadlessSession` as an **opt-in** config (`memory`
|
|
332
|
+
+ `project`); when unset the loop behaves exactly as before. When set, the
|
|
333
|
+
loop injects a `## PROJECT MEMORY` section (recalled facts + rolling
|
|
334
|
+
recap) into the first user message and records the session + extracted
|
|
335
|
+
facts afterwards — and memory failures are always non-fatal.
|
|
336
|
+
- [`src/orchestrator/`](./src/orchestrator/index.ts) — Phase 2 orchestration
|
|
337
|
+
layer: `split.ts` (issue-splitting heuristics, deterministic +
|
|
338
|
+
unit-tested), `state.ts` (`.worktrees/.orchestrator-state.json` read/write),
|
|
339
|
+
`reviewer.ts` (adversarial fresh-context review run with a read-only
|
|
340
|
+
executor), `watch.ts` (completion polling of `.harness.done` markers + stall
|
|
341
|
+
guard), and `cli.ts` (the `orchestrate` subcommand — split → spawn via
|
|
342
|
+
`scripts/spawn-parallel-worktrees.sh` → watch → review → QA → deploy gate).
|
|
343
|
+
- [`src/qa/qa.ts`](./src/qa/qa.ts) — Phase 4 headless QA: `runQa()` runs a
|
|
344
|
+
second harness session against a worktree in the target repo's `qa-agent`
|
|
345
|
+
mode (auto-spliced from `.roomodes` + `.roo/rules-qa-agent/`), with a
|
|
346
|
+
generic checklist fallback when the repo has no such mode. Read + command
|
|
347
|
+
tools only (no `write_to_file`) — QA verifies and reports, it never edits.
|
|
348
|
+
Verdict parsing is fail-closed (`pass`/`fail`/`error`, default `fail`).
|
|
349
|
+
- [`src/deploy/gate.ts`](./src/deploy/gate.ts) + [`src/deploy/gate-cli.ts`](./src/deploy/gate-cli.ts) —
|
|
350
|
+
Phase 4 human-approval deploy gate: the pure, unit-tested decision function
|
|
351
|
+
`decideApproval` (interactive y/N, one-time approval file, or
|
|
352
|
+
`DEPLOY_APPROVAL_TOKEN` matching `<repo>/.deploy-approval`; never
|
|
353
|
+
auto-approves) plus a thin CLI the bash wrapper calls.
|
|
354
|
+
- [`src/watcher/`](./src/watcher/index.ts) — Phase 5 GitHub issue watcher:
|
|
355
|
+
[`github.ts`](./src/watcher/github.ts) (native-fetch GitHub REST client with
|
|
356
|
+
label filter, PR filtering, pagination, `GITHUB_API_BASE_URL` override for
|
|
357
|
+
tests/mocks), [`state.ts`](./src/watcher/state.ts) (durable idempotency
|
|
358
|
+
state file — write-ahead `spawned` → `done`/`failed`, `pending` for capped
|
|
359
|
+
issues, restart-safe), [`watch.ts`](./src/watcher/watch.ts) (the poll loop:
|
|
360
|
+
list by label → split → spawn via the existing bash spawner, bounded by
|
|
361
|
+
`maxPerSweep` and the Phase 6 global cap), and [`cli.ts`](./src/watcher/cli.ts)
|
|
362
|
+
(the `watch` subcommand — continuous or `--run-once`, `--dry-run`,
|
|
363
|
+
`--retry-failed`).
|
|
364
|
+
- [`src/budget/`](./src/budget/index.ts) — Phase 6 guardrails:
|
|
365
|
+
[`cost.ts`](./src/budget/cost.ts) (model pricing table + `estimateCost`,
|
|
366
|
+
`HEADLESSCODE_PRICING_JSON` override, conservative fallback for unlisted
|
|
367
|
+
models), [`budget.ts`](./src/budget/budget.ts) (`SessionBudget` +
|
|
368
|
+
`BudgetTracker`: `tick()` before each LLM call, `record()` after with usage
|
|
369
|
+
tokens, `check()` snapshot, `BudgetExceededError`), and
|
|
370
|
+
[`concurrency.ts`](./src/budget/concurrency.ts) (`ConcurrencyLimiter` —
|
|
371
|
+
fail-fast in-process semaphore — plus `activeSessionCount` reading the
|
|
372
|
+
durable orchestrator/watcher state files for a cross-process view). Wired
|
|
373
|
+
into `HeadlessSession` (`budget` config, `budgetUsage` on results), the base
|
|
374
|
+
CLI (`--max-cost-usd` / `--max-duration-ms`), `orchestrate` (aborts at the
|
|
375
|
+
cap), the watcher (defers cap-exceeding issues to `pending`), and
|
|
376
|
+
`run-worker.sh`/`run-qa.sh` (env forwarding).
|
|
377
|
+
- [`src/cloud/`](./src/cloud/provider.ts) — Phase 6 ephemeral compute
|
|
378
|
+
abstraction: the `CloudProvider` lifecycle interface
|
|
379
|
+
(`spawnWorktreeSession` → `waitReady` → `runHarness` → `collectResults` →
|
|
380
|
+
`teardown`) with `LocalProcessProvider` as the current local behavior behind
|
|
381
|
+
it (reuses `spawn-parallel-worktrees.sh` + `run-worker.sh`), so a
|
|
382
|
+
container/VM-per-issue backend slots in without touching the orchestration
|
|
383
|
+
layer. A cloud-provider sketch is documented (evaluation only — no
|
|
384
|
+
live setup; see [`docs/phase6-cloud.md`](./docs/phase6-cloud.md)).
|
|
385
|
+
- [`src/cli.ts`](./src/cli.ts) — the `headlesscode` bin entry (+ `orchestrate`
|
|
386
|
+
and `watch` subcommand dispatch).
|
|
387
|
+
- [`scripts/run-worker.sh`](./scripts/run-worker.sh) — launches one headless
|
|
388
|
+
harness worker per worktree (pid, log, exit code, `.harness.done` marker).
|
|
389
|
+
- [`scripts/run-qa.sh`](./scripts/run-qa.sh) — Phase 4 QA wrapper mirroring
|
|
390
|
+
run-worker.sh: launches one harness QA session per worktree (`.qa-task.md`,
|
|
391
|
+
`.qa.pid`, `qa.log`, `.qa.exit`, `.qa.done/`).
|
|
392
|
+
- [`scripts/deploy-gate.sh`](./scripts/deploy-gate.sh) — Phase 4 gate wrapper:
|
|
393
|
+
path safety, deployment summary, interactive + token/file approval, then
|
|
394
|
+
(and only then) invokes the repo's `scripts/deploy-production.sh` with
|
|
395
|
+
forwarded deploy args. Exit 3 = human DENIED (hard stop).
|
|
396
|
+
- [`scripts/spawn-parallel-worktrees.sh`](./scripts/spawn-parallel-worktrees.sh)
|
|
397
|
+
— spawns one git worktree + harness worker per group (worktree/.env/branch
|
|
398
|
+
conventions, `run-worker.sh` + state-file writes, no GUI involved).
|
|
399
|
+
|
|
400
|
+
### Phase 1 tool filtering decision
|
|
401
|
+
|
|
402
|
+
The loop exposes to the model exactly the tools the executor can actually run
|
|
403
|
+
for the selected mode: the intersection of the vendored mode tool groups
|
|
404
|
+
(`getToolsForMode`) with the Phase 1 executable set
|
|
405
|
+
(`read_file`, `write_to_file`, `execute_command`, `list_files`,
|
|
406
|
+
`attempt_completion`, `ask_followup_question`). Stub-only tools (`apply_diff`,
|
|
407
|
+
`search_files`, …) stay registered in the executor purely as a safety net
|
|
408
|
+
(clear "not implemented" error) but are NOT advertised to the model, so it
|
|
409
|
+
doesn't waste turns calling them.
|
|
410
|
+
|
|
411
|
+
## Development
|
|
412
|
+
|
|
413
|
+
```bash
|
|
414
|
+
npm run typecheck # npx tsc --noEmit (whole repo incl. vendored core)
|
|
415
|
+
npm run smoke # vendored prompt builder smoke test (no network)
|
|
416
|
+
npm test # unit tests with fake LLM clients (no network/key)
|
|
417
|
+
bash scripts/e2e/run.sh # Phase 1 integration (mock OpenRouter)
|
|
418
|
+
bash scripts/e2e-phase2/run.sh # Phase 2 integration (spawn + watch + review)
|
|
419
|
+
bash scripts/e2e-phase4/run.sh # Phase 4 integration (QA + deploy gate, fake deploy)
|
|
420
|
+
bash scripts/e2e-phase5/run.sh # Phase 5 integration (watcher vs fake GitHub server,
|
|
421
|
+
# stubbed spawner: state transitions + no double-spawn)
|
|
422
|
+
bash scripts/e2e-phase6/run.sh # Phase 6 integration (budget abort via mock OpenRouter
|
|
423
|
+
# + concurrency-cap sweep via fake GitHub + stubbed spawner)
|
|
424
|
+
npm run cli -- --dry-run --workspace . # build this repo's system prompt
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
The engine tests (`src/engine/__tests__/loop.test.ts`) run the full loop with a
|
|
428
|
+
fake `LlmClient` injected via the `HeadlessSession` constructor — no network,
|
|
429
|
+
no API key required. Phase 2 adds `src/orchestrator/__tests__/` (split
|
|
430
|
+
heuristics, state round-trip, reviewer verdict parsing), and the e2e scripts
|
|
431
|
+
drive the real CLI through a local mock OpenRouter server, including a
|
|
432
|
+
2-worktree parallel spawn, completion-marker polling, and a read-only review
|
|
433
|
+
invocation. Phase 5 adds `src/watcher/__tests__/` (github client with an
|
|
434
|
+
injected fetch, watcher-state idempotency semantics, and the watch loop with
|
|
435
|
+
an injected gh client + spawner covering spawn/idempotency/cap/failure/
|
|
436
|
+
dry-run/abort) and `scripts/e2e-phase5/run.sh` (watcher against a fake GitHub
|
|
437
|
+
server with a stubbed spawner).
|
|
438
|
+
|
|
439
|
+
## Phase status
|
|
440
|
+
|
|
441
|
+
- ✅ Phase 1 Subtask 1 — vendored portable Zoo Code core
|
|
442
|
+
([`src/vendor/zoo-code/`](./src/vendor/zoo-code/), read-only dependency).
|
|
443
|
+
- ✅ Phase 1 Subtask 2 — runtime engine (OpenRouter client, tool executor,
|
|
444
|
+
parser, orchestration loop, CLI, tests).
|
|
445
|
+
- ✅ Phase 2 — headless orchestration layer: drop-in
|
|
446
|
+
`spawn-parallel-worktrees.sh` + `run-worker.sh` (harness subprocess per
|
|
447
|
+
worktree, `.harness.done` completion markers), issue-splitting heuristics
|
|
448
|
+
port, `.orchestrator-state.json` state management, headless reviewer, and
|
|
449
|
+
the `orchestrate` CLI subcommand (see
|
|
450
|
+
[`docs/phase2-orchestration.md`](./docs/phase2-orchestration.md)).
|
|
451
|
+
- ✅ Phase 3 — memory subsystem: per-project knowledge facts + rolling session
|
|
452
|
+
summaries (`src/memory/`), local deterministic embedder for semantic recall,
|
|
453
|
+
opt-in `HeadlessSession`/CLI wiring (`--memory-dir`, `--no-memory`), and a
|
|
454
|
+
pluggable `MemoryStore` contract with a remote-backend client stub.
|
|
455
|
+
- ✅ Phase 4 — QA + deploy gate: headless QA runs the target repo's `qa-agent`
|
|
456
|
+
mode (`--qa` / `--qa-mode`), verdict parsing is fail-closed, results land in
|
|
457
|
+
the state file's per-group `qa` field; the human-approval deploy gate
|
|
458
|
+
(`--deploy`, `scripts/deploy-gate.sh` + `src/deploy/gate.ts`) is a hard stop
|
|
459
|
+
in front of `deploy-production.sh` that never auto-approves (see
|
|
460
|
+
[`docs/phase4-qa.md`](./docs/phase4-qa.md) and
|
|
461
|
+
[`docs/phase4-deploy-gate.md`](./docs/phase4-deploy-gate.md)).
|
|
462
|
+
- ✅ Phase 5 — GitHub issue watcher: poll-based intake (`watch` subcommand) —
|
|
463
|
+
detect issues by label via the GitHub REST API (`GH_TOKEN`), fan each out
|
|
464
|
+
through `splitIssues` + the existing `spawn-parallel-worktrees.sh`, track
|
|
465
|
+
idempotency durably (`.worktrees/.watcher-state.json`, write-ahead
|
|
466
|
+
ordering, `pending` cap deferral, restart-safe), optional `--dry-run` /
|
|
467
|
+
`--run-once` / `--retry-failed`; webhook upgrade designed but not built as
|
|
468
|
+
a server (see [`docs/phase5-issue-watcher.md`](./docs/phase5-issue-watcher.md)).
|
|
469
|
+
- ✅ Phase 6 — cloud scaling, guardrails-first: per-session cost/time/iteration
|
|
470
|
+
budget (`src/budget/`, `--max-cost-usd` / `--max-duration-ms`, budgetUsage on
|
|
471
|
+
results, worker env forwarding) + a hard concurrent-session cap
|
|
472
|
+
(`HEADLESSCODE_MAX_CONCURRENT_SESSIONS`, orchestrate aborts / watcher defers
|
|
473
|
+
to `pending`) + the `CloudProvider` abstraction with `LocalProcessProvider`
|
|
474
|
+
and an evaluation-only cloud-provider sketch (see
|
|
475
|
+
[`docs/phase6-cloud.md`](./docs/phase6-cloud.md)). No live cloud launched;
|
|
476
|
+
a container/VM-per-issue backend slots in behind the same interface.
|
|
477
|
+
- ⏳ Phase 3 (remaining) — token-based condensation.
|
|
478
|
+
|
|
479
|
+
## Attribution
|
|
480
|
+
|
|
481
|
+
This project contains Apache-2.0-licensed code derived from
|
|
482
|
+
[Zoo Code](https://github.com/Zoo-Code-Org/Zoo-Code)
|
|
483
|
+
(`Zoo-Code-Org/Zoo-Code`, commit `ca9b60f`), itself a Roo Code fork. Prompt
|
|
484
|
+
text, tool schemas, and mode/rules loading logic are reused under the terms of
|
|
485
|
+
the Apache License 2.0. See [`LICENSE`](./LICENSE) and
|
|
486
|
+
[`ATTRIBUTION.md`](./ATTRIBUTION.md).
|