@entelligentsia/forgecli 1.0.10 → 1.0.14

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 (167) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/CHANGELOG-forge-plugin.md +150 -0
  3. package/dist/bin/forge.js +0 -0
  4. package/dist/extensions/forgecli/config-layer.d.ts +16 -0
  5. package/dist/extensions/forgecli/config-layer.js +5 -0
  6. package/dist/extensions/forgecli/config-layer.js.map +1 -1
  7. package/dist/extensions/forgecli/dashboard/component.d.ts +102 -0
  8. package/dist/extensions/forgecli/dashboard/component.js +882 -0
  9. package/dist/extensions/forgecli/dashboard/component.js.map +1 -0
  10. package/dist/extensions/forgecli/dashboard/register.d.ts +2 -0
  11. package/dist/extensions/forgecli/dashboard/register.js +45 -0
  12. package/dist/extensions/forgecli/dashboard/register.js.map +1 -0
  13. package/dist/extensions/forgecli/dashboard/view-model.d.ts +35 -0
  14. package/dist/extensions/forgecli/dashboard/view-model.js +54 -0
  15. package/dist/extensions/forgecli/dashboard/view-model.js.map +1 -0
  16. package/dist/extensions/forgecli/fix-bug.js +72 -7
  17. package/dist/extensions/forgecli/fix-bug.js.map +1 -1
  18. package/dist/extensions/forgecli/forge-cli-schema.json +4 -0
  19. package/dist/extensions/forgecli/forge-commands.js +1 -0
  20. package/dist/extensions/forgecli/forge-commands.js.map +1 -1
  21. package/dist/extensions/forgecli/forge-init/phase4-register.js +53 -0
  22. package/dist/extensions/forgecli/forge-init/phase4-register.js.map +1 -1
  23. package/dist/extensions/forgecli/forge-subagent.js +6 -4
  24. package/dist/extensions/forgecli/forge-subagent.js.map +1 -1
  25. package/dist/extensions/forgecli/index.js +5 -0
  26. package/dist/extensions/forgecli/index.js.map +1 -1
  27. package/dist/extensions/forgecli/lib/halt-advisor.d.ts +54 -0
  28. package/dist/extensions/forgecli/lib/halt-advisor.js +90 -0
  29. package/dist/extensions/forgecli/lib/halt-advisor.js.map +1 -0
  30. package/dist/extensions/forgecli/migration-engine.js +25 -12
  31. package/dist/extensions/forgecli/migration-engine.js.map +1 -1
  32. package/dist/extensions/forgecli/orchestrator-status-bar.d.ts +25 -0
  33. package/dist/extensions/forgecli/orchestrator-status-bar.js +183 -0
  34. package/dist/extensions/forgecli/orchestrator-status-bar.js.map +1 -0
  35. package/dist/extensions/forgecli/orchestrator-tree.d.ts +96 -0
  36. package/dist/extensions/forgecli/orchestrator-tree.js +390 -0
  37. package/dist/extensions/forgecli/orchestrator-tree.js.map +1 -0
  38. package/dist/extensions/forgecli/project-orientation.js +12 -8
  39. package/dist/extensions/forgecli/project-orientation.js.map +1 -1
  40. package/dist/extensions/forgecli/regenerate.d.ts +16 -0
  41. package/dist/extensions/forgecli/regenerate.js +110 -0
  42. package/dist/extensions/forgecli/regenerate.js.map +1 -1
  43. package/dist/extensions/forgecli/run-sprint.js +33 -3
  44. package/dist/extensions/forgecli/run-sprint.js.map +1 -1
  45. package/dist/extensions/forgecli/run-task.d.ts +32 -0
  46. package/dist/extensions/forgecli/run-task.js +185 -12
  47. package/dist/extensions/forgecli/run-task.js.map +1 -1
  48. package/dist/extensions/forgecli/thread-switcher.js +105 -764
  49. package/dist/extensions/forgecli/thread-switcher.js.map +1 -1
  50. package/dist/extensions/forgecli/viewport-events.js +32 -0
  51. package/dist/extensions/forgecli/viewport-events.js.map +1 -1
  52. package/dist/forge-payload/.base-pack/commands/fix-bug.md +1 -1
  53. package/dist/forge-payload/.base-pack/commands/run-sprint.md +1 -1
  54. package/dist/forge-payload/.base-pack/commands/run-task.md +1 -1
  55. package/dist/forge-payload/.base-pack/personas/architect.md +1 -1
  56. package/dist/forge-payload/.base-pack/personas/bug-fixer.md +1 -1
  57. package/dist/forge-payload/.base-pack/personas/collator.md +3 -3
  58. package/dist/forge-payload/.base-pack/personas/engineer.md +1 -1
  59. package/dist/forge-payload/.base-pack/personas/librarian.md +1 -1
  60. package/dist/forge-payload/.base-pack/personas/orchestrator.md +1 -1
  61. package/dist/forge-payload/.base-pack/personas/product-manager.md +1 -1
  62. package/dist/forge-payload/.base-pack/personas/qa-engineer.md +1 -1
  63. package/dist/forge-payload/.base-pack/personas/supervisor.md +1 -1
  64. package/dist/forge-payload/.base-pack/workflows/_fragments/event-emission-schema.md +1 -1
  65. package/dist/forge-payload/.base-pack/workflows/_fragments/friction-emit.md +1 -1
  66. package/dist/forge-payload/.base-pack/workflows/_fragments/iron-laws.md +1 -1
  67. package/dist/forge-payload/.base-pack/workflows/_fragments/progress-reporting.md +2 -2
  68. package/dist/forge-payload/.base-pack/workflows/_fragments/store-cli-verbs.md +11 -2
  69. package/dist/forge-payload/.base-pack/workflows/architect_approve.md +6 -7
  70. package/dist/forge-payload/.base-pack/workflows/architect_review_sprint_completion.md +2 -2
  71. package/dist/forge-payload/.base-pack/workflows/architect_sprint_intake.md +2 -2
  72. package/dist/forge-payload/.base-pack/workflows/architect_sprint_plan.md +5 -5
  73. package/dist/forge-payload/.base-pack/workflows/collator_agent.md +4 -6
  74. package/dist/forge-payload/.base-pack/workflows/commit_task.md +5 -6
  75. package/dist/forge-payload/.base-pack/workflows/enhance.md +5 -5
  76. package/dist/forge-payload/.base-pack/workflows/implement_plan.md +6 -7
  77. package/dist/forge-payload/.base-pack/workflows/migrate_structural.md +12 -13
  78. package/dist/forge-payload/.base-pack/workflows/plan_task.md +5 -6
  79. package/dist/forge-payload/.base-pack/workflows/review_code.md +8 -8
  80. package/dist/forge-payload/.base-pack/workflows/review_plan.md +8 -8
  81. package/dist/forge-payload/.base-pack/workflows/sprint_retrospective.md +3 -3
  82. package/dist/forge-payload/.base-pack/workflows/triage.md +12 -9
  83. package/dist/forge-payload/.base-pack/workflows/update_implementation.md +2 -2
  84. package/dist/forge-payload/.base-pack/workflows/update_plan.md +2 -2
  85. package/dist/forge-payload/.base-pack/workflows/validate_task.md +5 -6
  86. package/dist/forge-payload/.base-pack/workflows-js/wfl-fix-bug.js +490 -0
  87. package/dist/forge-payload/.base-pack/workflows-js/wfl-run-sprint.js +416 -0
  88. package/dist/forge-payload/.base-pack/workflows-js/wfl-run-task.js +608 -0
  89. package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
  90. package/dist/forge-payload/.schemas/config.schema.json +2 -3
  91. package/dist/forge-payload/.schemas/enum-catalog.json +2 -2
  92. package/dist/forge-payload/.schemas/event.schema.json +16 -0
  93. package/dist/forge-payload/.schemas/migrations.json +236 -0
  94. package/dist/forge-payload/commands/health.md +29 -0
  95. package/dist/forge-payload/commands/rebuild.md +143 -15
  96. package/dist/forge-payload/commands/update.md +28 -27
  97. package/dist/forge-payload/hooks/preflight-session.cjs +99 -0
  98. package/dist/forge-payload/init/phases/phase-3-materialize.md +18 -5
  99. package/dist/forge-payload/integrity.json +7 -6
  100. package/dist/forge-payload/meta/fragments/tool-discipline.md +1 -1
  101. package/dist/forge-payload/meta/personas/meta-architect.md +1 -1
  102. package/dist/forge-payload/meta/personas/meta-bug-fixer.md +1 -1
  103. package/dist/forge-payload/meta/personas/meta-collator.md +7 -7
  104. package/dist/forge-payload/meta/personas/meta-engineer.md +1 -1
  105. package/dist/forge-payload/meta/personas/meta-orchestrator.md +1 -1
  106. package/dist/forge-payload/meta/personas/meta-supervisor.md +1 -1
  107. package/dist/forge-payload/meta/tool-specs/store-cli.spec.md +1 -1
  108. package/dist/forge-payload/meta/workflows/_fragments/event-emission-schema.md +1 -1
  109. package/dist/forge-payload/meta/workflows/_fragments/friction-emit.md +1 -1
  110. package/dist/forge-payload/meta/workflows/_fragments/iron-laws.md +1 -1
  111. package/dist/forge-payload/meta/workflows/_fragments/progress-reporting.md +2 -2
  112. package/dist/forge-payload/meta/workflows/_fragments/store-cli-verbs.md +11 -2
  113. package/dist/forge-payload/meta/workflows/meta-approve.md +6 -7
  114. package/dist/forge-payload/meta/workflows/meta-bug-triage.md +12 -9
  115. package/dist/forge-payload/meta/workflows/meta-collate.md +5 -7
  116. package/dist/forge-payload/meta/workflows/meta-commit.md +5 -6
  117. package/dist/forge-payload/meta/workflows/meta-enhance.md +5 -5
  118. package/dist/forge-payload/meta/workflows/meta-fix-bug.md +35 -11
  119. package/dist/forge-payload/meta/workflows/meta-implement.md +15 -7
  120. package/dist/forge-payload/meta/workflows/meta-migrate.md +13 -14
  121. package/dist/forge-payload/meta/workflows/meta-new-sprint.md +3 -3
  122. package/dist/forge-payload/meta/workflows/meta-orchestrate.md +138 -39
  123. package/dist/forge-payload/meta/workflows/meta-plan-sprint.md +6 -6
  124. package/dist/forge-payload/meta/workflows/meta-plan-task.md +12 -6
  125. package/dist/forge-payload/meta/workflows/meta-retro.md +4 -4
  126. package/dist/forge-payload/meta/workflows/meta-retrospective.md +4 -4
  127. package/dist/forge-payload/meta/workflows/meta-review-implementation.md +8 -8
  128. package/dist/forge-payload/meta/workflows/meta-review-plan.md +8 -8
  129. package/dist/forge-payload/meta/workflows/meta-review-sprint-completion.md +3 -3
  130. package/dist/forge-payload/meta/workflows/meta-sprint-intake.md +3 -3
  131. package/dist/forge-payload/meta/workflows/meta-sprint-plan.md +6 -6
  132. package/dist/forge-payload/meta/workflows/meta-update-implementation.md +2 -2
  133. package/dist/forge-payload/meta/workflows/meta-update-plan.md +2 -2
  134. package/dist/forge-payload/meta/workflows/meta-validate.md +5 -6
  135. package/dist/forge-payload/schemas/config.schema.json +2 -3
  136. package/dist/forge-payload/schemas/enum-catalog.json +2 -2
  137. package/dist/forge-payload/schemas/event.schema.json +16 -0
  138. package/dist/forge-payload/schemas/structure-manifest.json +75 -73
  139. package/dist/forge-payload/skills/refresh-kb-links/SKILL.md +14 -7
  140. package/dist/forge-payload/tools/banners.cjs +29 -10
  141. package/dist/forge-payload/tools/check-structure.cjs +88 -7
  142. package/dist/forge-payload/tools/collate.cjs +16 -2
  143. package/dist/forge-payload/tools/manage-config.cjs +5 -7
  144. package/dist/forge-payload/tools/parse-gates.cjs +73 -1
  145. package/dist/forge-payload/tools/postflight-gate.cjs +252 -0
  146. package/dist/forge-payload/tools/preflight-gate.cjs +47 -0
  147. package/dist/forge-payload/tools/substitute-placeholders.cjs +5 -4
  148. package/dist/forge-payload/tools/verify-phase.cjs +17 -0
  149. package/package.json +1 -1
  150. package/dist/bin/forgecli.d.ts +0 -2
  151. package/dist/bin/forgecli.js +0 -6
  152. package/dist/bin/forgecli.js.map +0 -1
  153. package/dist/extensions/forgecli/config-tui/index.d.ts +0 -5
  154. package/dist/extensions/forgecli/config-tui/index.js +0 -5
  155. package/dist/extensions/forgecli/config-tui/index.js.map +0 -1
  156. package/dist/extensions/forgecli/loaders/persona-skill-loader.d.ts +0 -45
  157. package/dist/extensions/forgecli/loaders/persona-skill-loader.js +0 -227
  158. package/dist/extensions/forgecli/loaders/persona-skill-loader.js.map +0 -1
  159. package/dist/extensions/forgecli/loaders/template-render.d.ts +0 -20
  160. package/dist/extensions/forgecli/loaders/template-render.js +0 -85
  161. package/dist/extensions/forgecli/loaders/template-render.js.map +0 -1
  162. package/dist/extensions/forgecli/loaders/workflow-loader.d.ts +0 -41
  163. package/dist/extensions/forgecli/loaders/workflow-loader.js +0 -164
  164. package/dist/extensions/forgecli/loaders/workflow-loader.js.map +0 -1
  165. package/dist/forge-payload/.base-pack/workflows/fix_bug.md +0 -446
  166. package/dist/forge-payload/.base-pack/workflows/orchestrate_task.md +0 -928
  167. package/dist/forge-payload/.base-pack/workflows/run_sprint.md +0 -225
