@loucompanion/forge-bridge 0.1.1-dev.40fbc494669b → 0.1.1-dev.5093acd6bece
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.
- package/dist/session/phase-completion.d.ts +12 -1
- package/dist/session/phase-completion.js +202 -20
- package/dist/session/provider-cli/provider-cli.service.js +122 -9
- package/dist/session/session.types.d.ts +2 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -3,8 +3,10 @@ import type { WorktreeHandle } from "../worktree/worktree.types.js";
|
|
|
3
3
|
export type DevelopCompletionPhase = "implementation" | "self_review" | "validation";
|
|
4
4
|
export type DebugCompletionPhase = "debug_repro" | "debug_root_cause" | "debug_fix" | "debug_regression";
|
|
5
5
|
export type ArchitectCompletionPhase = "architect";
|
|
6
|
-
export type
|
|
6
|
+
export type DraftCompletionPhase = "draft";
|
|
7
|
+
export type CompletionPhase = DevelopCompletionPhase | "qa" | DebugCompletionPhase | ArchitectCompletionPhase | DraftCompletionPhase;
|
|
7
8
|
export type QaCompletionGate = "qa_plan" | "qa_regression" | "qa_api" | "qa_manual_web" | "qa_bug_repro" | "qa_findings" | "qa_verdict";
|
|
9
|
+
export type DraftCompletionGate = "draft_intent" | "draft_repo_context" | "draft_graph_context" | "draft_product_scope" | "draft_work_order_plan" | "draft_critic" | "draft_proposal";
|
|
8
10
|
export interface CompletionToolRuntime {
|
|
9
11
|
readonly serverName: string;
|
|
10
12
|
readonly command: string;
|
|
@@ -30,6 +32,7 @@ export interface CompletionToolServerConfig {
|
|
|
30
32
|
readonly qaCycle?: boolean;
|
|
31
33
|
readonly debugCycle?: boolean;
|
|
32
34
|
readonly architectCycle?: boolean;
|
|
35
|
+
readonly draftCycle?: boolean;
|
|
33
36
|
}
|
|
34
37
|
export interface CompletionToolCallResult {
|
|
35
38
|
readonly content: readonly {
|
|
@@ -40,6 +43,7 @@ export interface CompletionToolCallResult {
|
|
|
40
43
|
readonly gate: {
|
|
41
44
|
readonly phase: CompletionPhase;
|
|
42
45
|
readonly qaGate?: QaCompletionGate;
|
|
46
|
+
readonly draftGate?: DraftCompletionGate;
|
|
43
47
|
readonly status: string;
|
|
44
48
|
readonly allowed: boolean;
|
|
45
49
|
readonly reason: string;
|
|
@@ -62,6 +66,7 @@ export interface CompletionArtifactReference {
|
|
|
62
66
|
}
|
|
63
67
|
export declare const debugCompletionPhaseOrder: readonly DebugCompletionPhase[];
|
|
64
68
|
export declare const qaCompletionGateOrder: readonly QaCompletionGate[];
|
|
69
|
+
export declare const draftCompletionGateOrder: readonly DraftCompletionGate[];
|
|
65
70
|
export declare function completionPhaseFromDispatch(dispatch: RunOnceDispatchPacket): CompletionPhase;
|
|
66
71
|
export declare function normalizeCompletionPhase(value: string | null | undefined): CompletionPhase;
|
|
67
72
|
export declare function usesDevelopCycleCompletion(dispatch: RunOnceDispatchPacket): boolean;
|
|
@@ -71,9 +76,11 @@ export declare function usesArchitectCycleCompletion(dispatch: RunOnceDispatchPa
|
|
|
71
76
|
export declare function completionToolNamesForPhase(phase: CompletionPhase): readonly string[];
|
|
72
77
|
export declare function completionToolNamesForQaGate(gate: QaCompletionGate): readonly string[];
|
|
73
78
|
export declare function completionToolNamesForDebugGate(gate: DebugCompletionPhase): readonly string[];
|
|
79
|
+
export declare function completionToolNamesForDraftGate(gate: DraftCompletionGate): readonly string[];
|
|
74
80
|
export declare function expectedCompletionToolName(phase: CompletionPhase): string;
|
|
75
81
|
export declare function expectedQaCompletionToolName(gate: QaCompletionGate): string;
|
|
76
82
|
export declare function expectedDebugCompletionToolName(gate: DebugCompletionPhase): string;
|
|
83
|
+
export declare function expectedDraftCompletionToolName(gate: DraftCompletionGate): string;
|
|
77
84
|
export declare function artifactRootFor(worktreePath: string, workItemId: string): string;
|
|
78
85
|
export declare function artifactRootForOwner(worktreePath: string, ownerType: string, ownerId: string): string;
|
|
79
86
|
export declare function completionArtifactJsonPath(worktreePath: string, workItemId: string, phase: CompletionPhase, ownerType?: string | null, ownerId?: string | null): string;
|
|
@@ -85,20 +92,24 @@ export declare function cursorMcpServerConfig(runtime: CompletionToolRuntime): u
|
|
|
85
92
|
export declare function completionPromptForPhase(phase: CompletionPhase): string;
|
|
86
93
|
export declare function completionPromptForDevelopCycle(phase: CompletionPhase): string;
|
|
87
94
|
export declare function completionPromptForQaCycle(currentGate?: QaCompletionGate): string;
|
|
95
|
+
export declare function completionPromptForDraftCycle(currentGate?: DraftCompletionGate): string;
|
|
88
96
|
export declare function completionPromptForDebugCycle(currentGate?: DebugCompletionPhase): string;
|
|
89
97
|
export declare function completionToolNamesForServer(config: CompletionToolServerConfig): readonly string[];
|
|
90
98
|
export declare class CompletionToolServerSession {
|
|
91
99
|
private readonly config;
|
|
92
100
|
private currentPhase;
|
|
93
101
|
private currentQaGate;
|
|
102
|
+
private currentDraftGate;
|
|
94
103
|
private completed;
|
|
95
104
|
constructor(config: CompletionToolServerConfig);
|
|
96
105
|
get instructions(): string;
|
|
97
106
|
toolDefinitions(): readonly unknown[];
|
|
98
107
|
callTool(toolName: string, input: unknown): Promise<CompletionToolCallResult>;
|
|
108
|
+
private callDraftGateTool;
|
|
99
109
|
private callDebugGateTool;
|
|
100
110
|
private callQaGateTool;
|
|
101
111
|
private advanceAfterPass;
|
|
112
|
+
private advanceAfterDraftGate;
|
|
102
113
|
private advanceAfterQaGate;
|
|
103
114
|
private advanceAfterDebugGate;
|
|
104
115
|
}
|
|
@@ -22,6 +22,33 @@ export const qaCompletionGateOrder = [
|
|
|
22
22
|
"qa_findings",
|
|
23
23
|
"qa_verdict"
|
|
24
24
|
];
|
|
25
|
+
export const draftCompletionGateOrder = [
|
|
26
|
+
"draft_intent",
|
|
27
|
+
"draft_repo_context",
|
|
28
|
+
"draft_graph_context",
|
|
29
|
+
"draft_product_scope",
|
|
30
|
+
"draft_work_order_plan",
|
|
31
|
+
"draft_critic",
|
|
32
|
+
"draft_proposal"
|
|
33
|
+
];
|
|
34
|
+
const draftGateFileNames = {
|
|
35
|
+
draft_intent: "draft.intent.json",
|
|
36
|
+
draft_repo_context: "draft.repo-context.json",
|
|
37
|
+
draft_graph_context: "draft.graph-context.json",
|
|
38
|
+
draft_product_scope: "draft.product-scope.json",
|
|
39
|
+
draft_work_order_plan: "draft.work-order-plan.json",
|
|
40
|
+
draft_critic: "draft.critic.json",
|
|
41
|
+
draft_proposal: "draft.proposal.json"
|
|
42
|
+
};
|
|
43
|
+
const draftGateToolNames = {
|
|
44
|
+
draft_intent: "complete_draft_intent",
|
|
45
|
+
draft_repo_context: "complete_draft_repo_context",
|
|
46
|
+
draft_graph_context: "complete_draft_graph_context",
|
|
47
|
+
draft_product_scope: "complete_draft_product_scope",
|
|
48
|
+
draft_work_order_plan: "complete_draft_work_order_plan",
|
|
49
|
+
draft_critic: "complete_draft_critic",
|
|
50
|
+
draft_proposal: "complete_draft_proposal"
|
|
51
|
+
};
|
|
25
52
|
const qaGateFileNames = {
|
|
26
53
|
qa_plan: "qa.plan.json",
|
|
27
54
|
qa_regression: "qa.regression.json",
|
|
@@ -95,6 +122,10 @@ const phaseFiles = {
|
|
|
95
122
|
architect: {
|
|
96
123
|
jsonFileName: "architect.verdict.json",
|
|
97
124
|
reportFileName: "architect.report.md"
|
|
125
|
+
},
|
|
126
|
+
draft: {
|
|
127
|
+
jsonFileName: "draft.proposal.json",
|
|
128
|
+
reportFileName: "draft.report.md"
|
|
98
129
|
}
|
|
99
130
|
};
|
|
100
131
|
export function completionPhaseFromDispatch(dispatch) {
|
|
@@ -118,6 +149,9 @@ export function normalizeCompletionPhase(value) {
|
|
|
118
149
|
if (normalized === "architect" || normalized === "architect_plan" || normalized === "planning" || normalized === "plan") {
|
|
119
150
|
return "architect";
|
|
120
151
|
}
|
|
152
|
+
if (normalized === "draft" || normalized === "drafting" || normalized === "draft_work_order") {
|
|
153
|
+
return "draft";
|
|
154
|
+
}
|
|
121
155
|
return "implementation";
|
|
122
156
|
}
|
|
123
157
|
export function usesDevelopCycleCompletion(dispatch) {
|
|
@@ -154,6 +188,9 @@ export function usesArchitectCycleCompletion(dispatch) {
|
|
|
154
188
|
return kind === "architect" || role === "architect" || lane === "architect";
|
|
155
189
|
}
|
|
156
190
|
export function completionToolNamesForPhase(phase) {
|
|
191
|
+
if (phase === "draft") {
|
|
192
|
+
return completionToolNamesForDraftGate("draft_proposal");
|
|
193
|
+
}
|
|
157
194
|
if (isDebugCompletionPhase(phase)) {
|
|
158
195
|
return completionToolNamesForDebugGate(phase);
|
|
159
196
|
}
|
|
@@ -176,6 +213,10 @@ export function completionToolNamesForDebugGate(gate) {
|
|
|
176
213
|
const base = debugGateToolNames[gate];
|
|
177
214
|
return [`forge.${base}`, base];
|
|
178
215
|
}
|
|
216
|
+
export function completionToolNamesForDraftGate(gate) {
|
|
217
|
+
const base = draftGateToolNames[gate];
|
|
218
|
+
return [`forge.${base}`, base];
|
|
219
|
+
}
|
|
179
220
|
export function expectedCompletionToolName(phase) {
|
|
180
221
|
return completionToolNamesForPhase(phase)[0];
|
|
181
222
|
}
|
|
@@ -185,6 +226,9 @@ export function expectedQaCompletionToolName(gate) {
|
|
|
185
226
|
export function expectedDebugCompletionToolName(gate) {
|
|
186
227
|
return completionToolNamesForDebugGate(gate)[0];
|
|
187
228
|
}
|
|
229
|
+
export function expectedDraftCompletionToolName(gate) {
|
|
230
|
+
return completionToolNamesForDraftGate(gate)[0];
|
|
231
|
+
}
|
|
188
232
|
export function artifactRootFor(worktreePath, workItemId) {
|
|
189
233
|
return path.join(worktreePath, ".forge", "work-items", safePathSegment(workItemId, "work item id"), "artifacts");
|
|
190
234
|
}
|
|
@@ -219,7 +263,8 @@ export async function prepareCompletionToolRuntime(options) {
|
|
|
219
263
|
developCycle: usesDevelopCycleCompletion(options.dispatch),
|
|
220
264
|
qaCycle: usesQaCycleCompletion(options.dispatch),
|
|
221
265
|
debugCycle: usesDebugCycleCompletion(options.dispatch),
|
|
222
|
-
architectCycle: usesArchitectCycleCompletion(options.dispatch)
|
|
266
|
+
architectCycle: usesArchitectCycleCompletion(options.dispatch),
|
|
267
|
+
draftCycle: options.dispatch.execution.lane.trim().toLowerCase() === "draft" || options.dispatch.cycleRun.kind.trim().toLowerCase() === "draft"
|
|
223
268
|
});
|
|
224
269
|
const runtime = {
|
|
225
270
|
serverName: toolServerName,
|
|
@@ -261,6 +306,9 @@ export function buildCompletionToolServerArgs(config) {
|
|
|
261
306
|
if (config.architectCycle) {
|
|
262
307
|
args.push("--architect-cycle");
|
|
263
308
|
}
|
|
309
|
+
if (config.draftCycle) {
|
|
310
|
+
args.push("--draft-cycle");
|
|
311
|
+
}
|
|
264
312
|
return args;
|
|
265
313
|
}
|
|
266
314
|
export function codexMcpConfigArgs(runtime) {
|
|
@@ -335,6 +383,19 @@ Use \`status: "skipped"\` with a reason for gates that are not applicable. If a
|
|
|
335
383
|
The final verdict tool writes the canonical QA bundle under \`.forge/work-items/{workItemId}/artifacts/\`: \`qa.plan.json\`, \`qa.evidence.json\`, \`qa.findings.json\`, \`qa.verdict.json\`, and \`qa.report.md\`.
|
|
336
384
|
The legacy \`forge.complete_qa\` one-shot tool is not available in normal QA-cycle mode. Reporting QA completion in prose is not enough.`;
|
|
337
385
|
}
|
|
386
|
+
export function completionPromptForDraftCycle(currentGate = "draft_intent") {
|
|
387
|
+
return `Forge completion tools are available through the MCP server named \`${toolServerName}\`.
|
|
388
|
+
Do not finish Draft by prose or raw JSON. The tool server enforces Draft gate order and starts at \`${currentGate}\`.
|
|
389
|
+
Complete the Draft cycle in order inside this same provider session:
|
|
390
|
+
1. Call \`forge.complete_draft_intent(...)\` after interpreting user intent and non-goals.
|
|
391
|
+
2. Follow the returned repo-context instruction, inspect selected project/repo assumptions, then call \`forge.complete_draft_repo_context(...)\`.
|
|
392
|
+
3. Follow the returned graph-context instruction, account for DAG/dependency placement, then call \`forge.complete_draft_graph_context(...)\`.
|
|
393
|
+
4. Follow the returned product-scope instruction, choose product scope for the selected project, then call \`forge.complete_draft_product_scope(...)\`.
|
|
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(...)\`.
|
|
395
|
+
6. Follow the returned critic instruction, critique literalism, wrong-project scope, and decomposition quality, then call \`forge.complete_draft_critic(...)\`.
|
|
396
|
+
7. Follow the returned proposal instruction, then call \`forge.complete_draft_proposal(...)\` with the final Work Order JSON in \`draft_json\`.
|
|
397
|
+
The final proposal tool writes \`draft.proposal.json\` under \`.forge/work-items/{workItemId}/artifacts/\`. Reporting Draft completion in prose is not enough.`;
|
|
398
|
+
}
|
|
338
399
|
export function completionPromptForDebugCycle(currentGate = "debug_repro") {
|
|
339
400
|
return `Forge completion tools are available through the MCP server named \`${toolServerName}\`.
|
|
340
401
|
Do not write Debug artifacts by hand. The tool server enforces Debug gate order and starts this run at \`${currentGate}\`.
|
|
@@ -347,33 +408,38 @@ Each tool writes a canonical Debug artifact under \`.forge/work-items/{workItemI
|
|
|
347
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.`;
|
|
348
409
|
}
|
|
349
410
|
export function completionToolNamesForServer(config) {
|
|
350
|
-
return config.
|
|
351
|
-
?
|
|
352
|
-
: config.
|
|
353
|
-
?
|
|
354
|
-
: config.
|
|
355
|
-
?
|
|
356
|
-
: config.
|
|
357
|
-
? completionToolNamesForPhase(
|
|
358
|
-
:
|
|
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);
|
|
359
422
|
}
|
|
360
423
|
export class CompletionToolServerSession {
|
|
361
424
|
config;
|
|
362
425
|
currentPhase;
|
|
363
426
|
currentQaGate = "qa_plan";
|
|
427
|
+
currentDraftGate = "draft_intent";
|
|
364
428
|
completed = false;
|
|
365
429
|
constructor(config) {
|
|
366
430
|
this.config = config;
|
|
367
431
|
this.currentPhase = config.phase;
|
|
368
432
|
}
|
|
369
433
|
get instructions() {
|
|
370
|
-
return this.config.
|
|
371
|
-
?
|
|
372
|
-
: this.config.
|
|
373
|
-
?
|
|
374
|
-
: this.config.
|
|
375
|
-
?
|
|
376
|
-
:
|
|
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);
|
|
377
443
|
}
|
|
378
444
|
toolDefinitions() {
|
|
379
445
|
return completionToolNamesForServer(this.config).map((name) => toolDefinition(name, phaseFromToolName(name), qaGateFromToolName(name)));
|
|
@@ -383,6 +449,9 @@ export class CompletionToolServerSession {
|
|
|
383
449
|
if (this.completed) {
|
|
384
450
|
throw new Error("Forge completion cycle is already complete; no more completion calls are accepted.");
|
|
385
451
|
}
|
|
452
|
+
if (this.config.draftCycle) {
|
|
453
|
+
return await this.callDraftGateTool(toolName, requestedPhase, input);
|
|
454
|
+
}
|
|
386
455
|
if (this.config.debugCycle) {
|
|
387
456
|
return await this.callDebugGateTool(toolName, requestedPhase, input);
|
|
388
457
|
}
|
|
@@ -423,6 +492,30 @@ export class CompletionToolServerSession {
|
|
|
423
492
|
}
|
|
424
493
|
};
|
|
425
494
|
}
|
|
495
|
+
async callDraftGateTool(toolName, requestedPhase, input) {
|
|
496
|
+
const requestedGate = draftGateFromToolName(toolName);
|
|
497
|
+
if (requestedPhase !== "draft" || !requestedGate) {
|
|
498
|
+
throw new Error(`Tool ${toolName} is not available in Draft-cycle mode. Current Forge Draft gate is ${this.currentDraftGate}.`);
|
|
499
|
+
}
|
|
500
|
+
if (requestedGate !== this.currentDraftGate) {
|
|
501
|
+
throw new Error(`Tool ${toolName} is out of order. Current Forge Draft gate is ${this.currentDraftGate}.`);
|
|
502
|
+
}
|
|
503
|
+
const completedGate = this.currentDraftGate;
|
|
504
|
+
const reference = await writeDraftGateCompletionArtifact(this.config, completedGate, toolName, input);
|
|
505
|
+
const status = reference.status.trim().toLowerCase();
|
|
506
|
+
const nextGate = this.advanceAfterDraftGate(completedGate);
|
|
507
|
+
const nextPhase = nextGate ? "draft" : "sign_off_ready";
|
|
508
|
+
const nextInstruction = completionToolNextInstructionForDraftGate(completedGate, status, nextGate);
|
|
509
|
+
return {
|
|
510
|
+
content: [{ type: "text", text: `Forge recorded ${completedGate} artifact ${path.relative(reference.repoPath, reference.path)} (${reference.status}, ${reference.contentHash}). ${nextInstruction}` }],
|
|
511
|
+
structuredContent: {
|
|
512
|
+
...reference,
|
|
513
|
+
gate: { phase: "draft", draftGate: completedGate, status, allowed: status !== "blocked", reason: nextGate ? `${completedGate} artifact accepted; continue to ${nextGate}.` : `Draft proposal artifact accepted with status ${status}.` },
|
|
514
|
+
nextPhase,
|
|
515
|
+
nextInstruction
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
}
|
|
426
519
|
async callDebugGateTool(toolName, requestedPhase, input) {
|
|
427
520
|
const currentDebugGate = asDebugCompletionPhase(this.currentPhase);
|
|
428
521
|
if (!isDebugCompletionPhase(requestedPhase)) {
|
|
@@ -519,6 +612,16 @@ export class CompletionToolServerSession {
|
|
|
519
612
|
this.completed = true;
|
|
520
613
|
return "dev_test_ready";
|
|
521
614
|
}
|
|
615
|
+
advanceAfterDraftGate(gate) {
|
|
616
|
+
const index = draftCompletionGateOrder.indexOf(gate);
|
|
617
|
+
const next = index >= 0 ? draftCompletionGateOrder[index + 1] ?? null : null;
|
|
618
|
+
if (next) {
|
|
619
|
+
this.currentDraftGate = next;
|
|
620
|
+
return next;
|
|
621
|
+
}
|
|
622
|
+
this.completed = true;
|
|
623
|
+
return null;
|
|
624
|
+
}
|
|
522
625
|
advanceAfterQaGate(gate) {
|
|
523
626
|
const index = qaCompletionGateOrder.indexOf(gate);
|
|
524
627
|
const next = index >= 0 ? qaCompletionGateOrder[index + 1] ?? null : null;
|
|
@@ -540,6 +643,43 @@ export class CompletionToolServerSession {
|
|
|
540
643
|
return null;
|
|
541
644
|
}
|
|
542
645
|
}
|
|
646
|
+
async function writeDraftGateCompletionArtifact(config, gate, toolName, input) {
|
|
647
|
+
const payload = asRecord(input, "Draft completion payload");
|
|
648
|
+
const status = normalizeDraftStatus(optionalString(payload, "status") ?? "pass");
|
|
649
|
+
if (gate === "draft_proposal") {
|
|
650
|
+
const draftJson = asRecord(payload["draft_json"], "draft_json");
|
|
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
|
+
}
|
|
656
|
+
}
|
|
657
|
+
const redactedPayload = redactSecrets({ ...payload, status, phase: "draft", gate, tool_name: toolName }, config.redactionSecrets ?? []);
|
|
658
|
+
const root = artifactRootFor(config.worktreePath, config.workItemId);
|
|
659
|
+
await fs.mkdir(root, { recursive: true });
|
|
660
|
+
const artifactPath = path.join(root, draftGateFileNames[gate]);
|
|
661
|
+
const reportPath = gate === "draft_proposal" ? path.join(root, phaseFiles.draft.reportFileName) : path.join(root, `${gate}.md`);
|
|
662
|
+
const verdictJson = jsonText(redactedPayload);
|
|
663
|
+
const report = optionalString(redactedPayload, "report_markdown") ?? `# ${gate.replace(/_/g, " ")}\n\n${optionalString(redactedPayload, "summary") ?? "Draft gate recorded."}\n`;
|
|
664
|
+
await writeFileAtomic(artifactPath, verdictJson);
|
|
665
|
+
await writeFileAtomic(reportPath, report);
|
|
666
|
+
return {
|
|
667
|
+
phase: "draft",
|
|
668
|
+
workItemId: config.workItemId,
|
|
669
|
+
ownerType: config.ownerType,
|
|
670
|
+
ownerId: config.ownerId,
|
|
671
|
+
repoPath: path.resolve(config.worktreePath),
|
|
672
|
+
path: artifactPath,
|
|
673
|
+
reportPath,
|
|
674
|
+
contentHash: sha256(verdictJson),
|
|
675
|
+
status,
|
|
676
|
+
producedBy: config.producedBy ?? "forge-completion-tool"
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
function normalizeDraftStatus(value) {
|
|
680
|
+
const normalized = value.trim().toLowerCase();
|
|
681
|
+
return normalized === "blocked" || normalized === "needs_revision" || normalized === "needs_rework" ? normalized : "pass";
|
|
682
|
+
}
|
|
543
683
|
export async function writeCompletionArtifact(config, toolName, input) {
|
|
544
684
|
const phase = phaseFromToolName(toolName);
|
|
545
685
|
if (phase !== config.phase) {
|
|
@@ -1037,6 +1177,7 @@ export function parseCompletionMcpArgs(argv) {
|
|
|
1037
1177
|
let qaCycle = false;
|
|
1038
1178
|
let debugCycle = false;
|
|
1039
1179
|
let architectCycle = false;
|
|
1180
|
+
let draftCycle = false;
|
|
1040
1181
|
for (let index = 1; index < argv.length; index += 1) {
|
|
1041
1182
|
const arg = argv[index];
|
|
1042
1183
|
if (arg === "--worktree") {
|
|
@@ -1058,7 +1199,7 @@ export function parseCompletionMcpArgs(argv) {
|
|
|
1058
1199
|
const rawPhase = argv[++index];
|
|
1059
1200
|
const parsedPhase = parseStrictCompletionPhase(rawPhase);
|
|
1060
1201
|
if (!parsedPhase) {
|
|
1061
|
-
return { ok: false, error: "mcp --phase must be one of: implementation, self-review, validation, qa, architect, debug-repro, debug-root-cause, debug-fix, debug-regression." };
|
|
1202
|
+
return { ok: false, error: "mcp --phase must be one of: implementation, self-review, validation, qa, architect, draft, debug-repro, debug-root-cause, debug-fix, debug-regression." };
|
|
1062
1203
|
}
|
|
1063
1204
|
phase = parsedPhase;
|
|
1064
1205
|
}
|
|
@@ -1077,6 +1218,9 @@ export function parseCompletionMcpArgs(argv) {
|
|
|
1077
1218
|
else if (arg === "--architect-cycle") {
|
|
1078
1219
|
architectCycle = true;
|
|
1079
1220
|
}
|
|
1221
|
+
else if (arg === "--draft-cycle") {
|
|
1222
|
+
draftCycle = true;
|
|
1223
|
+
}
|
|
1080
1224
|
else {
|
|
1081
1225
|
return { ok: false, error: `Unknown mcp argument: ${arg}` };
|
|
1082
1226
|
}
|
|
@@ -1084,7 +1228,7 @@ export function parseCompletionMcpArgs(argv) {
|
|
|
1084
1228
|
if (!worktreePath?.trim() || !workItemId?.trim()) {
|
|
1085
1229
|
return { ok: false, error: "mcp requires --worktree and --work-item-id." };
|
|
1086
1230
|
}
|
|
1087
|
-
if ([developCycle, qaCycle, debugCycle, architectCycle].filter(Boolean).length > 1) {
|
|
1231
|
+
if ([developCycle, qaCycle, debugCycle, architectCycle, draftCycle].filter(Boolean).length > 1) {
|
|
1088
1232
|
return { ok: false, error: "mcp accepts only one cycle mode." };
|
|
1089
1233
|
}
|
|
1090
1234
|
if (developCycle && !isDevelopCompletionPhase(phase)) {
|
|
@@ -1099,6 +1243,9 @@ export function parseCompletionMcpArgs(argv) {
|
|
|
1099
1243
|
if (architectCycle && phase !== "architect") {
|
|
1100
1244
|
return { ok: false, error: "mcp --architect-cycle requires --phase architect." };
|
|
1101
1245
|
}
|
|
1246
|
+
if (draftCycle && phase !== "draft") {
|
|
1247
|
+
return { ok: false, error: "mcp --draft-cycle requires --phase draft." };
|
|
1248
|
+
}
|
|
1102
1249
|
return {
|
|
1103
1250
|
ok: true,
|
|
1104
1251
|
config: {
|
|
@@ -1112,7 +1259,8 @@ export function parseCompletionMcpArgs(argv) {
|
|
|
1112
1259
|
developCycle,
|
|
1113
1260
|
qaCycle,
|
|
1114
1261
|
debugCycle,
|
|
1115
|
-
architectCycle
|
|
1262
|
+
architectCycle,
|
|
1263
|
+
draftCycle
|
|
1116
1264
|
}
|
|
1117
1265
|
};
|
|
1118
1266
|
}
|
|
@@ -1250,6 +1398,24 @@ function completionToolNextInstruction(completedPhase, status, nextPhase) {
|
|
|
1250
1398
|
}
|
|
1251
1399
|
return "Validation accepted. Forge has the structured evidence needed to move this work item to QA.";
|
|
1252
1400
|
}
|
|
1401
|
+
function completionToolNextInstructionForDraftGate(completedGate, status, nextGate) {
|
|
1402
|
+
if (status === "blocked") {
|
|
1403
|
+
return `Stay in ${completedGate}; record the blocker and retry the same Draft gate when unblocked.`;
|
|
1404
|
+
}
|
|
1405
|
+
if (!nextGate) {
|
|
1406
|
+
return "Draft proposal accepted. Forge has the structured Work Order draft JSON needed to populate the form.";
|
|
1407
|
+
}
|
|
1408
|
+
const instructions = {
|
|
1409
|
+
draft_intent: "Capture intent/non-goals, then call forge.complete_draft_intent.",
|
|
1410
|
+
draft_repo_context: "Inspect selected repo/project assumptions, then call forge.complete_draft_repo_context.",
|
|
1411
|
+
draft_graph_context: "Account for DAG dependencies and placement, then call forge.complete_draft_graph_context.",
|
|
1412
|
+
draft_product_scope: "Choose product scope for the selected project, then call forge.complete_draft_product_scope.",
|
|
1413
|
+
draft_work_order_plan: "Draft Work Order fields and candidate Feature boundaries, then call forge.complete_draft_work_order_plan.",
|
|
1414
|
+
draft_critic: "Critique literalism/wrong-project scope/decomposition risk, then call forge.complete_draft_critic.",
|
|
1415
|
+
draft_proposal: "Submit final draft_json through forge.complete_draft_proposal."
|
|
1416
|
+
};
|
|
1417
|
+
return `${completedGate} accepted. Continue to ${nextGate}. ${instructions[nextGate]}`;
|
|
1418
|
+
}
|
|
1253
1419
|
function completionToolNextInstructionForQaGate(completedGate, status, nextGate) {
|
|
1254
1420
|
if (!nextGate) {
|
|
1255
1421
|
if (status === "blocked") {
|
|
@@ -2529,6 +2695,9 @@ function parseStrictCompletionPhase(value) {
|
|
|
2529
2695
|
if (normalized === "architect" || normalized === "architect_plan" || normalized === "planning" || normalized === "plan") {
|
|
2530
2696
|
return "architect";
|
|
2531
2697
|
}
|
|
2698
|
+
if (normalized === "draft" || normalized === "drafting" || normalized === "draft_work_order") {
|
|
2699
|
+
return "draft";
|
|
2700
|
+
}
|
|
2532
2701
|
return null;
|
|
2533
2702
|
}
|
|
2534
2703
|
function normalizeDebugCompletionPhase(value) {
|
|
@@ -2600,6 +2769,9 @@ function phaseFromToolName(toolName) {
|
|
|
2600
2769
|
if (normalized === "forge.complete_architect_plan" || normalized === "complete_architect_plan") {
|
|
2601
2770
|
return "architect";
|
|
2602
2771
|
}
|
|
2772
|
+
if (draftGateFromToolName(toolName)) {
|
|
2773
|
+
return "draft";
|
|
2774
|
+
}
|
|
2603
2775
|
const debugGate = debugGateFromToolName(toolName);
|
|
2604
2776
|
if (debugGate) {
|
|
2605
2777
|
return debugGate;
|
|
@@ -2609,6 +2781,16 @@ function phaseFromToolName(toolName) {
|
|
|
2609
2781
|
}
|
|
2610
2782
|
throw new Error(`Unknown Forge completion tool: ${toolName}`);
|
|
2611
2783
|
}
|
|
2784
|
+
function draftGateFromToolName(toolName) {
|
|
2785
|
+
const normalized = toolName.trim().toLowerCase();
|
|
2786
|
+
for (const gate of draftCompletionGateOrder) {
|
|
2787
|
+
const base = draftGateToolNames[gate];
|
|
2788
|
+
if (normalized === `forge.${base}` || normalized === base) {
|
|
2789
|
+
return gate;
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
return null;
|
|
2793
|
+
}
|
|
2612
2794
|
function debugGateFromToolName(toolName) {
|
|
2613
2795
|
const normalized = toolName.trim().toLowerCase();
|
|
2614
2796
|
for (const gate of debugCompletionPhaseOrder) {
|
|
@@ -9,7 +9,7 @@ import { GitClient } from "../../shared/git/git.js";
|
|
|
9
9
|
import { resolveCursorAgentDirectLaunch } from "../../shared/cursor-agent/cursor-agent.launch.js";
|
|
10
10
|
import { ensureNoSymbolicLinkPathSegments, isPathUnderRoot, sha256File } from "../qa-sidecar-validation.js";
|
|
11
11
|
import { buildSpecBody, selectLaneCommand } from "../../worktree/artifact-injector.js";
|
|
12
|
-
import { codexMcpConfigArgs, completionPhaseFromDispatch, completionPromptForDebugCycle, completionPromptForDevelopCycle, completionPromptForPhase, completionPromptForQaCycle, completionRedactionEnv, expectedCompletionToolName, expectedDebugCompletionToolName, expectedQaCompletionToolName, prepareCompletionToolRuntime, readCompletionArtifact, usesDebugCycleCompletion, usesDevelopCycleCompletion, writeCompletionArtifact, artifactRootFor, artifactRootForOwner, debugCompletionPhaseOrder, qaCompletionGateOrder } from "../phase-completion.js";
|
|
12
|
+
import { codexMcpConfigArgs, completionPhaseFromDispatch, completionPromptForDebugCycle, completionPromptForDevelopCycle, completionPromptForDraftCycle, completionPromptForPhase, completionPromptForQaCycle, completionRedactionEnv, expectedCompletionToolName, expectedDebugCompletionToolName, expectedQaCompletionToolName, prepareCompletionToolRuntime, readCompletionArtifact, usesDebugCycleCompletion, usesDevelopCycleCompletion, writeCompletionArtifact, artifactRootFor, artifactRootForOwner, debugCompletionPhaseOrder, draftCompletionGateOrder, qaCompletionGateOrder } from "../phase-completion.js";
|
|
13
13
|
import { addUsage, buildProviderUsageEvent, emptyUsage, hasUsage, readUsageNumber } from "./provider-usage.js";
|
|
14
14
|
const fakeWorkerModel = "server-local-fake-worker";
|
|
15
15
|
const execFileAsync = promisify(execFile);
|
|
@@ -189,17 +189,45 @@ export class ProcessBackedProviderCliService {
|
|
|
189
189
|
throw new Error("Draft dispatch context is missing.");
|
|
190
190
|
}
|
|
191
191
|
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "forge-bridge-draft-"));
|
|
192
|
+
const draftWorktree = {
|
|
193
|
+
repoPath: tempDir,
|
|
194
|
+
worktreePath: tempDir,
|
|
195
|
+
branchName: "draft",
|
|
196
|
+
baseBranch: dispatch.cycleRun.baseBranch || dispatch.repo.defaultBranch || "draft",
|
|
197
|
+
cleanupWorktrees: true
|
|
198
|
+
};
|
|
199
|
+
const metadataRoot = path.join(tempDir, ".forge", "work-items", dispatch.workItem.id);
|
|
200
|
+
await fs.mkdir(metadataRoot, { recursive: true });
|
|
201
|
+
await fs.writeFile(path.join(metadataRoot, "draft-input.json"), JSON.stringify(dispatch.draft, null, 2) + "\n", "utf8");
|
|
202
|
+
await fs.writeFile(path.join(metadataRoot, "spec.md"), buildDraftSpec(dispatch), "utf8");
|
|
203
|
+
const completionTools = await prepareCompletionToolRuntime({ dispatch, worktree: draftWorktree, phase: "draft" });
|
|
192
204
|
const command = buildProviderCliCommand(dispatch, {
|
|
193
205
|
worktreePath: tempDir,
|
|
194
206
|
prompt: buildDraftPrompt(dispatch),
|
|
207
|
+
completionTools,
|
|
195
208
|
skipGitRepoCheck: true
|
|
196
209
|
});
|
|
210
|
+
const draftArtifactSlugs = dispatch.artifacts
|
|
211
|
+
.filter((artifact) => artifact.scope === "operational" && ["command", "skill", "hook"].includes(artifact.kind))
|
|
212
|
+
.map((artifact) => `${artifact.kind}:${artifact.slug}`);
|
|
213
|
+
await context.emit({
|
|
214
|
+
role: "runner",
|
|
215
|
+
kind: "checkpoint",
|
|
216
|
+
content: `Draft artifacts injected: ${draftArtifactSlugs.length}${draftArtifactSlugs.length ? ` (${draftArtifactSlugs.join(", ")})` : ""}. Project: ${dispatch.draft.projectName ?? dispatch.repo.name}. Repo: ${dispatch.draft.repoName ?? dispatch.repo.name}.`,
|
|
217
|
+
eventType: "draft_artifacts_injected"
|
|
218
|
+
});
|
|
197
219
|
await context.emit({
|
|
198
220
|
role: "runner",
|
|
199
221
|
kind: "checkpoint",
|
|
200
222
|
content: `Starting provider CLI Draft: ${formatProviderCliCommand(command)}`,
|
|
201
223
|
eventType: "provider_cli_started"
|
|
202
224
|
});
|
|
225
|
+
await context.emit({
|
|
226
|
+
role: "runner",
|
|
227
|
+
kind: "checkpoint",
|
|
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.",
|
|
229
|
+
eventType: "completion_tools_configured"
|
|
230
|
+
});
|
|
203
231
|
const outputState = createProviderOutputState(dispatch.execution.provider);
|
|
204
232
|
try {
|
|
205
233
|
const result = await this.processRunner.run({
|
|
@@ -217,23 +245,38 @@ export class ProcessBackedProviderCliService {
|
|
|
217
245
|
if (result.exitCode !== 0) {
|
|
218
246
|
throw new Error(`Provider CLI exited with code ${result.exitCode}${summarizeStderr(result.stderr)}.`);
|
|
219
247
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
248
|
+
await context.emit({
|
|
249
|
+
role: "runner",
|
|
250
|
+
kind: "checkpoint",
|
|
251
|
+
content: `Provider CLI Draft completed: ${command.command} exit ${result.exitCode}${outputState.sessionLabel ? ` ${outputState.sessionLabel}` : ""}.`,
|
|
252
|
+
eventType: "provider_cli_completed"
|
|
253
|
+
});
|
|
254
|
+
await emitProviderUsageEvent(context.emit, dispatch, outputState);
|
|
255
|
+
await emitDraftGateArtifacts(context.emit, tempDir, dispatch.workItem.id);
|
|
256
|
+
const proposalArtifact = await readCompletionArtifact(tempDir, dispatch.workItem.id, "draft");
|
|
257
|
+
if (!proposalArtifact) {
|
|
258
|
+
throw new Error("Provider CLI Draft completed but did not call forge.complete_draft_proposal.");
|
|
259
|
+
}
|
|
260
|
+
const proposal = parseJsonLine(proposalArtifact.verdictJson) ?? {};
|
|
261
|
+
const draftJson = proposal["draft_json"];
|
|
262
|
+
if (!draftJson || typeof draftJson !== "object" || Array.isArray(draftJson)) {
|
|
263
|
+
throw new Error("Provider CLI Draft completed but draft.proposal.json did not contain draft_json.");
|
|
223
264
|
}
|
|
224
|
-
const json =
|
|
265
|
+
const json = JSON.stringify(draftJson);
|
|
225
266
|
await context.emit({
|
|
226
267
|
role: "assistant",
|
|
227
268
|
kind: "message",
|
|
228
269
|
content: json,
|
|
229
270
|
eventType: "draft_complete"
|
|
230
271
|
});
|
|
231
|
-
await emitProviderUsageEvent(context.emit, dispatch, outputState);
|
|
232
272
|
return {
|
|
233
|
-
summary: "Draft generated through Forge
|
|
273
|
+
summary: "Draft generated through Forge Draft completion cycle.",
|
|
274
|
+
artifact: proposalArtifact,
|
|
275
|
+
artifacts: [proposalArtifact]
|
|
234
276
|
};
|
|
235
277
|
}
|
|
236
278
|
finally {
|
|
279
|
+
await cleanupCompletionTools(completionTools, context.emit);
|
|
237
280
|
await fs.rm(tempDir, { recursive: true, force: true });
|
|
238
281
|
}
|
|
239
282
|
}
|
|
@@ -1900,6 +1943,67 @@ function isFakeWorkerModel(model) {
|
|
|
1900
1943
|
function sha256(content) {
|
|
1901
1944
|
return crypto.createHash("sha256").update(content).digest("hex");
|
|
1902
1945
|
}
|
|
1946
|
+
function buildDraftSpec(dispatch) {
|
|
1947
|
+
return `# Draft Generation Context
|
|
1948
|
+
|
|
1949
|
+
Project: ${dispatch.draft?.projectName ?? dispatch.repo.name}
|
|
1950
|
+
Repository: ${dispatch.draft?.repoName ?? dispatch.repo.name}
|
|
1951
|
+
Draft kind: ${dispatch.draft?.kind ?? "draft"}
|
|
1952
|
+
|
|
1953
|
+
## User Prompt
|
|
1954
|
+
|
|
1955
|
+
${dispatch.draft?.userPrompt ?? ""}
|
|
1956
|
+
`;
|
|
1957
|
+
}
|
|
1958
|
+
async function emitDraftGateArtifacts(emit, worktreePath, workItemId) {
|
|
1959
|
+
const draftArtifactFiles = {
|
|
1960
|
+
draft_intent: "draft.intent.json",
|
|
1961
|
+
draft_repo_context: "draft.repo-context.json",
|
|
1962
|
+
draft_graph_context: "draft.graph-context.json",
|
|
1963
|
+
draft_product_scope: "draft.product-scope.json",
|
|
1964
|
+
draft_work_order_plan: "draft.work-order-plan.json",
|
|
1965
|
+
draft_critic: "draft.critic.json",
|
|
1966
|
+
draft_proposal: "draft.proposal.json"
|
|
1967
|
+
};
|
|
1968
|
+
for (const gate of draftCompletionGateOrder) {
|
|
1969
|
+
const actualPath = path.join(worktreePath, ".forge", "work-items", workItemId, "artifacts", draftArtifactFiles[gate]);
|
|
1970
|
+
if (!await fileExists(actualPath)) {
|
|
1971
|
+
continue;
|
|
1972
|
+
}
|
|
1973
|
+
const relativePath = path.relative(worktreePath, actualPath).split(path.sep).join("/");
|
|
1974
|
+
await emit({
|
|
1975
|
+
role: "tool",
|
|
1976
|
+
kind: "tool_result",
|
|
1977
|
+
content: `Forge Draft completion tool recorded ${gate} artifact ${relativePath}.`,
|
|
1978
|
+
eventType: "completion_tool_artifact_recorded",
|
|
1979
|
+
data: JSON.stringify({ phase: "draft", draft_gate: gate, artifact_relative_paths: [relativePath] })
|
|
1980
|
+
});
|
|
1981
|
+
await emit({
|
|
1982
|
+
role: "hook",
|
|
1983
|
+
kind: "checkpoint",
|
|
1984
|
+
content: `phase_gate: ${gate} artifact accepted with status pass.`,
|
|
1985
|
+
eventType: "phase_gate"
|
|
1986
|
+
});
|
|
1987
|
+
const next = draftCompletionGateOrder[draftCompletionGateOrder.indexOf(gate) + 1];
|
|
1988
|
+
if (next) {
|
|
1989
|
+
await emit({
|
|
1990
|
+
role: "hook",
|
|
1991
|
+
kind: "checkpoint",
|
|
1992
|
+
content: `phase_progression: ${gate} -> ${next}`,
|
|
1993
|
+
eventType: "phase_gate"
|
|
1994
|
+
});
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
async function fileExists(filePath) {
|
|
1999
|
+
try {
|
|
2000
|
+
await fs.access(filePath);
|
|
2001
|
+
return true;
|
|
2002
|
+
}
|
|
2003
|
+
catch {
|
|
2004
|
+
return false;
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
1903
2007
|
function buildDraftPrompt(dispatch) {
|
|
1904
2008
|
const draft = dispatch.draft;
|
|
1905
2009
|
if (!draft) {
|
|
@@ -1912,13 +2016,22 @@ function buildDraftPrompt(dispatch) {
|
|
|
1912
2016
|
const artifactSection = draftArtifacts
|
|
1913
2017
|
? `\n\n# Injected Draft command/skills/hooks\n\n${draftArtifacts}`
|
|
1914
2018
|
: "";
|
|
1915
|
-
|
|
2019
|
+
const projectSection = [
|
|
2020
|
+
draft.projectName ? `Project: ${draft.projectName}` : null,
|
|
2021
|
+
draft.repoName ? `Repository: ${draft.repoName}` : null
|
|
2022
|
+
].filter(Boolean).join("\n");
|
|
2023
|
+
const scopedProjectSection = projectSection ? `\n\n# Selected project context\n\n${projectSection}` : "";
|
|
2024
|
+
return `${draft.systemPrompt}${scopedProjectSection}${artifactSection}
|
|
1916
2025
|
|
|
1917
2026
|
Draft kind: ${draft.kind}
|
|
1918
2027
|
|
|
1919
2028
|
${draft.userPrompt}
|
|
1920
2029
|
|
|
1921
|
-
|
|
2030
|
+
${completionPromptForDraftCycle("draft_intent")}
|
|
2031
|
+
|
|
2032
|
+
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.
|
|
2033
|
+
|
|
2034
|
+
Do not modify source files.`;
|
|
1922
2035
|
}
|
|
1923
2036
|
function extractJsonObject(content) {
|
|
1924
2037
|
let trimmed = content.trim();
|
|
@@ -23,6 +23,8 @@ export interface DispatchDraftPrompt {
|
|
|
23
23
|
readonly kind: string;
|
|
24
24
|
readonly systemPrompt: string;
|
|
25
25
|
readonly userPrompt: string;
|
|
26
|
+
readonly projectName?: string | null;
|
|
27
|
+
readonly repoName?: string | null;
|
|
26
28
|
}
|
|
27
29
|
export interface DispatchQaBrowserContext {
|
|
28
30
|
readonly required: boolean;
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const forgeBridgeVersion = "0.1.1-dev.
|
|
1
|
+
export declare const forgeBridgeVersion = "0.1.1-dev.5093acd6bece";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const forgeBridgeVersion = "0.1.1-dev.
|
|
1
|
+
export const forgeBridgeVersion = "0.1.1-dev.5093acd6bece";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loucompanion/forge-bridge",
|
|
3
|
-
"version": "0.1.1-dev.
|
|
3
|
+
"version": "0.1.1-dev.5093acd6bece",
|
|
4
4
|
"description": "Forge runner bridge CLI for connecting local developer machines to Forge dispatch.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|