@loucompanion/forge-bridge 0.1.3-dev.04a84914da09 → 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,11 +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: (phase === "draft"
312
- || options.dispatch.execution.lane.trim().toLowerCase() === "draft"
313
- || options.dispatch.cycleRun.kind.trim().toLowerCase() === "draft")
314
- && !isDecomposeDispatch(options.dispatch),
315
- decomposeCycle: isDecomposeDispatch(options.dispatch)
267
+ draftCycle: options.dispatch.execution.lane.trim().toLowerCase() === "draft" || options.dispatch.cycleRun.kind.trim().toLowerCase() === "draft"
316
268
  });
317
269
  const runtime = {
318
270
  serverName: toolServerName,
@@ -357,9 +309,6 @@ export function buildCompletionToolServerArgs(config) {
357
309
  if (config.draftCycle) {
358
310
  args.push("--draft-cycle");
359
311
  }
360
- if (config.decomposeCycle) {
361
- args.push("--decompose-cycle");
362
- }
363
312
  return args;
364
313
  }
365
314
  export function codexMcpConfigArgs(runtime) {
@@ -442,27 +391,11 @@ Complete the Draft cycle in order inside this same provider session:
442
391
  2. Follow the returned repo-context instruction, inspect selected project/repo assumptions, then call \`forge.complete_draft_repo_context(...)\`.
443
392
  3. Follow the returned graph-context instruction, account for DAG/dependency placement, then call \`forge.complete_draft_graph_context(...)\`.
444
393
  4. Follow the returned product-scope instruction, choose product scope for the selected project, then call \`forge.complete_draft_product_scope(...)\`.
445
- 5. Follow the returned work-order-plan instruction, draft the requested final JSON shape and any candidate boundaries, then call \`forge.complete_draft_work_order_plan(...)\`.
394
+ 5. Follow the returned work-order-plan instruction, draft Work Order fields and candidate feature boundaries, then call \`forge.complete_draft_work_order_plan(...)\`.
446
395
  6. Follow the returned critic instruction, critique literalism, wrong-project scope, and decomposition quality, then call \`forge.complete_draft_critic(...)\`.
447
- 7. Follow the returned proposal instruction, then call \`forge.complete_draft_proposal(...)\` with the final Draft JSON payload in \`draft_json\`.
396
+ 7. Follow the returned proposal instruction, then call \`forge.complete_draft_proposal(...)\` with the final Work Order JSON in \`draft_json\`.
448
397
  The final proposal tool writes \`draft.proposal.json\` under \`.forge/work-items/{workItemId}/artifacts/\`. Reporting Draft completion in prose is not enough.`;
449
398
  }
450
- export function completionPromptForDecomposeCycle(currentGate = "decompose_subject_intake") {
451
- return `# Forge Decompose completion cycle
452
- You must complete the Decompose gates in order using the Forge MCP tools. Reporting progress in prose is not enough.
453
-
454
- Current gate: ${currentGate}
455
-
456
- Required order:
457
- 1. Call \`forge.complete_decompose_subject_intake(...)\` after identifying the Work Order/Feature subject, scope, and non-goals.
458
- 2. Follow the returned context-review instruction, inspect injected project/repo/dependency context, then call \`forge.complete_decompose_context_review(...)\`.
459
- 3. Follow the returned breakdown-strategy instruction, choose safe Feature/Story boundaries, then call \`forge.complete_decompose_breakdown_strategy(...)\`.
460
- 4. Follow the returned child-items instruction, draft candidate child items, then call \`forge.complete_decompose_child_items(...)\`.
461
- 5. Follow the returned dependency-graph instruction, validate keys/dependencies/order, then call \`forge.complete_decompose_dependency_graph(...)\`.
462
- 6. Follow the returned critic instruction, critique scope, DAG safety, manual reconciliation risk, and missing acceptance detail, then call \`forge.complete_decompose_critic(...)\`.
463
- 7. Follow the returned proposal instruction, then call \`forge.complete_decompose_proposal(...)\` with final JSON in \`proposal_json\`.
464
- 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.`;
465
- }
466
399
  export function completionPromptForDebugCycle(currentGate = "debug_repro") {
467
400
  return `Forge completion tools are available through the MCP server named \`${toolServerName}\`.
468
401
  Do not write Debug artifacts by hand. The tool server enforces Debug gate order and starts this run at \`${currentGate}\`.
@@ -475,43 +408,38 @@ Each tool writes a canonical Debug artifact under \`.forge/work-items/{workItemI
475
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.`;
476
409
  }
477
410
  export function completionToolNamesForServer(config) {
478
- return config.decomposeCycle
479
- ? decomposeCompletionGateOrder.flatMap((gate) => completionToolNamesForDecomposeGate(gate))
480
- : config.draftCycle
481
- ? draftCompletionGateOrder.flatMap((gate) => completionToolNamesForDraftGate(gate))
482
- : config.debugCycle
483
- ? debugCompletionPhaseOrder.flatMap((gate) => completionToolNamesForDebugGate(gate))
484
- : config.qaCycle
485
- ? qaCompletionGateOrder.flatMap((gate) => completionToolNamesForQaGate(gate))
486
- : config.developCycle
487
- ? completionPhaseOrder.flatMap((phase) => completionToolNamesForPhase(phase))
488
- : config.architectCycle
489
- ? completionToolNamesForPhase("architect")
490
- : 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);
491
422
  }
