@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.
Files changed (65) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/{chunk-EM4MJBOD.js → chunk-2AZKRWC6.js} +4 -4
  3. package/dist/{chunk-UXBOTMDY.js → chunk-2P3ETOT2.js} +2 -2
  4. package/dist/chunk-2VMFXAJH.js +795 -0
  5. package/dist/chunk-5ZVUFNCP.js +140 -0
  6. package/dist/{chunk-W37UX3U2.js → chunk-6Y27RQQL.js} +2 -2
  7. package/dist/{chunk-RZWOWTKF.js → chunk-EKNRKZWR.js} +4 -4
  8. package/dist/{chunk-YGNDPKIW.js → chunk-HQUCVJ4G.js} +3 -1
  9. package/dist/{chunk-OABVEBWW.js → chunk-MBEVC327.js} +1 -1
  10. package/dist/{chunk-SB3MLROC.js → chunk-MCM47HH4.js} +8 -3
  11. package/dist/{chunk-F3AFRUT2.js → chunk-PTIO556V.js} +2 -2
  12. package/dist/{chunk-E2UNZIZT.js → chunk-R2WXQ54P.js} +1 -1
  13. package/dist/{chunk-I2KUXY3I.js → chunk-SMDIOB5B.js} +2 -2
  14. package/dist/{chunk-SPULDN7P.js → chunk-YY7EWHMA.js} +5 -3
  15. package/dist/cli.js +31 -20
  16. package/dist/commands/autopilot-interactive.d.ts +89 -0
  17. package/dist/commands/autopilot-interactive.js +248 -0
  18. package/dist/commands/autopilot-raw.d.ts +1 -0
  19. package/dist/commands/autopilot-raw.js +368 -0
  20. package/dist/commands/autopilot-tui.d.ts +7 -0
  21. package/dist/commands/autopilot-tui.js +7 -0
  22. package/dist/commands/autopilot.d.ts +39 -0
  23. package/dist/commands/autopilot.js +395 -0
  24. package/dist/commands/cleanup.js +3 -3
  25. package/dist/commands/create.js +4 -4
  26. package/dist/commands/dashboard.d.ts +3 -0
  27. package/dist/commands/dashboard.js +1549 -0
  28. package/dist/commands/debrief.d.ts +57 -0
  29. package/dist/commands/debrief.js +9 -0
  30. package/dist/commands/delete.js +3 -3
  31. package/dist/commands/go.js +2 -2
  32. package/dist/commands/list.js +3 -3
  33. package/dist/commands/loop.d.ts +42 -0
  34. package/dist/commands/loop.js +133 -0
  35. package/dist/commands/plan-picker.d.ts +15 -0
  36. package/dist/commands/plan-picker.js +76 -0
  37. package/dist/commands/scoper.d.ts +54 -0
  38. package/dist/commands/scoper.js +341 -0
  39. package/dist/commands/switch.js +3 -3
  40. package/dist/index.d.ts +2 -2
  41. package/dist/index.js +1 -1
  42. package/dist/lib/auto-update.js +1 -1
  43. package/dist/lib/config.d.ts +3 -2
  44. package/dist/lib/config.js +1 -1
  45. package/dist/lib/registry.d.ts +2 -0
  46. package/dist/lib/registry.js +1 -1
  47. package/dist/lib/worktree.js +3 -3
  48. package/dist/vendor/harness-opencode/dist/agents/prompts/build.md +16 -0
  49. package/dist/vendor/harness-opencode/dist/agents/prompts/code-reviewer-thorough.md +6 -7
  50. package/dist/vendor/harness-opencode/dist/agents/prompts/debriefer.md +55 -0
  51. package/dist/vendor/harness-opencode/dist/agents/prompts/plan-reviewer.md +2 -1
  52. package/dist/vendor/harness-opencode/dist/agents/prompts/plan.md +104 -7
  53. package/dist/vendor/harness-opencode/dist/agents/prompts/prime.md +4 -2
  54. package/dist/vendor/harness-opencode/dist/agents/prompts/scoper.md +129 -0
  55. package/dist/vendor/harness-opencode/dist/agents/prompts/spec-reviewer.md +0 -1
  56. package/dist/vendor/harness-opencode/dist/agents/prompts/spec-reviewer.open.md +0 -1
  57. package/dist/vendor/harness-opencode/dist/chunk-GILWWWMB.js +66 -0
  58. package/dist/vendor/harness-opencode/dist/cli.js +328 -687
  59. package/dist/vendor/harness-opencode/dist/index.js +123 -20
  60. package/dist/vendor/harness-opencode/dist/plugin-check-GJRD2OK6.js +14 -0
  61. package/dist/vendor/harness-opencode/dist/skills/spear-protocol/SKILL.md +2 -1
  62. package/dist/vendor/harness-opencode/package.json +1 -1
  63. package/package.json +10 -2
  64. package/dist/vendor/harness-opencode/dist/autopilot/prompt-template.md +0 -80
  65. package/dist/vendor/harness-opencode/dist/bin/plan-check.sh +0 -255
