@entelligentsia/forgecli 0.10.0 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/CHANGELOG.md +95 -0
  2. package/README.md +21 -3
  3. package/dist/CHANGELOG-forge-plugin.md +90 -0
  4. package/dist/bin/config.js +6 -0
  5. package/dist/bin/config.js.map +1 -1
  6. package/dist/extensions/forgecli/add-pipeline.d.ts +19 -0
  7. package/dist/extensions/forgecli/add-pipeline.js +143 -0
  8. package/dist/extensions/forgecli/add-pipeline.js.map +1 -0
  9. package/dist/extensions/forgecli/add-task.d.ts +20 -0
  10. package/dist/extensions/forgecli/add-task.js +154 -0
  11. package/dist/extensions/forgecli/add-task.js.map +1 -0
  12. package/dist/extensions/forgecli/calibrate.d.ts +61 -0
  13. package/dist/extensions/forgecli/calibrate.js +488 -0
  14. package/dist/extensions/forgecli/calibrate.js.map +1 -0
  15. package/dist/extensions/forgecli/fix-bug.d.ts +9 -1
  16. package/dist/extensions/forgecli/fix-bug.js +155 -45
  17. package/dist/extensions/forgecli/fix-bug.js.map +1 -1
  18. package/dist/extensions/forgecli/forge-commands.js +15 -22
  19. package/dist/extensions/forgecli/forge-commands.js.map +1 -1
  20. package/dist/extensions/forgecli/forge-subagent.d.ts +16 -1
  21. package/dist/extensions/forgecli/forge-subagent.js +45 -8
  22. package/dist/extensions/forgecli/forge-subagent.js.map +1 -1
  23. package/dist/extensions/forgecli/forge-update-command.d.ts +9 -0
  24. package/dist/extensions/forgecli/forge-update-command.js +106 -7
  25. package/dist/extensions/forgecli/forge-update-command.js.map +1 -1
  26. package/dist/extensions/forgecli/health-check.d.ts +22 -1
  27. package/dist/extensions/forgecli/health-check.js +177 -4
  28. package/dist/extensions/forgecli/health-check.js.map +1 -1
  29. package/dist/extensions/forgecli/hook-dispatcher.d.ts +25 -1
  30. package/dist/extensions/forgecli/hook-dispatcher.js +104 -9
  31. package/dist/extensions/forgecli/hook-dispatcher.js.map +1 -1
  32. package/dist/extensions/forgecli/hooks/check-update.d.ts +81 -0
  33. package/dist/extensions/forgecli/hooks/check-update.js +308 -0
  34. package/dist/extensions/forgecli/hooks/check-update.js.map +1 -0
  35. package/dist/extensions/forgecli/hooks/forge-permissions.d.ts +32 -0
  36. package/dist/extensions/forgecli/hooks/forge-permissions.js +119 -0
  37. package/dist/extensions/forgecli/hooks/forge-permissions.js.map +1 -0
  38. package/dist/extensions/forgecli/hooks/triage-error.d.ts +23 -0
  39. package/dist/extensions/forgecli/hooks/triage-error.js +62 -0
  40. package/dist/extensions/forgecli/hooks/triage-error.js.map +1 -0
  41. package/dist/extensions/forgecli/hooks/write-guard.d.ts +28 -0
  42. package/dist/extensions/forgecli/hooks/write-guard.js +225 -0
  43. package/dist/extensions/forgecli/hooks/write-guard.js.map +1 -0
  44. package/dist/extensions/forgecli/index.js +60 -0
  45. package/dist/extensions/forgecli/index.js.map +1 -1
  46. package/dist/extensions/forgecli/init-context.d.ts +1 -1
  47. package/dist/extensions/forgecli/init-context.js +21 -6
  48. package/dist/extensions/forgecli/init-context.js.map +1 -1
  49. package/dist/extensions/forgecli/materialize.d.ts +16 -0
  50. package/dist/extensions/forgecli/materialize.js +195 -0
  51. package/dist/extensions/forgecli/materialize.js.map +1 -0
  52. package/dist/extensions/forgecli/migrate.d.ts +19 -0
  53. package/dist/extensions/forgecli/migrate.js +258 -0
  54. package/dist/extensions/forgecli/migrate.js.map +1 -0
  55. package/dist/extensions/forgecli/migration-engine.d.ts +111 -0
  56. package/dist/extensions/forgecli/migration-engine.js +533 -0
  57. package/dist/extensions/forgecli/migration-engine.js.map +1 -0
  58. package/dist/extensions/forgecli/quiz-agent.d.ts +17 -0
  59. package/dist/extensions/forgecli/quiz-agent.js +98 -0
  60. package/dist/extensions/forgecli/quiz-agent.js.map +1 -0
  61. package/dist/extensions/forgecli/remove-command.d.ts +17 -0
  62. package/dist/extensions/forgecli/remove-command.js +124 -0
  63. package/dist/extensions/forgecli/remove-command.js.map +1 -0
  64. package/dist/extensions/forgecli/report-bug.d.ts +25 -0
  65. package/dist/extensions/forgecli/report-bug.js +159 -0
  66. package/dist/extensions/forgecli/report-bug.js.map +1 -0
  67. package/dist/extensions/forgecli/retrospective.d.ts +19 -0
  68. package/dist/extensions/forgecli/retrospective.js +156 -0
  69. package/dist/extensions/forgecli/retrospective.js.map +1 -0
  70. package/dist/extensions/forgecli/run-sprint.js +36 -3
  71. package/dist/extensions/forgecli/run-sprint.js.map +1 -1
  72. package/dist/extensions/forgecli/run-task.d.ts +9 -1
  73. package/dist/extensions/forgecli/run-task.js +66 -13
  74. package/dist/extensions/forgecli/run-task.js.map +1 -1
  75. package/dist/extensions/forgecli/session-registry.d.ts +40 -2
  76. package/dist/extensions/forgecli/session-registry.js +71 -1
  77. package/dist/extensions/forgecli/session-registry.js.map +1 -1
  78. package/dist/extensions/forgecli/status-command.d.ts +19 -0
  79. package/dist/extensions/forgecli/status-command.js +140 -0
  80. package/dist/extensions/forgecli/status-command.js.map +1 -0
  81. package/dist/extensions/forgecli/store-query.d.ts +22 -0
  82. package/dist/extensions/forgecli/store-query.js +107 -0
  83. package/dist/extensions/forgecli/store-query.js.map +1 -0
  84. package/dist/extensions/forgecli/store-repair.d.ts +17 -0
  85. package/dist/extensions/forgecli/store-repair.js +123 -0
  86. package/dist/extensions/forgecli/store-repair.js.map +1 -0
  87. package/dist/extensions/forgecli/test-orchestrate.js +1 -0
  88. package/dist/extensions/forgecli/test-orchestrate.js.map +1 -1
  89. package/dist/extensions/forgecli/thread-switcher.js +286 -41
  90. package/dist/extensions/forgecli/thread-switcher.js.map +1 -1
  91. package/dist/extensions/forgecli/transition-guard.js +7 -2
  92. package/dist/extensions/forgecli/transition-guard.js.map +1 -1
  93. package/dist/extensions/forgecli/update-tools.d.ts +23 -0
  94. package/dist/extensions/forgecli/update-tools.js +136 -0
  95. package/dist/extensions/forgecli/update-tools.js.map +1 -0
  96. package/dist/extensions/forgecli/viewport-events.js +10 -0
  97. package/dist/extensions/forgecli/viewport-events.js.map +1 -1
  98. package/dist/extensions/forgecli/viewport-renderer.d.ts +18 -0
  99. package/dist/extensions/forgecli/viewport-renderer.js +27 -0
  100. package/dist/extensions/forgecli/viewport-renderer.js.map +1 -1
  101. package/dist/extensions/forgecli/viewport-theme.js +4 -0
  102. package/dist/extensions/forgecli/viewport-theme.js.map +1 -1
  103. package/dist/extensions/forgecli/whats-new-widget.d.ts +13 -8
  104. package/dist/extensions/forgecli/whats-new-widget.js +111 -42
  105. package/dist/extensions/forgecli/whats-new-widget.js.map +1 -1
  106. package/dist/forge-payload/.base-pack/workflows/architect_approve.md +29 -3
  107. package/dist/forge-payload/.base-pack/workflows/commit_task.md +15 -8
  108. package/dist/forge-payload/.base-pack/workflows/fix_bug.md +327 -185
  109. package/dist/forge-payload/.base-pack/workflows/implement_plan.md +18 -10
  110. package/dist/forge-payload/.base-pack/workflows/plan_task.md +15 -9
  111. package/dist/forge-payload/.base-pack/workflows/review_code.md +14 -6
  112. package/dist/forge-payload/.base-pack/workflows/review_plan.md +18 -10
  113. package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
  114. package/dist/forge-payload/.schemas/bug.schema.json +3 -2
  115. package/dist/forge-payload/.schemas/config.schema.json +83 -0
  116. package/dist/forge-payload/.schemas/migrations.json +2049 -0
  117. package/dist/forge-payload/commands/regenerate.md +17 -1
  118. package/dist/forge-payload/meta/personas/README.md +16 -0
  119. package/dist/forge-payload/meta/personas/meta-architect.md +70 -0
  120. package/dist/forge-payload/meta/personas/meta-bug-fixer.md +73 -0
  121. package/dist/forge-payload/meta/personas/meta-collator.md +72 -0
  122. package/dist/forge-payload/meta/personas/meta-engineer.md +70 -0
  123. package/dist/forge-payload/meta/personas/meta-orchestrator.md +71 -0
  124. package/dist/forge-payload/meta/personas/meta-product-manager.md +82 -0
  125. package/dist/forge-payload/meta/personas/meta-qa-engineer.md +91 -0
  126. package/dist/forge-payload/meta/personas/meta-supervisor.md +92 -0
  127. package/dist/forge-payload/meta/skill-recommendations.md +154 -0
  128. package/dist/forge-payload/meta/skills/meta-architect-skills.md +43 -0
  129. package/dist/forge-payload/meta/skills/meta-bug-fixer-skills.md +43 -0
  130. package/dist/forge-payload/meta/skills/meta-collator-skills.md +41 -0
  131. package/dist/forge-payload/meta/skills/meta-engineer-skills.md +43 -0
  132. package/dist/forge-payload/meta/skills/meta-generic-skills.md +58 -0
  133. package/dist/forge-payload/meta/skills/meta-qa-engineer-skills.md +46 -0
  134. package/dist/forge-payload/meta/skills/meta-supervisor-skills.md +43 -0
  135. package/dist/forge-payload/meta/store-schema/bug.schema.md +71 -0
  136. package/dist/forge-payload/meta/store-schema/event.schema.md +76 -0
  137. package/dist/forge-payload/meta/store-schema/feature.schema.md +65 -0
  138. package/dist/forge-payload/meta/store-schema/sprint.schema.md +64 -0
  139. package/dist/forge-payload/meta/store-schema/task.schema.md +78 -0
  140. package/dist/forge-payload/meta/templates/meta-code-review.md +26 -0
  141. package/dist/forge-payload/meta/templates/meta-plan-review.md +28 -0
  142. package/dist/forge-payload/meta/templates/meta-plan.md +28 -0
  143. package/dist/forge-payload/meta/templates/meta-progress.md +25 -0
  144. package/dist/forge-payload/meta/templates/meta-retrospective.md +28 -0
  145. package/dist/forge-payload/meta/templates/meta-sprint-manifest.md +26 -0
  146. package/dist/forge-payload/meta/templates/meta-sprint-requirements.md +91 -0
  147. package/dist/forge-payload/meta/templates/meta-task-prompt.md +26 -0
  148. package/dist/forge-payload/meta/tool-specs/collate.spec.md +88 -0
  149. package/dist/forge-payload/meta/tool-specs/generation-manifest.spec.md +139 -0
  150. package/dist/forge-payload/meta/tool-specs/manage-config.spec.md +143 -0
  151. package/dist/forge-payload/meta/tool-specs/seed-store.spec.md +91 -0
  152. package/dist/forge-payload/meta/tool-specs/store-cli.spec.md +328 -0
  153. package/dist/forge-payload/meta/tool-specs/validate-store.spec.md +191 -0
  154. package/dist/forge-payload/meta/workflows/_fragments/context-injection.md +75 -0
  155. package/dist/forge-payload/meta/workflows/_fragments/event-emission-schema.md +73 -0
  156. package/dist/forge-payload/meta/workflows/_fragments/finalize.md +13 -0
  157. package/dist/forge-payload/meta/workflows/_fragments/friction-emit.md +73 -0
  158. package/dist/forge-payload/meta/workflows/_fragments/progress-reporting.md +38 -0
  159. package/dist/forge-payload/meta/workflows/_fragments/store-cli-verbs.md +39 -0
  160. package/dist/forge-payload/meta/workflows/meta-approve.md +119 -0
  161. package/dist/forge-payload/meta/workflows/meta-collate.md +89 -0
  162. package/dist/forge-payload/meta/workflows/meta-commit.md +93 -0
  163. package/dist/forge-payload/meta/workflows/meta-enhance.md +286 -0
  164. package/dist/forge-payload/meta/workflows/meta-fix-bug.md +501 -0
  165. package/dist/forge-payload/meta/workflows/meta-implement.md +132 -0
  166. package/dist/forge-payload/meta/workflows/meta-migrate.md +455 -0
  167. package/dist/forge-payload/meta/workflows/meta-orchestrate.md +993 -0
  168. package/dist/forge-payload/meta/workflows/meta-plan-task.md +133 -0
  169. package/dist/forge-payload/meta/workflows/meta-quiz-agent.md +135 -0
  170. package/dist/forge-payload/meta/workflows/meta-retrospective.md +65 -0
  171. package/dist/forge-payload/meta/workflows/meta-review-implementation.md +119 -0
  172. package/dist/forge-payload/meta/workflows/meta-review-plan.md +108 -0
  173. package/dist/forge-payload/meta/workflows/meta-review-sprint-completion.md +65 -0
  174. package/dist/forge-payload/meta/workflows/meta-sprint-intake.md +76 -0
  175. package/dist/forge-payload/meta/workflows/meta-sprint-plan.md +147 -0
  176. package/dist/forge-payload/meta/workflows/meta-update-implementation.md +76 -0
  177. package/dist/forge-payload/meta/workflows/meta-update-plan.md +76 -0
  178. package/dist/forge-payload/meta/workflows/meta-validate.md +111 -0
  179. package/dist/forge-payload/tools/check-structure.cjs +344 -0
  180. package/dist/forge-payload/tools/collate.cjs +34 -9
  181. package/dist/forge-payload/tools/list-skills.js +76 -0
  182. package/dist/forge-payload/tools/parse-gates.cjs +8 -2
  183. package/dist/forge-payload/tools/store-cli.cjs +56 -11
  184. package/dist/forge-payload/tools/store.cjs +61 -0
  185. package/dist/forge-payload/tools/substitute-placeholders.cjs +60 -8
  186. package/dist/forge-payload/tools/validate-store.cjs +6 -2
  187. package/dist/forge-payload/tools/verify-integrity.cjs +86 -0
  188. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"test-orchestrate.js","sourceRoot":"","sources":["../../../src/extensions/forgecli/test-orchestrate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"test-orchestrate.js","sourceRoot":"","sources":["../../../src/extensions/forgecli/test-orchestrate.ts"],"names":[],"mappings":"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"}
@@ -41,7 +41,7 @@
41
41
  import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
42
42
  import { getInputRouter } from "./input-router.js";
43
43
  import { getSessionRegistry } from "./session-registry.js";
44
- import { fmtTokenFooter } from "./viewport-renderer.js";
44
+ import { fmtModelAndTokenFooter, fmtModelLabel, fmtTokenFooter } from "./viewport-renderer.js";
45
45
  import { paintFooterLine, paintTailLine } from "./viewport-theme.js";
46
46
  const WIDGET_KEY = "forge:thread-switcher";
47
47
  const FOOTER_WIDGET_KEY = "forge:viewport-footer";
@@ -73,7 +73,7 @@ class TailViewComponent {
73
73
  const lines = this.registry.getTailLines(this.taskId, this.phaseRole);
74
74
  const session = this.registry.getSession(this.taskId);
75
75
  const phase = session?.phases.find((p) => p.role === this.phaseRole);
76
- const footerText = fmtTokenFooter(phase?.usage);
76
+ const footerText = fmtModelAndTokenFooter(phase ? { provider: phase.provider, model: phase.model } : undefined, phase?.usage);
77
77
  const bodyLines = lines.length === 0
78
78
  ? [truncateToWidth(`(no output yet for ${this.phaseRole})`, width)]
79
79
  : lines.map((line) => {
@@ -105,10 +105,20 @@ class TailViewComponent {
105
105
  class ViewportFooterComponent {
106
106
  registry;
107
107
  theme;
108
+ getOrchestratorModel;
108
109
  invalidationCb;
109
- constructor(registry, theme) {
110
+ constructor(registry, theme,
111
+ /**
112
+ * Returns the parent pi session's current (provider, model) — the
113
+ * "outer orchestrator" model. Caller closes over ExtensionContext
114
+ * and is responsible for guarding stale-ctx access; return undefined
115
+ * on failure or when no model is set. When undefined, the footer
116
+ * just shows `Σ ↑X ↓Y`.
117
+ */
118
+ getOrchestratorModel) {
110
119
  this.registry = registry;
111
120
  this.theme = theme;
121
+ this.getOrchestratorModel = getOrchestratorModel;
112
122
  const onChange = () => this.invalidationCb?.();
113
123
  registry.on("change", onChange);
114
124
  registry.on("tail", onChange);
@@ -120,10 +130,22 @@ class ViewportFooterComponent {
120
130
  };
121
131
  }
122
132
  render(width) {
123
- const text = fmtTokenFooter(this.registry.getAggregateUsage());
124
- if (!text)
125
- return []; // cold-start: render nothing rather than blank row.
126
- return [paintFooterLine(`Σ ${text}`, width, this.theme)];
133
+ // Only show the footer when an orchestrator session is active.
134
+ // When all sessions are terminal (completed/failed/cancelled) or
135
+ // when no session exists, hide the footer main viewport has no
136
+ // subagent aggregate to display.
137
+ const sessions = this.registry.listSessions();
138
+ const hasActive = sessions.some((s) => s.status === "running" || s.status === "cancelling");
139
+ if (!hasActive)
140
+ return [];
141
+ const tokens = fmtTokenFooter(this.registry.getAggregateUsage());
142
+ const orchModel = fmtModelLabel(this.getOrchestratorModel?.());
143
+ if (!tokens && !orchModel)
144
+ return [];
145
+ const left = orchModel ? `⌂ ${orchModel}` : "";
146
+ const right = tokens ? `Σ ${tokens}` : "";
147
+ const text = left && right ? `${left} ${right}` : (left || right);
148
+ return [paintFooterLine(text, width, this.theme)];
127
149
  }
128
150
  invalidate() {
129
151
  // Re-render driven by external invalidationCb → tui.requestRender().
@@ -143,6 +165,9 @@ class ChipStripComponent {
143
165
  focusedChipId = "main";
144
166
  stripActive = false;
145
167
  invalidationCb;
168
+ /** When non-null, the strip shows a cancellation confirmation prompt
169
+ * for this chip instead of the normal chip view. */
170
+ cancelTarget = null;
146
171
  constructor(registry, theme) {
147
172
  this.registry = registry;
148
173
  this.theme = theme;
@@ -213,11 +238,20 @@ class ChipStripComponent {
213
238
  chipGlyph(chip) {
214
239
  if (chip.id === "main")
215
240
  return this.focusedChipId === "main" ? "●" : "○";
241
+ const session = this.activeSession();
216
242
  const p = this.chipPhase(chip);
217
243
  if (!p)
218
244
  return "·";
219
245
  if (this.focusedChipId === chip.id)
220
246
  return "●";
247
+ // Cancelling/cancelled glyphs are session-level, not phase-level.
248
+ // Show ⏳ for any phase when the session is cancelling, ⊘ when cancelled.
249
+ if (session?.status === "cancelled" && p.status !== "completed" && p.status !== "failed")
250
+ return "⊘";
251
+ if (session?.status === "cancelling" && p.status === "running")
252
+ return "⏳";
253
+ if (p.status === "cancelled")
254
+ return "⊘";
221
255
  if (p.status === "completed")
222
256
  return "✓";
223
257
  if (p.status === "failed")
@@ -236,7 +270,7 @@ class ChipStripComponent {
236
270
  return session.phases[session.phases.length - 1]?.role;
237
271
  }
238
272
  spinnerFrame(session) {
239
- if (session.status !== "running")
273
+ if (session.status !== "running" && session.status !== "cancelling")
240
274
  return "";
241
275
  const idx = Math.floor(Date.now() / SPINNER_INTERVAL_MS) % SPINNER_FRAMES.length;
242
276
  return SPINNER_FRAMES[idx];
@@ -245,6 +279,10 @@ class ChipStripComponent {
245
279
  const session = this.activeSession();
246
280
  if (!session)
247
281
  return []; // UX-B: hide entirely when no session.
282
+ // If user is confirming cancellation, show the confirmation prompt.
283
+ if (this.cancelTarget) {
284
+ return this.renderCancelPrompt(width, this.cancelTarget);
285
+ }
248
286
  const chips = this.chips();
249
287
  // Clamp cursor.
250
288
  if (this.cursorIdx >= chips.length)
@@ -256,29 +294,47 @@ class ChipStripComponent {
256
294
  renderInactive(width, session) {
257
295
  const dim = (s) => this.theme.fg("dim", s);
258
296
  const accent = (s) => this.theme.fg("accent", s);
259
- const spin = this.spinnerFrame(session);
260
297
  const chips = this.chips();
261
- // Orchestrator chip stays bracketed + accent-colored (anchor identity).
262
- // Subagent chips render after it as `<glyph> <role>`, dimmed —
263
- // completed phases show ✓, failed ✗, live ◇ (or ◆ with unread).
264
- // This makes phase progression visible at a glance without expanding
265
- // the strip.
298
+ const spin = this.spinnerFrame(session);
299
+ // Orchestrator chip: bracketed + accent-colored (anchor identity).
300
+ // Subagent chips: <glyph> <role>, dimmed.
266
301
  const orchChip = accent(`[${session.taskId}]`);
267
302
  const phaseChips = chips
268
303
  .filter((c) => c.id !== "main")
269
304
  .map((c) => dim(`${this.chipGlyph(c)} ${c.label}`));
270
305
  const chipsLine = [orchChip, ...phaseChips].join(" ");
271
- const prefix = dim("threads ");
272
- const hint = dim(" ↓ to navigate");
306
+ // Right-side: status · spinner · command hints.
307
+ // Cancelled sessions show "r resume" affordance; all others show "↓ to navigate".
308
+ const statusLabel = session.status === "cancelled" ? "cancelled"
309
+ : session.status === "cancelling" ? "cancelling…"
310
+ : "";
311
+ const statusPart = statusLabel ? dim(` ${statusLabel}`) : "";
273
312
  const spinPart = spin ? ` ${spin}` : "";
274
- const previewText = session.currentTurnPreview ? ` "${session.currentTurnPreview}"` : "";
275
- const fixedWidth = visibleWidth(prefix) +
276
- visibleWidth(chipsLine) +
277
- visibleWidth(spinPart) +
278
- visibleWidth(hint);
279
- const previewBudget = Math.max(0, width - fixedWidth);
280
- const preview = previewText ? dim(truncateToWidth(previewText, previewBudget)) : "";
281
- let line = `${prefix}${chipsLine}${spinPart}${preview}${hint}`;
313
+ const hint = session.status === "cancelled"
314
+ ? dim(" ↓ nav · r resume")
315
+ : dim(" ↓ to navigate");
316
+ // Truncate preview text from the MIDDLE of the line to keep chips and hints visible.
317
+ const previewText = session.currentTurnPreview ? `"${session.currentTurnPreview}"` : "";
318
+ // Priority: chips + status + spinner + hint are fixed.
319
+ // Truncate preview first, then truncate from the right as fallback.
320
+ const fixedRight = visibleWidth(statusPart) + visibleWidth(spinPart) + visibleWidth(hint);
321
+ const previewBudget = Math.max(0, width - fixedRight - 4); // 4 = safety margin
322
+ let preview = "";
323
+ if (previewText) {
324
+ // Truncate the preview text itself to fit the budget
325
+ const truncated = truncateToWidth(previewText, previewBudget);
326
+ if (visibleWidth(truncated) > 0)
327
+ preview = dim(` ${truncated}`);
328
+ }
329
+ // Build line; truncate from the right (preview tail) if still over-width.
330
+ let line = `${chipsLine}${statusPart}${spinPart}${preview}${hint}`;
331
+ if (visibleWidth(line) > width) {
332
+ // Truncate preview tail first (not chips)
333
+ const budget = Math.max(0, width - visibleWidth(chipsLine) - fixedRight);
334
+ const previewOnly = truncateToWidth(previewText, budget);
335
+ preview = previewOnly ? dim(` ${previewOnly}`) : "";
336
+ line = `${chipsLine}${statusPart}${spinPart}${preview}${hint}`;
337
+ }
282
338
  if (visibleWidth(line) > width)
283
339
  line = truncateToWidth(line, width);
284
340
  return [line];
@@ -298,8 +354,18 @@ class ChipStripComponent {
298
354
  return bold(inner);
299
355
  return dim(inner);
300
356
  });
301
- const prefix = accent("threads ─ ");
302
- const hint = dim(" ←→ · enter · back · esc back+main");
357
+ const prefix = "";
358
+ // "r resume" shown only for cancelled sessions; "x cancel" for running ones.
359
+ const cancelWord = session.status === "cancelled" ? dim("r resume") : dim("x cancel");
360
+ const navHints = dim(" ←→ · enter · ↑ back · esc back+main");
361
+ // Show status-based text for non-running sessions
362
+ let statusPart = "";
363
+ if (session.status === "cancelling") {
364
+ statusPart = " cancelling…";
365
+ }
366
+ else if (session.status === "cancelled") {
367
+ statusPart = " cancelled";
368
+ }
303
369
  const spin = this.spinnerFrame(session);
304
370
  const spinPart = spin ? ` ${spin}` : "";
305
371
  const previewText = session.currentTurnPreview ? ` "${session.currentTurnPreview}"` : "";
@@ -308,15 +374,44 @@ class ChipStripComponent {
308
374
  const fixed = visibleWidth(prefix) +
309
375
  visibleWidth(chipsJoined) +
310
376
  visibleWidth(spinPart) +
311
- visibleWidth(hint);
377
+ visibleWidth(statusPart) +
378
+ visibleWidth(cancelWord) +
379
+ visibleWidth(navHints);
312
380
  const previewBudget = Math.max(0, width - fixed);
313
381
  const preview = previewText ? dim(truncateToWidth(previewText, previewBudget)) : "";
314
- let line = `${prefix}${chipsJoined}${spinPart}${preview}${hint}`;
382
+ let line = `${prefix}${chipsJoined}${spinPart}${statusPart}${preview} ${cancelWord} ${navHints}`;
315
383
  // Hard cap as last-resort defence (visibleWidth is best-effort).
316
384
  if (visibleWidth(line) > width)
317
385
  line = truncateToWidth(line, width);
318
386
  return [line];
319
387
  }
388
+ /**
389
+ * Render the cancellation confirmation prompt. Replaces the normal
390
+ * chip strip when cancelTarget is non-null.
391
+ * ⚠ Cancel [taskId] → [phaseRole]? y/n · esc to abort
392
+ */
393
+ renderCancelPrompt(width, target) {
394
+ const dim = (s) => this.theme.fg("dim", s);
395
+ const warning = (s) => this.theme.fg("warning", s);
396
+ const bold = (s) => this.theme.bold(s);
397
+ const taskLabel = target.taskId ?? target.label;
398
+ const phaseLabel = target.id === "main" ? "session" : target.label;
399
+ // "cancel" sits right after the prompt — most visible position.
400
+ // Truncation sacrifices the dim dismiss-hints from the END, keeping
401
+ // the action word and the warning always readable.
402
+ const actionWord = dim("cancel");
403
+ const hints = dim(" · n/esc dismiss · y confirm");
404
+ const prompt = warning(`⚠ Cancel ${bold(taskLabel)} → ${bold(phaseLabel)}? `);
405
+ const budget = Math.max(0, width - visibleWidth(prompt) - visibleWidth(actionWord));
406
+ const tail = budget > 0 ? dim(` · n/esc dismiss · y confirm`) : "";
407
+ const budgetedTail = visibleWidth(tail) > budget
408
+ ? dim(truncateToWidth(` · n/esc dismiss · y confirm`, budget))
409
+ : tail;
410
+ let line = `${prompt}${actionWord}${budgetedTail}`;
411
+ if (visibleWidth(line) > width)
412
+ line = truncateToWidth(line, width);
413
+ return [line];
414
+ }
320
415
  invalidate() {
321
416
  // Re-render driven by external invalidationCb → tui.requestRender().
322
417
  }
@@ -328,6 +423,47 @@ class ChipStripComponent {
328
423
  this.stripActive = active;
329
424
  this.invalidationCb?.();
330
425
  }
426
+ /** Initiate cancel confirmation for a chip. Sets cancelTarget so the
427
+ * next render shows the confirmation prompt. */
428
+ requestCancelChip(chip) {
429
+ this.cancelTarget = chip;
430
+ this.invalidationCb?.();
431
+ }
432
+ /** Confirm the pending cancellation (user pressed y). */
433
+ confirmCancel() {
434
+ const target = this.cancelTarget;
435
+ this.cancelTarget = null;
436
+ this.invalidationCb?.();
437
+ return target;
438
+ }
439
+ /** Dismiss the cancel prompt (user pressed n/Esc). */
440
+ dismissCancel() {
441
+ this.cancelTarget = null;
442
+ this.invalidationCb?.();
443
+ }
444
+ /** Whether a cancel confirmation prompt is active. */
445
+ isCancelPromptActive() {
446
+ return this.cancelTarget !== null;
447
+ }
448
+ /** Check if the chip at the current cursor is a running phase that can be cancelled. */
449
+ isCursorCancellable() {
450
+ const chip = this.cursorChip();
451
+ if (!chip)
452
+ return false;
453
+ if (chip.id === "main") {
454
+ const session = this.activeSession();
455
+ return (session?.status ?? "") === "running";
456
+ }
457
+ const p = this.chipPhase(chip);
458
+ if (!p)
459
+ return false;
460
+ return p.status === "running";
461
+ }
462
+ /** True when the current session is cancelled — r key triggers resume. */
463
+ isCursorResumable() {
464
+ const session = this.activeSession();
465
+ return session?.status === "cancelled";
466
+ }
331
467
  getStripActive() {
332
468
  return this.stripActive;
333
469
  }
@@ -394,6 +530,18 @@ function isEsc(d) {
394
530
  // by the arrow checks above first.
395
531
  return d === "\x1b";
396
532
  }
533
+ function isXKey(d) {
534
+ return d === "x";
535
+ }
536
+ function isRKey(d) {
537
+ return d === "r" || d === "R";
538
+ }
539
+ function isYKey(d) {
540
+ return d === "y" || d === "Y";
541
+ }
542
+ function isNKey(d) {
543
+ return d === "n" || d === "N";
544
+ }
397
545
  // ── Registrar ───────────────────────────────────────────────────────────────
398
546
  /**
399
547
  * Custom renderer for the `forge:turn` chat-history rows we append on every
@@ -434,15 +582,22 @@ export function registerThreadSwitcher(pi) {
434
582
  let themeRef;
435
583
  let spinnerTimer;
436
584
  let mounted = false;
585
+ // Pi invalidates the ExtensionContext after newSession / fork /
586
+ // switchSession / reload. The input-router handler and the focus
587
+ // helpers are registered once at mount but fire after arbitrary
588
+ // session replacements — they must read the *live* ctx, not the
589
+ // one captured at mount time. We refresh this on every
590
+ // session_start and on every forge:threads command invocation.
591
+ let currentCtx;
437
592
  function ensureSpinnerTimer() {
438
- // Tick re-renders while any session is "running" so the spinner
439
- // glyph animates and the preview text refreshes between user input.
593
+ // Tick re-renders while any session is "running" or "cancelling" so the
594
+ // spinner glyph animates and the preview text refreshes between user input.
440
595
  // When all sessions are terminal, the timer stops itself.
441
596
  if (spinnerTimer)
442
597
  return;
443
598
  spinnerTimer = setInterval(() => {
444
- const anyRunning = registry.listSessions().some((s) => s.status === "running");
445
- if (!anyRunning) {
599
+ const anyActive = registry.listSessions().some((s) => s.status === "running" || s.status === "cancelling");
600
+ if (!anyActive) {
446
601
  if (spinnerTimer)
447
602
  clearInterval(spinnerTimer);
448
603
  spinnerTimer = undefined;
@@ -454,6 +609,7 @@ export function registerThreadSwitcher(pi) {
454
609
  }, SPINNER_INTERVAL_MS);
455
610
  }
456
611
  function mount(ctx) {
612
+ currentCtx = ctx;
457
613
  if (mounted)
458
614
  return;
459
615
  process.stderr.write("[forge:threads] mount() invoked\n");
@@ -470,7 +626,21 @@ export function registerThreadSwitcher(pi) {
470
626
  // viewport (mirrors the per-phase footer rendered inside TailView
471
627
  // when a subagent chip is focused).
472
628
  ctx.ui.setWidget(FOOTER_WIDGET_KEY, (tui, theme) => {
473
- const footer = new ViewportFooterComponent(registry, theme);
629
+ // Guard against stale-ctx access after session replacement
630
+ // (newSession / fork / switchSession / reload) — touching
631
+ // ctx.model on a stale ctx throws and would crash the row.
632
+ const getOrchestratorModel = () => {
633
+ try {
634
+ const m = ctx.model;
635
+ if (!m)
636
+ return undefined;
637
+ return { provider: m.provider, model: m.id };
638
+ }
639
+ catch {
640
+ return undefined;
641
+ }
642
+ };
643
+ const footer = new ViewportFooterComponent(registry, theme, getOrchestratorModel);
474
644
  footer.setInvalidationCallback(() => tui.requestRender());
475
645
  return footer;
476
646
  }, { placement: "aboveEditor" });
@@ -509,21 +679,81 @@ export function registerThreadSwitcher(pi) {
509
679
  getInputRouter().register((data) => {
510
680
  if (!stripRef)
511
681
  return undefined;
682
+ const live = currentCtx;
683
+ if (!live)
684
+ return undefined;
512
685
  if (!stripRef.getStripActive()) {
513
686
  if (!isDownArrow(data))
514
687
  return undefined;
515
- const editorText = ctx.ui.getEditorText();
688
+ let editorText = "";
689
+ try {
690
+ editorText = live.ui.getEditorText();
691
+ }
692
+ catch {
693
+ return undefined;
694
+ }
516
695
  if (editorText.includes("\n"))
517
696
  return undefined; // multi-line nav
518
697
  if (!stripRef.hasSession())
519
698
  return undefined; // strip hidden anyway
520
699
  stripRef.setStripActive(true);
521
- // Park cursor on the currently-running subagent — that's
522
- // the chip the user almost always wants to see. Falls back
523
- // to orchestrator (index 0) when no phase is live.
524
700
  stripRef.parkCursorOnCurrentPhase();
525
701
  return { consume: true };
526
702
  }
703
+ // ── Cancel-confirmation handling (cancelTarget active) ────────
704
+ // When the strip shows a cancel prompt, y/Enter confirms,
705
+ // n/Esc dismisses. All other keys are consumed (no passthrough).
706
+ if (stripRef.isCancelPromptActive()) {
707
+ if (isYKey(data) || isEnter(data)) {
708
+ const target = stripRef.confirmCancel();
709
+ if (target?.taskId) {
710
+ registry.requestCancel(target.taskId);
711
+ }
712
+ stripRef.setStripActive(false);
713
+ setFocusToMain(live);
714
+ return { consume: true };
715
+ }
716
+ // Dismiss: n, Esc
717
+ if (isNKey(data) || isEsc(data)) {
718
+ stripRef.dismissCancel();
719
+ stripRef.setStripActive(false);
720
+ return { consume: true };
721
+ }
722
+ // Any other key in cancel-confirmation mode is consumed silently.
723
+ return { consume: true };
724
+ }
725
+ if (isXKey(data)) {
726
+ const chip = stripRef.cursorChip();
727
+ if (chip && stripRef.isCursorCancellable()) {
728
+ stripRef.requestCancelChip(chip);
729
+ return { consume: true };
730
+ }
731
+ return undefined;
732
+ }
733
+ if (isRKey(data)) {
734
+ // Resume a cancelled session. The state file is preserved on cancel
735
+ // (ADR-S21-01). Write the slash command to the editor and simulate
736
+ // Enter — exactly mirrors how a user types and submits the command.
737
+ const session = registry.listSessions()[0];
738
+ if (session && stripRef.isCursorResumable()) {
739
+ const entityId = session.taskId;
740
+ const cmd = entityId.startsWith("FORGE-BUG-")
741
+ ? `forge:fix-bug ${entityId}`
742
+ : `forge:run-task ${entityId}`;
743
+ stripRef.setStripActive(false);
744
+ try {
745
+ live.ui.setEditorText(`/${cmd}`);
746
+ }
747
+ catch {
748
+ // Non-fatal — editor may not be accessible in all contexts.
749
+ live.ui.notify(`↻ Resume: /${cmd}`, "info");
750
+ }
751
+ // Return Enter to submit the command. The router dispatches
752
+ // normally; pi processes it as a slash-command submit.
753
+ return { data: "\r" };
754
+ }
755
+ return undefined;
756
+ }
527
757
  if (isLeftArrow(data)) {
528
758
  stripRef.moveCursor(-1);
529
759
  return { consume: true };
@@ -537,12 +767,12 @@ export function registerThreadSwitcher(pi) {
537
767
  return { consume: true };
538
768
  }
539
769
  if (isEnter(data)) {
540
- commitFocus(ctx);
770
+ commitFocus(live);
541
771
  return { consume: true };
542
772
  }
543
773
  if (isEsc(data)) {
544
774
  stripRef.setStripActive(false);
545
- setFocusToMain(ctx);
775
+ setFocusToMain(live);
546
776
  return { consume: true };
547
777
  }
548
778
  return undefined;
@@ -571,14 +801,25 @@ export function registerThreadSwitcher(pi) {
571
801
  if (tuiRef)
572
802
  tail.setInvalidationCallback(() => tuiRef?.requestRender());
573
803
  tailRef = tail;
574
- ctx.ui.setOutputSource(tail);
804
+ try {
805
+ ctx.ui.setOutputSource(tail);
806
+ }
807
+ catch {
808
+ // ctx went stale between keypress and dispatch — drop quietly;
809
+ // the next session_start will refresh currentCtx.
810
+ }
575
811
  registry.markRead(chip.taskId, chip.id);
576
812
  }
577
813
  function setFocusToMain(ctx) {
578
814
  stripRef?.setFocusedChipId("main");
579
815
  tailRef?.dispose?.();
580
816
  tailRef = undefined;
581
- ctx.ui.setOutputSource(null);
817
+ try {
818
+ ctx.ui.setOutputSource(null);
819
+ }
820
+ catch {
821
+ // see commitFocus
822
+ }
582
823
  }
583
824
  pi.registerCommand("forge:threads", {
584
825
  description: "Activate the Forge thread-switcher strip below the editor. " +
@@ -591,6 +832,10 @@ export function registerThreadSwitcher(pi) {
591
832
  });
592
833
  // Mount at session_start so the Down listener + chip strip are live
593
834
  // from the first keystroke. mount() is idempotent.
835
+ // session_start fires for the initial session and for every
836
+ // post-replacement session (newSession / fork / switchSession /
837
+ // reload), so mount() refreshing currentCtx here is the single
838
+ // chokepoint for keeping the input-router handler's ctx live.
594
839
  pi.on("session_start", async (_event, ctx) => {
595
840
  mount(ctx);
596
841
  });