@glrs-dev/cli 2.2.0 → 2.4.0
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/CHANGELOG.md +4 -0
- package/dist/{chunk-EM4MJBOD.js → chunk-2AZKRWC6.js} +4 -4
- package/dist/{chunk-UXBOTMDY.js → chunk-2P3ETOT2.js} +2 -2
- package/dist/chunk-2VMFXAJH.js +795 -0
- package/dist/chunk-5ZVUFNCP.js +140 -0
- package/dist/{chunk-W37UX3U2.js → chunk-6Y27RQQL.js} +2 -2
- package/dist/{chunk-RZWOWTKF.js → chunk-EKNRKZWR.js} +4 -4
- package/dist/{chunk-YGNDPKIW.js → chunk-HQUCVJ4G.js} +3 -1
- package/dist/{chunk-OABVEBWW.js → chunk-MBEVC327.js} +1 -1
- package/dist/{chunk-SB3MLROC.js → chunk-MCM47HH4.js} +8 -3
- package/dist/{chunk-F3AFRUT2.js → chunk-PTIO556V.js} +2 -2
- package/dist/{chunk-E2UNZIZT.js → chunk-R2WXQ54P.js} +1 -1
- package/dist/{chunk-I2KUXY3I.js → chunk-SMDIOB5B.js} +2 -2
- package/dist/{chunk-SPULDN7P.js → chunk-YY7EWHMA.js} +5 -3
- package/dist/cli.js +31 -20
- package/dist/commands/autopilot-interactive.d.ts +89 -0
- package/dist/commands/autopilot-interactive.js +248 -0
- package/dist/commands/autopilot-raw.d.ts +1 -0
- package/dist/commands/autopilot-raw.js +368 -0
- package/dist/commands/autopilot-tui.d.ts +7 -0
- package/dist/commands/autopilot-tui.js +7 -0
- package/dist/commands/autopilot.d.ts +39 -0
- package/dist/commands/autopilot.js +395 -0
- package/dist/commands/cleanup.js +3 -3
- package/dist/commands/create.js +4 -4
- package/dist/commands/dashboard.d.ts +3 -0
- package/dist/commands/dashboard.js +1549 -0
- package/dist/commands/debrief.d.ts +57 -0
- package/dist/commands/debrief.js +9 -0
- package/dist/commands/delete.js +3 -3
- package/dist/commands/go.js +2 -2
- package/dist/commands/list.js +3 -3
- package/dist/commands/loop.d.ts +42 -0
- package/dist/commands/loop.js +133 -0
- package/dist/commands/plan-picker.d.ts +15 -0
- package/dist/commands/plan-picker.js +76 -0
- package/dist/commands/scoper.d.ts +54 -0
- package/dist/commands/scoper.js +341 -0
- package/dist/commands/switch.js +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/lib/auto-update.js +1 -1
- package/dist/lib/config.d.ts +3 -2
- package/dist/lib/config.js +1 -1
- package/dist/lib/registry.d.ts +2 -0
- package/dist/lib/registry.js +1 -1
- package/dist/lib/worktree.js +3 -3
- package/dist/vendor/harness-opencode/dist/agents/prompts/build.md +16 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/code-reviewer-thorough.md +6 -7
- package/dist/vendor/harness-opencode/dist/agents/prompts/debriefer.md +55 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/plan-reviewer.md +2 -1
- package/dist/vendor/harness-opencode/dist/agents/prompts/plan.md +104 -7
- package/dist/vendor/harness-opencode/dist/agents/prompts/prime.md +4 -2
- package/dist/vendor/harness-opencode/dist/agents/prompts/scoper.md +129 -0
- package/dist/vendor/harness-opencode/dist/agents/prompts/spec-reviewer.md +0 -1
- package/dist/vendor/harness-opencode/dist/agents/prompts/spec-reviewer.open.md +0 -1
- package/dist/vendor/harness-opencode/dist/chunk-GILWWWMB.js +66 -0
- package/dist/vendor/harness-opencode/dist/cli.js +328 -687
- package/dist/vendor/harness-opencode/dist/index.js +123 -20
- package/dist/vendor/harness-opencode/dist/plugin-check-GJRD2OK6.js +14 -0
- package/dist/vendor/harness-opencode/dist/skills/spear-protocol/SKILL.md +2 -1
- package/dist/vendor/harness-opencode/package.json +1 -1
- package/package.json +10 -2
- package/dist/vendor/harness-opencode/dist/autopilot/prompt-template.md +0 -80
- package/dist/vendor/harness-opencode/dist/bin/plan-check.sh +0 -255
|
@@ -59,6 +59,7 @@ function readPrompt(name) {
|
|
|
59
59
|
throw new Error(`Could not find prompt file: ${name}`);
|
|
60
60
|
}
|
|
61
61
|
var primePrompt = readPrompt("prime.md");
|
|
62
|
+
var scoperPrompt = readPrompt("scoper.md");
|
|
62
63
|
var planPrompt = readPrompt("plan.md");
|
|
63
64
|
var buildPrompt = readPrompt("build.md");
|
|
64
65
|
var buildOpenPrompt = readPrompt("build.open.md");
|
|
@@ -78,6 +79,7 @@ var researchPrompt = readPrompt("research.md");
|
|
|
78
79
|
var researchWebPrompt = readPrompt("research-web.md");
|
|
79
80
|
var researchLocalPrompt = readPrompt("research-local.md");
|
|
80
81
|
var researchAutoPrompt = readPrompt("research-auto.md");
|
|
82
|
+
var debrieferPrompt = readPrompt("debriefer.md");
|
|
81
83
|
var EXECUTOR_VARIANT_AGENTS = {
|
|
82
84
|
build: { reasoning: buildPrompt, strict: buildOpenPrompt },
|
|
83
85
|
"spec-reviewer": { reasoning: specReviewerPrompt, strict: specReviewerOpenPrompt },
|
|
@@ -214,7 +216,7 @@ var CORE_BASH_ALLOW_LIST = {
|
|
|
214
216
|
"eslint *": "allow",
|
|
215
217
|
"prettier *": "allow",
|
|
216
218
|
"biome *": "allow",
|
|
217
|
-
// Our own CLI
|
|
219
|
+
// Our own CLI (install, doctor, autopilot, etc.) — reviewer/build invocations.
|
|
218
220
|
"bunx @glrs-dev/harness-plugin-opencode *": "allow",
|
|
219
221
|
"glrs-oc *": "allow",
|
|
220
222
|
// GitHub CLI — read-only gh calls are fine; destructive `gh pr merge`
|
|
@@ -269,21 +271,33 @@ var PRIME_PERMISSIONS = {
|
|
|
269
271
|
playwright: "allow",
|
|
270
272
|
linear: "allow"
|
|
271
273
|
};
|
|
274
|
+
var SCOPER_PERMISSIONS = {
|
|
275
|
+
...PRIME_PERMISSIONS
|
|
276
|
+
};
|
|
277
|
+
var SCOPER_DISABLED_TOOLS = {
|
|
278
|
+
question: false
|
|
279
|
+
};
|
|
280
|
+
var AUTOPILOT_PRIME_DISABLED_TOOLS = {
|
|
281
|
+
question: false
|
|
282
|
+
};
|
|
272
283
|
var PLAN_PERMISSIONS = {
|
|
273
284
|
edit: "allow",
|
|
274
|
-
|
|
275
|
-
//
|
|
276
|
-
//
|
|
277
|
-
//
|
|
278
|
-
//
|
|
279
|
-
//
|
|
280
|
-
//
|
|
285
|
+
write: "allow",
|
|
286
|
+
// Plan agent is read-only aside from writing under the plan dir. It
|
|
287
|
+
// resolves the plan dir inline (see src/agents/prompts/plan.md
|
|
288
|
+
// `## 4. Write the plan`): `$HOME/.glorious/opencode/<repo-folder>/plans/`,
|
|
289
|
+
// where `<repo-folder>` comes from
|
|
290
|
+
// `basename(dirname(git rev-parse --git-common-dir))`. The object-form
|
|
291
|
+
// denies bash broadly and re-allows only the four commands that snippet
|
|
292
|
+
// needs. Everything else remains denied, preserving the "plan writes only
|
|
293
|
+
// plan files" invariant (the write-scope constraint is prompt-enforced,
|
|
294
|
+
// not permission-enforced).
|
|
281
295
|
bash: {
|
|
282
296
|
"*": "deny",
|
|
283
|
-
"
|
|
284
|
-
"
|
|
285
|
-
"
|
|
286
|
-
"
|
|
297
|
+
"git rev-parse --git-common-dir": "allow",
|
|
298
|
+
"basename *": "allow",
|
|
299
|
+
"dirname *": "allow",
|
|
300
|
+
"mkdir -p *": "allow"
|
|
287
301
|
},
|
|
288
302
|
webfetch: "allow",
|
|
289
303
|
ast_grep: "deny",
|
|
@@ -500,8 +514,40 @@ var RESEARCH_PERMISSIONS = {
|
|
|
500
514
|
playwright: "allow",
|
|
501
515
|
linear: "allow"
|
|
502
516
|
};
|
|
517
|
+
var DEBRIEFER_PERMISSIONS = {
|
|
518
|
+
edit: "deny",
|
|
519
|
+
bash: {
|
|
520
|
+
"*": "deny",
|
|
521
|
+
"git log *": "allow",
|
|
522
|
+
"git diff *": "allow",
|
|
523
|
+
"git show *": "allow",
|
|
524
|
+
"git status *": "allow",
|
|
525
|
+
"git rev-parse *": "allow",
|
|
526
|
+
"git branch *": "allow",
|
|
527
|
+
"cat *": "allow",
|
|
528
|
+
"head *": "allow",
|
|
529
|
+
"tail *": "allow",
|
|
530
|
+
"ls *": "allow",
|
|
531
|
+
"wc *": "allow"
|
|
532
|
+
},
|
|
533
|
+
webfetch: "deny",
|
|
534
|
+
ast_grep: "deny",
|
|
535
|
+
tsc_check: "deny",
|
|
536
|
+
eslint_check: "deny",
|
|
537
|
+
todo_scan: "deny",
|
|
538
|
+
comment_check: "deny",
|
|
539
|
+
question: "deny",
|
|
540
|
+
serena: "deny",
|
|
541
|
+
memory: "deny",
|
|
542
|
+
git: "allow",
|
|
543
|
+
playwright: "deny",
|
|
544
|
+
linear: "deny"
|
|
545
|
+
};
|
|
503
546
|
var AGENT_TIERS = {
|
|
504
547
|
prime: "deep",
|
|
548
|
+
scoper: "deep",
|
|
549
|
+
"autopilot-prime": "deep",
|
|
550
|
+
"autopilot-fast": "autopilot-execute",
|
|
505
551
|
plan: "deep",
|
|
506
552
|
"architecture-advisor": "deep",
|
|
507
553
|
"plan-reviewer": "deep",
|
|
@@ -517,6 +563,7 @@ var AGENT_TIERS = {
|
|
|
517
563
|
"docs-maintainer": "mid",
|
|
518
564
|
"lib-reader": "mid",
|
|
519
565
|
"agents-md-writer": "mid",
|
|
566
|
+
debriefer: "mid",
|
|
520
567
|
"code-searcher": "fast"
|
|
521
568
|
};
|
|
522
569
|
function createAgents() {
|
|
@@ -529,11 +576,45 @@ function createAgents() {
|
|
|
529
576
|
temperature: 0.2,
|
|
530
577
|
permission: PRIME_PERMISSIONS
|
|
531
578
|
}),
|
|
579
|
+
scoper: agentFromPrompt(scoperPrompt, {
|
|
580
|
+
description: "Interactive scoping agent. Runs an inquirer-driven wizard loop \u2014 asks short questions via assistant text, collects answers via inquirer, then writes scope.md. Use at the start of a new feature to align on intent, constraints, and acceptance criteria before planning.",
|
|
581
|
+
mode: "primary",
|
|
582
|
+
model: "anthropic/claude-opus-4-7",
|
|
583
|
+
temperature: 0.3,
|
|
584
|
+
permission: SCOPER_PERMISSIONS,
|
|
585
|
+
tools: SCOPER_DISABLED_TOOLS
|
|
586
|
+
}),
|
|
587
|
+
"autopilot-prime": agentFromPrompt(primePrompt, {
|
|
588
|
+
description: "PRIME for unattended autopilot sessions. Identical to `prime` except the `question` tool is disabled \u2014 autopilot has no user to answer interactive prompts, and a blocking question deadlocks the session. Not user-selectable; invoked by the Ralph loop.",
|
|
589
|
+
mode: "subagent",
|
|
590
|
+
model: "anthropic/claude-opus-4-7",
|
|
591
|
+
temperature: 0.2,
|
|
592
|
+
permission: {
|
|
593
|
+
...PRIME_PERMISSIONS,
|
|
594
|
+
question: "deny"
|
|
595
|
+
},
|
|
596
|
+
tools: AUTOPILOT_PRIME_DISABLED_TOOLS
|
|
597
|
+
}),
|
|
598
|
+
"autopilot-fast": agentFromPrompt(primePrompt, {
|
|
599
|
+
description: "Fast executor for autopilot sessions. Same prompt as autopilot-prime but runs on the mid-execute tier (Kimi 2.5/2.6, GLM-5). Used when --fast is passed. Plans must be enriched with mirror refs and code pointers before using this agent.",
|
|
600
|
+
mode: "subagent",
|
|
601
|
+
model: "anthropic/claude-sonnet-4-6",
|
|
602
|
+
temperature: 0.1,
|
|
603
|
+
permission: {
|
|
604
|
+
...PRIME_PERMISSIONS,
|
|
605
|
+
question: "deny"
|
|
606
|
+
},
|
|
607
|
+
tools: AUTOPILOT_PRIME_DISABLED_TOOLS
|
|
608
|
+
}),
|
|
532
609
|
plan: agentFromPrompt(planPrompt, {
|
|
533
|
-
description: "Interactive planner. Orchestrates gap analysis and adversarial review. Produces a written plan in the repo-shared plan directory (
|
|
610
|
+
description: "Interactive planner. Orchestrates gap analysis and adversarial review. Produces a written plan in the repo-shared plan directory (`~/.glorious/opencode/<repo-folder>/plans/`, resolved inline via `git rev-parse --git-common-dir`).",
|
|
534
611
|
mode: "all",
|
|
535
612
|
model: "anthropic/claude-opus-4-7",
|
|
536
613
|
temperature: 0.3,
|
|
614
|
+
// @plan dispatches @gap-analyzer, @code-searcher, and @plan-reviewer
|
|
615
|
+
// as subagents. OpenCode strips the `task` tool from subagent contexts
|
|
616
|
+
// by default; explicit opt-in re-enables it.
|
|
617
|
+
tools: { task: true },
|
|
537
618
|
permission: PLAN_PERMISSIONS
|
|
538
619
|
}),
|
|
539
620
|
build: agentFromPrompt(buildPrompt, {
|
|
@@ -580,6 +661,8 @@ function createAgents() {
|
|
|
580
661
|
mode: "all",
|
|
581
662
|
model: "anthropic/claude-opus-4-7",
|
|
582
663
|
temperature: 0.3,
|
|
664
|
+
// @research dispatches @research-web, @research-local, @research-auto.
|
|
665
|
+
tools: { task: true },
|
|
583
666
|
permission: RESEARCH_PERMISSIONS
|
|
584
667
|
}),
|
|
585
668
|
// Research subagents — thin shims that load the bundled skills.
|
|
@@ -591,6 +674,8 @@ function createAgents() {
|
|
|
591
674
|
mode: "subagent",
|
|
592
675
|
model: "anthropic/claude-opus-4-7",
|
|
593
676
|
temperature: 0.3,
|
|
677
|
+
// @research-web dispatches its own parallel workstream agents.
|
|
678
|
+
tools: { task: true },
|
|
594
679
|
permission: RESEARCH_PERMISSIONS
|
|
595
680
|
}),
|
|
596
681
|
"research-local": agentFromPrompt(researchLocalPrompt, {
|
|
@@ -598,6 +683,8 @@ function createAgents() {
|
|
|
598
683
|
mode: "subagent",
|
|
599
684
|
model: "anthropic/claude-opus-4-7",
|
|
600
685
|
temperature: 0.3,
|
|
686
|
+
// @research-local dispatches parallel Explore subagents.
|
|
687
|
+
tools: { task: true },
|
|
601
688
|
permission: RESEARCH_PERMISSIONS
|
|
602
689
|
}),
|
|
603
690
|
"research-auto": agentFromPrompt(researchAutoPrompt, {
|
|
@@ -606,6 +693,10 @@ function createAgents() {
|
|
|
606
693
|
model: "anthropic/claude-opus-4-7",
|
|
607
694
|
temperature: 0.3,
|
|
608
695
|
permission: RESEARCH_PERMISSIONS
|
|
696
|
+
}),
|
|
697
|
+
// Debriefer — post-run summary agent for the autopilot CLI
|
|
698
|
+
debriefer: agentFromPrompt(debrieferPrompt, {
|
|
699
|
+
permission: DEBRIEFER_PERMISSIONS
|
|
609
700
|
})
|
|
610
701
|
};
|
|
611
702
|
}
|
|
@@ -773,6 +864,9 @@ function resolveHarnessModels(agents, config, pluginOptions) {
|
|
|
773
864
|
const tier = AGENT_TIERS[agentName];
|
|
774
865
|
if (tier) {
|
|
775
866
|
let perTier = modelsConfig[tier];
|
|
867
|
+
if (tier === "autopilot-execute" && perTier === void 0) {
|
|
868
|
+
perTier = modelsConfig["mid-execute"] ?? modelsConfig["mid"];
|
|
869
|
+
}
|
|
776
870
|
if (tier === "mid-execute" && perTier === void 0) {
|
|
777
871
|
perTier = modelsConfig["mid"];
|
|
778
872
|
}
|
|
@@ -1366,8 +1460,14 @@ var plugin = async ({ $, client }) => {
|
|
|
1366
1460
|
}
|
|
1367
1461
|
}
|
|
1368
1462
|
return {
|
|
1369
|
-
// Notify when a permission prompt fires (replaces the old permission.asked event)
|
|
1463
|
+
// Notify when a permission prompt fires (replaces the old permission.asked event).
|
|
1464
|
+
// In headless autopilot mode, auto-deny question permissions at the plugin level
|
|
1465
|
+
// so they never reach the event stream — prevents wasted loop iterations.
|
|
1370
1466
|
"permission.ask": async (input, output) => {
|
|
1467
|
+
if (process.env["GLRS_AUTOPILOT_HEADLESS"] === "1" && (input?.type === "question" || input?.title === "")) {
|
|
1468
|
+
output.status = "deny";
|
|
1469
|
+
return;
|
|
1470
|
+
}
|
|
1371
1471
|
const tool6 = input?.tool ?? input?.title ?? "a tool";
|
|
1372
1472
|
await notify("opencode permission required", `Approval needed for ${tool6}.`);
|
|
1373
1473
|
}
|
|
@@ -2025,7 +2125,7 @@ import { join as join9 } from "path";
|
|
|
2025
2125
|
var APP_KEY = "A-US-3617699429";
|
|
2026
2126
|
var ENDPOINT = "https://us.aptabase.com/api/v0/event";
|
|
2027
2127
|
var PKG_NAME = "@glrs-dev/harness-plugin-opencode";
|
|
2028
|
-
var PKG_VERSION = true ? "2.
|
|
2128
|
+
var PKG_VERSION = true ? "2.4.0" : "dev";
|
|
2029
2129
|
var DISABLED = process.env.HARNESS_OPENCODE_TELEMETRY === "0" || process.env.HARNESS_OPENCODE_TELEMETRY === "false" || process.env.DO_NOT_TRACK === "1" || process.env.CI === "true";
|
|
2030
2130
|
var SESSION_ID = randomUUID();
|
|
2031
2131
|
function getInstallId() {
|
|
@@ -2231,11 +2331,14 @@ var plugin5 = async (input, options) => {
|
|
|
2231
2331
|
}
|
|
2232
2332
|
};
|
|
2233
2333
|
const hasTelemetryBefore = telemetryHooks["tool.execute.before"] !== void 0;
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2334
|
+
hooks["tool.execute.before"] = async (input2, output) => {
|
|
2335
|
+
if (process.env["GLRS_AUTOPILOT_HEADLESS"] === "1" && input2.tool === "question") {
|
|
2336
|
+
throw new Error(
|
|
2337
|
+
"The question tool is not available in autopilot mode. Pick a sensible default and continue without asking the user."
|
|
2338
|
+
);
|
|
2339
|
+
}
|
|
2340
|
+
if (hasTelemetryBefore) await telemetryHooks["tool.execute.before"](input2, output);
|
|
2341
|
+
};
|
|
2239
2342
|
const hasToolHooksAfter = toolHooks["tool.execute.after"] !== void 0;
|
|
2240
2343
|
const hasTelemetryAfter = telemetryHooks["tool.execute.after"] !== void 0;
|
|
2241
2344
|
if (hasToolHooksAfter || hasTelemetryAfter) {
|
|
@@ -14,7 +14,8 @@ Before Scope, run this probe inline (no subagent) — sessions typically start i
|
|
|
14
14
|
1. `pwd` — confirm working directory.
|
|
15
15
|
2. `git status --short` — see uncommitted work.
|
|
16
16
|
3. `git log --oneline -5` — recent history.
|
|
17
|
-
4.
|
|
17
|
+
4. Resolve the plan dir and list recent plans:
|
|
18
|
+
`PLAN_BASE="${GLORIOUS_PLAN_DIR:-$HOME/.glorious/opencode}" && GIT_COMMON="$(git rev-parse --git-common-dir 2>/dev/null)" && [ -n "$GIT_COMMON" ] && [[ "$GIT_COMMON" != /* ]] && GIT_COMMON="$PWD/$GIT_COMMON"; REPO_FOLDER="$(basename "$(dirname "$GIT_COMMON")" 2>/dev/null)" && [ -n "$REPO_FOLDER" ] && [ "$REPO_FOLDER" != "." ] && ls "$PLAN_BASE/$REPO_FOLDER/plans" 2>/dev/null | tail -5` — plans for this repo.
|
|
18
19
|
|
|
19
20
|
For each plan found, read it and count unchecked acceptance items. Classify as **stale** (ignore) only if `git merge-base --is-ancestor HEAD origin/main` (fallback `origin/master`) exits 0. If classification fails, treat as active.
|
|
20
21
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glrs-dev/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Unified CLI for the @glrs-dev ecosystem — OpenCode agent harness dispatch + worktree management.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -38,15 +38,22 @@
|
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "bun scripts/build.ts",
|
|
40
40
|
"typecheck": "tsc --noEmit",
|
|
41
|
-
"test": "bun test src/",
|
|
41
|
+
"test": "bun test src/ test/",
|
|
42
42
|
"lint": "echo 'no linter configured yet'"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
+
"@glrs-dev/autopilot": "workspace:*",
|
|
46
|
+
"@glrs-dev/adapter-opencode": "workspace:*",
|
|
47
|
+
"@inkjs/ui": "^2.0.0",
|
|
45
48
|
"@inquirer/prompts": "^8.4.2",
|
|
46
49
|
"@opencode-ai/plugin": "^1.14",
|
|
47
50
|
"@opencode-ai/sdk": "^1.14",
|
|
48
51
|
"cmd-ts": "^0.15.0",
|
|
52
|
+
"ink": "^5.0.0",
|
|
49
53
|
"picomatch": "^4.0.4",
|
|
54
|
+
"pino": "^10.3.1",
|
|
55
|
+
"pino-pretty": "^13.1.3",
|
|
56
|
+
"react": "^18.3.1",
|
|
50
57
|
"ulid": "^3.0.2",
|
|
51
58
|
"yaml": "^2.8.3",
|
|
52
59
|
"zod": "4.1.8"
|
|
@@ -55,6 +62,7 @@
|
|
|
55
62
|
"@glrs-dev/harness-plugin-opencode": "workspace:*",
|
|
56
63
|
"@types/bun": "latest",
|
|
57
64
|
"@types/node": "^22",
|
|
65
|
+
"@types/react": "^18.3.0",
|
|
58
66
|
"tsup": "^8",
|
|
59
67
|
"typescript": "^5"
|
|
60
68
|
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Self-driving PRIME run. Accepts an issue-tracker reference, a free-form task description, or a question.
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
You are running in autopilot mode. The user invoked `/autopilot` to hand off a task for lights-out execution. Work through the normal SPEAR workflow until all work described in the prompt is complete, then emit `<autopilot-done>` as the **first token** of your final message.
|
|
6
|
-
|
|
7
|
-
**Sentinel contract.** When ALL work described in the user's prompt is genuinely complete (plan executed, Resolve stage done, PR open), emit `<autopilot-done>` at the very start of your final message. The Ralph loop driver watches for this tag to know when to stop. Do NOT emit `<autopilot-done>` prematurely — only when you have truly finished everything the prompt asked for.
|
|
8
|
-
|
|
9
|
-
**Single-shot TUI path.** When invoked from the TUI (not the CLI driver), there is no external loop watching for the sentinel. Run the SPEAR workflow once to completion. The sentinel is harmless in this context — emit it anyway so the output is consistent.
|
|
10
|
-
|
|
11
|
-
**Kill switch.** If `.agent/autopilot-disable` exists in the worktree, the CLI driver will have already stopped before sending this prompt. No action needed from you.
|
|
12
|
-
|
|
13
|
-
The user wants autopilot to process: $ARGUMENTS
|
|
14
|
-
|
|
15
|
-
## 0. Workflow-mechanics: decide before anything else
|
|
16
|
-
|
|
17
|
-
Before classifying the argument, apply the workflow-mechanics heuristic from `prime.md` § `# Workflow-mechanics decisions`. Autopilot is lights-out: the rule fires automatically and silently (single line of chat, no `question` tool). Never ask the user whether to open a fresh worktree, switch branches, or stack on current — the heuristic decides.
|
|
18
|
-
|
|
19
|
-
Abort paths (dirty tree on default branch; dirty tree on feature branch with unrelated work) mean STOP and report the one-sentence reason. The user resolves and re-runs.
|
|
20
|
-
|
|
21
|
-
If you auto-invoke `/fresh`, do NOT pass `--clean`. Cleanup stays user-triggered.
|
|
22
|
-
|
|
23
|
-
## 1. Classify the argument
|
|
24
|
-
|
|
25
|
-
Pick ONE of these paths:
|
|
26
|
-
|
|
27
|
-
- **Issue-tracker reference** (single issue) — match any of:
|
|
28
|
-
- `<PROJECT>-<NUMBER>` where PROJECT is 2–10 uppercase letters (e.g. `ENG-1234`, `GEN-1114`) — Linear, Jira, YouTrack, Shortcut, etc.
|
|
29
|
-
- `#<NUMBER>` alone (e.g. `#1234`) — GitHub shorthand
|
|
30
|
-
- A URL to a recognized tracker (`github.com/.../issues/123`, `linear.app/.../issue/...`, `*.atlassian.net/browse/...`)
|
|
31
|
-
- **Free-form task description** — any natural-language request that isn't a recognized issue ref
|
|
32
|
-
- **Question** — starts with what/why/how/when/where/which/who, or ends with `?`
|
|
33
|
-
|
|
34
|
-
## 2. Fetch issue content (only if step 1 returned an issue ref)
|
|
35
|
-
|
|
36
|
-
Probe in order, stop at the first that returns real content:
|
|
37
|
-
|
|
38
|
-
1. **Linear MCP** — if configured and the arg matches `<PROJECT>-<NUMBER>` shape OR is a `linear.app` URL: `linear_get_issue`.
|
|
39
|
-
2. **GitHub MCP** — if configured OR the arg is a `github.com/.../issues/...` URL OR is `#<NUMBER>` and `gh` CLI is available.
|
|
40
|
-
3. **Jira / Atlassian MCP** — if configured and the arg matches `<PROJECT>-<NUMBER>` OR is an `*.atlassian.net` URL.
|
|
41
|
-
|
|
42
|
-
If no probe resolves, report once: *"I see a ref that looks like a ticket (`<arg>`), but no issue-tracker MCP is configured. Treating as free-form — paste the issue body if you want me to ground in it."* Then proceed as free-form.
|
|
43
|
-
|
|
44
|
-
Treat the fetched issue's title + description + acceptance criteria as the intent baseline. Map to the plan's `## Acceptance criteria` 1:1, in order. Do not invent entries.
|
|
45
|
-
|
|
46
|
-
## 3. Run the PRIME arc
|
|
47
|
-
|
|
48
|
-
Run the normal SPEAR workflow from `prime.md`. Key adaptations for autopilot mode:
|
|
49
|
-
|
|
50
|
-
- **Scope.** Already classified; skip redundant classification. Announce the frame as `→ Frame:` and proceed — do NOT use the `question` tool to confirm. The user is walked away.
|
|
51
|
-
- **Plan.** Delegate to `@plan`. For ref-originated requests, cite the issue ID in the plan's `## Goal`. The plan's `## Acceptance criteria` maps 1:1 to the ticket's Changes / Definition of Done list.
|
|
52
|
-
- **Execute.** Delegate to `@build`. `@build` executes file-by-file and returns a summary; PRIME relays progress. Acceptance boxes get checked during `@build`'s execution.
|
|
53
|
-
- **Assess.** Full suite pass + `@spec-reviewer` → `@code-reviewer` → iterate to `[PASS]`. No sentinel tokens during intermediate steps.
|
|
54
|
-
- **Resolve.** Complete the Resolve stage: push branch, open PR via `gh pr create`, print PR URL.
|
|
55
|
-
- **Multi-issue workflows.** If the prompt describes multiple issues, use `/fresh` between issues to isolate each on its own branch. Complete each issue's full SPEAR arc (including Resolve) before moving to the next.
|
|
56
|
-
|
|
57
|
-
## 4. Guardrails
|
|
58
|
-
|
|
59
|
-
- **Never ask scoping questions.** The issue's acceptance list IS the authoritative scope. If you're tempted to ask whether to include X, the answer is: if the ticket didn't ask for it, don't include it. The `question` tool is forbidden in autopilot mode except for one narrow case: an architectural fork that blocks all progress AFTER codebase inspection, `@gap-analyzer` consultation, and precedent search (`git log`) have ALL failed to determine a default.
|
|
60
|
-
- **Precedent defaults.** For helper-file location, naming, logging verbosity, error-wrapper style: search `git log` for a recent similar PR and mirror its structure. Cite the precedent commit in `## Constraints`.
|
|
61
|
-
- **Plan-revision budget.** After `@plan-reviewer` returns `[REJECT]`: 1st REJECT → fix listed issues, resubmit. 2nd REJECT → narrow scope (move disputed items to `## Out of scope`). 3rd REJECT → escalate to `@architecture-advisor`.
|
|
62
|
-
- **Resolve auto-ships.** When Assess returns `[PASS]`, complete the Resolve stage: push branch, open PR via `gh pr create`, print the PR URL, then stop. Do NOT re-invoke `/ship` — Resolve already did the work. `/ship` exists only as a manual resume path for interrupted sessions.
|
|
63
|
-
- **Hard rules from Resolve still apply.** Never `--force`-push, never push to `main`/`master`, never `--no-verify`, never merge the PR yourself. Resolve only pushes the feature branch and opens the PR; the human gate is PR review and merge.
|
|
64
|
-
- **Circular failure.** If the same test fails after the same fix twice, delegate to `@architecture-advisor` before a third attempt.
|
|
65
|
-
- **STOP when stuck, don't churn.** If the plan is structurally wrong for this session (wrong branch, un-tickable AC, missing upstream work), emit a single line starting with `STOP:` followed by the specific reason. Do not re-attempt.
|
|
66
|
-
|
|
67
|
-
## 5. Completion
|
|
68
|
-
|
|
69
|
-
When ALL work described in the prompt is complete (every issue resolved, every PR open), emit `<autopilot-done>` as the first token of your final message, followed by a brief summary:
|
|
70
|
-
|
|
71
|
-
```
|
|
72
|
-
<autopilot-done>
|
|
73
|
-
|
|
74
|
-
Done. <One-sentence summary of what was built.>
|
|
75
|
-
PR(s): <url(s)>
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
If Resolve failed or was interrupted, report the failure and the resume command: `/ship <plan-path>`.
|
|
79
|
-
|
|
80
|
-
If you stopped early due to a structural block, emit `STOP: <reason>` instead of `<autopilot-done>`.
|
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# plan-check.sh — parse a plan file's plan-state fence and report on it.
|
|
3
|
-
#
|
|
4
|
-
# Modes:
|
|
5
|
-
# plan-check.sh <path> Prints a summary line then one line per item:
|
|
6
|
-
# `total=N done=M pending=K invalid=I`
|
|
7
|
-
# `STATUS ID VERIFY` (one per item)
|
|
8
|
-
#
|
|
9
|
-
# plan-check.sh --run <path> Prints the verify command of each PENDING
|
|
10
|
-
# item on stdout, one per line, raw. The
|
|
11
|
-
# caller is responsible for executing them.
|
|
12
|
-
# This script NEVER executes verify commands
|
|
13
|
-
# itself — that would bypass the caller's
|
|
14
|
-
# bash-permission scope.
|
|
15
|
-
#
|
|
16
|
-
# plan-check.sh --check <path>
|
|
17
|
-
# Structural validation only. Exits 1 if any
|
|
18
|
-
# fence item is missing a required field.
|
|
19
|
-
#
|
|
20
|
-
# Fence format, inside `## Acceptance criteria`:
|
|
21
|
-
#
|
|
22
|
-
# ```plan-state
|
|
23
|
-
# - [ ] id: a1
|
|
24
|
-
# intent: Prose description of business intent (one line).
|
|
25
|
-
# tests:
|
|
26
|
-
# - path/to/test.sh::"some test name"
|
|
27
|
-
# - path/to/other.ts::"another test"
|
|
28
|
-
# verify: bash path/to/test.sh
|
|
29
|
-
#
|
|
30
|
-
# - [x] id: a2
|
|
31
|
-
# ...
|
|
32
|
-
# ```
|
|
33
|
-
#
|
|
34
|
-
# Backward compat: a plan without a ```plan-state fence emits the line
|
|
35
|
-
# `legacy` and exits 0 — callers treat it as "old format, fall back".
|
|
36
|
-
#
|
|
37
|
-
# Portability: POSIX bash + awk + grep only. No sed -i.
|
|
38
|
-
|
|
39
|
-
set -eu
|
|
40
|
-
|
|
41
|
-
MODE=""
|
|
42
|
-
PLAN_PATH=""
|
|
43
|
-
|
|
44
|
-
case "${1:-}" in
|
|
45
|
-
--run) MODE=run; PLAN_PATH="${2:-}" ;;
|
|
46
|
-
--check) MODE=check; PLAN_PATH="${2:-}" ;;
|
|
47
|
-
-h|--help|"")
|
|
48
|
-
sed -n '2,34p' "$0"
|
|
49
|
-
exit 0
|
|
50
|
-
;;
|
|
51
|
-
*) MODE=summary; PLAN_PATH="${1:-}" ;;
|
|
52
|
-
esac
|
|
53
|
-
|
|
54
|
-
if [[ -z "${PLAN_PATH:-}" ]]; then
|
|
55
|
-
echo "plan-check.sh: missing plan path" >&2
|
|
56
|
-
exit 2
|
|
57
|
-
fi
|
|
58
|
-
|
|
59
|
-
if [[ ! -f "$PLAN_PATH" ]]; then
|
|
60
|
-
echo "plan-check.sh: file not found: $PLAN_PATH" >&2
|
|
61
|
-
exit 2
|
|
62
|
-
fi
|
|
63
|
-
|
|
64
|
-
# Extract the plan-state fence body into a temp file. awk state machine:
|
|
65
|
-
# enter `## Acceptance criteria`, enter ``` plan-state, exit on next ```.
|
|
66
|
-
FENCE_BODY="$(awk '
|
|
67
|
-
/^## Acceptance criteria/ { in_ac = 1; next }
|
|
68
|
-
/^## / && in_ac && !in_fence { in_ac = 0 }
|
|
69
|
-
in_ac && /^```plan-state[[:space:]]*$/ { in_fence = 1; next }
|
|
70
|
-
in_fence && /^```[[:space:]]*$/ { in_fence = 0; next }
|
|
71
|
-
in_fence { print }
|
|
72
|
-
' "$PLAN_PATH")"
|
|
73
|
-
|
|
74
|
-
if [[ -z "$FENCE_BODY" ]]; then
|
|
75
|
-
# No fence found — legacy plan. Report and exit cleanly.
|
|
76
|
-
if [[ "$MODE" == "summary" ]]; then
|
|
77
|
-
echo "legacy (no plan-state fence)"
|
|
78
|
-
fi
|
|
79
|
-
# --run on a legacy plan emits nothing (no commands to run).
|
|
80
|
-
# --check on a legacy plan succeeds (we're accepting legacy plans).
|
|
81
|
-
exit 0
|
|
82
|
-
fi
|
|
83
|
-
|
|
84
|
-
# Parse items. awk state machine:
|
|
85
|
-
# - A line `- [ ] id: ID` or `- [x] id: ID` starts a new item.
|
|
86
|
-
# - While inside an item, indented keys `intent:`, `tests:`, `verify:` set
|
|
87
|
-
# fields. Under `tests:`, subsequent ` - ...` lines extend the list
|
|
88
|
-
# until the next key or the next item.
|
|
89
|
-
# - Items are separated by one or more blank lines OR by the next `- [`.
|
|
90
|
-
#
|
|
91
|
-
# We emit a tab-delimited record per item:
|
|
92
|
-
# STATUS<TAB>ID<TAB>INTENT<TAB>TESTS<TAB>VERIFY
|
|
93
|
-
# TESTS is a `|`-delimited list. Missing fields are the empty string.
|
|
94
|
-
PARSED="$(echo "$FENCE_BODY" | awk '
|
|
95
|
-
function flush() {
|
|
96
|
-
if (cur_id != "") {
|
|
97
|
-
# Trim trailing/leading whitespace on each field
|
|
98
|
-
gsub(/^[[:space:]]+|[[:space:]]+$/, "", cur_intent)
|
|
99
|
-
gsub(/^[[:space:]]+|[[:space:]]+$/, "", cur_verify)
|
|
100
|
-
gsub(/^\||\|$/, "", cur_tests)
|
|
101
|
-
printf "%s\t%s\t%s\t%s\t%s\n", cur_status, cur_id, cur_intent, cur_tests, cur_verify
|
|
102
|
-
}
|
|
103
|
-
cur_status = ""; cur_id = ""; cur_intent = ""; cur_tests = ""; cur_verify = ""
|
|
104
|
-
in_tests = 0
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/^-[[:space:]]+\[[[:space:]xX[:space:]]\][[:space:]]+id:/ {
|
|
108
|
-
flush()
|
|
109
|
-
status = $0
|
|
110
|
-
sub(/^-[[:space:]]+\[[[:space:]]*/, "", status)
|
|
111
|
-
sub(/\].*$/, "", status)
|
|
112
|
-
# status is either empty/space (" ") -> pending, or "x"/"X" -> done
|
|
113
|
-
if (status ~ /[xX]/) cur_status = "done"; else cur_status = "pending"
|
|
114
|
-
# Capture id
|
|
115
|
-
id_part = $0
|
|
116
|
-
sub(/^.*id:[[:space:]]*/, "", id_part)
|
|
117
|
-
cur_id = id_part
|
|
118
|
-
next
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/^[[:space:]]*intent:/ {
|
|
122
|
-
field = $0
|
|
123
|
-
sub(/^[[:space:]]*intent:[[:space:]]*/, "", field)
|
|
124
|
-
cur_intent = field
|
|
125
|
-
in_tests = 0
|
|
126
|
-
next
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/^[[:space:]]*intent\b/ {
|
|
130
|
-
# already handled
|
|
131
|
-
next
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/^[[:space:]]*tests:/ {
|
|
135
|
-
in_tests = 1
|
|
136
|
-
next
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/^[[:space:]]*verify:/ {
|
|
140
|
-
field = $0
|
|
141
|
-
sub(/^[[:space:]]*verify:[[:space:]]*/, "", field)
|
|
142
|
-
cur_verify = field
|
|
143
|
-
in_tests = 0
|
|
144
|
-
next
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
# Continuation lines inside tests: list
|
|
148
|
-
in_tests && /^[[:space:]]+-[[:space:]]/ {
|
|
149
|
-
line = $0
|
|
150
|
-
sub(/^[[:space:]]+-[[:space:]]+/, "", line)
|
|
151
|
-
if (cur_tests == "") cur_tests = line
|
|
152
|
-
else cur_tests = cur_tests "|" line
|
|
153
|
-
next
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
# Continuation line for intent (indented without `-`, after intent is set
|
|
157
|
-
# and before another key). Append with a space separator.
|
|
158
|
-
!in_tests && /^[[:space:]]{4,}[^-[:space:]]/ && cur_id != "" && cur_intent != "" && cur_verify == "" {
|
|
159
|
-
line = $0
|
|
160
|
-
sub(/^[[:space:]]+/, "", line)
|
|
161
|
-
cur_intent = cur_intent " " line
|
|
162
|
-
next
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
END { flush() }
|
|
166
|
-
' 2>&1)"
|
|
167
|
-
|
|
168
|
-
# If PARSED contains awk errors, surface them as invalid.
|
|
169
|
-
if echo "$PARSED" | grep -q '^awk:'; then
|
|
170
|
-
echo "plan-check.sh: parser error" >&2
|
|
171
|
-
echo "$PARSED" >&2
|
|
172
|
-
exit 3
|
|
173
|
-
fi
|
|
174
|
-
|
|
175
|
-
# Count totals.
|
|
176
|
-
total=0
|
|
177
|
-
done_count=0
|
|
178
|
-
pending_count=0
|
|
179
|
-
invalid_count=0
|
|
180
|
-
invalid_reasons=()
|
|
181
|
-
|
|
182
|
-
while IFS=$'\t' read -r status id intent tests verify; do
|
|
183
|
-
[[ -z "$status" ]] && continue
|
|
184
|
-
total=$((total + 1))
|
|
185
|
-
if [[ -z "$id" ]]; then
|
|
186
|
-
invalid_count=$((invalid_count + 1))
|
|
187
|
-
invalid_reasons+=("missing id")
|
|
188
|
-
continue
|
|
189
|
-
fi
|
|
190
|
-
if [[ -z "$intent" ]]; then
|
|
191
|
-
invalid_count=$((invalid_count + 1))
|
|
192
|
-
invalid_reasons+=("$id: missing intent")
|
|
193
|
-
continue
|
|
194
|
-
fi
|
|
195
|
-
if [[ -z "$tests" ]]; then
|
|
196
|
-
invalid_count=$((invalid_count + 1))
|
|
197
|
-
invalid_reasons+=("$id: missing tests")
|
|
198
|
-
continue
|
|
199
|
-
fi
|
|
200
|
-
if [[ -z "$verify" ]]; then
|
|
201
|
-
invalid_count=$((invalid_count + 1))
|
|
202
|
-
invalid_reasons+=("$id: missing verify")
|
|
203
|
-
continue
|
|
204
|
-
fi
|
|
205
|
-
if [[ "$status" == "done" ]]; then
|
|
206
|
-
done_count=$((done_count + 1))
|
|
207
|
-
else
|
|
208
|
-
pending_count=$((pending_count + 1))
|
|
209
|
-
fi
|
|
210
|
-
done <<< "$PARSED"
|
|
211
|
-
|
|
212
|
-
case "$MODE" in
|
|
213
|
-
summary)
|
|
214
|
-
printf 'total=%d done=%d pending=%d invalid=%d\n' \
|
|
215
|
-
"$total" "$done_count" "$pending_count" "$invalid_count"
|
|
216
|
-
while IFS=$'\t' read -r status id intent tests verify; do
|
|
217
|
-
[[ -z "$status" ]] && continue
|
|
218
|
-
# For the summary-per-item line, prefer displaying the verify
|
|
219
|
-
# command (truncated) so the reader sees what gates each item.
|
|
220
|
-
v="${verify:0:60}"
|
|
221
|
-
if [[ -n "$verify" && ${#verify} -gt 60 ]]; then v="${v}…"; fi
|
|
222
|
-
printf '%s %s %s\n' "$status" "$id" "$v"
|
|
223
|
-
done <<< "$PARSED"
|
|
224
|
-
if [[ "$invalid_count" -gt 0 ]]; then
|
|
225
|
-
echo "invalid:"
|
|
226
|
-
for r in "${invalid_reasons[@]}"; do
|
|
227
|
-
echo " $r"
|
|
228
|
-
done
|
|
229
|
-
fi
|
|
230
|
-
;;
|
|
231
|
-
|
|
232
|
-
run)
|
|
233
|
-
# Emit verify command per PENDING item, one per line. Skip done items,
|
|
234
|
-
# skip invalid items. Caller executes via their own bash permission.
|
|
235
|
-
while IFS=$'\t' read -r status id intent tests verify; do
|
|
236
|
-
[[ -z "$status" ]] && continue
|
|
237
|
-
[[ "$status" == "done" ]] && continue
|
|
238
|
-
[[ -z "$verify" ]] && continue
|
|
239
|
-
[[ -z "$intent" || -z "$tests" ]] && continue
|
|
240
|
-
echo "$verify"
|
|
241
|
-
done <<< "$PARSED"
|
|
242
|
-
;;
|
|
243
|
-
|
|
244
|
-
check)
|
|
245
|
-
# Structural validation. Exit 1 if anything invalid.
|
|
246
|
-
if [[ "$invalid_count" -gt 0 ]]; then
|
|
247
|
-
echo "plan-check: $invalid_count invalid item(s):" >&2
|
|
248
|
-
for r in "${invalid_reasons[@]}"; do
|
|
249
|
-
echo " $r" >&2
|
|
250
|
-
done
|
|
251
|
-
exit 1
|
|
252
|
-
fi
|
|
253
|
-
printf 'ok: %d item(s) pass structural validation\n' "$total"
|
|
254
|
-
;;
|
|
255
|
-
esac
|