492
423
  export class CompletionToolServerSession {
493
424
  config;
494
425
  currentPhase;
495
426
  currentQaGate = "qa_plan";
496
427
  currentDraftGate = "draft_intent";
497
- currentDecomposeGate = "decompose_subject_intake";
498
428
  completed = false;
499
429
  constructor(config) {
500
430
  this.config = config;
501
431
  this.currentPhase = config.phase;
502
432
  }
503
433
  get instructions() {
504
- return this.config.decomposeCycle
505
- ? completionPromptForDecomposeCycle(this.currentDecomposeGate)
506
- : this.config.draftCycle
507
- ? completionPromptForDraftCycle(this.currentDraftGate)
508
- : this.config.debugCycle
509
- ? completionPromptForDebugCycle(asDebugCompletionPhase(this.currentPhase))
510
- : this.config.qaCycle
511
- ? completionPromptForQaCycle(this.currentQaGate)
512
- : this.config.developCycle
513
- ? completionPromptForDevelopCycle(this.currentPhase)
514
- : 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);
515
443
  }
516
444
  toolDefinitions() {
517
445
  return completionToolNamesForServer(this.config).map((name) => toolDefinition(name, phaseFromToolName(name), qaGateFromToolName(name)));
@@ -521,9 +449,6 @@ export class CompletionToolServerSession {
521
449
  if (this.completed) {
522
450
  throw new Error("Forge completion cycle is already complete; no more completion calls are accepted.");
523
451
  }
524
- if (this.config.decomposeCycle) {
525
- return await this.callDecomposeGateTool(toolName, requestedPhase, input);
526
- }
527
452
  if (this.config.draftCycle) {
528
453
  return await this.callDraftGateTool(toolName, requestedPhase, input);
529
454
  }
@@ -567,38 +492,6 @@ export class CompletionToolServerSession {
567
492
  }
568
493
  };
569
494
  }