@@ -0,0 +1,341 @@
1
+ import "../chunk-3RG5ZIWI.js";
2
+
3
+ // src/commands/scoper.ts
4
+ import * as fs from "fs";
5
+ import * as path from "path";
6
+ import {
7
+ startServer,
8
+ createSession,
9
+ sendAndWait,
10
+ getLastAssistantMessage
11
+ } from "@glrs-dev/adapter-opencode";
12
+ var ANSI_RESET = "\x1B[0m\x1B[2K\r";
13
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
14
+ function createSpinner() {
15
+ let timer = null;
16
+ let frame = 0;
17
+ return {
18
+ start(label = "Thinking") {
19
+ if (timer) return;
20
+ frame = 0;
21
+ const isTTY = process.stderr.isTTY ?? false;
22
+ if (!isTTY) return;
23
+ timer = setInterval(() => {
24
+ const f = SPINNER_FRAMES[frame % SPINNER_FRAMES.length];
25
+ process.stderr.write(`\x1B[2K\r\x1B[36m${f}\x1B[0m ${label}...`);
26
+ frame++;
27
+ }, 80);
28
+ },
29
+ stop() {
30
+ if (!timer) return;
31
+ clearInterval(timer);
32
+ timer = null;
33
+ process.stderr.write("\x1B[2K\r");
34
+ }
35
+ };
36
+ }
37
+ function parseQuestion(response) {
38
+ const match = response.match(/^([^\n]{1,199}\?)\s*$/);
39
+ return match ? match[1] ?? null : null;
40
+ }
41
+ function parseScopeSummary(response) {
42
+ const match = response.match(/^SCOPE_SUMMARY:\s*\n?([\s\S]+)$/);
43
+ return match ? match[1]?.trim() ?? null : null;
44
+ }
45
+ function extractScopeCompletePath(output) {
46
+ const lines = output.split("\n");
47
+ let lastMatch = null;
48
+ for (const line of lines) {
49
+ const trimmed = line.trim();
50
+ if (trimmed.startsWith("SCOPE_COMPLETE:")) {
51
+ const rest = trimmed.slice("SCOPE_COMPLETE:".length).trim();
52
+ if (rest.length > 0) {
53
+ lastMatch = rest;
54
+ }
55
+ }
56
+ }
57
+ return lastMatch;
58
+ }
59
+ var DEFAULT_SCOPER_TIMEOUT_MS = 5 * 60 * 1e3;
60
+ var MAX_QUESTIONS = 8;
61
+ var FORCED_FINALIZE_MESSAGE = "You have asked enough questions. Present a SCOPE_SUMMARY for user approval, then write scope.md and emit SCOPE_COMPLETE.";
62
+ var PARSE_RETRY_REMINDER = "Your last response did not follow the strict contract. Respond with EXACTLY one of: (a) a single question (\u2264200 chars, ending with '?'), (b) a scope summary starting with 'SCOPE_SUMMARY:', or (c) the sentinel 'SCOPE_COMPLETE: <absolute-path>'. Nothing else.";
63
+ async function runScoperSession(opts) {
64
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_SCOPER_TIMEOUT_MS;
65
+ const deps = opts._deps;
66
+ const _startServer = deps?.startServer ?? startServer;
67
+ const _createSession = deps?.createSession ?? createSession;
68
+ const _sendAndWait = deps?.sendAndWait ?? sendAndWait;
69
+ const _getLastAssistantMessage = deps?.getLastAssistantMessage ?? getLastAssistantMessage;
70
+ const _existsSync = deps?.existsSync ?? fs.existsSync;
71
+ const _promptUser = deps?.promptUser ?? (async (question) => {
72
+ const { input } = await import("@inquirer/prompts");
73
+ return input({ message: question });
74
+ });
75
+ const server = await _startServer({ cwd: opts.planDir });
76
+ const spinner = createSpinner();
77
+ try {
78
+ const sessionId = await _createSession(server.client, {
79
+ cwd: opts.planDir,
80
+ agentName: "scoper"
81
+ });
82
+ const initialPromptParts = [
83
+ "You are running in an inquirer-driven wizard. Follow the strict response contract:",
84
+ "- Every response must be EXACTLY one of:",
85
+ " (a) A single question (\u2264200 chars, ending with '?')",
86
+ " (b) A scope summary starting with 'SCOPE_SUMMARY:' for user approval",
87
+ " (c) The sentinel 'SCOPE_COMPLETE: <absolute-path>'",
88
+ "- Do NOT call the question tool. Emit questions as plain assistant text.",
89
+ "- Start with first-principles questions (WHAT and WHY), not implementation details.",
90
+ "",
91
+ `The user wants to build: ${opts.initialGoal}`,
92
+ "",
93
+ "Begin by asking your first clarifying question about the problem being solved."
94
+ ];
95
+ if (opts.existingPlanContent) {
96
+ initialPromptParts.push(
97
+ "",
98
+ "The user has an existing plan. Use it as context for your scoping questions:",
99
+ "",
100
+ "---",
101
+ opts.existingPlanContent,
102
+ "---",
103
+ "",
104
+ "Validate this plan against the codebase and ask clarifying questions about gaps or stale assumptions."
105
+ );
106
+ }
107
+ const initialPrompt = initialPromptParts.join("\n");
108
+ spinner.start("Scoper is thinking");
109
+ const firstResult = await _sendAndWait(server.client, {
110
+ sessionId,
111
+ message: initialPrompt,
112
+ agentName: "scoper",
113
+ stallMs: timeoutMs,
114
+ autoRejectPermissions: true
115
+ });
116
+ if (firstResult.kind === "abort") {
117
+ throw new Error(`Scoper session aborted (timeout after ${timeoutMs}ms).`);
118
+ }
119
+ if (firstResult.kind === "stall") {
120
+ throw new Error(
121
+ `Scoper session stalled for ${firstResult.stallMs}ms with no idle signal.`
122
+ );
123
+ }
124
+ if (firstResult.kind === "error") {
125
+ throw new Error(`Scoper session error: ${firstResult.message}`);
126
+ }
127
+ let questionsAsked = 0;
128
+ let parseRetryPending = false;
129
+ while (true) {
130
+ const lastMessage = await _getLastAssistantMessage(
131
+ server.client,
132
+ sessionId
133
+ );
134
+ const scopePath = extractScopeCompletePath(lastMessage);
135
+ if (scopePath) {
136
+ spinner.stop();
137
+ if (!_existsSync(scopePath)) {
138
+ throw new Error(
139
+ `Scoper emitted SCOPE_COMPLETE but scope.md does not exist at: ${scopePath}`
140
+ );
141
+ }
142
+ return { scopePath };
143
+ }
144
+ if (questionsAsked >= MAX_QUESTIONS) {
145
+ spinner.start("Finalizing scope");
146
+ const finalResult = await _sendAndWait(server.client, {
147
+ sessionId,
148
+ message: FORCED_FINALIZE_MESSAGE,
149
+ stallMs: timeoutMs,
150
+ autoRejectPermissions: true
151
+ });
152
+ if (finalResult.kind !== "idle") {
153
+ throw new Error(
154
+ `Scoper session failed during forced finalize: ${finalResult.kind}`
155
+ );
156
+ }
157
+ const finalMessage = await _getLastAssistantMessage(
158
+ server.client,
159
+ sessionId
160
+ );
161
+ const finalScopePath = extractScopeCompletePath(finalMessage);
162
+ if (finalScopePath) {
163
+ spinner.stop();
164
+ if (!_existsSync(finalScopePath)) {
165
+ throw new Error(
166
+ `Scoper emitted SCOPE_COMPLETE after forced finalize but scope.md does not exist at: ${finalScopePath}`
167
+ );
168
+ }
169
+ return { scopePath: finalScopePath };
170
+ }
171
+ const expectedScopePath = path.join(opts.planDir, opts.slug, "scope.md");
172
+ if (_existsSync(expectedScopePath)) {
173
+ spinner.stop();
174
+ process.stderr.write(
175
+ `
176
+ \u26A0 Scoper didn't emit sentinel, but scope.md exists at ${expectedScopePath}. Using it.
177
+
178
+ `
179
+ );
180
+ return { scopePath: expectedScopePath };
181
+ }
182
+ spinner.stop();
183
+ process.stderr.write(
184
+ `
185
+ \u26A0 Scoper didn't write scope.md. Constructing from conversation.
186
+
187
+ `
188
+ );
189
+ const scopeDir = path.join(opts.planDir, opts.slug);
190
+ if (!_existsSync(scopeDir)) {
191
+ fs.mkdirSync(scopeDir, { recursive: true });
192
+ }
193
+ const constructedScope = [
194
+ `# ${opts.initialGoal}`,
195
+ "",
196
+ "## Goal",
197
+ "",
198
+ opts.initialGoal,
199
+ "",
200
+ "## Scoper conversation summary",
201
+ "",
202
+ "The scoper agent asked 8 questions and the user provided answers,",
203
+ "but the agent did not produce a formal scope.md. The last agent",
204
+ "response is included below for the plan agent to work from.",
205
+ "",
206
+ "### Last agent response",
207
+ "",
208
+ finalMessage || "(no response captured)",
209
+ "",
210
+ "## Acceptance criteria",
211
+ "",
212
+ "- To be determined by the plan agent based on the conversation above.",
213
+ "",
214
+ "## Constraints",
215
+ "",
216
+ "- To be determined by the plan agent.",
217
+ "",
218
+ "## Out of scope",
219
+ "",
220
+ "- To be determined by the plan agent.",
221
+ "",
222
+ "## Open questions for the plan agent",
223
+ "",
224
+ "- The scoper did not complete formally. Review the conversation summary above and fill in the missing sections."
225
+ ].join("\n");
226
+ const constructedPath = path.join(scopeDir, "scope.md");
227
+ fs.writeFileSync(constructedPath, constructedScope);
228
+ return { scopePath: constructedPath };
229
+ }
230
+ const summary = parseScopeSummary(lastMessage);
231
+ if (summary) {
232
+ spinner.stop();
233
+ parseRetryPending = false;
234
+ process.stderr.write(ANSI_RESET);
235
+ process.stderr.write(`
236
+ \x1B[1m\u{1F4CB} Scope summary:\x1B[0m
237
+
238
+ ${summary}
239
+
240
+ `);
241
+ const approval = await _promptUser("Approve this scope? (yes / or describe what to change)");
242
+ if (approval.toLowerCase().startsWith("yes") || approval.toLowerCase() === "y" || approval.toLowerCase() === "approve") {
243
+ spinner.start("Writing scope.md");
244
+ const writeResult = await _sendAndWait(server.client, {
245
+ sessionId,
246
+ message: "The user approved the scope. Write scope.md now and emit SCOPE_COMPLETE.",
247
+ stallMs: timeoutMs,
248
+ autoRejectPermissions: true
249
+ });
250
+ if (writeResult.kind !== "idle") {
251
+ throw new Error(
252
+ `Scoper session failed after scope approval: ${writeResult.kind}`
253
+ );
254
+ }
255
+ continue;
256
+ } else {
257
+ spinner.start("Scoper is revising");
258
+ const reviseResult = await _sendAndWait(server.client, {
259
+ sessionId,
260
+ message: approval,
261
+ stallMs: timeoutMs,
262
+ autoRejectPermissions: true
263
+ });
264
+ if (reviseResult.kind !== "idle") {
265
+ throw new Error(
266
+ `Scoper session failed during revision: ${reviseResult.kind}`
267
+ );
268
+ }
269
+ continue;
270
+ }
271
+ }
272
+ const question = parseQuestion(lastMessage);
273
+ if (question) {
274
+ parseRetryPending = false;
275
+ spinner.stop();
276
+ process.stderr.write(ANSI_RESET);
277
+ questionsAsked++;
278
+ const userAnswer = await _promptUser(question);
279
+ spinner.start("Scoper is thinking");
280
+ const nextResult = await _sendAndWait(server.client, {
281
+ sessionId,
282
+ message: userAnswer,
283
+ stallMs: timeoutMs,
284
+ autoRejectPermissions: true
285
+ });
286
+ if (nextResult.kind === "abort") {
287
+ throw new Error(
288
+ `Scoper session aborted (timeout after ${timeoutMs}ms).`
289
+ );
290
+ }
291
+ if (nextResult.kind === "stall") {
292
+ throw new Error(
293
+ `Scoper session stalled for ${nextResult.stallMs}ms with no idle signal.`
294
+ );
295
+ }
296
+ if (nextResult.kind === "error") {
297
+ throw new Error(`Scoper session error: ${nextResult.message}`);
298
+ }
299
+ continue;
300
+ }
301
+ if (parseRetryPending) {
302
+ spinner.stop();
303
+ throw new Error(
304
+ `Scoper response did not follow the strict contract after retry. Last response: ${lastMessage.slice(0, 200)}`
305
+ );
306
+ }
307
+ parseRetryPending = true;
308
+ spinner.start("Retrying");
309
+ const retryResult = await _sendAndWait(server.client, {
310
+ sessionId,
311
+ message: PARSE_RETRY_REMINDER,
312
+ stallMs: timeoutMs,
313
+ autoRejectPermissions: true
314
+ });
315
+ if (retryResult.kind === "abort") {
316
+ throw new Error(
317
+ `Scoper session aborted during parse retry (timeout after ${timeoutMs}ms).`
318
+ );
319
+ }
320
+ if (retryResult.kind === "stall") {
321
+ throw new Error(
322
+ `Scoper session stalled during parse retry for ${retryResult.stallMs}ms.`
323
+ );
324
+ }
325
+ if (retryResult.kind === "error") {
326
+ throw new Error(
327
+ `Scoper session error during parse retry: ${retryResult.message}`
328
+ );
329
+ }
330
+ }
331
+ } finally {
332
+ spinner.stop();
333
+ await server.shutdown();
334
+ }
335
+ }
336
+ export {
337
+ extractScopeCompletePath,
338
+ parseQuestion,
339
+ parseScopeSummary,
340
+ runScoperSession
341
+ };
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  switchCmd
3
- } from "../chunk-OABVEBWW.js";
4
- import "../chunk-E2UNZIZT.js";
3
+ } from "../chunk-MBEVC327.js";
4
+ import "../chunk-R2WXQ54P.js";
5
5
  import "../chunk-P7PRH4I3.js";