@@ -0,0 +1,390 @@
1
+ // orchestrator-tree.ts — Generic observable tree for orchestrator > subagent
2
+ // visualization.
3
+ //
4
+ // Sibling to SessionRegistry (which drives the chip strip). The dashboard
5
+ // overlay reads from OrchestratorTree; the chip strip reads from
6
+ // SessionRegistry. Both are written to by the same orchestrator call-sites.
7
+ //
8
+ // The tree is depth-agnostic: Sprint>Tasks>Phases, Fix-bug>Phases, and
9
+ // standalone Run-task are all fan-out trees with the same node shape. Any
10
+ // future orchestrator (run-release, etc.) adds nodes without dashboard
11
+ // changes.
12
+ //
13
+ // Node IDs are dot/colon-separated paths for readability:
14
+ // "FORGE-S27" — sprint root (orchestrator)
15
+ // "FORGE-S27-T01" — task (orchestrator)
16
+ // "FORGE-S27-T01:plan:1" — phase (leaf)
17
+ // "FORGE-S27:ceremony" — ceremony (leaf)
18
+ //
19
+ // LRU-capped at MAX_NODES to bound memory across long-running processes.
20
+ // Ring-buffer tail caps at MAX_TAIL_LINES_PER_NODE.
21
+ import { EventEmitter } from "node:events";
22
+ // ── Constants ──────────────────────────────────────────────────────────────
23
+ const MAX_NODES = 50;
24
+ const MAX_TAIL_LINES_PER_NODE = 2048;
25
+ const MAX_TAIL_APPEND = 500; // per-append cap before trimming
26
+ // ── OrchestratorTree ───────────────────────────────────────────────────────
27
+ export class OrchestratorTree extends EventEmitter {
28
+ nodes = new Map();
29
+ roots = new Set();
30
+ // ── Mutation: lifecycle ────────────────────────────────────────────────
31
+ startNode(id, opts = {}) {
32
+ const existing = this.nodes.get(id);
33
+ if (existing) {
34
+ // Resume — refresh status and timestamps, keep events for visibility.
35
+ existing.status = "running";
36
+ existing.startedAt = Date.now();
37
+ existing.endedAt = undefined;
38
+ existing.abortController = new AbortController();
39
+ // Reconcile parentId: if the caller specifies a different parent,
40
+ // reparent the node so stale root placements self-correct.
41
+ const newParentId = opts.parentId ?? null;
42
+ const oldParentId = existing.parentId;
43
+ if (newParentId !== oldParentId) {
44
+ this.reparentNode(existing, oldParentId, newParentId);
45
+ }
46
+ // Re-apply label/kind if caller supplies them (resume may change role).
47
+ if (opts.label !== undefined)
48
+ existing.label = opts.label;
49
+ if (opts.kind !== undefined)
50
+ existing.kind = opts.kind;
51
+ if (opts.promptPreview !== undefined)
52
+ existing.promptPreview = opts.promptPreview;
53
+ this.emit("change", id);
54
+ this.emit("tree", id);
55
+ return existing;
56
+ }
57
+ const node = {
58
+ id,
59
+ label: opts.label ?? id,
60
+ kind: opts.kind ?? "leaf",
61
+ parentId: opts.parentId ?? null,
62
+ children: [],
63
+ status: "running",
64
+ startedAt: Date.now(),
65
+ usage: { input: 0, output: 0, cacheRead: 0 },
66
+ metrics: { turn: 0, toolCount: 0, errCount: 0 },
67
+ tailBuffer: [],
68
+ unreadWarnings: 0,
69
+ abortController: new AbortController(),
70
+ };
71
+ if (opts.promptPreview) {
72
+ node.promptPreview = opts.promptPreview;
73
+ }
74
+ // Link to parent.
75
+ if (node.parentId) {
76
+ const parent = this.nodes.get(node.parentId);
77
+ if (parent && !parent.children.includes(id)) {
78
+ parent.children.push(id);
79
+ }
80
+ }
81
+ else {
82
+ this.roots.add(id);
83
+ }
84
+ this.nodes.set(id, node);
85
+ this.evictIfNeeded();
86
+ this.emit("change", id);
87
+ this.emit("tree", id);
88
+ return node;
89
+ }
90
+ completeNode(id, status) {
91
+ const node = this.nodes.get(id);
92
+ if (!node)
93
+ return;
94
+ // Idempotent for terminal states — don't clobber a success-path mark.
95
+ if (node.status !== "running" && node.status !== "cancelling")
96
+ return;
97
+ node.status = status;
98
+ node.endedAt = Date.now();
99
+ node.abortController = undefined;
100
+ this.emit("change", id);
101
+ }
102
+ // ── Mutation: telemetry ─────────────────────────────────────────────────
103
+ setNodeUsage(id, usage) {
104
+ const node = this.nodes.get(id);
105
+ if (!node)
106
+ return;
107
+ node.usage = { input: usage.input, output: usage.output, cacheRead: usage.cacheRead };
108
+ this.emit("change", id);
109
+ }
110
+ setNodeModel(id, model, provider) {
111
+ const node = this.nodes.get(id);
112
+ if (!node)
113
+ return;
114
+ // Only emit when the value actually changes — turn_end fires every
115
+ // turn and the model is stable across a phase.
116
+ if (node.provider === provider && node.model === model)
117
+ return;
118
+ node.model = model;
119
+ node.provider = provider;
120
+ this.emit("change", id);
121
+ }
122
+ setNodeCompression(id, compression) {
123
+ const node = this.nodes.get(id);
124
+ if (!node)
125
+ return;
126
+ node.compression = { calls: compression.calls, tokensSaved: compression.tokensSaved };
127
+ this.emit("change", id);
128
+ }
129
+ bumpNodeTurn(id) {
130
+ const node = this.nodes.get(id);
131
+ if (!node)
132
+ return;
133
+ node.metrics.turn++;
134
+ this.emit("change", id);
135
+ }
136
+ incrementNodeToolCount(id, delta = 1) {
137
+ const node = this.nodes.get(id);
138
+ if (!node)
139
+ return;
140
+ node.metrics.toolCount += delta;
141
+ this.emit("change", id);
142
+ }
143
+ incrementNodeErrCount(id, delta = 1) {
144
+ const node = this.nodes.get(id);
145
+ if (!node)
146
+ return;
147
+ node.metrics.errCount += delta;
148
+ this.emit("change", id);
149
+ }
150
+ setNodeIteration(id, iteration) {
151
+ const node = this.nodes.get(id);
152
+ if (!node)
153
+ return;
154
+ node.iteration = iteration;
155
+ this.emit("change", id);
156
+ }
157
+ setNodeOutcome(id, preview) {
158
+ const node = this.nodes.get(id);
159
+ if (!node)
160
+ return;
161
+ node.outcomePreview = preview;
162
+ this.emit("change", id);
163
+ }
164
+ // ── Mutation: tail buffer ───────────────────────────────────────────────
165
+ appendTail(id, line, opts) {
166
+ const node = this.nodes.get(id);
167
+ if (!node)
168
+ return;
169
+ node.tailBuffer.push(line);
170
+ if (node.tailBuffer.length > MAX_TAIL_LINES_PER_NODE) {
171
+ node.tailBuffer.splice(0, node.tailBuffer.length - MAX_TAIL_LINES_PER_NODE);
172
+ }
173
+ if (opts?.warning)
174
+ node.unreadWarnings++;
175
+ this.emit("tail", id);
176
+ }
177
+ markTailRead(id) {
178
+ const node = this.nodes.get(id);
179
+ if (!node)
180
+ return;
181
+ if (node.unreadWarnings === 0)
182
+ return;
183
+ node.unreadWarnings = 0;
184
+ this.emit("change", id);
185
+ }
186
+ setTurnPreview(id, preview) {
187
+ const node = this.nodes.get(id);
188
+ if (!node)
189
+ return;
190
+ if (node.lastTurnPreview === preview)
191
+ return;
192
+ node.lastTurnPreview = preview;
193
+ if (node.firstTurnPreview === undefined)
194
+ node.firstTurnPreview = preview;
195
+ this.emit("preview", id);
196
+ }
197
+ // ── Mutation: cancellation ───────────────────────────────────────────────
198
+ requestCancel(id) {
199
+ const node = this.nodes.get(id);
200
+ if (!node)
201
+ return false;
202
+ if (node.status !== "running")
203
+ return false;
204
+ node.status = "cancelling";
205
+ node.abortController?.abort();
206
+ this.emit("change", id);
207
+ return true;
208
+ }
209
+ getAbortSignal(id) {
210
+ return this.nodes.get(id)?.abortController?.signal;
211
+ }
212
+ // ── Queries ──────────────────────────────────────────────────────────────
213
+ getNode(id) {
214
+ return this.nodes.get(id);
215
+ }
216
+ getChildren(id) {
217
+ const node = this.nodes.get(id);
218
+ if (!node)
219
+ return [];
220
+ return node.children
221
+ .map((cid) => this.nodes.get(cid))
222
+ .filter((n) => n !== undefined);
223
+ }
224
+ getAncestors(id) {
225
+ const result = [];
226
+ let current = this.nodes.get(id);
227
+ while (current?.parentId) {
228
+ const parent = this.nodes.get(current.parentId);
229
+ if (parent)
230
+ result.push(parent);
231
+ current = parent;
232
+ }
233
+ return result;
234
+ }
235
+ getRoots() {
236
+ return [...this.roots]
237
+ .map((id) => this.nodes.get(id))
238
+ .filter((n) => n !== undefined);
239
+ }
240
+ /** Active roots: all roots if any are running/cancelling, otherwise
241
+ * only non-terminal roots. Prunes stale terminal roots that have no
242
+ * running descendants — the dashboard should not show completed sprints
243
+ * from prior invocations alongside the current one. */
244
+ getActiveRoots() {
245
+ const roots = this.getRoots();
246
+ const hasActive = roots.some((r) => r.status === "running" || r.status === "cancelling");
247
+ if (!hasActive)
248
+ return roots; // nothing in flight — show all
249
+ return roots.filter((r) => r.status === "running" ||
250
+ r.status === "cancelling" ||
251
+ r.status === "pending" ||
252
+ (r.kind === "orchestrator" && !this.isSubtreeTerminal(r.id)));
253
+ }
254
+ /** Returns true if every node in the subtree rooted at `id` has reached a
255
+ * terminal state (completed, failed, cancelled, or escalated). */
256
+ isSubtreeTerminal(id) {
257
+ const stack = [id];
258
+ while (stack.length > 0) {
259
+ const current = stack.pop();
260
+ const node = this.nodes.get(current);
261
+ if (!node)
262
+ continue;
263
+ if (node.status !== "completed" &&
264
+ node.status !== "failed" &&
265
+ node.status !== "cancelled" &&
266
+ node.status !== "escalated") {
267
+ return false;
268
+ }
269
+ stack.push(...node.children);
270
+ }
271
+ return true;
272
+ }
273
+ /** Re-parent a node from its current position to a new parent (or to root
274
+ * if newParentId is null). Removes from old parent's children and/or
275
+ * roots, adds to new parent's children and/or roots, and updates the
276
+ * node's parentId field. */
277
+ reparentNode(node, oldParentId, newParentId) {
278
+ if (oldParentId === newParentId)
279
+ return;
280
+ // Unlink from old parent (or roots).
281
+ if (oldParentId) {
282
+ const oldParent = this.nodes.get(oldParentId);
283
+ if (oldParent) {
284
+ oldParent.children = oldParent.children.filter((cid) => cid !== node.id);
285
+ }
286
+ }
287
+ else {
288
+ this.roots.delete(node.id);
289
+ }
290
+ // Link to new parent (or roots).
291
+ if (newParentId) {
292
+ const newParent = this.nodes.get(newParentId);
293
+ if (newParent && !newParent.children.includes(node.id)) {
294
+ newParent.children.push(node.id);
295
+ }
296
+ }
297
+ else {
298
+ this.roots.add(node.id);
299
+ }
300
+ node.parentId = newParentId;
301
+ }
302
+ getSubtreeUsage(id) {
303
+ const agg = { input: 0, output: 0, cacheRead: 0 };
304
+ const stack = [id];
305
+ while (stack.length > 0) {
306
+ const current = stack.pop();
307
+ const node = this.nodes.get(current);
308
+ if (!node)
309
+ continue;
310
+ agg.input += node.usage.input;
311
+ agg.output += node.usage.output;
312
+ agg.cacheRead += node.usage.cacheRead;
313
+ stack.push(...node.children);
314
+ }
315
+ return agg;
316
+ }
317
+ getSubtreeProgress(id) {
318
+ let completed = 0;
319
+ let total = 0;
320
+ const stack = [id];
321
+ while (stack.length > 0) {
322
+ const current = stack.pop();
323
+ const node = this.nodes.get(current);
324
+ if (!node)
325
+ continue;
326
+ if (node.kind === "leaf") {
327
+ total++;
328
+ if (node.status === "completed")
329
+ completed++;
330
+ }
331
+ stack.push(...node.children);
332
+ }
333
+ return { completed, total };
334
+ }
335
+ /** Depth of a node (root = 0). Useful for tree indentation. */
336
+ getDepth(id) {
337
+ let depth = 0;
338
+ let current = this.nodes.get(id);
339
+ while (current?.parentId) {
340
+ depth++;
341
+ current = this.nodes.get(current.parentId);
342
+ }
343
+ return depth;
344
+ }
345
+ // ── Private ─────────────────────────────────────────────────────────────
346
+ evictIfNeeded() {
347
+ if (this.nodes.size <= MAX_NODES)
348
+ return;
349
+ // Evict the oldest completed/failed/cancelled root node.
350
+ const roots = this.getRoots();
351
+ const evictable = roots
352
+ .filter((r) => r.status === "completed" ||
353
+ r.status === "failed" ||
354
+ r.status === "cancelled" ||
355
+ r.status === "escalated")
356
+ .sort((a, b) => a.startedAt - b.startedAt);
357
+ if (evictable.length === 0)
358
+ return;
359
+ const victim = evictable[0];
360
+ this.removeSubtree(victim.id);
361
+ }
362
+ removeSubtree(id) {
363
+ const node = this.nodes.get(id);
364
+ if (!node)
365
+ return;
366
+ // Remove all descendants first.
367
+ for (const childId of [...node.children]) {
368
+ this.removeSubtree(childId);
369
+ }
370
+ // Unlink from parent.
371
+ if (node.parentId) {
372
+ const parent = this.nodes.get(node.parentId);
373
+ if (parent) {
374
+ parent.children = parent.children.filter((cid) => cid !== id);
375
+ }
376
+ }
377
+ else {
378
+ this.roots.delete(id);
379
+ }
380
+ this.nodes.delete(id);
381
+ }
382
+ }
383
+ // ── Singleton ──────────────────────────────────────────────────────────────
384
+ let _tree;
385
+ export function getOrchestratorTree() {
386
+ if (!_tree)
387
+ _tree = new OrchestratorTree();
388
+ return _tree;
389
+ }
390
+ //# sourceMappingURL=orchestrator-tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator-tree.js","sourceRoot":"","sources":["../../../src/extensions/forgecli/orchestrator-tree.ts"],"names":[],"mappings":"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"}
@@ -25,7 +25,8 @@ function findUpConfigJson(cwd) {
25
25
  }