570
- async callDecomposeGateTool(toolName, requestedPhase, input) {
571
- const requestedGate = decomposeGateFromToolName(toolName);
572
- if (requestedPhase !== "decompose" || !requestedGate) {
573
- throw new Error(`Tool ${toolName} is not available in Decompose-cycle mode. Current Forge Decompose gate is ${this.currentDecomposeGate}.`);
574
- }
575
- if (requestedGate !== this.currentDecomposeGate) {
576
- throw new Error(`Tool ${toolName} is out of order. Current Forge Decompose gate is ${this.currentDecomposeGate}.`);
577
- }
578
- const completedGate = this.currentDecomposeGate;
579
- const reference = await writeDecomposeGateCompletionArtifact(this.config, completedGate, toolName, input);
580
- const status = reference.status.trim().toLowerCase();
581
- const pass = status === "pass";
582
- const nextGate = pass ? this.advanceAfterDecomposeGate(completedGate) : completedGate;
583
- const nextPhase = pass
584
- ? (nextGate ? "decompose" : "sign_off_ready")
585
- : "decompose";
586
- const nextInstruction = completionToolNextInstructionForDecomposeGate(completedGate, status, nextGate);
587
- const reason = pass
588
- ? nextGate
589
- ? `${completedGate} artifact accepted; continue to ${nextGate}.`
590
- : `Decompose proposal artifact accepted with status ${status}.`
591
- : `${completedGate} artifact recorded with status ${status}; rework is required before continuing.`;
592
- return {
593
- content: [{ type: "text", text: `Forge recorded ${completedGate} artifact ${path.relative(reference.repoPath, reference.path)} (${reference.status}, ${reference.contentHash}). ${nextInstruction}` }],
594
- structuredContent: {
595
- ...reference,
596
- gate: { phase: "decompose", decomposeGate: completedGate, status, allowed: pass, reason },
597
- nextPhase,
598
- nextInstruction
599
- }
600
- };
601
- }
602
495
  async callDraftGateTool(toolName, requestedPhase, input) {
603
496
  const requestedGate = draftGateFromToolName(toolName);
604
497
  if (requestedPhase !== "draft" || !requestedGate) {
@@ -719,16 +612,6 @@ export class CompletionToolServerSession {
719
612
  this.completed = true;
720
613
  return "dev_test_ready";
721
614
  }
722
- advanceAfterDecomposeGate(gate) {
723
- const index = decomposeCompletionGateOrder.indexOf(gate);
724
- const next = index >= 0 ? decomposeCompletionGateOrder[index + 1] ?? null : null;
725
- if (next) {
726
- this.currentDecomposeGate = next;
727
- return next;
728
- }
729
- this.completed = true;
730
- return null;
731
- }
732
615
  advanceAfterDraftGate(gate) {
733
616
  const index = draftCompletionGateOrder.indexOf(gate);
734
617
  const next = index >= 0 ? draftCompletionGateOrder[index + 1] ?? null : null;
@@ -760,46 +643,16 @@ export class CompletionToolServerSession {
760
643
  return null;
761
644
  }
762
645
  }
763
- async function writeDecomposeGateCompletionArtifact(config, gate, toolName, input) {
764
- const payload = asRecord(input, "Decompose completion payload");
765
- const status = normalizeDraftStatus(optionalString(payload, "status") ?? "pass");
766
- if (gate === "decompose_proposal") {
767
- const proposalJson = asRecord(payload["proposal_json"] ?? payload["decomposition_json"], "proposal_json");
768
- if (!Array.isArray(proposalJson["candidates"])) {
769
- throw new Error("proposal_json.candidates: expected array");
770
- }
771
- }
772
- const redactedPayload = redactSecrets({ ...payload, status, phase: "decompose", gate, tool_name: toolName }, config.redactionSecrets ?? []);
773
- const root = artifactRootFor(config.worktreePath, config.workItemId);
774
- await fs.mkdir(root, { recursive: true });
775
- const artifactPath = path.join(root, decomposeGateFileNames[gate]);
776
- const reportPath = gate === "decompose_proposal" ? path.join(root, phaseFiles.decompose.reportFileName) : path.join(root, `${gate}.md`);
777
- const verdictJson = jsonText(redactedPayload);
778
- const report = optionalString(redactedPayload, "report_markdown") ?? `# ${gate.replace(/_/g, " ")}
779
-
780
- ${optionalString(redactedPayload, "summary") ?? "Decompose gate recorded."}
781
- `;
782
- await writeFileAtomic(artifactPath, verdictJson);
783
- await writeFileAtomic(reportPath, report);
784
- return {
785
- phase: "decompose",
786
- workItemId: config.workItemId,
787
- ownerType: config.ownerType,
788
- ownerId: config.ownerId,
789
- repoPath: path.resolve(config.worktreePath),
790
- path: artifactPath,
791
- reportPath,
792
- contentHash: sha256(verdictJson),
793
- status,
794
- producedBy: config.producedBy ?? "forge-completion-tool"
795
- };
796
- }
797
646
  async function writeDraftGateCompletionArtifact(config, gate, toolName, input) {
798
647
  const payload = asRecord(input, "Draft completion payload");
799
648
  const status = normalizeDraftStatus(optionalString(payload, "status") ?? "pass");
800
649
  if (gate === "draft_proposal") {
801
650
  const draftJson = asRecord(payload["draft_json"], "draft_json");
802
- validateDraftProposalJson(draftJson);
651
+ for (const key of ["title", "summary", "objective", "planningPacket", "decompositionContext"]) {
652
+ if (typeof draftJson[key] !== "string" || draftJson[key].trim().length === 0) {
653
+ throw new Error(`draft_json.${key}: expected non-empty string`);
654
+ }
655
+ }
803
656
  }
804
657
  const redactedPayload = redactSecrets({ ...payload, status, phase: "draft", gate, tool_name: toolName }, config.redactionSecrets ?? []);
805
658
  const root = artifactRootFor(config.worktreePath, config.workItemId);
@@ -827,22 +680,6 @@ function normalizeDraftStatus(value) {
827
680
  const normalized = value.trim().toLowerCase();
828
681
  return normalized === "blocked" || normalized === "needs_revision" || normalized === "needs_rework" ? normalized : "pass";
829
682
  }
830
- function validateDraftProposalJson(draftJson) {
831
- if (isDecompositionDraftJson(draftJson)) {
832
- return;
833
- }
834
- for (const key of ["title", "summary", "objective", "planningPacket", "decompositionContext"]) {
835
- if (typeof draftJson[key] !== "string" || draftJson[key].trim().length === 0) {
836
- throw new Error(`draft_json.${key}: expected non-empty string`);
837
- }
838
- }
839
- }
840
- function isDecompositionDraftJson(draftJson) {
841
- const summary = draftJson["summary"];
842
- return typeof summary === "string"
843
- && summary.trim().length > 0
844
- && Array.isArray(draftJson["candidates"]);
845
- }
846
683
  export async function writeCompletionArtifact(config, toolName, input) {
847
684
  const phase = phaseFromToolName(toolName);
848
685
  if (phase !== config.phase) {
@@ -1341,7 +1178,6 @@ export function parseCompletionMcpArgs(argv) {
1341
1178
  let debugCycle = false;
1342
1179
  let architectCycle = false;
1343
1180
  let draftCycle = false;
1344
- let decomposeCycle = false;
1345
1181
  for (let index = 1; index < argv.length; index += 1) {
1346
1182
  const arg = argv[index];
1347
1183
  if (arg === "--worktree") {
@@ -1385,9 +1221,6 @@ export function parseCompletionMcpArgs(argv) {
1385
1221
  else if (arg === "--draft-cycle") {
1386
1222
  draftCycle = true;
1387
1223
  }
1388
- else if (arg === "--decompose-cycle") {
1389
- decomposeCycle = true;
1390
- }
1391
1224
  else {
1392
1225
  return { ok: false, error: `Unknown mcp argument: ${arg}` };
1393
1226
  }
@@ -1395,7 +1228,7 @@ export function parseCompletionMcpArgs(argv) {
1395
1228
  if (!worktreePath?.trim() || !workItemId?.trim()) {
1396
1229
  return { ok: false, error: "mcp requires --worktree and --work-item-id." };
1397
1230
  }
1398
- if ([developCycle, qaCycle, debugCycle, architectCycle, draftCycle, decomposeCycle].filter(Boolean).length > 1) {
1231
+ if ([developCycle, qaCycle, debugCycle, architectCycle, draftCycle].filter(Boolean).length > 1) {
1399
1232
  return { ok: false, error: "mcp accepts only one cycle mode." };
1400
1233
  }
1401
1234
  if (developCycle && !isDevelopCompletionPhase(phase)) {
@@ -1413,9 +1246,6 @@ export function parseCompletionMcpArgs(argv) {
1413
1246
  if (draftCycle && phase !== "draft") {
1414
1247
  return { ok: false, error: "mcp --draft-cycle requires --phase draft." };
1415
1248
  }
1416
- if (decomposeCycle && phase !== "decompose") {
1417
- return { ok: false, error: "mcp --decompose-cycle requires --phase decompose." };
1418
- }
1419
1249
  return {
1420
1250
  ok: true,
1421
1251
  config: {
@@ -1430,8 +1260,7 @@ export function parseCompletionMcpArgs(argv) {
1430
1260
  qaCycle,
1431
1261
  debugCycle,
1432
1262
  architectCycle,
1433
- draftCycle,
1434
- decomposeCycle
1263
+ draftCycle
1435
1264
  }
1436
1265
  };
1437
1266
  }
@@ -1606,24 +1435,6 @@ function completionToolNextInstructionForQaGate(completedGate, status, nextGate)
1606
1435
  : `${completedGate} accepted. Continue to ${nextGate}.`;
1607
1436
  return `${prefix} ${qaGateInstruction(nextGate)}`;
1608
1437
  }
1609
- function completionToolNextInstructionForDecomposeGate(completedGate, status, nextGate) {
1610
- if (status !== "pass") {
1611
- return `Stay in ${completedGate}, address the recorded blockers/rework, then call ${expectedDecomposeCompletionToolName(completedGate)} again.`;
1612
- }
1613
- if (!nextGate) {
1614
- return "Decompose proposal accepted. Forge has the structured child-item proposal JSON needed to populate the proposal row.";
1615
- }
1616
- const nextInstructions = {
1617
- decompose_subject_intake: "Capture subject/scope, then call forge.complete_decompose_subject_intake.",
1618
- decompose_context_review: "Review project/repo/dependency context, then call forge.complete_decompose_context_review.",
1619
- decompose_breakdown_strategy: "Choose safe child boundaries, then call forge.complete_decompose_breakdown_strategy.",
1620
- decompose_child_items: "Draft child candidates, then call forge.complete_decompose_child_items.",
1621
- decompose_dependency_graph: "Validate dependency DAG, then call forge.complete_decompose_dependency_graph.",
1622
- decompose_critic: "Critique the decomposition, then call forge.complete_decompose_critic.",
1623
- decompose_proposal: "Submit final proposal_json through forge.complete_decompose_proposal."
1624
- };
1625
- return `${completedGate} accepted. ${nextInstructions[nextGate]}`;
1626
- }
1627
1438
  function completionToolNextInstructionForDebugGate(completedGate, status, nextGate) {
1628
1439
  if (status !== "pass") {
1629
1440
  return `Stay in ${completedGate}, address the recorded blockers, then call ${expectedDebugCompletionToolName(completedGate)} again.`;
@@ -2887,9 +2698,6 @@ function parseStrictCompletionPhase(value) {
2887
2698
  if (normalized === "draft" || normalized === "drafting" || normalized === "draft_work_order") {
2888
2699
  return "draft";
2889
2700
  }
2890
- if (normalized === "decompose" || normalized === "decomposition" || normalized === "work_order_decomposition" || normalized === "feature_decomposition") {
2891
- return "decompose";
2892
- }
2893
2701
  return null;
2894
2702
  }
2895
2703
  function normalizeDebugCompletionPhase(value) {
@@ -2964,9 +2772,6 @@ function phaseFromToolName(toolName) {
2964
2772
  if (draftGateFromToolName(toolName)) {
2965
2773
  return "draft";
2966
2774
  }
2967
- if (decomposeGateFromToolName(toolName)) {
2968
- return "decompose";
2969
- }
2970
2775
  const debugGate = debugGateFromToolName(toolName);
2971
2776
  if (debugGate) {
2972
2777
  return debugGate;
@@ -2986,22 +2791,6 @@ function draftGateFromToolName(toolName) {
2986
2791
  }
2987
2792
  return null;
2988
2793
  }
2989
- function decomposeGateFromToolName(toolName) {
2990
- for (const gate of decomposeCompletionGateOrder) {
2991
- const base = decomposeGateToolNames[gate];
2992
- if (toolName === base || toolName === `forge.${base}`) {
2993
- return gate;
2994
- }
2995
- }
2996
- return null;
2997
- }
2998
- function isDecomposeDispatch(dispatch) {
2999
- const draftKind = dispatch.draft?.kind?.trim().toLowerCase();
3000
- return dispatch.execution.lane.trim().toLowerCase() === "decompose"
3001
- || dispatch.session.lane.trim().toLowerCase() === "decompose"
3002
- || draftKind === "work_order_decomposition"
3003
- || draftKind === "feature_decomposition";
3004
- }
3005
2794
  function debugGateFromToolName(toolName) {
3006
2795
  const normalized = toolName.trim().toLowerCase();
3007
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);
@@ -241,110 +238,65 @@ export class ProcessBackedProviderCliService {
241
238
  else {
242
239
  context.signal?.addEventListener("abort", abortProvider, { once: true });
243
240
  }
244
- try {
245
- const runPromise = this.processRunner.run({
246
- command: command.command,
247
- args: command.args,
248
- cwd: tempDir,
249
- env: providerProcessEnv(dispatch)
250
- }, {
251
- onStdoutLine: async (line) => {
252
- for (const event of outputState.consume(line)) {
253
- await context.emit(event);
254
- }
255
- }
256
- }, providerCancellation.signal);
257
- const artifactWaitCancellation = new AbortController();
258
- const abortArtifactWait = () => artifactWaitCancellation.abort();
259
- if (context.signal?.aborted) {
260
- abortArtifactWait();
261
- }
262
- else {
263
- context.signal?.addEventListener("abort", abortArtifactWait, { once: true });
264
- }
265
- const artifactWaitPromise = waitForCompletionArtifact(tempDir, dispatch.workItem.id, decomposeDraft ? "decompose" : "draft", artifactWaitCancellation.signal);
266
- let result;
267
- let earlyProposalArtifact = null;
268
- try {
269
- const terminal = await Promise.race([
270
- runPromise.then(value => ({ kind: "process", value })),
271
- artifactWaitPromise.then(value => ({ kind: "artifact", value }))
272
- ]);
273
- artifactWaitCancellation.abort();
274
- await artifactWaitPromise.catch(() => undefined);
275
- if (terminal.kind === "artifact") {
276
- earlyProposalArtifact = terminal.value;
277
- providerCancellation.abort();
278
- result = await runPromise.catch(() => ({ exitCode: 0, stdout: "", stderr: "" }));
279
- }
280
- else {
281
- result = terminal.value;
282
- }
283
- }
284
- finally {
285
- artifactWaitCancellation.abort();
286
- context.signal?.removeEventListener("abort", abortArtifactWait);
287
- }
288
- if (result.exitCode !== 0 && !earlyProposalArtifact) {
289
- throw new Error(`Provider CLI exited with code ${result.exitCode}${summarizeStderr(result.stderr)}.`);
290
- }
291
- await context.emit({
292
- role: "runner",
293
- kind: "checkpoint",
294
- content: `Provider CLI Draft completed: ${command.command} exit ${result.exitCode}${outputState.sessionLabel ? ` ${outputState.sessionLabel}` : ""}.`,
295
- eventType: "provider_cli_completed"
296
- });
297
- await emitProviderUsageEvent(context.emit, dispatch, outputState);
298
- if (decomposeDraft) {
299
- await emitDecomposeGateArtifacts(context.emit, tempDir, dispatch.workItem.id);
300
- const proposalArtifact = earlyProposalArtifact ?? await readCompletionArtifact(tempDir, dispatch.workItem.id, "decompose");
301
- if (!proposalArtifact) {
302
- throw new Error("Provider CLI Decompose completed but did not call forge.complete_decompose_proposal.");
303
- }
304
- const proposal = parseJsonLine(proposalArtifact.verdictJson) ?? {};
305
- const proposalJson = proposal["proposal_json"] ?? proposal["decomposition_json"];
306
- if (!proposalJson || typeof proposalJson !== "object" || Array.isArray(proposalJson)) {
307
- throw new Error("Provider CLI Decompose completed but decompose.proposal.json did not contain proposal_json.");
241
+ const runPromise = this.processRunner.run({
242
+ command: command.command,
243
+ args: command.args,
244
+ cwd: tempDir,
245
+ env: providerProcessEnv(dispatch)
246
+ }, {
247
+ onStdoutLine: async (line) => {
248
+ for (const event of outputState.consume(line)) {
249
+ await context.emit(event);
308
250
  }
309
- const json = JSON.stringify(proposalJson);
310
- await context.emit({
311
- role: "assistant",
312
- kind: "message",
313
- content: json,
314
- eventType: "decompose_proposal"
315
- });
316
- return {
317
- summary: "Decomposition proposal generated through Forge Decompose completion cycle.",
318
- artifact: proposalArtifact,
319
- artifacts: [proposalArtifact]
320
- };
321
- }
322
- await emitDraftGateArtifacts(context.emit, tempDir, dispatch.workItem.id);
323
- const proposalArtifact = earlyProposalArtifact ?? await readCompletionArtifact(tempDir, dispatch.workItem.id, "draft");
324
- if (!proposalArtifact) {
325
- throw new Error("Provider CLI Draft completed but did not call forge.complete_draft_proposal.");
326
251
  }
327
- const proposal = parseJsonLine(proposalArtifact.verdictJson) ?? {};
328
- const draftJson = proposal["draft_json"];
329
- if (!draftJson || typeof draftJson !== "object" || Array.isArray(draftJson)) {
330
- throw new Error("Provider CLI Draft completed but draft.proposal.json did not contain draft_json.");
331
- }
332
- const json = JSON.stringify(draftJson);
333
- await context.emit({
334
- role: "assistant",
335
- kind: "message",
336
- content: json,
337
- eventType: "draft_complete"
338
- });
339
- return {
340
- summary: "Draft generated through Forge Draft completion cycle.",
341
- artifact: proposalArtifact,
342
- artifacts: [proposalArtifact]
343
- };
252
+ }, providerCancellation.signal);
253
+ let result;
254
+ let earlyProposalArtifact = null;
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;
266
+ }
267
+ context.signal?.removeEventListener("abort", abortProvider);
268
+ if (result.exitCode !== 0 && !earlyProposalArtifact) {
269
+ throw new Error(`Provider CLI exited with code ${result.exitCode}${summarizeStderr(result.stderr)}.`);
270
+ }
271
+ await context.emit({
272
+ role: "runner",
273
+ kind: "checkpoint",
274
+ content: `Provider CLI Draft completed: ${command.command} exit ${result.exitCode}${outputState.sessionLabel ? ` ${outputState.sessionLabel}` : ""}.`,
275
+ eventType: "provider_cli_completed"
276
+ });
277
+ await emitProviderUsageEvent(context.emit, dispatch, outputState);
278
+ await emitDraftGateArtifacts(context.emit, tempDir, dispatch.workItem.id);
279
+ const proposalArtifact = earlyProposalArtifact ?? await readCompletionArtifact(tempDir, dispatch.workItem.id, "draft");
280
+ if (!proposalArtifact) {
281
+ throw new Error("Provider CLI Draft completed but did not call forge.complete_draft_proposal.");
344
282
  }
345
- finally {
346
- context.signal?.removeEventListener("abort", abortProvider);
283
+ const proposal = parseJsonLine(proposalArtifact.verdictJson) ?? {};
284
+ const draftJson = proposal["draft_json"];
285
+ if (!draftJson || typeof draftJson !== "object" || Array.isArray(draftJson)) {
286
+ throw new Error("Provider CLI Draft completed but draft.proposal.json did not contain draft_json.");
347
287
  }
288
+ const json = JSON.stringify(draftJson);
289
+ await context.emit({
290
+ role: "assistant",
291
+ kind: "message",
292
+ content: json,
293
+ eventType: "draft_complete"
294
+ });
295
+ return {
296
+ summary: "Draft generated through Forge Draft completion cycle.",
297
+ artifact: proposalArtifact,
298
+ artifacts: [proposalArtifact]
299
+ };
348
300
  }
349
301
  finally {
350
302
  await cleanupCompletionTools(completionTools, context.emit);
@@ -2083,47 +2035,6 @@ async function emitDraftGateArtifacts(emit, worktreePath, workItemId) {
2083
2035
  }
2084
2036
  }
2085
2037
  }
2086
- async function emitDecomposeGateArtifacts(emit, worktreePath, workItemId) {
2087
- const decomposeArtifactFiles = {
2088
- decompose_subject_intake: "decompose.subject-intake.json",
2089
- decompose_context_review: "decompose.context-review.json",
2090
- decompose_breakdown_strategy: "decompose.breakdown-strategy.json",
2091
- decompose_child_items: "decompose.child-items.json",
2092
- decompose_dependency_graph: "decompose.dependency-graph.json",
2093
- decompose_critic: "decompose.critic.json",
2094
- decompose_proposal: "decompose.proposal.json"
2095
- };
2096
- const gates = Object.keys(decomposeArtifactFiles);
2097
- for (const gate of gates) {
2098
- const actualPath = path.join(worktreePath, ".forge", "work-items", workItemId, "artifacts", decomposeArtifactFiles[gate]);
2099
- if (!await fileExists(actualPath)) {
2100
- continue;
2101
- }
2102
- const relativePath = path.relative(worktreePath, actualPath).split(path.sep).join("/");
2103
- await emit({
2104
- role: "tool",
2105
- kind: "tool_result",
2106
- content: `Forge Decompose completion tool recorded ${gate} artifact ${relativePath}.`,
2107
- eventType: "completion_tool_artifact_recorded",
2108
- data: JSON.stringify({ phase: "decompose", decompose_gate: gate, artifact_relative_paths: [relativePath] })
2109
- });
2110
- await emit({
2111
- role: "hook",
2112
- kind: "checkpoint",
2113
- content: `phase_gate: ${gate} artifact accepted with status pass.`,
2114
- eventType: "phase_gate"
2115
- });
2116
- const next = gates[gates.indexOf(gate) + 1];
2117
- if (next) {
2118
- await emit({
2119
- role: "hook",
2120
- kind: "checkpoint",
2121
- content: `phase_progression: ${gate} -> ${next}`,
2122
- eventType: "phase_gate"
2123
- });
2124
- }
2125
- }
2126
- }
2127
2038
  async function fileExists(filePath) {
2128
2039
  try {
2129
2040
  await fs.access(filePath);
@@ -2138,7 +2049,6 @@ function buildDraftPrompt(dispatch) {
2138
2049
  if (!draft) {
2139
2050
  throw new Error("Draft prompt is missing.");
2140
2051
  }
2141
- const decomposeDraft = isDecomposeDraftDispatch(dispatch);
2142
2052
  const draftArtifacts = dispatch.artifacts
2143
2053
  .filter((artifact) => artifact.scope === "operational" && ["command", "skill", "hook"].includes(artifact.kind))
2144
2054
  .map((artifact) => `## ${artifact.kind}: ${artifact.slug}\n${artifact.body.trim()}`)
@@ -2157,62 +2067,25 @@ Draft kind: ${draft.kind}
2157
2067
 
2158
2068
  ${draft.userPrompt}
2159
2069
 
2160
- ${decomposeDraft
2161
- ? `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.
2162
-
2163
- Complete these gates in order: decompose_subject_intake -> decompose_context_review -> decompose_breakdown_strategy -> decompose_child_items -> decompose_dependency_graph -> decompose_critic -> decompose_proposal.
2164
-
2165
- The final proposal_json object must contain summary, optional blockedReason, and candidates.`
2166
- : `${completionPromptForDraftCycle("draft_intent")}
2070
+ ${completionPromptForDraftCycle("draft_intent")}
2167
2071
 
2168
- 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.
2169
2073
 
2170
2074
  Do not modify source files.`;
2171
2075
  }
2172
- function isDecomposeDraftKind(kind) {
2173
- return kind === "work_order_decomposition" || kind === "feature_decomposition";
2174
- }
2175
- function isDecomposeDraftDispatch(dispatch) {
2176
- return dispatch.execution.lane.trim().toLowerCase() === "decompose"
2177
- || dispatch.session.lane.trim().toLowerCase() === "decompose"
2178
- || isDecomposeDraftKind(dispatch.draft?.kind?.trim().toLowerCase());
2179
- }
2180
2076
  function summarizeStderr(stderr) {
2181
2077
  const trimmed = stderr.trim();
2182
2078
  return trimmed ? `: ${clip(trimmed, 1000)}` : "";
2183
2079
  }
2184
- async function waitForCompletionArtifact(worktreePath, workItemId, phase, signal) {
2080
+ async function waitForDraftProposalArtifact(worktreePath, workItemId, signal) {
2185
2081
  while (!signal?.aborted) {
2186
- const artifact = await readCompletionArtifact(worktreePath, workItemId, phase);
2082
+ const artifact = await readCompletionArtifact(worktreePath, workItemId, "draft");
2187
2083
  if (artifact) {
2188
2084
  return artifact;
2189
2085
  }
2190
- await delay(1000, signal);
2086
+ await new Promise((resolve) => setTimeout(resolve, 1000));
2191
2087
  }
2192
- throw new Error(`${phase} proposal artifact wait was cancelled.`);
2193
- }
2194
- async function delay(ms, signal) {
2195
- if (!signal) {
2196
- await new Promise((resolve) => setTimeout(resolve, ms));
2197
- return;
2198
- }
2199
- const abortSignal = signal;
2200
- if (abortSignal.aborted) {
2201
- return;
2202
- }
2203
- await new Promise((resolve) => {
2204
- const timeout = setTimeout(done, ms);
2205
- const abort = () => done();
2206
- function done() {
2207
- clearTimeout(timeout);
2208
- abortSignal.removeEventListener("abort", abort);
2209
- resolve();
2210
- }
2211
- abortSignal.addEventListener("abort", abort, { once: true });
2212
- if (abortSignal.aborted) {
2213
- done();
2214
- }
2215
- });
2088
+ throw new Error("Draft proposal artifact wait was cancelled.");
2216
2089
  }
2217
2090
  function deterministicDraftPayload(dispatch) {
2218
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.04a84914da09";
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.04a84914da09";
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.04a84914da09",
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",