@loucompanion/forge-bridge 0.1.3-dev.6e59f152916f → 0.1.3-dev.f66395c3ac21

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.
@@ -4,11 +4,9 @@ export type DevelopCompletionPhase = "implementation" | "self_review" | "validat
4
4
  export type DebugCompletionPhase = "debug_repro" | "debug_root_cause" | "debug_fix" | "debug_regression";
5
5
  export type ArchitectCompletionPhase = "architect";
6
6
  export type DraftCompletionPhase = "draft";
7
- export type DecomposeCompletionPhase = "decompose";
8
- export type CompletionPhase = DevelopCompletionPhase | "qa" | DebugCompletionPhase | ArchitectCompletionPhase | DraftCompletionPhase | DecomposeCompletionPhase;
7
+ export type CompletionPhase = DevelopCompletionPhase | "qa" | DebugCompletionPhase | ArchitectCompletionPhase | DraftCompletionPhase;
9
8
  export type QaCompletionGate = "qa_plan" | "qa_regression" | "qa_api" | "qa_manual_web" | "qa_bug_repro" | "qa_findings" | "qa_verdict";
10
9
  export type DraftCompletionGate = "draft_intent" | "draft_repo_context" | "draft_graph_context" | "draft_product_scope" | "draft_work_order_plan" | "draft_critic" | "draft_proposal";