6
6
  import "../chunk-LMRDQ4GW.js";
7
- import "../chunk-SPULDN7P.js";
7
+ import "../chunk-YY7EWHMA.js";
8
8
  import "../chunk-YBCA3IP6.js";
9
9
  import "../chunk-3RG5ZIWI.js";
10
10
  export {
package/dist/index.d.ts CHANGED
@@ -24,7 +24,7 @@ interface ResolvedBin {
24
24
  }
25
25
  declare function resolveSubcommand(sub: Subcommand): ResolvedBin;
26
26
  declare const SUBCOMMANDS: Subcommand[];
27
- declare const HELP_TEXT = "glrs \u2014 unified CLI for the @glrs-dev ecosystem\n\nUSAGE\n glrs <subcommand> [args...]\n\nSUBCOMMANDS\n oc OpenCode agent harness (install, pilot, etc.)\n wt Worktree management (create, list, switch, delete, cleanup)\n\nRun 'glrs <subcommand> --help' for per-command help.\n\nEXAMPLES\n glrs oc install\n glrs wt new\n glrs wt list\n glrs wt switch\n\nREQUIREMENTS\n Bun >= 1.2.0 on PATH (install: https://bun.sh)\n\nDOCS https://glrs.dev\nISSUES https://github.com/iceglober/glrs/issues\n";
28
- declare const WORKTREE_HELP_TEXT = "glrs wt \u2014 worktree management\n\nUSAGE\n glrs wt <command> [args...]\n\nCOMMANDS\n new Create a new worktree (auto-named from origin/default)\n list, ls List all worktrees across repos\n switch, sw Interactively select and switch to a worktree\n delete, rm Remove worktrees (interactive or by name)\n cleanup Delete merged/stale worktrees\n\nEXAMPLES\n glrs wt new # Create worktree in current repo\n glrs wt new myrepo # Create worktree for named repo\n glrs wt list # Show all worktrees\n glrs wt list -i # Interactive picker\n glrs wt switch # Interactive switcher\n glrs wt delete my-branch # Delete specific worktree\n glrs wt cleanup # Clean up merged worktrees\n\nWorktrees are stored in ~/.glorious/worktrees/<repo>/<name>/\n";
27
+ declare const HELP_TEXT = "glrs \u2014 unified CLI for the @glrs-dev ecosystem\n\nUSAGE\n glrs <subcommand> [args...]\n\nSUBCOMMANDS\n oc OpenCode agent harness (install, pilot, etc.)\n wt Worktree management (create, list, switch, delete, cleanup)\n dashboard Live TUI dashboard for all running autopilot sessions\n\nRun 'glrs <subcommand> --help' for per-command help.\n\nEXAMPLES\n glrs oc install\n glrs wt new\n glrs wt list\n glrs wt switch\n glrs dashboard\n\nREQUIREMENTS\n Bun >= 1.2.0 on PATH (install: https://bun.sh)\n\nDOCS https://glrs.dev\nISSUES https://github.com/iceglober/glrs/issues\n";
28
+ declare const WORKTREE_HELP_TEXT = "glrs wt \u2014 worktree management\n\nUSAGE\n glrs wt <command> [args...]\n\nCOMMANDS\n new Create a new worktree (auto-named from origin/default)\n list, ls List all worktrees across repos\n switch, sw Interactively select and switch to a worktree\n delete, rm Remove worktrees (interactive or by name)\n cleanup Delete merged/stale worktrees\n\nEXAMPLES\n glrs wt new # Create worktree in current repo\n glrs wt new myrepo # Create worktree for named repo\n glrs wt list # Show all worktrees\n glrs wt list -i # Interactive picker\n glrs wt switch # Interactive switcher\n glrs wt delete my-branch # Delete specific worktree\n glrs wt cleanup # Clean up merged worktrees\n\nWorktrees are stored in ~/.glrs/worktrees/<repo>/<name>/\n";
29
29
 
30
30
  export { HELP_TEXT, type ResolvedBin, SUBCOMMANDS, type Subcommand, WORKTREE_HELP_TEXT, resolveSubcommand };
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  SUBCOMMANDS,
4
4
  WORKTREE_HELP_TEXT,
5
5
  resolveSubcommand
6
- } from "./chunk-YGNDPKIW.js";
6
+ } from "./chunk-HQUCVJ4G.js";
7
7
  import "./chunk-3RG5ZIWI.js";