26
26
  export function buildProjectOrientation(cwdAbs) {
27
27
  // Resolve FORGE_ROOT from .forge/config.json so subagent bash sessions
28
- // get the absolute path instead of the unresolved $FORGE_ROOT variable.
28
+ // can use the transition-fallback $FORGE_ROOT path for un-migrated projects.
29
+ // Post-FORGE-S29-T05, tools are also available at .forge/tools/ (preferred path).
29
30
  // Subagent processes do NOT inherit FORGE_ROOT from the parent environment.
30
31
  let resolvedForgeRoot = "$FORGE_ROOT";
31
32
  try {
@@ -53,19 +54,22 @@ export function buildProjectOrientation(cwdAbs) {
53
54
  "",
54
55
  "## Forge Tools (canonical shapes)",
55
56
  "",
56
- `$FORGE_ROOT resolves to \`${resolvedForgeRoot}\`.`,
57
+ "Forge tools are available at `.forge/tools/` (preferred, project-relative) after /forge:init.",
58
+ "If `.forge/tools/store-cli.cjs` does not exist, run `/forge:rebuild tools` to populate it.",
59
+ `Backward-compat: $FORGE_ROOT resolves to \`${resolvedForgeRoot}\` (transition fallback for un-migrated projects).`,
57
60
  "",
58
- `- Store read: \`node "${resolvedForgeRoot}/tools/store-cli.cjs" read <entity> <id>\``,
59
- `- Store list: \`node "${resolvedForgeRoot}/tools/store-cli.cjs" list <entity>\``,
60
- `- Store write: \`node "${resolvedForgeRoot}/tools/store-cli.cjs" write <entity> '<json>'\` (id INSIDE json)`,
61
- `- Update status: \`node "${resolvedForgeRoot}/tools/store-cli.cjs" update-status <entity> <id> <status>\``,
62
- `- Emit event: \`node "${resolvedForgeRoot}/tools/store-cli.cjs" emit <sprintId> '<json>' [--sidecar]\``,
63
- `- Help anytime: \`node "${resolvedForgeRoot}/tools/store-cli.cjs" --help\``,
61
+ `- Store read: \`node ".forge/tools/store-cli.cjs" read <entity> <id>\``,
62
+ `- Store list: \`node ".forge/tools/store-cli.cjs" list <entity>\``,
63
+ `- Store write: \`node ".forge/tools/store-cli.cjs" write <entity> '<json>'\` (id INSIDE json)`,
64
+ `- Update status: \`node ".forge/tools/store-cli.cjs" update-status <entity> <id> <status>\``,
65
+ `- Emit event: \`node ".forge/tools/store-cli.cjs" emit <sprintId> '<json>' [--sidecar]\``,
66
+ `- Help anytime: \`node ".forge/tools/store-cli.cjs" --help\``,
64
67
  "",
65
68
  "store-cli verbs: read | list | write | emit | update-status | set-summary | describe | nlp | query | delete — there is no get/set/find.",
66
69
  "",
67
70
  "## Forge Paths",
68
71
  "",
72
+ "- Forge tools: `.forge/tools/` (vendored at /forge:init, refreshed via /forge:rebuild tools)",
69
73
  "- Workflow fragments: `.forge/workflows/_fragments/` (NOT `.forge/_fragments/`)",
70
74
  "- Personas: `.forge/personas/`",
71
75
  "- Templates: `.forge/templates/`",
@@ -1 +1 @@
1
- {"version":3,"file":"project-orientation.js","sourceRoot":"","sources":["../../../src/extensions/forgecli/project-orientation.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,EAAE;AACF,6EAA6E;AAC7E,0EAA0E;AAC1E,wEAAwE;AACxE,0EAA0E;AAC1E,+CAA+C;AAC/C,EAAE;AACF,8DAA8D;AAE9D,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,mFAAmF;AACnF,SAAS,gBAAgB,CAAC,GAAW;IACpC,IAAI,GAAG,GAAG,GAAG,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;QAC1D,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,GAAG;YAAE,MAAM;QAC1B,GAAG,GAAG,MAAM,CAAC;IACd,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,MAAc;IACrD,uEAAuE;IACvE,wEAAwE;IACxE,4EAA4E;IAC5E,IAAI,iBAAiB,GAAG,aAAa,CAAC;IACtC,IAAI,CAAC;QACJ,MAAM,UAAU,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,UAAU,EAAE,CAAC;YAChB,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YAChD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC/B,MAAM,SAAS,GAAG,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC;YAC3C,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;gBACnC,iBAAiB,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC,CAAC;YACvE,CAAC;QACF,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,4CAA4C;IAC7C,CAAC;IAED,OAAO;QACN,wBAAwB;QACxB,EAAE;QACF,0BAA0B,MAAM,kDAAkD;QAClF,EAAE;QACF,+GAA+G;QAC/G,oIAAoI;QACpI,yDAAyD;QACzD,EAAE;QACF,mCAAmC;QACnC,EAAE;QACF,6BAA6B,iBAAiB,KAAK;QACnD,EAAE;QACF,2BAA2B,iBAAiB,4CAA4C;QACxF,2BAA2B,iBAAiB,uCAAuC;QACnF,2BAA2B,iBAAiB,mEAAmE;QAC/G,4BAA4B,iBAAiB,8DAA8D;QAC3G,2BAA2B,iBAAiB,8DAA8D;QAC1G,2BAA2B,iBAAiB,gCAAgC;QAC5E,EAAE;QACF,yIAAyI;QACzI,EAAE;QACF,gBAAgB;QAChB,EAAE;QACF,iFAAiF;QACjF,0CAA0C;QAC1C,2CAA2C;QAC3C,oEAAoE;QACpE,EAAE;QACF,oBAAoB;QACpB,EAAE;QACF,qHAAqH;QACrH,EAAE;QACF,yDAAyD;QACzD,iEAAiE;QACjE,EAAE;KACF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"project-orientation.js","sourceRoot":"","sources":["../../../src/extensions/forgecli/project-orientation.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAC7B,EAAE;AACF,6EAA6E;AAC7E,0EAA0E;AAC1E,wEAAwE;AACxE,0EAA0E;AAC1E,+CAA+C;AAC/C,EAAE;AACF,8DAA8D;AAE9D,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,mFAAmF;AACnF,SAAS,gBAAgB,CAAC,GAAW;IACpC,IAAI,GAAG,GAAG,GAAG,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;QAC1D,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;QAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,GAAG;YAAE,MAAM;QAC1B,GAAG,GAAG,MAAM,CAAC;IACd,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,MAAc;IACrD,uEAAuE;IACvE,6EAA6E;IAC7E,kFAAkF;IAClF,4EAA4E;IAC5E,IAAI,iBAAiB,GAAG,aAAa,CAAC;IACtC,IAAI,CAAC;QACJ,MAAM,UAAU,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,UAAU,EAAE,CAAC;YAChB,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YAChD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC/B,MAAM,SAAS,GAAG,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC;YAC3C,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;gBACnC,iBAAiB,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC,CAAC;YACvE,CAAC;QACF,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,4CAA4C;IAC7C,CAAC;IAED,OAAO;QACN,wBAAwB;QACxB,EAAE;QACF,0BAA0B,MAAM,kDAAkD;QAClF,EAAE;QACF,+GAA+G;QAC/G,oIAAoI;QACpI,yDAAyD;QACzD,EAAE;QACF,mCAAmC;QACnC,EAAE;QACF,+FAA+F;QAC/F,4FAA4F;QAC5F,8CAA8C,iBAAiB,oDAAoD;QACnH,EAAE;QACF,0EAA0E;QAC1E,qEAAqE;QACrE,iGAAiG;QACjG,6FAA6F;QAC7F,4FAA4F;QAC5F,8DAA8D;QAC9D,EAAE;QACF,yIAAyI;QACzI,EAAE;QACF,gBAAgB;QAChB,EAAE;QACF,qGAAqG;QACrG,iFAAiF;QACjF,0CAA0C;QAC1C,2CAA2C;QAC3C,oEAAoE;QACpE,EAAE;QACF,oBAAoB;QACpB,EAAE;QACF,qHAAqH;QACrH,EAAE;QACF,yDAAyD;QACzD,iEAAiE;QACjE,EAAE;KACF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACd,CAAC"}
@@ -21,4 +21,20 @@ export interface ModifiedFile {
21
21
  * Pure function (no I/O beyond fs reads + manifest tool spawns).
22
22
  */
23
23
  export declare function findModifiedStructuralFiles(cwd: string, toolsRoot: string): ModifiedFile[];
24
+ /**
25
+ * Copy bundled tools (*.cjs, *.js, lib/) from bundleToolsRoot into cwd/.forge/tools/.
26
+ *
27
+ * Atomic handler pattern: no LLM in the loop, errors surfaced via returned messages[],
28
+ * not throw. Accepts _testBundleToolsRoot for test isolation (defaults to getBundledToolsRoot()).
29
+ *
30
+ * When singleTool is provided (e.g. "store-cli"), only that file is copied (both .cjs
31
+ * variant checked first). Used by the `forge:rebuild tools <name>` sub-target.
32
+ *
33
+ * @param cwd Project root directory (must contain .forge/)
34
+ * @param _testBundleToolsRoot Override bundle tools root for tests; omit to use getBundledToolsRoot()
35
+ * @param singleTool Optional: copy only this one tool by stem name (without extension)
36
+ */
37
+ export declare function runRebuildTools(cwd: string, _testBundleToolsRoot?: string, singleTool?: string): {
38
+ messages: string[];
39
+ };
24
40
  export declare function registerRegenerate(pi: ExtensionAPI): void;
@@ -111,6 +111,104 @@ async function runTool(toolPath, argv, cwd, ctx, label, timeoutMs = 60_000) {
111
111
  });
112
112
  });
113
113
  }
114
+ /**
115
+ * Copy bundled tools (*.cjs, *.js, lib/) from bundleToolsRoot into cwd/.forge/tools/.
116
+ *
117
+ * Atomic handler pattern: no LLM in the loop, errors surfaced via returned messages[],
118
+ * not throw. Accepts _testBundleToolsRoot for test isolation (defaults to getBundledToolsRoot()).
119
+ *
120
+ * When singleTool is provided (e.g. "store-cli"), only that file is copied (both .cjs
121
+ * variant checked first). Used by the `forge:rebuild tools <name>` sub-target.
122
+ *
123
+ * @param cwd Project root directory (must contain .forge/)
124
+ * @param _testBundleToolsRoot Override bundle tools root for tests; omit to use getBundledToolsRoot()
125
+ * @param singleTool Optional: copy only this one tool by stem name (without extension)
126
+ */
127
+ export function runRebuildTools(cwd, _testBundleToolsRoot, singleTool) {
128
+ const messages = [];
129
+ const toolsSrc = _testBundleToolsRoot ?? getBundledToolsRoot();
130
+ if (!fs.existsSync(toolsSrc)) {
131
+ messages.push(`× forge:rebuild tools — bundle tools dir not found: ${toolsSrc}`);
132
+ return { messages };
133
+ }
134
+ const toolsDest = path.join(cwd, ".forge", "tools");
135
+ try {
136
+ fs.mkdirSync(toolsDest, { recursive: true });
137
+ }
138
+ catch (err) {
139
+ messages.push(`× forge:rebuild tools — could not create .forge/tools/: ${err.message ?? String(err)}`);
140
+ return { messages };
141
+ }
142
+ if (singleTool) {
143
+ // Single-file sub-target: copy only the named tool
144
+ const srcCjs = path.join(toolsSrc, `${singleTool}.cjs`);
145
+ const srcJs = path.join(toolsSrc, `${singleTool}.js`);
146
+ const resolvedSrc = fs.existsSync(srcCjs) ? srcCjs : fs.existsSync(srcJs) ? srcJs : null;
147
+ if (resolvedSrc) {
148
+ const destFile = path.join(toolsDest, path.basename(resolvedSrc));
149
+ try {
150
+ fs.copyFileSync(resolvedSrc, destFile);
151
+ messages.push(`〇 forge:rebuild tools — copied ${path.basename(resolvedSrc)}`);
152
+ }
153
+ catch (err) {
154
+ messages.push(`× forge:rebuild tools — copy failed for ${singleTool}: ${err.message ?? String(err)}`);
155
+ }
156
+ }
157
+ else {
158
+ messages.push(`△ forge:rebuild tools — tool not found: ${singleTool} (neither .cjs nor .js)`);
159
+ }
160
+ return { messages };
161
+ }
162
+ // Full copy: all .cjs and .js files
163
+ let copiedCount = 0;
164
+ try {
165
+ const entries = fs.readdirSync(toolsSrc, { withFileTypes: true });
166
+ for (const entry of entries) {
167
+ if (!entry.isFile())
168
+ continue;
169
+ if (!entry.name.endsWith(".cjs") && !entry.name.endsWith(".js"))
170
+ continue;
171
+ try {
172
+ fs.copyFileSync(path.join(toolsSrc, entry.name), path.join(toolsDest, entry.name));
173
+ copiedCount++;
174
+ }
175
+ catch {
176
+ // non-fatal per-file
177
+ }
178
+ }
179
+ }
180
+ catch (err) {
181
+ messages.push(`× forge:rebuild tools — could not read bundle tools dir: ${err.message ?? String(err)}`);
182
+ return { messages };
183
+ }
184
+ // Copy lib/ subdirectory
185
+ const libSrc = path.join(toolsSrc, "lib");
186
+ const libDest = path.join(toolsDest, "lib");
187
+ if (fs.existsSync(libSrc)) {
188
+ try {
189
+ fs.mkdirSync(libDest, { recursive: true });
190
+ const libEntries = fs.readdirSync(libSrc, { withFileTypes: true });
191
+ for (const entry of libEntries) {
192
+ if (!entry.isFile())
193
+ continue;
194
+ if (!entry.name.endsWith(".cjs") && !entry.name.endsWith(".js"))
195
+ continue;
196
+ try {
197
+ fs.copyFileSync(path.join(libSrc, entry.name), path.join(libDest, entry.name));
198
+ copiedCount++;
199
+ }
200
+ catch {
201
+ // non-fatal per-file
202
+ }
203
+ }
204
+ }
205
+ catch {
206
+ // non-fatal lib copy failure
207
+ }
208
+ }
209
+ messages.push(`〇 forge:rebuild tools — copied ${copiedCount} tool file(s) to .forge/tools/`);
210
+ return { messages };
211
+ }
114
212
  export function registerRegenerate(pi) {
115
213
  pi.registerCommand("forge:rebuild", {
116
214
  description: "Re-materialize .forge/ and .claude/commands/ from the bundled forge-payload " +
@@ -119,8 +217,20 @@ export function registerRegenerate(pi) {
119
217
  async handler(args, ctx) {
120
218
  const cwd = process.cwd();
121
219
  // FORGE-S26-T11: --enrich flag dispatches to enhance behavior (absorbed from forge:enhance).
220
+ // FORGE-S29-T05: `tools` sub-arg dispatches to runRebuildTools.
122
221
  const parts = (args ?? "").trim().split(/\s+/).filter(Boolean);
123
222
  const hasEnrich = parts.includes("--enrich");
223
+ const toolsIdx = parts.indexOf("tools");
224
+ if (toolsIdx !== -1) {
225
+ // forge:rebuild tools [<name>]
226
+ // Optional single tool name follows the "tools" keyword.
227
+ const singleTool = parts[toolsIdx + 1];
228
+ const result = runRebuildTools(cwd, undefined, singleTool);
229
+ for (const msg of result.messages) {
230
+ ctx.ui.notify(msg, msg.startsWith("×") ? "error" : "info");
231
+ }
232
+ return;
233
+ }
124
234
  if (hasEnrich) {
125
235
  // Strip --enrich, pass remaining args to enhance handler (Phase 2 default)
126
236
  const enhanceArgs = parts.filter((p) => p !== "--enrich").join(" ");