11
- export type DecomposeCompletionGate = "decompose_subject_intake" | "decompose_context_review" | "decompose_breakdown_strategy" | "decompose_child_items" | "decompose_dependency_graph" | "decompose_critic" | "decompose_proposal";
12
10
  export interface CompletionToolRuntime {
13
11
  readonly serverName: string;
14
12
  readonly command: string;
@@ -35,7 +33,6 @@ export interface CompletionToolServerConfig {
35
33
  readonly debugCycle?: boolean;
36
34
  readonly architectCycle?: boolean;
37
35
  readonly draftCycle?: boolean;
38
- readonly decomposeCycle?: boolean;
39
36
  }
40
37
  export interface CompletionToolCallResult {
41
38
  readonly content: readonly {
@@ -47,7 +44,6 @@ export interface CompletionToolCallResult {
47
44
  readonly phase: CompletionPhase;
48
45
  readonly qaGate?: QaCompletionGate;
49
46
  readonly draftGate?: DraftCompletionGate;
50
- readonly decomposeGate?: DecomposeCompletionGate;
51
47
  readonly status: string;
52
48
  readonly allowed: boolean;
53
49
  readonly reason: string;
@@ -71,7 +67,6 @@ export interface CompletionArtifactReference {
71
67
  export declare const debugCompletionPhaseOrder: readonly DebugCompletionPhase[];
72
68
  export declare const qaCompletionGateOrder: readonly QaCompletionGate[];
73
69
  export declare const draftCompletionGateOrder: readonly DraftCompletionGate[];
74
- export declare const decomposeCompletionGateOrder: readonly DecomposeCompletionGate[];
75
70
  export declare function completionPhaseFromDispatch(dispatch: RunOnceDispatchPacket): CompletionPhase;
76
71
  export declare function normalizeCompletionPhase(value: string | null | undefined): CompletionPhase;
77
72
  export declare function usesDevelopCycleCompletion(dispatch: RunOnceDispatchPacket): boolean;
@@ -82,12 +77,10 @@ export declare function completionToolNamesForPhase(phase: CompletionPhase): rea
82
77
  export declare function completionToolNamesForQaGate(gate: QaCompletionGate): readonly string[];
83
78
  export declare function completionToolNamesForDebugGate(gate: DebugCompletionPhase): readonly string[];
84
79
  export declare function completionToolNamesForDraftGate(gate: DraftCompletionGate): readonly string[];
85
- export declare function completionToolNamesForDecomposeGate(gate: DecomposeCompletionGate): readonly string[];
86
80
  export declare function expectedCompletionToolName(phase: CompletionPhase): string;
87
81
  export declare function expectedQaCompletionToolName(gate: QaCompletionGate): string;
88
82
  export declare function expectedDebugCompletionToolName(gate: DebugCompletionPhase): string;
89
83
  export declare function expectedDraftCompletionToolName(gate: DraftCompletionGate): string;
90
- export declare function expectedDecomposeCompletionToolName(gate: DecomposeCompletionGate): string;
91
84
  export declare function artifactRootFor(worktreePath: string, workItemId: string): string;
92
85
  export declare function artifactRootForOwner(worktreePath: string, ownerType: string, ownerId: string): string;
93
86
  export declare function completionArtifactJsonPath(worktreePath: string, workItemId: string, phase: CompletionPhase, ownerType?: string | null, ownerId?: string | null): string;
@@ -100,7 +93,6 @@ export declare function completionPromptForPhase(phase: CompletionPhase): string
100
93
  export declare function completionPromptForDevelopCycle(phase: CompletionPhase): string;
101
94
  export declare function completionPromptForQaCycle(currentGate?: QaCompletionGate): string;
102
95
  export declare function completionPromptForDraftCycle(currentGate?: DraftCompletionGate): string;
103
- export declare function completionPromptForDecomposeCycle(currentGate?: DecomposeCompletionGate): string;
104
96
  export declare function completionPromptForDebugCycle(currentGate?: DebugCompletionPhase): string;
105
97
  export declare function completionToolNamesForServer(config: CompletionToolServerConfig): readonly string[];
106
98
  export declare class CompletionToolServerSession {
@@ -108,18 +100,15 @@ export declare class CompletionToolServerSession {
108
100
  private currentPhase;
109
101
  private currentQaGate;
110
102
  private currentDraftGate;
111
- private currentDecomposeGate;
112
103
  private completed;
113
104
  constructor(config: CompletionToolServerConfig);
114
105
  get instructions(): string;
115
106
  toolDefinitions(): readonly unknown[];
116
107
  callTool(toolName: string, input: unknown): Promise<CompletionToolCallResult>;
117
- private callDecomposeGateTool;
118
108
  private callDraftGateTool;
119
109
  private callDebugGateTool;
120
110
  private callQaGateTool;
121
111
  private advanceAfterPass;
122
- private advanceAfterDecomposeGate;
123
112
  private advanceAfterDraftGate;
124
113
  private advanceAfterQaGate;
125
114
  private advanceAfterDebugGate;
@@ -31,33 +31,6 @@ export const draftCompletionGateOrder = [
31
31
  "draft_critic",
32
32
  "draft_proposal"
33
33
  ];
34
- export const decomposeCompletionGateOrder = [
35
- "decompose_subject_intake",
36
- "decompose_context_review",
37
- "decompose_breakdown_strategy",
38
- "decompose_child_items",
39
- "decompose_dependency_graph",
40
- "decompose_critic",
41
- "decompose_proposal"
42
- ];
43
- const decomposeGateFileNames = {
44
- decompose_subject_intake: "decompose.subject-intake.json",
45
- decompose_context_review: "decompose.context-review.json",
46
- decompose_breakdown_strategy: "decompose.breakdown-strategy.json",
47
- decompose_child_items: "decompose.child-items.json",
48
- decompose_dependency_graph: "decompose.dependency-graph.json",
49
- decompose_critic: "decompose.critic.json",
50
- decompose_proposal: "decompose.proposal.json"
51
- };
52
- const decomposeGateToolNames = {
53
- decompose_subject_intake: "complete_decompose_subject_intake",
54
- decompose_context_review: "complete_decompose_context_review",
55
- decompose_breakdown_strategy: "complete_decompose_breakdown_strategy",
56
- decompose_child_items: "complete_decompose_child_items",
57
- decompose_dependency_graph: "complete_decompose_dependency_graph",
58
- decompose_critic: "complete_decompose_critic",
59
- decompose_proposal: "complete_decompose_proposal"
60
- };
61
34
  const draftGateFileNames = {
62
35
  draft_intent: "draft.intent.json",
63
36
  draft_repo_context: "draft.repo-context.json",
@@ -153,10 +126,6 @@ const phaseFiles = {
153
126
  draft: {
154
127
  jsonFileName: "draft.proposal.json",
155
128
  reportFileName: "draft.report.md"
156
- },
157
- decompose: {
158
- jsonFileName: "decompose.proposal.json",
159
- reportFileName: "decompose.report.md"
160
129
  }
161
130
  };
162
131
  export function completionPhaseFromDispatch(dispatch) {
@@ -183,9 +152,6 @@ export function normalizeCompletionPhase(value) {
183
152
  if (normalized === "draft" || normalized === "drafting" || normalized === "draft_work_order") {
184
153
  return "draft";
185
154
  }
186
- if (normalized === "decompose" || normalized === "decomposition" || normalized === "work_order_decomposition" || normalized === "feature_decomposition") {
187
- return "decompose";
188
- }
189
155
  return "implementation";
190
156
  }
191
157
  export function usesDevelopCycleCompletion(dispatch) {
@@ -225,9 +191,6 @@ export function completionToolNamesForPhase(phase) {
225
191
  if (phase === "draft") {
226
192
  return completionToolNamesForDraftGate("draft_proposal");
227
193
  }
228
- if (phase === "decompose") {
229
- return completionToolNamesForDecomposeGate("decompose_proposal");
230
- }
231
194
  if (isDebugCompletionPhase(phase)) {
232
195
  return completionToolNamesForDebugGate(phase);
233
196
  }
@@ -254,10 +217,6 @@ export function completionToolNamesForDraftGate(gate) {
254
217
  const base = draftGateToolNames[gate];
255
218
  return [`forge.${base}`, base];
256
219
  }
257
- export function completionToolNamesForDecomposeGate(gate) {
258
- const base = decomposeGateToolNames[gate];
259
- return [`forge.${base}`, base];
260
- }
261
220
  export function expectedCompletionToolName(phase) {
262
221
  return completionToolNamesForPhase(phase)[0];
263
222
  }
@@ -270,9 +229,6 @@ export function expectedDebugCompletionToolName(gate) {
270
229
  export function expectedDraftCompletionToolName(gate) {
271
230
  return completionToolNamesForDraftGate(gate)[0];
272
231
  }
273
- export function expectedDecomposeCompletionToolName(gate) {
274
- return completionToolNamesForDecomposeGate(gate)[0];
275
- }
276
232
  export function artifactRootFor(worktreePath, workItemId) {
277
233
  return path.join(worktreePath, ".forge", "work-items", safePathSegment(workItemId, "work item id"), "artifacts");
278
234
  }
@@ -308,8 +264,7 @@ export async function prepareCompletionToolRuntime(options) {
308
264
  qaCycle: usesQaCycleCompletion(options.dispatch),
309
265
  debugCycle: usesDebugCycleCompletion(options.dispatch),
310
266
  architectCycle: usesArchitectCycleCompletion(options.dispatch),
311
- draftCycle: (options.dispatch.execution.lane.trim().toLowerCase() === "draft" || options.dispatch.cycleRun.kind.trim().toLowerCase() === "draft") && !isDecomposeDispatch(options.dispatch),
312
- decomposeCycle: isDecomposeDispatch(options.dispatch)
267
+ draftCycle: options.dispatch.execution.lane.trim().toLowerCase() === "draft" || options.dispatch.cycleRun.kind.trim().toLowerCase() === "draft"
313
268
  });
314
269
  const runtime = {
315
270
  serverName: toolServerName,
@@ -354,9 +309,6 @@ export function buildCompletionToolServerArgs(config) {
354
309
  if (config.draftCycle) {
355
310
  args.push("--draft-cycle");
356
311
  }
357
- if (config.decomposeCycle) {
358
- args.push("--decompose-cycle");
359
- }
360
312
  return args;
361
313
  }
362
314
  export function codexMcpConfigArgs(runtime) {
@@ -444,22 +396,6 @@ Complete the Draft cycle in order inside this same provider session:
444
396
  7. Follow the returned proposal instruction, then call \`forge.complete_draft_proposal(...)\` with the final Work Order JSON in \`draft_json\`.
445
397
  The final proposal tool writes \`draft.proposal.json\` under \`.forge/work-items/{workItemId}/artifacts/\`. Reporting Draft completion in prose is not enough.`;
446
398
  }
447
- export function completionPromptForDecomposeCycle(currentGate = "decompose_subject_intake") {
448
- return `# Forge Decompose completion cycle
449
- You must complete the Decompose gates in order using the Forge MCP tools. Reporting progress in prose is not enough.
450
-
451
- Current gate: ${currentGate}
452
-
453
- Required order:
454
- 1. Call \`forge.complete_decompose_subject_intake(...)\` after identifying the Work Order/Feature subject, scope, and non-goals.
455
- 2. Follow the returned context-review instruction, inspect injected project/repo/dependency context, then call \`forge.complete_decompose_context_review(...)\`.
456
- 3. Follow the returned breakdown-strategy instruction, choose safe Feature/Story boundaries, then call \`forge.complete_decompose_breakdown_strategy(...)\`.
457
- 4. Follow the returned child-items instruction, draft candidate child items, then call \`forge.complete_decompose_child_items(...)\`.
458
- 5. Follow the returned dependency-graph instruction, validate keys/dependencies/order, then call \`forge.complete_decompose_dependency_graph(...)\`.
459
- 6. Follow the returned critic instruction, critique scope, DAG safety, manual reconciliation risk, and missing acceptance detail, then call \`forge.complete_decompose_critic(...)\`.
460
- 7. Follow the returned proposal instruction, then call \`forge.complete_decompose_proposal(...)\` with final JSON in \`proposal_json\`.
461
- The final proposal tool writes \`decompose.proposal.json\` under \`.forge/work-items/{workItemId}/artifacts/\`. The proposal_json object must contain summary, optional blockedReason, and candidates.`;
462
- }
463
399
  export function completionPromptForDebugCycle(currentGate = "debug_repro") {
464
400
  return `Forge completion tools are available through the MCP server named \`${toolServerName}\`.
465
401
  Do not write Debug artifacts by hand. The tool server enforces Debug gate order and starts this run at \`${currentGate}\`.
@@ -472,43 +408,38 @@ Each tool writes a canonical Debug artifact under \`.forge/work-items/{workItemI
472
408
  Use a non-pass status with explicit blockers when the current Debug gate cannot pass. Reporting Debug completion in prose is not enough; the relevant MCP completion tool is the transition request.`;
473
409
  }
474
410
  export function completionToolNamesForServer(config) {
475
- return config.decomposeCycle
476
- ? decomposeCompletionGateOrder.flatMap((gate) => completionToolNamesForDecomposeGate(gate))
477
- : config.draftCycle
478
- ? draftCompletionGateOrder.flatMap((gate) => completionToolNamesForDraftGate(gate))
479
- : config.debugCycle
480
- ? debugCompletionPhaseOrder.flatMap((gate) => completionToolNamesForDebugGate(gate))
481
- : config.qaCycle
482
- ? qaCompletionGateOrder.flatMap((gate) => completionToolNamesForQaGate(gate))
483
- : config.developCycle
484
- ? completionPhaseOrder.flatMap((phase) => completionToolNamesForPhase(phase))
485
- : config.architectCycle
486
- ? completionToolNamesForPhase("architect")
487
- : completionToolNamesForPhase(config.phase);
411
+ return config.draftCycle
412
+ ? draftCompletionGateOrder.flatMap((gate) => completionToolNamesForDraftGate(gate))
413
+ : config.debugCycle
414
+ ? debugCompletionPhaseOrder.flatMap((gate) => completionToolNamesForDebugGate(gate))
415
+ : config.qaCycle
416
+ ? qaCompletionGateOrder.flatMap((gate) => completionToolNamesForQaGate(gate))
417
+ : config.developCycle
418
+ ? completionPhaseOrder.flatMap((phase) => completionToolNamesForPhase(phase))
419
+ : config.architectCycle
420
+ ? completionToolNamesForPhase("architect")
421
+ : completionToolNamesForPhase(config.phase);
488
422
  }
489
423
  export class CompletionToolServerSession {
490
424
  config;
491
425
  currentPhase;
492
426
  currentQaGate = "qa_plan";
493
427
  currentDraftGate = "draft_intent";
494
- currentDecomposeGate = "decompose_subject_intake";
495
428
  completed = false;
496
429
  constructor(config) {
497
430
  this.config = config;
498
431
  this.currentPhase = config.phase;
499
432
  }
500
433
  get instructions() {
501
- return this.config.decomposeCycle
502
- ? completionPromptForDecomposeCycle(this.currentDecomposeGate)
503
- : this.config.draftCycle
504
- ? completionPromptForDraftCycle(this.currentDraftGate)
505
- : this.config.debugCycle
506
- ? completionPromptForDebugCycle(asDebugCompletionPhase(this.currentPhase))
507
- : this.config.qaCycle
508
- ? completionPromptForQaCycle(this.currentQaGate)
509
- : this.config.developCycle
510
- ? completionPromptForDevelopCycle(this.currentPhase)
511
- : completionPromptForPhase(this.currentPhase);
434
+ return this.config.draftCycle
435
+ ? completionPromptForDraftCycle(this.currentDraftGate)
436
+ : this.config.debugCycle
437
+ ? completionPromptForDebugCycle(asDebugCompletionPhase(this.currentPhase))
438
+ : this.config.qaCycle
439
+ ? completionPromptForQaCycle(this.currentQaGate)
440
+ : this.config.developCycle
441
+ ? completionPromptForDevelopCycle(this.currentPhase)
442
+ : completionPromptForPhase(this.currentPhase);
512
443
  }
513
444
  toolDefinitions() {
514
445
  return completionToolNamesForServer(this.config).map((name) => toolDefinition(name, phaseFromToolName(name), qaGateFromToolName(name)));
@@ -518,9 +449,6 @@ export class CompletionToolServerSession {
518
449
  if (this.completed) {
519
450
  throw new Error("Forge completion cycle is already complete; no more completion calls are accepted.");
520
451
  }
521
- if (this.config.decomposeCycle) {
522
- return await this.callDecomposeGateTool(toolName, requestedPhase, input);
523
- }
524
452
  if (this.config.draftCycle) {
525
453
  return await this.callDraftGateTool(toolName, requestedPhase, input);
526
454
  }
@@ -564,38 +492,6 @@ export class CompletionToolServerSession {
564
492
  }
565
493
  };
566
494
  }
567
- async callDecomposeGateTool(toolName, requestedPhase, input) {
568
- const requestedGate = decomposeGateFromToolName(toolName);
569
- if (requestedPhase !== "decompose" || !requestedGate) {
570
- throw new Error(`Tool ${toolName} is not available in Decompose-cycle mode. Current Forge Decompose gate is ${this.currentDecomposeGate}.`);
571
- }
572
- if (requestedGate !== this.currentDecomposeGate) {
573
- throw new Error(`Tool ${toolName} is out of order. Current Forge Decompose gate is ${this.currentDecomposeGate}.`);
574
- }
575
- const completedGate = this.currentDecomposeGate;
576
- const reference = await writeDecomposeGateCompletionArtifact(this.config, completedGate, toolName, input);
577
- const status = reference.status.trim().toLowerCase();
578
- const pass = status === "pass";
579
- const nextGate = pass ? this.advanceAfterDecomposeGate(completedGate) : completedGate;
580
- const nextPhase = pass
581
- ? (nextGate ? "decompose" : "sign_off_ready")
582
- : "decompose";
583
- const nextInstruction = completionToolNextInstructionForDecomposeGate(completedGate, status, nextGate);
584
- const reason = pass
585
- ? nextGate
586
- ? `${completedGate} artifact accepted; continue to ${nextGate}.`
587
- : `Decompose proposal artifact accepted with status ${status}.`
588
- : `${completedGate} artifact recorded with status ${status}; rework is required before continuing.`;
589
- return {
590
- content: [{ type: "text", text: `Forge recorded ${completedGate} artifact ${path.relative(reference.repoPath, reference.path)} (${reference.status}, ${reference.contentHash}). ${nextInstruction}` }],
591
- structuredContent: {
592
- ...reference,
593
- gate: { phase: "decompose", decomposeGate: completedGate, status, allowed: pass, reason },
594
- nextPhase,
595
- nextInstruction
596
- }
597
- };
598
- }
599
495
  async callDraftGateTool(toolName, requestedPhase, input) {
600
496
  const requestedGate = draftGateFromToolName(toolName);
601
497
  if (requestedPhase !== "draft" || !requestedGate) {
@@ -716,16 +612,6 @@ export class CompletionToolServerSession {
716
612
  this.completed = true;
717
613
  return "dev_test_ready";
718
614
  }
719
- advanceAfterDecomposeGate(gate) {
720
- const index = decomposeCompletionGateOrder.indexOf(gate);
721
- const next = index >= 0 ? decomposeCompletionGateOrder[index + 1] ?? null : null;
722
- if (next) {
723
- this.currentDecomposeGate = next;
724
- return next;
725
- }
726
- this.completed = true;
727
- return null;
728
- }
729
615
  advanceAfterDraftGate(gate) {
730
616
  const index = draftCompletionGateOrder.indexOf(gate);
731
617
  const next = index >= 0 ? draftCompletionGateOrder[index + 1] ?? null : null;
@@ -757,40 +643,6 @@ export class CompletionToolServerSession {
757
643
  return null;
758
644
  }
759
645
  }
760
- async function writeDecomposeGateCompletionArtifact(config, gate, toolName, input) {
761
- const payload = asRecord(input, "Decompose completion payload");
762
- const status = normalizeDraftStatus(optionalString(payload, "status") ?? "pass");
763
- if (gate === "decompose_proposal") {
764
- const proposalJson = asRecord(payload["proposal_json"] ?? payload["decomposition_json"], "proposal_json");
765
- if (!Array.isArray(proposalJson["candidates"])) {
766
- throw new Error("proposal_json.candidates: expected array");
767
- }
768
- }
769
- const redactedPayload = redactSecrets({ ...payload, status, phase: "decompose", gate, tool_name: toolName }, config.redactionSecrets ?? []);
770
- const root = artifactRootFor(config.worktreePath, config.workItemId);
771
- await fs.mkdir(root, { recursive: true });
772
- const artifactPath = path.join(root, decomposeGateFileNames[gate]);
773
- const reportPath = gate === "decompose_proposal" ? path.join(root, phaseFiles.decompose.reportFileName) : path.join(root, `${gate}.md`);
774
- const verdictJson = jsonText(redactedPayload);
775
- const report = optionalString(redactedPayload, "report_markdown") ?? `# ${gate.replace(/_/g, " ")}
776
-
777
- ${optionalString(redactedPayload, "summary") ?? "Decompose gate recorded."}
778
- `;
779
- await writeFileAtomic(artifactPath, verdictJson);
780
- await writeFileAtomic(reportPath, report);
781
- return {
782
- phase: "decompose",
783
- workItemId: config.workItemId,
784
- ownerType: config.ownerType,
785
- ownerId: config.ownerId,
786
- repoPath: path.resolve(config.worktreePath),
787
- path: artifactPath,
788
- reportPath,
789
- contentHash: sha256(verdictJson),
790
- status,
791
- producedBy: config.producedBy ?? "forge-completion-tool"
792
- };
793
- }
794
646
  async function writeDraftGateCompletionArtifact(config, gate, toolName, input) {
795
647
  const payload = asRecord(input, "Draft completion payload");
796
648
  const status = normalizeDraftStatus(optionalString(payload, "status") ?? "pass");
@@ -1326,7 +1178,6 @@ export function parseCompletionMcpArgs(argv) {
1326
1178
  let debugCycle = false;
1327
1179
  let architectCycle = false;
1328
1180
  let draftCycle = false;
1329
- let decomposeCycle = false;
1330
1181
  for (let index = 1; index < argv.length; index += 1) {
1331
1182
  const arg = argv[index];
1332
1183
  if (arg === "--worktree") {
@@ -1370,9 +1221,6 @@ export function parseCompletionMcpArgs(argv) {
1370
1221
  else if (arg === "--draft-cycle") {
1371
1222
  draftCycle = true;
1372
1223
  }
1373
- else if (arg === "--decompose-cycle") {
1374
- decomposeCycle = true;
1375
- }
1376
1224
  else {
1377
1225
  return { ok: false, error: `Unknown mcp argument: ${arg}` };
1378
1226
  }
@@ -1380,7 +1228,7 @@ export function parseCompletionMcpArgs(argv) {
1380
1228
  if (!worktreePath?.trim() || !workItemId?.trim()) {
1381
1229
  return { ok: false, error: "mcp requires --worktree and --work-item-id." };
1382
1230
  }
1383
- if ([developCycle, qaCycle, debugCycle, architectCycle, draftCycle, decomposeCycle].filter(Boolean).length > 1) {
1231
+ if ([developCycle, qaCycle, debugCycle, architectCycle, draftCycle].filter(Boolean).length > 1) {
1384
1232
  return { ok: false, error: "mcp accepts only one cycle mode." };
1385
1233
  }
1386
1234
  if (developCycle && !isDevelopCompletionPhase(phase)) {
@@ -1398,9 +1246,6 @@ export function parseCompletionMcpArgs(argv) {
1398
1246
  if (draftCycle && phase !== "draft") {
1399
1247
  return { ok: false, error: "mcp --draft-cycle requires --phase draft." };
1400
1248
  }
1401
- if (decomposeCycle && phase !== "decompose") {
1402
- return { ok: false, error: "mcp --decompose-cycle requires --phase decompose." };
1403
- }
1404
1249
  return {
1405
1250
  ok: true,
1406
1251
  config: {
@@ -1415,8 +1260,7 @@ export function parseCompletionMcpArgs(argv) {
1415
1260
  qaCycle,
1416
1261
  debugCycle,
1417
1262
  architectCycle,
1418
- draftCycle,
1419
- decomposeCycle
1263
+ draftCycle
1420
1264
  }
1421
1265
  };
1422
1266
  }
@@ -1591,24 +1435,6 @@ function completionToolNextInstructionForQaGate(completedGate, status, nextGate)
1591
1435
  : `${completedGate} accepted. Continue to ${nextGate}.`;
1592
1436
  return `${prefix} ${qaGateInstruction(nextGate)}`;
1593
1437
  }
1594
- function completionToolNextInstructionForDecomposeGate(completedGate, status, nextGate) {
1595
- if (status !== "pass") {
1596
- return `Stay in ${completedGate}, address the recorded blockers/rework, then call ${expectedDecomposeCompletionToolName(completedGate)} again.`;
1597
- }
1598
- if (!nextGate) {
1599
- return "Decompose proposal accepted. Forge has the structured child-item proposal JSON needed to populate the proposal row.";
1600
- }
1601
- const nextInstructions = {
1602
- decompose_subject_intake: "Capture subject/scope, then call forge.complete_decompose_subject_intake.",
1603
- decompose_context_review: "Review project/repo/dependency context, then call forge.complete_decompose_context_review.",
1604
- decompose_breakdown_strategy: "Choose safe child boundaries, then call forge.complete_decompose_breakdown_strategy.",
1605
- decompose_child_items: "Draft child candidates, then call forge.complete_decompose_child_items.",
1606
- decompose_dependency_graph: "Validate dependency DAG, then call forge.complete_decompose_dependency_graph.",
1607
- decompose_critic: "Critique the decomposition, then call forge.complete_decompose_critic.",
1608
- decompose_proposal: "Submit final proposal_json through forge.complete_decompose_proposal."
1609
- };
1610
- return `${completedGate} accepted. ${nextInstructions[nextGate]}`;
1611
- }
1612
1438
  function completionToolNextInstructionForDebugGate(completedGate, status, nextGate) {
1613
1439
  if (status !== "pass") {
1614
1440
  return `Stay in ${completedGate}, address the recorded blockers, then call ${expectedDebugCompletionToolName(completedGate)} again.`;
@@ -2872,9 +2698,6 @@ function parseStrictCompletionPhase(value) {
2872
2698
  if (normalized === "draft" || normalized === "drafting" || normalized === "draft_work_order") {
2873
2699
  return "draft";
2874
2700
  }
2875
- if (normalized === "decompose" || normalized === "decomposition" || normalized === "work_order_decomposition" || normalized === "feature_decomposition") {
2876
- return "decompose";
2877
- }
2878
2701
  return null;
2879
2702
  }
2880
2703
  function normalizeDebugCompletionPhase(value) {
@@ -2949,9 +2772,6 @@ function phaseFromToolName(toolName) {
2949
2772
  if (draftGateFromToolName(toolName)) {
2950
2773
  return "draft";
2951
2774
  }
2952
- if (decomposeGateFromToolName(toolName)) {
2953
- return "decompose";
2954
- }
2955
2775
  const debugGate = debugGateFromToolName(toolName);
2956
2776
  if (debugGate) {
2957
2777
  return debugGate;
@@ -2971,22 +2791,6 @@ function draftGateFromToolName(toolName) {
2971
2791
  }
2972
2792
  return null;
2973
2793
  }
2974
- function decomposeGateFromToolName(toolName) {
2975
- for (const gate of decomposeCompletionGateOrder) {
2976
- const base = decomposeGateToolNames[gate];
2977
- if (toolName === base || toolName === `forge.${base}`) {
2978
- return gate;
2979
- }
2980
- }
2981
- return null;
2982
- }
2983
- function isDecomposeDispatch(dispatch) {
2984
- const draftKind = dispatch.draft?.kind?.trim().toLowerCase();
2985
- return dispatch.execution.lane.trim().toLowerCase() === "decompose"
2986
- || dispatch.session.lane.trim().toLowerCase() === "decompose"
2987
- || draftKind === "work_order_decomposition"
2988
- || draftKind === "feature_decomposition";
2989
- }
2990
2794
  function debugGateFromToolName(toolName) {
2991
2795
  const normalized = toolName.trim().toLowerCase();
2992
2796
  for (const gate of debugCompletionPhaseOrder) {
@@ -200,8 +200,7 @@ export class ProcessBackedProviderCliService {
200
200
  await fs.mkdir(metadataRoot, { recursive: true });
201
201
  await fs.writeFile(path.join(metadataRoot, "draft-input.json"), JSON.stringify(dispatch.draft, null, 2) + "\n", "utf8");
202
202
  await fs.writeFile(path.join(metadataRoot, "spec.md"), buildDraftSpec(dispatch), "utf8");
203
- const decomposeDraft = isDecomposeDraftDispatch(dispatch);
204
- const completionTools = await prepareCompletionToolRuntime({ dispatch, worktree: draftWorktree, phase: decomposeDraft ? "decompose" : "draft" });
203
+ const completionTools = await prepareCompletionToolRuntime({ dispatch, worktree: draftWorktree, phase: "draft" });
205
204
  const command = buildProviderCliCommand(dispatch, {
206
205
  worktreePath: tempDir,
207
206
  prompt: buildDraftPrompt(dispatch),
@@ -226,9 +225,7 @@ export class ProcessBackedProviderCliService {
226
225
  await context.emit({
227
226
  role: "runner",
228
227
  kind: "checkpoint",
229
- content: decomposeDraft
230
- ? "Forge Decompose completion tools configured for decompose_subject_intake -> decompose_context_review -> decompose_breakdown_strategy -> decompose_child_items -> decompose_dependency_graph -> decompose_critic -> decompose_proposal; provider must call each MCP completion tool before exit."
231
- : "Forge Draft completion tools configured for draft_intent -> draft_repo_context -> draft_graph_context -> draft_product_scope -> draft_work_order_plan -> draft_critic -> draft_proposal; provider must call each MCP completion tool before exit.",
228
+ content: "Forge Draft completion tools configured for draft_intent -> draft_repo_context -> draft_graph_context -> draft_product_scope -> draft_work_order_plan -> draft_critic -> draft_proposal; provider must call each MCP completion tool before exit.",
232
229
  eventType: "completion_tools_configured"
233
230
  });
234
231
  const outputState = createProviderOutputState(dispatch.execution.provider);
@@ -255,19 +252,17 @@ export class ProcessBackedProviderCliService {
255
252
  }, providerCancellation.signal);
256
253
  let result;
257
254
  let earlyProposalArtifact = null;
258
- {
259
- const terminal = await Promise.race([
260
- runPromise.then(value => ({ kind: "process", value })),
261
- waitForCompletionArtifact(tempDir, dispatch.workItem.id, decomposeDraft ? "decompose" : "draft", context.signal).then(value => ({ kind: "artifact", value }))
262
- ]);
263
- if (terminal.kind === "artifact") {
264
- earlyProposalArtifact = terminal.value;
265
- providerCancellation.abort();
266
- result = await runPromise.catch(() => ({ exitCode: 0, stdout: "", stderr: "" }));
267
- }
268
- else {
269
- result = terminal.value;
270
- }
255
+ const terminal = await Promise.race([
256
+ runPromise.then(value => ({ kind: "process", value })),
257
+ waitForDraftProposalArtifact(tempDir, dispatch.workItem.id, context.signal).then(value => ({ kind: "artifact", value }))
258
+ ]);
259
+ if (terminal.kind === "artifact") {
260
+ earlyProposalArtifact = terminal.value;
261
+ providerCancellation.abort();
262
+ result = await runPromise.catch(() => ({ exitCode: 0, stdout: "", stderr: "" }));
263
+ }
264
+ else {
265
+ result = terminal.value;
271
266
  }
272
267
  context.signal?.removeEventListener("abort", abortProvider);
273
268
  if (result.exitCode !== 0 && !earlyProposalArtifact) {
@@ -280,30 +275,6 @@ export class ProcessBackedProviderCliService {
280
275
  eventType: "provider_cli_completed"
281
276
  });
282
277
  await emitProviderUsageEvent(context.emit, dispatch, outputState);
283
- if (decomposeDraft) {
284
- await emitDecomposeGateArtifacts(context.emit, tempDir, dispatch.workItem.id);
285
- const proposalArtifact = earlyProposalArtifact ?? await readCompletionArtifact(tempDir, dispatch.workItem.id, "decompose");
286
- if (!proposalArtifact) {
287
- throw new Error("Provider CLI Decompose completed but did not call forge.complete_decompose_proposal.");
288
- }
289
- const proposal = parseJsonLine(proposalArtifact.verdictJson) ?? {};
290
- const proposalJson = proposal["proposal_json"] ?? proposal["decomposition_json"];
291
- if (!proposalJson || typeof proposalJson !== "object" || Array.isArray(proposalJson)) {
292
- throw new Error("Provider CLI Decompose completed but decompose.proposal.json did not contain proposal_json.");
293
- }
294
- const json = JSON.stringify(proposalJson);
295
- await context.emit({
296
- role: "assistant",
297
- kind: "message",
298
- content: json,
299
- eventType: "decompose_proposal"
300
- });
301
- return {
302
- summary: "Decomposition proposal generated through Forge Decompose completion cycle.",
303
- artifact: proposalArtifact,
304
- artifacts: [proposalArtifact]
305
- };
306
- }
307
278
  await emitDraftGateArtifacts(context.emit, tempDir, dispatch.workItem.id);
308
279
  const proposalArtifact = earlyProposalArtifact ?? await readCompletionArtifact(tempDir, dispatch.workItem.id, "draft");
309
280
  if (!proposalArtifact) {
@@ -2064,47 +2035,6 @@ async function emitDraftGateArtifacts(emit, worktreePath, workItemId) {
2064
2035
  }
2065
2036
  }
2066
2037
  }
2067
- async function emitDecomposeGateArtifacts(emit, worktreePath, workItemId) {
2068
- const decomposeArtifactFiles = {
2069
- decompose_subject_intake: "decompose.subject-intake.json",
2070
- decompose_context_review: "decompose.context-review.json",
2071
- decompose_breakdown_strategy: "decompose.breakdown-strategy.json",
2072
- decompose_child_items: "decompose.child-items.json",
2073
- decompose_dependency_graph: "decompose.dependency-graph.json",
2074
- decompose_critic: "decompose.critic.json",
2075
- decompose_proposal: "decompose.proposal.json"
2076
- };
2077
- const gates = Object.keys(decomposeArtifactFiles);
2078
- for (const gate of gates) {
2079
- const actualPath = path.join(worktreePath, ".forge", "work-items", workItemId, "artifacts", decomposeArtifactFiles[gate]);
2080
- if (!await fileExists(actualPath)) {
2081
- continue;
2082
- }
2083
- const relativePath = path.relative(worktreePath, actualPath).split(path.sep).join("/");
2084
- await emit({
2085
- role: "tool",
2086
- kind: "tool_result",
2087
- content: `Forge Decompose completion tool recorded ${gate} artifact ${relativePath}.`,
2088
- eventType: "completion_tool_artifact_recorded",
2089
- data: JSON.stringify({ phase: "decompose", decompose_gate: gate, artifact_relative_paths: [relativePath] })
2090
- });
2091
- await emit({
2092
- role: "hook",
2093
- kind: "checkpoint",
2094
- content: `phase_gate: ${gate} artifact accepted with status pass.`,
2095
- eventType: "phase_gate"
2096
- });
2097
- const next = gates[gates.indexOf(gate) + 1];
2098
- if (next) {
2099
- await emit({
2100
- role: "hook",
2101
- kind: "checkpoint",
2102
- content: `phase_progression: ${gate} -> ${next}`,
2103
- eventType: "phase_gate"
2104
- });
2105
- }
2106
- }
2107
- }
2108
2038
  async function fileExists(filePath) {
2109
2039
  try {
2110
2040
  await fs.access(filePath);
@@ -2119,7 +2049,6 @@ function buildDraftPrompt(dispatch) {
2119
2049
  if (!draft) {
2120
2050
  throw new Error("Draft prompt is missing.");
2121
2051
  }
2122
- const decomposeDraft = isDecomposeDraftDispatch(dispatch);
2123
2052
  const draftArtifacts = dispatch.artifacts
2124
2053
  .filter((artifact) => artifact.scope === "operational" && ["command", "skill", "hook"].includes(artifact.kind))
2125
2054
  .map((artifact) => `## ${artifact.kind}: ${artifact.slug}\n${artifact.body.trim()}`)
@@ -2138,39 +2067,25 @@ Draft kind: ${draft.kind}
2138
2067
 
2139
2068
  ${draft.userPrompt}
2140
2069
 
2141
- ${decomposeDraft
2142
- ? `Use the injected command/skills/hooks as lane instructions. You must call each Forge Decompose MCP completion tool in order. Do not claim Decompose is complete until forge.complete_decompose_proposal accepts the final proposal_json.
2143
-
2144
- Complete these gates in order: decompose_subject_intake -> decompose_context_review -> decompose_breakdown_strategy -> decompose_child_items -> decompose_dependency_graph -> decompose_critic -> decompose_proposal.
2070
+ ${completionPromptForDraftCycle("draft_intent")}
2145
2071
 
2146
- The final proposal_json object must contain summary, optional blockedReason, and candidates.`
2147
- : `${completionPromptForDraftCycle("draft_intent")}
2148
-
2149
- Use the injected command/skills/hooks as lane instructions. You must call each Forge Draft MCP completion tool in order. Do not claim the Draft is complete until forge.complete_draft_proposal accepts the final draft_json.`}
2072
+ Use the injected command/skills/hooks as lane instructions. You must call each Forge Draft MCP completion tool in order. Do not claim the Draft is complete until forge.complete_draft_proposal accepts the final draft_json.
2150
2073
 
2151
2074
  Do not modify source files.`;
2152
2075
  }
2153
- function isDecomposeDraftKind(kind) {
2154
- return kind === "work_order_decomposition" || kind === "feature_decomposition";
2155
- }
2156
- function isDecomposeDraftDispatch(dispatch) {
2157
- return dispatch.execution.lane.trim().toLowerCase() === "decompose"
2158
- || dispatch.session.lane.trim().toLowerCase() === "decompose"
2159
- || isDecomposeDraftKind(dispatch.draft?.kind?.trim().toLowerCase());
2160
- }
2161
2076
  function summarizeStderr(stderr) {
2162
2077
  const trimmed = stderr.trim();
2163
2078
  return trimmed ? `: ${clip(trimmed, 1000)}` : "";
2164
2079
  }
2165
- async function waitForCompletionArtifact(worktreePath, workItemId, phase, signal) {
2080
+ async function waitForDraftProposalArtifact(worktreePath, workItemId, signal) {
2166
2081
  while (!signal?.aborted) {
2167
- const artifact = await readCompletionArtifact(worktreePath, workItemId, phase);
2082
+ const artifact = await readCompletionArtifact(worktreePath, workItemId, "draft");
2168
2083
  if (artifact) {
2169
2084
  return artifact;
2170
2085
  }
2171
2086
  await new Promise((resolve) => setTimeout(resolve, 1000));
2172
2087
  }
2173
- throw new Error(`${phase} proposal artifact wait was cancelled.`);
2088
+ throw new Error("Draft proposal artifact wait was cancelled.");
2174
2089
  }
2175
2090
  function deterministicDraftPayload(dispatch) {
2176
2091
  const kind = dispatch.draft?.kind ?? "work_item";
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const forgeBridgeVersion = "0.1.3-dev.6e59f152916f";
1
+ export declare const forgeBridgeVersion = "0.1.3-dev.f66395c3ac21";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const forgeBridgeVersion = "0.1.3-dev.6e59f152916f";
1
+ export const forgeBridgeVersion = "0.1.3-dev.f66395c3ac21";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loucompanion/forge-bridge",
3
- "version": "0.1.3-dev.6e59f152916f",
3
+ "version": "0.1.3-dev.f66395c3ac21",
4
4
  "description": "Forge runner bridge CLI for connecting local developer machines to Forge dispatch.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",