8
8
  export {
9
9
  HELP_TEXT,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  autoUpdate
3
- } from "../chunk-SB3MLROC.js";
3
+ } from "../chunk-MCM47HH4.js";
4
4
  import "../chunk-3RG5ZIWI.js";
5
5
  export {
6
6
  autoUpdate
@@ -6,8 +6,9 @@ declare function worktreesRoot(repo: string): string;
6
6
  /**
7
7
  * Resolve where a worktree should live.
8
8
  *
9
- * Default: ~/.glorious/worktrees/<repo>/<name>
10
- * If GLORIOUS_DIR is set: $GLORIOUS_DIR/<repo>/<name>
9
+ * Default: ~/.glrs/worktrees/<repo>/<name>
10
+ * If GLRS_DIR is set: $GLRS_DIR/<repo>/<name>
11
+ * If GLORIOUS_DIR is set (legacy): $GLORIOUS_DIR/<repo>/<name>
11
12
  */
12
13
  declare function worktreePath(name: string, repo?: string): string;
13
14
 
@@ -3,7 +3,7 @@ import {
3
3
  repoName,
4
4
  worktreePath,
5
5
  worktreesRoot
6
- } from "../chunk-W37UX3U2.js";
6
+ } from "../chunk-6Y27RQQL.js";
7
7
  import "../chunk-LMRDQ4GW.js";
