@autopilot-harness/cli 0.2.2 → 0.2.4
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/dist/assets/templates/.autopilotignore +92 -0
- package/dist/assets/templates/skills/autopilot-off/SKILL.md.tpl +8 -0
- package/dist/assets/templates/skills/autopilot-on/SKILL.md.tpl +14 -0
- package/dist/assets/templates/skills/autopilot-replan/SKILL.md.tpl +9 -0
- package/dist/assets/templates/skills/autopilot-resume/SKILL.md.tpl +10 -0
- package/dist/assets/templates/skills/autopilot-run/SKILL.md.tpl +12 -0
- package/dist/assets/templates/workflows/autopilot-executing.md +59 -0
- package/dist/assets/templates/workflows/autopilot-planning.md +42 -0
- package/dist/assets/vendor/runtime.mjs +130 -3
- package/dist/init/install.d.ts.map +1 -1
- package/dist/init/install.js +4 -9
- package/dist/init/install.js.map +1 -1
- package/dist/init/types.d.ts +1 -1
- package/dist/init/types.js +1 -1
- package/dist/locale-set.d.ts.map +1 -1
- package/dist/locale-set.js +3 -6
- package/dist/locale-set.js.map +1 -1
- package/dist/template-paths.d.ts +19 -0
- package/dist/template-paths.d.ts.map +1 -0
- package/dist/template-paths.js +66 -0
- package/dist/template-paths.js.map +1 -0
- package/package.json +24 -9
- package/assets/autopilot-harness-hook.mjs +0 -446
- package/assets/vendor/migrations/001_initial.sql +0 -62
- package/assets/vendor/migrations/002_pending_followup.sql +0 -4
- package/assets/vendor/migrations/003_reviewing_item.sql +0 -1
- package/assets/vendor/runtime.mjs +0 -5907
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Autopilot — paths that do NOT trigger self-review (gitignore syntax).
|
|
2
|
+
#
|
|
3
|
+
# What this file is:
|
|
4
|
+
# - Controls whether an afterFileEdit counts as "product code" (opens fix/confirm).
|
|
5
|
+
# - Does NOT change `git diff` / `git status` output (that is `.gitignore`).
|
|
6
|
+
# - Review followups ask the agent to skip these paths when reading diffs (soft).
|
|
7
|
+
#
|
|
8
|
+
# Semantics:
|
|
9
|
+
# - Same glob rules as gitignore; last matching pattern wins.
|
|
10
|
+
# - Use `!` to force-include an exception (e.g. `!docs/feed/**/*.yml`).
|
|
11
|
+
# - Markdown (*.md / *.mdx) is NOT ignored by default — design docs can be reviewed.
|
|
12
|
+
# - `docs/**` is NOT ignored by default.
|
|
13
|
+
# - Also skip untracked paths ignored by `.gitignore` (tracked files still count).
|
|
14
|
+
#
|
|
15
|
+
# Later (not implemented): hard-filtered review-diff / path ledger — see
|
|
16
|
+
# docs/autopilot/workflows/autopilot-executing.md (B2 strong).
|
|
17
|
+
|
|
18
|
+
# Runtime / editor (prefer also listing these in .gitignore)
|
|
19
|
+
.autopilot/**
|
|
20
|
+
.cursor/**
|
|
21
|
+
.claude/**
|
|
22
|
+
|
|
23
|
+
# Planning artifacts
|
|
24
|
+
plans/**
|
|
25
|
+
|
|
26
|
+
# Common build / vendor trees
|
|
27
|
+
node_modules/**
|
|
28
|
+
dist/**
|
|
29
|
+
build/**
|
|
30
|
+
out/**
|
|
31
|
+
target/**
|
|
32
|
+
.target/**
|
|
33
|
+
coverage/**
|
|
34
|
+
.venv/**
|
|
35
|
+
venv/**
|
|
36
|
+
__pycache__/**
|
|
37
|
+
|
|
38
|
+
# Lockfiles / package manager noise
|
|
39
|
+
package-lock.json
|
|
40
|
+
pnpm-lock.yaml
|
|
41
|
+
yarn.lock
|
|
42
|
+
bun.lock
|
|
43
|
+
bun.lockb
|
|
44
|
+
Cargo.lock
|
|
45
|
+
poetry.lock
|
|
46
|
+
composer.lock
|
|
47
|
+
|
|
48
|
+
# Media / binary (do not trigger self-review)
|
|
49
|
+
*.png
|
|
50
|
+
*.jpg
|
|
51
|
+
*.jpeg
|
|
52
|
+
*.gif
|
|
53
|
+
*.webp
|
|
54
|
+
*.ico
|
|
55
|
+
*.svg
|
|
56
|
+
*.bmp
|
|
57
|
+
*.mp3
|
|
58
|
+
*.mp4
|
|
59
|
+
*.wav
|
|
60
|
+
*.webm
|
|
61
|
+
*.mov
|
|
62
|
+
*.woff
|
|
63
|
+
*.woff2
|
|
64
|
+
*.ttf
|
|
65
|
+
*.otf
|
|
66
|
+
*.eot
|
|
67
|
+
*.pdf
|
|
68
|
+
*.zip
|
|
69
|
+
*.gz
|
|
70
|
+
*.tgz
|
|
71
|
+
*.7z
|
|
72
|
+
*.rar
|
|
73
|
+
*.jar
|
|
74
|
+
*.class
|
|
75
|
+
*.o
|
|
76
|
+
*.a
|
|
77
|
+
*.so
|
|
78
|
+
*.dylib
|
|
79
|
+
*.dll
|
|
80
|
+
*.exe
|
|
81
|
+
*.wasm
|
|
82
|
+
|
|
83
|
+
# Prose / data noise
|
|
84
|
+
*.txt
|
|
85
|
+
*.html
|
|
86
|
+
*.htm
|
|
87
|
+
*.csv
|
|
88
|
+
*.tsv
|
|
89
|
+
*.log
|
|
90
|
+
*.map
|
|
91
|
+
*.min.js
|
|
92
|
+
*.min.css
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: autopilot-off
|
|
3
|
+
description: "{{description}}"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
The submit hook has already disarmed Autopilot for this conversation (paused; phase unchanged unless done→idle).
|
|
7
|
+
|
|
8
|
+
Acknowledge pause. Do not auto-advance. Suggest Autopilot RESUME or Autopilot RUN · <slug> when ready.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: autopilot-on
|
|
3
|
+
description: "{{description}}"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
The submit hook has already set phase=planning for this conversation.
|
|
7
|
+
|
|
8
|
+
Follow **autopilot-planning** workflow (docs/autopilot/workflows/autopilot-planning.md).
|
|
9
|
+
|
|
10
|
+
- initial_brief from text after /autopilot-on → seed Round 1
|
|
11
|
+
- Optional slug: alone after the command, or after `·`, matching `[a-z0-9]+([.-][a-z0-9]+)*` and ≤128 chars (same as RUN); other text is initial_brief. Unsafe explicit slugs (e.g. from API) are rejected by the hook.
|
|
12
|
+
- Look up repo facts with platform tools; do not ask the user for what you can inspect
|
|
13
|
+
- Write plans/<slug>/ artifacts (slug rule above); no product code until /autopilot-run
|
|
14
|
+
- User-visible replies must match the user's language
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: autopilot-replan
|
|
3
|
+
description: "{{description}}"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
The submit hook has set phase=planning and reset the review chain for this track.
|
|
7
|
+
|
|
8
|
+
Revise plan.md and unchecked checklist items only. Do not silently delete completed `[x]` items.
|
|
9
|
+
When ready, prompt `/autopilot-run`.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: autopilot-resume
|
|
3
|
+
description: "{{description}}"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
The submit hook has resumed Autopilot for **this** conversation (cleared pause if any; review chain preserved).
|
|
7
|
+
|
|
8
|
+
If this chat had no session, the hook may have **claimed** an executing track from another conversation (same project) onto this one — including when the old Cursor chat is dead/unreadable. Optional: `/autopilot-resume <slug>` to pick the track when several are executing.
|
|
9
|
+
|
|
10
|
+
Continue from checklist progress and current phase. Do not reset review confirm rounds unless asked.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: autopilot-run
|
|
3
|
+
description: "{{description}}"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
The submit hook has already set phase=executing (or will after track pick) for this conversation.
|
|
7
|
+
|
|
8
|
+
Follow **autopilot-executing** workflow (docs/autopilot/workflows/autopilot-executing.md).
|
|
9
|
+
|
|
10
|
+
- Read plans/<slug>/checklist.md; implement the first unchecked item
|
|
11
|
+
- Obey fix/confirm/advance followups from the stop hook
|
|
12
|
+
- User-visible replies must match the user's language
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Autopilot Executing
|
|
2
|
+
|
|
3
|
+
Implement the current unchecked checklist item, then obey stop-hook followups.
|
|
4
|
+
|
|
5
|
+
## Per-item flow
|
|
6
|
+
|
|
7
|
+
1. Read `plans/<slug>/checklist.md` — work only on `firstUnchecked()` (`- [ ] <id> — <title>`).
|
|
8
|
+
2. Implement within that item's scope (align with `plan.md`).
|
|
9
|
+
3. Machine verify / completion evidence: write `.autopilot/verify-last.json` with matching `itemId` (and `ok: true` when using a hand-written report). Run configured verify commands when present.
|
|
10
|
+
- **Required for every item before you stop** — especially no-code / ops / verify-only items. A stale `itemId` from a prior item blocks advance.
|
|
11
|
+
- After writing the report, **end the turn** so the stop hook can inject advance/done. Do not ask the user to continue; do not invent your own Advance/Done.
|
|
12
|
+
4. Stop hook injects **fix** / **confirm** / **need_evidence** / **advance** / **done** — follow the injected message; do **not** invent your own review lens.
|
|
13
|
+
|
|
14
|
+
### Product code vs no-code items
|
|
15
|
+
|
|
16
|
+
| Situation | Stop behavior |
|
|
17
|
+
|-----------|----------------|
|
|
18
|
+
| You edited product code this item | **fix → confirm →** then verify / advance |
|
|
19
|
+
| No product-code diff (env, ops, paths listed in `.autopilotignore`, or untracked + `.gitignore`) | Skip fix/confirm when `verify-last.json` `itemId` matches the current item (or required verify **pass**); then **advance** / **done**. If the report is missing, stale, or `ok: false`, stop hook injects **need_evidence** — write the matching report and end the turn; do not wait for the user. |
|
|
20
|
+
| Required verify **fail** | `verify_fix` — fix env/report or code; if you edit product code next, fix chain runs first |
|
|
21
|
+
|
|
22
|
+
**What counts as product code (trigger):** any edited path that is **not** matched by `.autopilotignore`, and is **not** an untracked path ignored by `.gitignore`. There is no hardcoded extension allowlist — configure exclusions in `.autopilotignore` (comments in that file explain defaults). Markdown is reviewable by default; `docs/**` is not blocked by default.
|
|
23
|
+
|
|
24
|
+
**Agent review scope (B2 weak):** fix/confirm followups ask the agent to skip `.autopilotignore` hits and untracked `.gitignore` paths when reading `git diff` / `git status`. This is prompt guidance only (soft).
|
|
25
|
+
|
|
26
|
+
**B2 strong (not implemented — future):** harness could emit a filtered diff command or a per-chain product-path ledger so review scope is hard-enforced without relying on the agent. Revisit if soft guidance is insufficient.
|
|
27
|
+
|
|
28
|
+
## Fix vs confirm
|
|
29
|
+
|
|
30
|
+
| Mode | Behavior |
|
|
31
|
+
|------|----------|
|
|
32
|
+
| Fix round | Defect-first on the in-scope diff; fix CRITICAL/HIGH; run relevant tests; **no commit** |
|
|
33
|
+
| Confirm rounds | Only the **injected lens**; CRITICAL/HIGH may fix (returns to fix); final lens is **read-only** |
|
|
34
|
+
| Confirm 1–N | **Never commit** |
|
|
35
|
+
|
|
36
|
+
## Advance / done turn (mandatory order)
|
|
37
|
+
|
|
38
|
+
When followup is advance or done:
|
|
39
|
+
|
|
40
|
+
1. Mark **only** the completed current item named in the followup `[x]` in `checklist.md`. Do **not** mark the next item.
|
|
41
|
+
2. Scoped conventional commit if the working tree has this item's changes — **include `checklist.md`** when `plans/` is committed (no `git add -A`, no secrets / `.autopilot/state.db`).
|
|
42
|
+
3. **Then** start the next unchecked item named in the followup (next turn is OK for large code).
|
|
43
|
+
|
|
44
|
+
### Checklist `[x]` timing (hard)
|
|
45
|
+
|
|
46
|
+
- **Do not** mark the item you are still implementing `[x]` mid-work or mid-review.
|
|
47
|
+
- Only Advance/Done followups check off the **completed** current item.
|
|
48
|
+
- Premature `[x]` used to make the stop-hook name the wrong "next" item; the harness now sticks `reviewing_item_id`, but agents must still obey this rule.
|
|
49
|
+
|
|
50
|
+
If you write next-item code before checking off, `itemId` / verify binding will be wrong.
|
|
51
|
+
|
|
52
|
+
Advance leaves `chain_pending=0` so a docs-only / ignore-only next item does not open a phantom confirm chain; product edits still arm review via `afterFileEdit`.
|
|
53
|
+
|
|
54
|
+
## Hard rules
|
|
55
|
+
|
|
56
|
+
- Do not advance while verify required commands FAIL (hook blocks; rewrite `verify-last.json` after fixing).
|
|
57
|
+
- Configure verify under `.autopilot/config.yml` → `review.verify.commands` (`id` / `run` / `required`).
|
|
58
|
+
- User-visible replies match the user's language.
|
|
59
|
+
- No push / `--no-verify` / amend unless the user explicitly asks in this conversation.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Autopilot Planning
|
|
2
|
+
|
|
3
|
+
Built-in grill / design-tree workflow. Do **not** write product code until `/autopilot-run`.
|
|
4
|
+
|
|
5
|
+
## Frontier format (every round)
|
|
6
|
+
|
|
7
|
+
List every decision you can ask **now** (premises already settled):
|
|
8
|
+
|
|
9
|
+
```markdown
|
|
10
|
+
❓ **Q1** - **<title>**: <body; options if useful>
|
|
11
|
+
|
|
12
|
+
➡️ <recommended answer>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Wait for the user to answer the round, then open the next frontier. Round 1 usually covers goal / scope / acceptance. Later rounds go block → detail.
|
|
16
|
+
|
|
17
|
+
## Brownfield (existing repo)
|
|
18
|
+
|
|
19
|
+
1. Read README and manifests (`package.json`, `Cargo.toml`, `go.mod`, `pyproject.toml`, …).
|
|
20
|
+
2. Search / skim modules related to the request.
|
|
21
|
+
3. Put constraints under **Existing context** in `brief.md`.
|
|
22
|
+
4. Cite **real repo paths** in questions — do not ask the user for facts you can inspect.
|
|
23
|
+
|
|
24
|
+
## Greenfield
|
|
25
|
+
|
|
26
|
+
Skip repo survey; start from goals and constraints.
|
|
27
|
+
|
|
28
|
+
## Artifact timing
|
|
29
|
+
|
|
30
|
+
| When | Write |
|
|
31
|
+
|------|--------|
|
|
32
|
+
| Title is clear | Create `plans/<slug>/` (`brief.md`, `plan.md`, `checklist.md`); update `plans/README.md`. **Slug** = `[a-z0-9]+([.-][a-z0-9]+)*`, length 1–128 (kebab; single dots OK, e.g. `v0.1-npm-release`; no `..`, `/`, `\`, `_`) — same rule as `/autopilot-on|run <slug>` |
|
|
33
|
+
| Frontier nearly empty | Checklist **draft** (`- [ ]`) |
|
|
34
|
+
| User confirms the plan | Finalize checklist: `- [ ] <id> — <title>` (**item id** kebab-case letters/digits/hyphens only — **no dots**) |
|
|
35
|
+
| Ready to build | Prompt **`/autopilot-run`** (or `/autopilot-run <slug>`) |
|
|
36
|
+
|
|
37
|
+
## Hard rules
|
|
38
|
+
|
|
39
|
+
- Planning may only edit `plans/**` and docs — **no product code**.
|
|
40
|
+
- Directory `<slug>` must match `[a-z0-9]+([.-][a-z0-9]+)*` and ≤128 chars (same gate as `/autopilot-on|run <slug>`); checklist **item** ids stay `[a-z0-9]+(-[a-z0-9]+)*` (no dots).
|
|
41
|
+
- User shortcuts: “直接定稿 / skip grill / use your recommendations” may shorten rounds; still produce the three artifacts.
|
|
42
|
+
- User-visible replies match the user's language. Workflow procedure stays English.
|
|
@@ -43,6 +43,7 @@ var en_default = {
|
|
|
43
43
|
review_complete: "Review complete. All {total} confirm rounds passed; the review chain has ended. If the working tree still has uncommitted changes from this session, local commit only per the safe checklist (never stage .env/secrets/.autopilot runtime; no push unless the user asks); if clean, briefly confirm only.",
|
|
44
44
|
stuck: "Stuck: no progress for several stops. Change strategy or send Autopilot RESUME after fixing.",
|
|
45
45
|
verify_fix: "Verify failed ({reason}). Fix verify commands and rewrite verify-last.json; do not advance.",
|
|
46
|
+
need_evidence: `Need evidence: no-code item {currentId}{currentTitleSuffix} cannot advance without matching soft completion evidence. Write .autopilot/verify-last.json with itemId "{currentId}" and ok: true (only after this item's work is done). Then end the turn so the stop hook can advance/done. Do not ask the user to continue; do not invent Advance/Done.`,
|
|
46
47
|
track_pick: "Select a plan by number or slug."
|
|
47
48
|
},
|
|
48
49
|
error: {
|
|
@@ -118,6 +119,7 @@ var zh_CN_default = {
|
|
|
118
119
|
review_complete: "\u81EA\u5BA1\u5B8C\u6210\u3002\u8FDE\u7EED {total} \u8F6E\u786E\u8BA4\u5DF2\u901A\u8FC7\uFF0C\u81EA\u5BA1\u94FE\u5DF2\u7ED3\u675F\u3002\u82E5\u5DE5\u4F5C\u533A\u4ECD\u6709\u672C\u4F1A\u8BDD\u672A\u63D0\u4EA4\u6539\u52A8\uFF0C\u6309\u5B89\u5168\u6E05\u5355\u672C\u5730 commit\uFF08\u52FF stage .env/\u5BC6\u94A5/.autopilot \u8FD0\u884C\u65F6\uFF1B\u52FF push\uFF0C\u9664\u975E\u7528\u6237\u660E\u786E\u8981\u6C42\uFF09\uFF1B\u5DF2\u5E72\u51C0\u5219\u53EA\u7B80\u77ED\u786E\u8BA4\u5373\u53EF\u3002",
|
|
119
120
|
stuck: "\u5361\u4F4F\uFF1A\u8FDE\u7EED\u591A\u8F6E\u65E0\u8FDB\u5C55\u3002\u8BF7\u6362\u7B56\u7565\uFF0C\u6216\u4FEE\u597D\u540E\u53D1\u9001 Autopilot RESUME\u3002",
|
|
120
121
|
verify_fix: "\u6821\u9A8C\u5931\u8D25\uFF08{reason}\uFF09\u3002\u8BF7\u4FEE\u590D verify \u547D\u4EE4\u5E76\u91CD\u5199 verify-last.json\uFF1B\u4E0D\u8981\u63A8\u8FDB\u3002",
|
|
122
|
+
need_evidence: '\u9700\u8981\u5B8C\u6210\u8BC1\u636E\uFF1A\u65E0\u4EE3\u7801\u6539\u52A8\u9879 {currentId}{currentTitleSuffix} \u7F3A\u5C11\u5339\u914D\u7684 soft \u5B8C\u6210\u8BC1\u636E\uFF0C\u65E0\u6CD5\u63A8\u8FDB\u3002\u8BF7\u5199\u5165 .autopilot/verify-last.json\uFF08itemId \u4E3A "{currentId}"\uFF0Cok: true\uFF1B\u987B\u5728\u8BE5\u9879\u5DE5\u4F5C\u5B8C\u6210\u540E\uFF09\u3002\u7136\u540E\u7ED3\u675F\u672C\u56DE\u5408\uFF0C\u7531 stop hook \u63A8\u8FDB/\u5B8C\u6210\u3002\u4E0D\u8981\u8BA9\u7528\u6237\u8BF4\u300C\u7EE7\u7EED\u300D\uFF1B\u4E0D\u8981\u81EA\u884C\u53D1\u660E\u63A8\u8FDB/\u5B8C\u6210\u6307\u4EE4\u3002',
|
|
121
123
|
track_pick: "\u8BF7\u7528\u6570\u5B57\u6216 slug \u9009\u62E9\u8981\u6267\u884C\u7684 plan\u3002"
|
|
122
124
|
},
|
|
123
125
|
error: {
|
|
@@ -1236,6 +1238,8 @@ var StateStore = class _StateStore {
|
|
|
1236
1238
|
OR trim(pending_followup) GLOB '\u81EA\u5BA1\u5B8C\u6210*'
|
|
1237
1239
|
OR trim(pending_followup) GLOB 'Advance*'
|
|
1238
1240
|
OR trim(pending_followup) GLOB '\u63A8\u8FDB*'
|
|
1241
|
+
OR trim(pending_followup) GLOB 'Need evidence*'
|
|
1242
|
+
OR trim(pending_followup) GLOB '\u9700\u8981\u5B8C\u6210\u8BC1\u636E*'
|
|
1239
1243
|
)`;
|
|
1240
1244
|
/**
|
|
1241
1245
|
* SQL predicate: pending is absent or not a recover automation prompt.
|
|
@@ -1877,12 +1881,15 @@ var HARNESS_FOLLOWUP_PREFIXES = [
|
|
|
1877
1881
|
"Recover:",
|
|
1878
1882
|
"Review complete",
|
|
1879
1883
|
"Verify failed",
|
|
1884
|
+
"Need evidence:",
|
|
1880
1885
|
"\u81EA\u5BA1\u4FEE\u590D",
|
|
1881
1886
|
"\u81EA\u5BA1\u786E\u8BA4",
|
|
1882
1887
|
"\u81EA\u5BA1\u5B8C\u6210",
|
|
1883
1888
|
"\u63A8\u8FDB\u4E0B\u4E00\u9879",
|
|
1884
1889
|
"\u5168\u90E8\u5B8C\u6210",
|
|
1885
1890
|
"\u6821\u9A8C\u5931\u8D25",
|
|
1891
|
+
"\u9700\u8981\u5B8C\u6210\u8BC1\u636E\uFF1A",
|
|
1892
|
+
"\u9700\u8981\u5B8C\u6210\u8BC1\u636E:",
|
|
1886
1893
|
// Match zh recover/stuck templates (fullwidth colon) — bare「恢复」is too broad.
|
|
1887
1894
|
"\u6062\u590D\uFF1A",
|
|
1888
1895
|
"\u5361\u4F4F\uFF1A",
|
|
@@ -2284,6 +2291,8 @@ function defaultRender(kind, vars) {
|
|
|
2284
2291
|
return `Stuck: no progress for several stops. Change strategy or send Autopilot RESUME after fixing.`;
|
|
2285
2292
|
case "verify_fix":
|
|
2286
2293
|
return `Verify failed (${vars.reason ?? "unknown"}). Fix verify commands and rewrite verify-last.json; do not advance.`;
|
|
2294
|
+
case "need_evidence":
|
|
2295
|
+
return `Need evidence: no-code item ${vars.currentId ?? ""}${vars.currentTitle ? ` \u2014 ${vars.currentTitle}` : ""} cannot advance without matching soft completion evidence. Write .autopilot/verify-last.json with itemId "${vars.currentId ?? ""}" and ok: true (only after this item's work is done). Then end the turn so the stop hook can advance/done. Do not ask the user to continue; do not invent Advance/Done.`;
|
|
2287
2296
|
default:
|
|
2288
2297
|
return "";
|
|
2289
2298
|
}
|
|
@@ -2575,6 +2584,9 @@ var ReviewEngine = class {
|
|
|
2575
2584
|
if (m.startsWith("Recover") || m.startsWith("\u6062\u590D")) return "recover";
|
|
2576
2585
|
if (m.startsWith("Stuck") || m.startsWith("\u5361\u4F4F")) return "stuck";
|
|
2577
2586
|
if (m.startsWith("Verify failed") || m.startsWith("\u6821\u9A8C\u5931\u8D25")) return "verify_fix";
|
|
2587
|
+
if (m.startsWith("Need evidence") || m.startsWith("\u9700\u8981\u5B8C\u6210\u8BC1\u636E")) {
|
|
2588
|
+
return "need_evidence";
|
|
2589
|
+
}
|
|
2578
2590
|
return "review.confirm";
|
|
2579
2591
|
}
|
|
2580
2592
|
emit(conversationId, action) {
|
|
@@ -2952,6 +2964,9 @@ var ReviewEngine = class {
|
|
|
2952
2964
|
if (line.startsWith("Verify failed") || line.startsWith("\u6821\u9A8C\u5931\u8D25")) {
|
|
2953
2965
|
return "verify";
|
|
2954
2966
|
}
|
|
2967
|
+
if (line.startsWith("Need evidence") || line.startsWith("\u9700\u8981\u5B8C\u6210\u8BC1\u636E")) {
|
|
2968
|
+
return "need_evidence";
|
|
2969
|
+
}
|
|
2955
2970
|
if (line.startsWith("All checklist") || line.startsWith("\u5168\u90E8\u5B8C\u6210") || line.startsWith("Review complete") || line.startsWith("\u81EA\u5BA1\u5B8C\u6210")) {
|
|
2956
2971
|
return "terminal";
|
|
2957
2972
|
}
|
|
@@ -3292,9 +3307,26 @@ var ReviewEngine = class {
|
|
|
3292
3307
|
projectRoot: trustRoot ?? void 0
|
|
3293
3308
|
});
|
|
3294
3309
|
if (evalResult.outcome === "skip") {
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3310
|
+
const trySoftAdvance = (itemId) => this.e0DirectAdvance(session, reportPath, itemId, { kind: "soft" });
|
|
3311
|
+
const advanced = trySoftAdvance(currentItem.id);
|
|
3312
|
+
if (advanced) return advanced;
|
|
3313
|
+
const nudged = this.e0EmitNeedEvidence(session, currentItem);
|
|
3314
|
+
if (nudged) return nudged;
|
|
3315
|
+
const liveChain = this.store.getReviewChain(session.conversation_id);
|
|
3316
|
+
const liveParsed = this.parseSessionChecklist(session);
|
|
3317
|
+
const liveId = liveParsed?.checklist && this.resolveReviewingItemId(
|
|
3318
|
+
liveChain,
|
|
3319
|
+
liveParsed.checklist,
|
|
3320
|
+
currentItem.id
|
|
3321
|
+
) || currentItem.id;
|
|
3322
|
+
if (!hasNoCodeCompletionEvidence({
|
|
3323
|
+
reportPath,
|
|
3324
|
+
currentItemId: liveId,
|
|
3325
|
+
projectRoot: trustRoot ?? void 0
|
|
3326
|
+
})) {
|
|
3327
|
+
return null;
|
|
3328
|
+
}
|
|
3329
|
+
return trySoftAdvance(liveId);
|
|
3298
3330
|
}
|
|
3299
3331
|
if (evalResult.outcome === "pass") {
|
|
3300
3332
|
return this.e0DirectAdvance(session, reportPath, currentItem.id, {
|
|
@@ -3435,6 +3467,96 @@ var ReviewEngine = class {
|
|
|
3435
3467
|
}
|
|
3436
3468
|
return action;
|
|
3437
3469
|
}
|
|
3470
|
+
/**
|
|
3471
|
+
* E0 soft path without completion evidence: inject an actionable nudge instead
|
|
3472
|
+
* of returning null (silent stall). Keeps chain_pending=0 so the next stop
|
|
3473
|
+
* can soft-advance once verify-last.json matches — arming would force phantom E3.
|
|
3474
|
+
* Shared by all platforms (Cursor / Claude Code) via ReviewEngine.
|
|
3475
|
+
*
|
|
3476
|
+
* Do **not** call afterFollowupCommitted here: that resets idle_stop_count and
|
|
3477
|
+
* would undo the stuck progression (same pattern as E5c verify_fix).
|
|
3478
|
+
*/
|
|
3479
|
+
e0EmitNeedEvidence(session, expectedItem) {
|
|
3480
|
+
const cid2 = session.conversation_id;
|
|
3481
|
+
const trustRoot = this.trustedProjectRoot();
|
|
3482
|
+
const reportPath = this.config.verifyReportPath ?? defaultVerifyReportPath(trustRoot ?? "");
|
|
3483
|
+
return this.store.exclusiveWrite(() => {
|
|
3484
|
+
if (!this.sessionRunnable(cid2)) {
|
|
3485
|
+
return { commit: false, value: null };
|
|
3486
|
+
}
|
|
3487
|
+
const sess = this.store.getSession(cid2);
|
|
3488
|
+
if (!sess || !isChecklistExecuting(sess)) {
|
|
3489
|
+
return { commit: false, value: null };
|
|
3490
|
+
}
|
|
3491
|
+
const fresh = this.store.getReviewChain(cid2);
|
|
3492
|
+
if (!fresh || fresh.code_edited === 1 || fresh.confirm_left !== null || fresh.item_confirm_complete === 1 || fresh.chain_pending === 1) {
|
|
3493
|
+
return { commit: false, value: null };
|
|
3494
|
+
}
|
|
3495
|
+
const refreshed = this.parseSessionChecklist(sess);
|
|
3496
|
+
if (!refreshed?.checklist) {
|
|
3497
|
+
return { commit: false, value: null };
|
|
3498
|
+
}
|
|
3499
|
+
const reviewingId = this.resolveReviewingItemId(
|
|
3500
|
+
fresh,
|
|
3501
|
+
refreshed.checklist,
|
|
3502
|
+
expectedItem.id
|
|
3503
|
+
);
|
|
3504
|
+
const lockedItem = reviewingId && refreshed.checklist.items.find((i) => i.id === reviewingId) || refreshed.currentItem;
|
|
3505
|
+
if (!lockedItem) {
|
|
3506
|
+
return { commit: false, value: null };
|
|
3507
|
+
}
|
|
3508
|
+
if (hasNoCodeCompletionEvidence({
|
|
3509
|
+
reportPath,
|
|
3510
|
+
currentItemId: lockedItem.id,
|
|
3511
|
+
projectRoot: trustRoot ?? void 0
|
|
3512
|
+
})) {
|
|
3513
|
+
return { commit: false, value: null };
|
|
3514
|
+
}
|
|
3515
|
+
const nextIdle = sess.idle_stop_count + 1;
|
|
3516
|
+
const nowStuck = nextIdle >= this.config.maxIdleStops;
|
|
3517
|
+
if (nowStuck) {
|
|
3518
|
+
this.store.upsertSession({
|
|
3519
|
+
conversation_id: cid2,
|
|
3520
|
+
project_root: sess.project_root,
|
|
3521
|
+
code_root: sess.code_root,
|
|
3522
|
+
idle_stop_count: nextIdle,
|
|
3523
|
+
paused: 1,
|
|
3524
|
+
paused_reason: "stuck",
|
|
3525
|
+
armed: 0
|
|
3526
|
+
});
|
|
3527
|
+
} else {
|
|
3528
|
+
this.store.upsertSession({
|
|
3529
|
+
conversation_id: cid2,
|
|
3530
|
+
project_root: sess.project_root,
|
|
3531
|
+
code_root: sess.code_root,
|
|
3532
|
+
idle_stop_count: nextIdle
|
|
3533
|
+
});
|
|
3534
|
+
}
|
|
3535
|
+
const kind = nowStuck ? "stuck" : "need_evidence";
|
|
3536
|
+
const title = (lockedItem.title ?? "").trim();
|
|
3537
|
+
const message = this.render(
|
|
3538
|
+
kind,
|
|
3539
|
+
nowStuck ? {} : {
|
|
3540
|
+
currentId: lockedItem.id,
|
|
3541
|
+
currentTitle: title,
|
|
3542
|
+
currentTitleSuffix: title ? ` \u2014 ${title}` : ""
|
|
3543
|
+
}
|
|
3544
|
+
);
|
|
3545
|
+
this.store.updateReviewChain(cid2, {
|
|
3546
|
+
chain_pending: 0,
|
|
3547
|
+
pending_followup: message,
|
|
3548
|
+
pending_followup_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3549
|
+
pending_redeliver_at: null
|
|
3550
|
+
});
|
|
3551
|
+
const out = {
|
|
3552
|
+
kind,
|
|
3553
|
+
message,
|
|
3554
|
+
loop: true,
|
|
3555
|
+
meta: nowStuck ? void 0 : { currentId: lockedItem.id }
|
|
3556
|
+
};
|
|
3557
|
+
return { commit: true, value: out };
|
|
3558
|
+
});
|
|
3559
|
+
}
|
|
3438
3560
|
/** Arm confirm_left=0 + ICC=1 only when still idle on the no-code path. */
|
|
3439
3561
|
armAtE5ForNoCode(conversationId) {
|
|
3440
3562
|
return this.store.exclusiveWrite(() => {
|
|
@@ -4413,6 +4535,11 @@ function createRenderFollowup(bundle) {
|
|
|
4413
4535
|
f.verify_fix ?? "Verify failed ({reason}). Fix verify commands and rewrite verify-last.json; do not advance.",
|
|
4414
4536
|
vars
|
|
4415
4537
|
);
|
|
4538
|
+
case "need_evidence":
|
|
4539
|
+
return renderTemplate(
|
|
4540
|
+
f.need_evidence ?? `Need evidence: no-code item {currentId}{currentTitleSuffix} cannot advance without matching soft completion evidence. Write .autopilot/verify-last.json with itemId "{currentId}" and ok: true (only after this item's work is done). Then end the turn so the stop hook can advance/done. Do not ask the user to continue; do not invent Advance/Done.`,
|
|
4541
|
+
vars
|
|
4542
|
+
);
|
|
4416
4543
|
default:
|
|
4417
4544
|
return "";
|
|
4418
4545
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/init/install.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAGV,UAAU,EACV,cAAc,EACf,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/init/install.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAGV,UAAU,EACV,cAAc,EACf,MAAM,YAAY,CAAC;AA0CpB,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,kBAAkB,EAClB,wBAAwB,EACxB,kBAAkB,EAClB,yBAAyB,EACzB,uBAAuB,EACvB,6BAA6B,EAC7B,oBAAoB,GACrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,mBAAmB,EACnB,2BAA2B,EAC3B,qBAAqB,EACrB,+BAA+B,EAC/B,6BAA6B,EAC7B,4BAA4B,EAC5B,8BAA8B,EAC9B,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,4BAA4B,CAAC;AACpC,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAIxE,kEAAkE;AAClE,eAAO,MAAM,qBAAqB,qGAMxB,CAAC;AAEX,yEAAyE;AACzE,eAAO,MAAM,wBAAwB,8DAG3B,CAAC;AAyUX;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,GACpB,MAAM,GAAG,IAAI,CA6Df;AAwMD,MAAM,MAAM,eAAe,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1E;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,CA8C1E;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,cAAc,GAAG,UAAU,CAuhB/D"}
|
package/dist/init/install.js
CHANGED
|
@@ -12,6 +12,7 @@ import { DEFAULT_AUTOPILOT_IGNORE_TEXT } from "@autopilot-harness/core";
|
|
|
12
12
|
import { readConfigInstallHints, readConfigPlatformsOrThrow } from "./config-merge.js";
|
|
13
13
|
import { applyPlatformsToConfigYaml, assertInstallablePlatforms, isInstallableBinding, MAX_PLATFORM_BINDINGS, mergePlatformBindings, mergedIncludesAllRequested, normalizeBinding, primaryBinding, sanitizePlatformId, } from "./platforms.js";
|
|
14
14
|
import { MAX_UNTRUSTED_TEXT_BYTES, readUntrustedUtf8File, copyFileReplaceSync, copyFileNoFollowExclSync, writeFileReplaceSync, renameReplaceSync, } from "../read-untrusted-file.js";
|
|
15
|
+
import { resolveTemplatesRoot } from "../template-paths.js";
|
|
15
16
|
export { mergeHooksJson, stripAutopilotHooks, isAutopilotCommand, countAutopilotDuplicates, validateHooksShape, hasCompleteAutopilotHooks, summarizeAutopilotHooks, autopilotStopHasUnlimitedLoop, autopilotHookCommand, } from "./hooks-merge.js";
|
|
16
17
|
export { mergeClaudeSettings, validateClaudeSettingsShape, hasClaudeBlockCapZero, hasCompleteClaudeAutopilotHooks, summarizeClaudeAutopilotHooks, stripAutopilotClaudeSettings, claudeSettingsContainAutopilot, CLAUDE_AUTOPILOT_EVENTS, CLAUDE_BLOCK_CAP_ENV, } from "./claude-settings-merge.js";
|
|
17
18
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -33,13 +34,7 @@ const WORKFLOW_FILES = AUTOPILOT_WORKFLOW_FILES;
|
|
|
33
34
|
function resolvePackageRoots() {
|
|
34
35
|
// src/init → ../../ = packages/cli; dist/init → ../../ = packages/cli
|
|
35
36
|
const cliRoot = path.resolve(__dirname, "../..");
|
|
36
|
-
|
|
37
|
-
path.resolve(cliRoot, "../templates"),
|
|
38
|
-
path.resolve(cliRoot, "node_modules/@autopilot-harness/templates"),
|
|
39
|
-
];
|
|
40
|
-
const templatesRoot = candidates.find((p) => isRealDirectory(path.join(p, "skills"))) ??
|
|
41
|
-
candidates[0];
|
|
42
|
-
return { cliRoot, templatesRoot };
|
|
37
|
+
return { cliRoot, templatesRoot: resolveTemplatesRoot(cliRoot) };
|
|
43
38
|
}
|
|
44
39
|
function resolveHookAsset(cliRoot) {
|
|
45
40
|
const candidates = [
|
|
@@ -78,7 +73,7 @@ function resolveVendorRoot(cliRoot) {
|
|
|
78
73
|
function copyVendorDir(cliRoot, destBin, projectRoot) {
|
|
79
74
|
const vendorRoot = resolveVendorRoot(cliRoot);
|
|
80
75
|
if (!vendorRoot) {
|
|
81
|
-
throw new Error("Missing assets/vendor/runtime.mjs
|
|
76
|
+
throw new Error("Missing assets/vendor (or dist/assets/vendor) runtime.mjs/migrations — run pnpm bundle-vendor (or pnpm build)");
|
|
82
77
|
}
|
|
83
78
|
const runtimeSrc = path.join(vendorRoot, "runtime.mjs");
|
|
84
79
|
const migSrcDir = path.join(vendorRoot, "migrations");
|
|
@@ -537,7 +532,7 @@ export function preflightForceRefresh(projectRoot) {
|
|
|
537
532
|
if (!resolveVendorRoot(cliRoot)) {
|
|
538
533
|
return {
|
|
539
534
|
ok: false,
|
|
540
|
-
error: "Missing assets/vendor/runtime.mjs
|
|
535
|
+
error: "Missing assets/vendor (or dist/assets/vendor) runtime.mjs/migrations — run pnpm bundle-vendor (or pnpm build)",
|
|
541
536
|
};
|
|
542
537
|
}
|
|
543
538
|
// Host settings (`.cursor/hooks.json` / `.claude/settings.json`) are validated
|