8
8
  import "../chunk-3RG5ZIWI.js";
9
9
  export {
@@ -6,6 +6,8 @@ interface RegistryEntry {
6
6
  createdAt: string;
7
7
  }
8
8
  /** Load registry, pruning entries whose worktree paths no longer exist. */
9
+ /** Load registry, pruning entries whose worktree paths no longer exist.
10
+ * Checks ~/.glrs/worktrees.json first; falls back to ~/.glorious/worktrees.json. */
9
11
  declare function loadRegistry(): RegistryEntry[];
10
12
  declare function saveRegistry(entries: RegistryEntry[]): void;
11
13
  declare function registerWorktree(entry: RegistryEntry): void;
@@ -3,7 +3,7 @@ import {
3
3
  registerWorktree,
4
4
  saveRegistry,
5
5
  unregisterWorktree
6
- } from "../chunk-SPULDN7P.js";
6
+ } from "../chunk-YY7EWHMA.js";
7
7
  import "../chunk-3RG5ZIWI.js";
8
8
  export {
9
9
  loadRegistry,
@@ -3,10 +3,10 @@ import {
3
3
  autoName,
4
4
  createWorktree,
5
5
  ensureWorktree
6
- } from "../chunk-F3AFRUT2.js";
7
- import "../chunk-W37UX3U2.js";
6
+ } from "../chunk-PTIO556V.js";
7
+ import "../chunk-6Y27RQQL.js";
8
8
  import "../chunk-LMRDQ4GW.js";
9
- import "../chunk-SPULDN7P.js";
9
+ import "../chunk-YY7EWHMA.js";
10
10
  import "../chunk-YBCA3IP6.js";
11
11
  import "../chunk-3RG5ZIWI.js";
12
12
  export {
@@ -34,6 +34,22 @@ If ANY of these are missing, STOP and report to the user:
34
34
 
35
35
  Do NOT attempt to "fill in" missing structure on behalf of the plan. The plan is the spec; if the spec is wrong, fix it explicitly — don't improvise.
36
36
 
37
+ ## 1.5 Multi-file plan handling
38
+
39
+ If the plan path is a directory (contains `main.md`), it is a multi-file plan. Handle it as follows:
40
+
41
+ 1. Read `main.md`'s `## Phases` checklist.
42
+ 2. Find the first unchecked phase (`- [ ] phase_N.md — ...`).
43
+ 3. Open the corresponding `phase_N.md` as the working plan for this iteration.
44
+ 4. Execute its items per the normal workflow (sections 2–4 below).
45
+ 5. After completing all items in the phase file, re-read it and verify all ACs are `[x]`.
46
+ 6. Update `main.md`'s corresponding phase checkbox to `[x]`.
47
+ 7. Proceed to the next unchecked phase.
48
+
49
+ Cross-cutting ACs in `main.md` (under `## Cross-cutting acceptance criteria`) are verified independently via their own `verify:` commands after all phases are complete.
50
+
51
+ If the plan path is a single `.md` file, skip this section and proceed normally.
52
+
37
53
  ## 2. Prepare the return summary
38
54
 
39
55
  Before starting execution, prepare a brief summary for your eventual return payload to PRIME: file count, which acceptance criteria you will verify, any unknowns. When invoked as a subagent (the common case — PRIME delegates Phase 3 to you), this summary is for PRIME to relay to the user; do not narrate to the user directly. When invoked top-level by the user (`@build <plan-path>`), you may print the summary to chat.
@@ -21,17 +21,16 @@ You run ONLY after `@spec-reviewer` has returned `[PASS_SPEC]` — spec/scope co
21
21
  3. **Plan-drift check (AUTO-FAIL).** For each modified file in the diff, verify it appears in the plan's `## File-level changes`. A modified file NOT listed in `## File-level changes` is AUTO-FAIL regardless of how "implicit" the coverage seems — the plan should have listed it. Report as `Plan drift: <path> modified but not in ## File-level changes`.
22
22
  4. **Scope-creep check.** For each UNTRACKED file (from `git status`) that is NOT in `## File-level changes`, run `git log --oneline -- <file>` to determine whether the file is pre-existing work or scope creep. Do NOT accept the PRIME's verbal "pre-existing" claim without this check. If the file has no prior commits on this branch AND isn't in the plan, LOOP-TO-PLAN with `Scope creep: <path> untracked and not in plan`.
23
23
  5. **Semantic verification.** For each item in `## File-level changes`, verify the corresponding code change exists and matches the description. For each `## Acceptance criteria` item, verify it is actually met by reading the code — do NOT trust `[x]` checkboxes.
24
- 6. **Plan-state verify commands (fenced plans only).** Run `bunx @glrs-dev/harness-plugin-opencode plan-check --run <plan-path>` and execute each returned verify command via `bash`. Any non-zero exit LOOP-TO-PLAN with `Verify failed: <command> (exit N)`. If the plan has no fence (legacy), skip.
25
- 7. **Re-run the project's test command.** Unconditionally. Discover the invocation from `package.json` scripts / `Makefile` / `CONTRIBUTING.md` / `AGENTS.md` — typical forms: `pnpm test`, `npm test`, `bun test`, `cargo test`, `pytest`, `go test ./...`. Any failure → FIX-INLINE (if trivial) or LOOP-TO-PLAN (if structural).
26
- 8. **Re-run the project's lint command.** Unconditionally. E.g., `pnpm lint`, `npm run lint`, `ruff check`, `golangci-lint run`. Any failure → FIX-INLINE.
27
- 9. **Re-run the project's typecheck / build command.** Unconditionally. E.g., `pnpm typecheck`, `tsc --noEmit`, `mypy`, `cargo check`. Any failure → FIX-INLINE.
28
- 10. **Check for missed concerns:**
24
+ 6. **Re-run the project's test command.** Unconditionally. Discover the invocation from `package.json` scripts / `Makefile` / `CONTRIBUTING.md` / `AGENTS.md` typical forms: `pnpm test`, `npm test`, `bun test`, `cargo test`, `pytest`, `go test ./...`. Any failure FIX-INLINE (if trivial) or LOOP-TO-PLAN (if structural).
25
+ 7. **Re-run the project's lint command.** Unconditionally. E.g., `pnpm lint`, `npm run lint`, `ruff check`, `golangci-lint run`. Any failure → FIX-INLINE.
26
+ 8. **Re-run the project's typecheck / build command.** Unconditionally. E.g., `pnpm typecheck`, `tsc --noEmit`, `mypy`, `cargo check`. Any failure → FIX-INLINE.
27
+ 9. **Check for missed concerns:**
29
28
  - Regressions in adjacent code not mentioned in the plan
30
29
  - Missing test coverage for new behavior
31
30
  - Hardcoded values that should be config
32
31
  - Error paths not handled
33
- 11. **AGENTS.md freshness (hierarchical docs).** For each directory touched by the change, check whether a local `AGENTS.md` exists. If yes, read it and verify its conventions/claims still match the code. If the change shifts a convention and the local `AGENTS.md` wasn't updated, return FIX-INLINE with: `Update <path>/AGENTS.md to reflect <specific change>`. Do not fail on unrelated staleness — only on drift caused by THIS change.
34
- 12. **Scan for new tech debt.** Run `todo_scan` with `onlyChanged: true`. For every TODO / FIXME / HACK / XXX, check whether the plan's `## Out of scope` or `## Open questions` acknowledges it. Unacknowledged new debt → FIX-INLINE with `file:line`.
32
+ 10. **AGENTS.md freshness (hierarchical docs).** For each directory touched by the change, check whether a local `AGENTS.md` exists. If yes, read it and verify its conventions/claims still match the code. If the change shifts a convention and the local `AGENTS.md` wasn't updated, return FIX-INLINE with: `Update <path>/AGENTS.md to reflect <specific change>`. Do not fail on unrelated staleness — only on drift caused by THIS change.
33
+ 11. **Scan for new tech debt.** Run `todo_scan` with `onlyChanged: true`. For every TODO / FIXME / HACK / XXX, check whether the plan's `## Out of scope` or `## Open questions` acknowledges it. Unacknowledged new debt → FIX-INLINE with `file:line`.
35
34
 
36
35
  # Output
37
36
 
@@ -0,0 +1,55 @@
1
+ ---
2
+ name: debriefer
3
+ description: Post-run debrief agent. Given a context blob describing a completed autopilot session (exit reason, iterations, cost, git diff stat, plan state), produces a structured five-section summary: what was accomplished, what wasn't, cost summary, what to do next, and session artifacts. Read-only — no file edits, no destructive bash.
4
+ mode: subagent
5
+ model: anthropic/claude-sonnet-4-6
6
+ ---
7
+
8
+ You are the **@debriefer** agent. You receive a structured context blob from the autopilot CLI after a loop session completes. Your job is to produce a concise, actionable debrief.
9
+
10
+ ## Output format
11
+
12
+ Produce exactly five sections in this order. Use the exact headings shown.
13
+
14
+ ### 1. What was accomplished
15
+
16
+ List files changed, commits made, and PRs opened (if any). Pull from the git diff stat and commit log in the context. If nothing was committed, say so explicitly.
17
+
18
+ ### 2. What wasn't finished
19
+
20
+ List unchecked plan items (items still marked `- [ ]`). If the plan state is unavailable, note that. If all items were checked, say "All plan items completed."
21
+
22
+ ### 3. Cost summary
23
+
24
+ Report:
25
+ - Total cost in USD (from the context)
26
+ - Number of iterations completed
27
+ - Exit reason (sentinel / struggle / timeout / max-iterations / kill-switch / stall / error)
28
+
29
+ ### 4. What to do next
30
+
31
+ Give 2–4 actionable next steps based on the exit reason:
32
+
33
+ - **sentinel**: The agent completed successfully. Review the diff, run the full test suite, open a PR if not already done.
34
+ - **struggle**: The agent made no progress for N consecutive iterations. Inspect the last few iterations in the log, identify the blocker, and re-run with a more specific prompt or after fixing the blocker manually.
35
+ - **timeout** / **max-iterations**: The agent ran out of budget. Check what was completed, then re-run with the remaining work as the prompt.
36
+ - **kill-switch**: The loop was manually stopped. Resume when ready by re-running with the same prompt.
37
+ - **stall**: The agent's session stalled (no idle signal). Check the OpenCode server logs, then re-run.
38
+ - **error**: An error occurred. Check the error message in the context and fix the root cause before re-running.
39
+
40
+ ### 5. Session artifacts
41
+
42
+ List:
43
+ - Log file path (from context, if available)
44
+ - Plan file path (from context, if available)
45
+ - Session ID (from context)
46
+
47
+ ---
48
+
49
+ ## Rules
50
+
51
+ - Be concise. Each section should be 3–8 lines.
52
+ - Do not invent information not present in the context.
53
+ - Do not make file edits. Do not run destructive bash commands.
54
+ - If a field is missing from the context, say "not available" rather than guessing.
55
+ - Output plain markdown. No JSON, no code fences around the sections themselves.
@@ -17,7 +17,8 @@ Read the plan at the path provided. Validate against six criteria:
17
17
  3. **Context** — Is there enough information for an executor to proceed without more than ~10% guesswork? Are file paths real (use `read`/`grep` to spot-check)?
18
18
  4. **Big picture** — Is the `## Goal` clear? Is `## Out of scope` explicit?
19
19
  5. **Scope compliance** — If `## Goal` cites a ticket ID, the plan's `## File-level changes` must not introduce files or subsystems outside the ticket's Changes / Definition of Done section, unless `## Out of scope` (or an explicit sentence in `## Goal`) justifies each expansion. Invented scope is a REJECT.
20
- 6. **Plan-state fence integrity** — For any NEW plan (authored after the fence was introduced), `## Acceptance criteria` MUST contain a ```plan-state fenced block. Every item in the block must have all three of `intent:`, `tests:`, `verify:` populated. For each `tests:` entry, the referenced test file must either (a) exist in the repo (spot-check via `read` or `ls`), or (b) have its path listed in `## File-level changes`. Validate structural correctness by running `bunx @glrs-dev/harness-plugin-opencode plan-check --check <plan-path>`non-zero exit REJECT. Legacy plans (no fence) pass criterion 6 automatically.
20
+ 6. **Plan-state fence integrity** — For any NEW plan (authored after the fence was introduced), `## Acceptance criteria` MUST contain a ```plan-state fenced block. Every item in the block must have all three of `intent:`, `tests:`, `verify:` populated. For each `tests:` entry, the referenced test file must either (a) exist in the repo (spot-check via `read` or `ls`), or (b) have its path listed in `## File-level changes`. Read the plan with your `read` tool and eyeball the fence directly any missing field is REJECT. Legacy plans (no fence) pass criterion 6 automatically.
21
+ 7. **Multi-file consistency** — If the plan is a directory (main.md + phase files): every phase in main.md's `## Phases` list has a corresponding `phase_N.md` file; no phase file exists without a main.md reference; cross-cutting ACs in main.md don't duplicate phase-file ACs; file-level changes across phases that reference the same file are consistent with phase ordering (earlier phases create, later phases modify).
21
22
 
22
23
  Output exactly one of these two formats. Nothing else.
23
24