@loucompanion/forge-bridge 0.1.1-dev.f599637d804e → 0.1.3-dev.2c3feac3af65
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/bridge/bridge.service.js +89 -9
- package/dist/bridge/bridge.types.d.ts +2 -0
- package/dist/cli/cli.service.js +212 -1
- package/dist/cli/local-config.d.ts +1 -0
- package/dist/cli/local-config.js +18 -3
- package/dist/config.js +1 -1
- package/dist/session/phase-completion.d.ts +23 -1
- package/dist/session/phase-completion.js +1046 -31
- package/dist/session/provider-cli/provider-cli.base.d.ts +2 -1
- package/dist/session/provider-cli/provider-cli.service.d.ts +13 -1
- package/dist/session/provider-cli/provider-cli.service.js +602 -19
- package/dist/session/provider-cli/provider-cli.types.d.ts +5 -0
- package/dist/session/session.service.js +17 -0
- package/dist/session/session.types.d.ts +26 -0
- package/dist/session/session.types.js +47 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worktree/artifact-injector.js +72 -0
- package/package.json +1 -1
|
@@ -6,6 +6,11 @@ export interface ProviderCliRunContext {
|
|
|
6
6
|
readonly signal?: AbortSignal;
|
|
7
7
|
emit(event: RunnerDispatchEvent): Promise<void>;
|
|
8
8
|
}
|
|
9
|
+
export interface ProviderCliDraftContext {
|
|
10
|
+
readonly dispatch: RunOnceDispatchPacket;
|
|
11
|
+
readonly signal?: AbortSignal;
|
|
12
|
+
emit(event: RunnerDispatchEvent): Promise<void>;
|
|
13
|
+
}
|
|
9
14
|
export interface ProviderCliRunResult {
|
|
10
15
|
readonly summary: string;
|
|
11
16
|
readonly spec?: RunnerDispatchSpec | null;
|
|
@@ -27,6 +27,23 @@ export class BridgeSessionService {
|
|
|
27
27
|
content: "forge-bridge accepted run-once dispatch.",
|
|
28
28
|
eventType: "dispatch_accepted"
|
|
29
29
|
});
|
|
30
|
+
if (dispatch.draft) {
|
|
31
|
+
const run = await this.providerCli.runDraft({
|
|
32
|
+
dispatch,
|
|
33
|
+
emit,
|
|
34
|
+
signal
|
|
35
|
+
});
|
|
36
|
+
throwIfAborted(signal);
|
|
37
|
+
return redactSecrets({
|
|
38
|
+
schemaVersion: 1,
|
|
39
|
+
exitCode: 0,
|
|
40
|
+
summary: run.summary,
|
|
41
|
+
events,
|
|
42
|
+
spec: run.spec ?? null,
|
|
43
|
+
artifact: run.artifact ?? null,
|
|
44
|
+
artifacts: run.artifacts?.length ? run.artifacts : run.artifact ? [run.artifact] : []
|
|
45
|
+
}, redactionSecrets);
|
|
46
|
+
}
|
|
30
47
|
const worktree = await this.worktrees.prepare({
|
|
31
48
|
repo: dispatch.repo,
|
|
32
49
|
workItem: dispatch.workItem,
|
|
@@ -13,10 +13,19 @@ export interface RunOnceDispatchPacket {
|
|
|
13
13
|
readonly artifacts: readonly DispatchResolvedArtifact[];
|
|
14
14
|
readonly warmPathOverrides?: readonly string[];
|
|
15
15
|
readonly qaBrowser?: DispatchQaBrowserContext | null;
|
|
16
|
+
readonly qaScope?: DispatchQaScope | null;
|
|
16
17
|
readonly qaRework?: DispatchQaReworkContext | null;
|
|
17
18
|
readonly architectRevision?: DispatchArchitectRevisionContext | null;
|
|
18
19
|
readonly storyDevelop?: DispatchStoryDevelopContext | null;
|
|
19
20
|
readonly storyQa?: DispatchStoryQaContext | null;
|
|
21
|
+
readonly draft?: DispatchDraftPrompt | null;
|
|
22
|
+
}
|
|
23
|
+
export interface DispatchDraftPrompt {
|
|
24
|
+
readonly kind: string;
|
|
25
|
+
readonly systemPrompt: string;
|
|
26
|
+
readonly userPrompt: string;
|
|
27
|
+
readonly projectName?: string | null;
|
|
28
|
+
readonly repoName?: string | null;
|
|
20
29
|
}
|
|
21
30
|
export interface DispatchQaBrowserContext {
|
|
22
31
|
readonly required: boolean;
|
|
@@ -24,6 +33,21 @@ export interface DispatchQaBrowserContext {
|
|
|
24
33
|
readonly artifactsPath: string;
|
|
25
34
|
readonly missingTargetReason?: string | null;
|
|
26
35
|
}
|
|
36
|
+
export interface DispatchQaScope {
|
|
37
|
+
readonly schema_version: number;
|
|
38
|
+
readonly surface: readonly string[];
|
|
39
|
+
readonly required_evidence: readonly string[];
|
|
40
|
+
readonly not_applicable: readonly string[];
|
|
41
|
+
readonly browser_required: boolean;
|
|
42
|
+
readonly api_required: boolean;
|
|
43
|
+
readonly source?: string | null;
|
|
44
|
+
readonly authority?: string | null;
|
|
45
|
+
readonly confidence?: string | null;
|
|
46
|
+
readonly confirmed_by_user?: boolean | null;
|
|
47
|
+
readonly locked?: boolean | null;
|
|
48
|
+
readonly derived_from?: Readonly<Record<string, unknown>> | null;
|
|
49
|
+
readonly rationale?: string | null;
|
|
50
|
+
}
|
|
27
51
|
export interface DispatchQaAppLaunchTarget {
|
|
28
52
|
readonly url?: string | null;
|
|
29
53
|
readonly command?: string | null;
|
|
@@ -218,6 +242,8 @@ export interface RunnerDispatchArtifact {
|
|
|
218
242
|
readonly planJson?: string | null;
|
|
219
243
|
readonly evidenceJson?: string | null;
|
|
220
244
|
readonly findingsJson?: string | null;
|
|
245
|
+
readonly implementationPlanJson?: string | null;
|
|
246
|
+
readonly qaPlanJson?: string | null;
|
|
221
247
|
readonly report: string;
|
|
222
248
|
readonly producedBy: string;
|
|
223
249
|
readonly status: string;
|
|
@@ -20,10 +20,12 @@ export function parseRunOnceDispatchPacket(value) {
|
|
|
20
20
|
artifacts: parseArtifacts(packet.artifacts),
|
|
21
21
|
warmPathOverrides: parseWarmPathOverrides(packet.warmPathOverrides),
|
|
22
22
|
qaBrowser: parseQaBrowser(packet.qaBrowser),
|
|
23
|
+
qaScope: parseQaScope(packet.qaScope ?? packet.qa_scope),
|
|
23
24
|
qaRework: parseQaRework(packet.qaRework),
|
|
24
25
|
architectRevision: parseArchitectRevision(packet.architectRevision),
|
|
25
26
|
storyDevelop: parseStoryDevelop(packet.storyDevelop),
|
|
26
|
-
storyQa: parseStoryQa(packet.storyQa)
|
|
27
|
+
storyQa: parseStoryQa(packet.storyQa),
|
|
28
|
+
draft: parseDraftPrompt(packet.draft)
|
|
27
29
|
};
|
|
28
30
|
}
|
|
29
31
|
export function failureResult(summary, events = []) {
|
|
@@ -109,6 +111,17 @@ function parseExecution(value) {
|
|
|
109
111
|
runnerRepoLocalPath: optionalString(execution, "runnerRepoLocalPath")
|
|
110
112
|
};
|
|
111
113
|
}
|
|
114
|
+
function parseDraftPrompt(value) {
|
|
115
|
+
if (value === undefined || value === null) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const draft = asObject(value, "draft");
|
|
119
|
+
return {
|
|
120
|
+
kind: requiredString(draft, "kind"),
|
|
121
|
+
systemPrompt: requiredString(draft, "systemPrompt"),
|
|
122
|
+
userPrompt: requiredString(draft, "userPrompt")
|
|
123
|
+
};
|
|
124
|
+
}
|
|
112
125
|
function parseApiKeySecretGrant(value) {
|
|
113
126
|
if (value === undefined || value === null) {
|
|
114
127
|
return null;
|
|
@@ -185,6 +198,27 @@ function parseQaBrowser(value) {
|
|
|
185
198
|
missingTargetReason: optionalString(qaBrowser, "missingTargetReason") ?? optionalString(qaBrowser, "missing_target_reason")
|
|
186
199
|
};
|
|
187
200
|
}
|
|
201
|
+
function parseQaScope(value) {
|
|
202
|
+
if (value === undefined || value === null) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
const qaScope = asObject(value, "qaScope");
|
|
206
|
+
return {
|
|
207
|
+
schema_version: requiredNumber(qaScope, "schema_version"),
|
|
208
|
+
surface: parseOptionalStringArray(qaScope.surface, "qaScope.surface"),
|
|
209
|
+
required_evidence: parseOptionalStringArray(qaScope.required_evidence, "qaScope.required_evidence"),
|
|
210
|
+
not_applicable: parseOptionalStringArray(qaScope.not_applicable, "qaScope.not_applicable"),
|
|
211
|
+
browser_required: optionalBoolean(qaScope, "browser_required") ?? optionalBoolean(qaScope, "browserRequired") ?? false,
|
|
212
|
+
api_required: optionalBoolean(qaScope, "api_required") ?? optionalBoolean(qaScope, "apiRequired") ?? false,
|
|
213
|
+
source: optionalString(qaScope, "source"),
|
|
214
|
+
authority: optionalString(qaScope, "authority"),
|
|
215
|
+
confidence: optionalString(qaScope, "confidence"),
|
|
216
|
+
confirmed_by_user: optionalBoolean(qaScope, "confirmed_by_user") ?? optionalBoolean(qaScope, "confirmedByUser"),
|
|
217
|
+
locked: optionalBoolean(qaScope, "locked"),
|
|
218
|
+
derived_from: parseOptionalObject(qaScope.derived_from ?? qaScope.derivedFrom, "qaScope.derived_from"),
|
|
219
|
+
rationale: optionalString(qaScope, "rationale")
|
|
220
|
+
};
|
|
221
|
+
}
|
|
188
222
|
function parseQaAppLaunchTarget(value) {
|
|
189
223
|
if (value === undefined || value === null) {
|
|
190
224
|
return null;
|
|
@@ -364,6 +398,18 @@ function parseStringArray(value, label) {
|
|
|
364
398
|
return entry.trim();
|
|
365
399
|
});
|
|
366
400
|
}
|
|
401
|
+
function parseOptionalStringArray(value, label) {
|
|
402
|
+
if (value === undefined || value === null) {
|
|
403
|
+
return [];
|
|
404
|
+
}
|
|
405
|
+
return parseStringArray(value, label);
|
|
406
|
+
}
|
|
407
|
+
function parseOptionalObject(value, label) {
|
|
408
|
+
if (value === undefined || value === null) {
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
return asObject(value, label);
|
|
412
|
+
}
|
|
367
413
|
function parseArtifacts(value) {
|
|
368
414
|
if (value === undefined || value === null) {
|
|
369
415
|
return [];
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const forgeBridgeVersion = "0.1.
|
|
1
|
+
export declare const forgeBridgeVersion = "0.1.3-dev.2c3feac3af65";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const forgeBridgeVersion = "0.1.
|
|
1
|
+
export const forgeBridgeVersion = "0.1.3-dev.2c3feac3af65";
|
|
@@ -31,6 +31,34 @@ const ARCHITECT_OPERATIONAL_HOOKS = new Set([
|
|
|
31
31
|
"architect-complete",
|
|
32
32
|
"architect-revise"
|
|
33
33
|
]);
|
|
34
|
+
const DRAFT_OPERATIONAL_SKILLS = new Set([
|
|
35
|
+
"draft-intent",
|
|
36
|
+
"draft-repo-context",
|
|
37
|
+
"draft-graph-context",
|
|
38
|
+
"draft-product-scope",
|
|
39
|
+
"draft-critic",
|
|
40
|
+
"draft-proposal"
|
|
41
|
+
]);
|
|
42
|
+
const DRAFT_WORK_ITEM_OPERATIONAL_SKILLS = new Set([
|
|
43
|
+
"draft-work-item-plan"
|
|
44
|
+
]);
|
|
45
|
+
const DRAFT_WORK_ORDER_OPERATIONAL_SKILLS = new Set([
|
|
46
|
+
"draft-work-order-plan"
|
|
47
|
+
]);
|
|
48
|
+
const DRAFT_OPERATIONAL_HOOKS = new Set([
|
|
49
|
+
"draft-intent",
|
|
50
|
+
"draft-repo-context",
|
|
51
|
+
"draft-graph-context",
|
|
52
|
+
"draft-product-scope",
|
|
53
|
+
"draft-critic",
|
|
54
|
+
"draft-proposal"
|
|
55
|
+
]);
|
|
56
|
+
const DRAFT_WORK_ITEM_OPERATIONAL_HOOKS = new Set([
|
|
57
|
+
"draft-work-item-plan"
|
|
58
|
+
]);
|
|
59
|
+
const DRAFT_WORK_ORDER_OPERATIONAL_HOOKS = new Set([
|
|
60
|
+
"draft-work-order-plan"
|
|
61
|
+
]);
|
|
34
62
|
export async function injectWorktreeArtifacts(worktreePath, artifacts) {
|
|
35
63
|
for (const artifact of artifacts) {
|
|
36
64
|
await writeArtifact(worktreePath, artifact);
|
|
@@ -45,6 +73,9 @@ export function filterArtifactsForLane(artifacts, lane, phase) {
|
|
|
45
73
|
if (normalizedLane === "architect") {
|
|
46
74
|
return artifacts.filter((artifact) => shouldInjectArchitectArtifact(artifact, commandSlug));
|
|
47
75
|
}
|
|
76
|
+
if (normalizedLane === "draft") {
|
|
77
|
+
return artifacts.filter((artifact) => shouldInjectDraftArtifact(artifact, commandSlug, phase));
|
|
78
|
+
}
|
|
48
79
|
return artifacts;
|
|
49
80
|
}
|
|
50
81
|
export async function writeArtifact(worktreePath, artifact) {
|
|
@@ -79,6 +110,8 @@ export function resolveCommandSlug(lane, phase) {
|
|
|
79
110
|
return "rework-prompt";
|
|
80
111
|
}
|
|
81
112
|
switch (normalizedLane) {
|
|
113
|
+
case "draft":
|
|
114
|
+
return "draft-prompt";
|
|
82
115
|
case "architect":
|
|
83
116
|
return "architect-prompt";
|
|
84
117
|
case "qa":
|
|
@@ -119,6 +152,45 @@ function shouldInjectArchitectArtifact(artifact, commandSlug) {
|
|
|
119
152
|
return false;
|
|
120
153
|
}
|
|
121
154
|
}
|
|
155
|
+
function shouldInjectDraftArtifact(artifact, commandSlug, phase) {
|
|
156
|
+
if (artifact.scope.trim().toLowerCase() !== "operational") {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
const slug = artifact.slug.trim().toLowerCase();
|
|
160
|
+
switch (normalizedArtifactKind(artifact.kind)) {
|
|
161
|
+
case "command":
|
|
162
|
+
return slug === commandSlug;
|
|
163
|
+
case "skill":
|
|
164
|
+
return DRAFT_OPERATIONAL_SKILLS.has(slug) || draftPhaseSkills(phase).has(slug);
|
|
165
|
+
case "hook":
|
|
166
|
+
return DRAFT_OPERATIONAL_HOOKS.has(slug) || draftPhaseHooks(phase).has(slug);
|
|
167
|
+
default:
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function draftPhaseSkills(phase) {
|
|
172
|
+
switch (normalizeDraftPhase(phase)) {
|
|
173
|
+
case "work_order":
|
|
174
|
+
return DRAFT_WORK_ORDER_OPERATIONAL_SKILLS;
|
|
175
|
+
case "story":
|
|
176
|
+
return DRAFT_WORK_ITEM_OPERATIONAL_SKILLS;
|
|
177
|
+
default:
|
|
178
|
+
return DRAFT_WORK_ITEM_OPERATIONAL_SKILLS;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function draftPhaseHooks(phase) {
|
|
182
|
+
switch (normalizeDraftPhase(phase)) {
|
|
183
|
+
case "work_order":
|
|
184
|
+
return DRAFT_WORK_ORDER_OPERATIONAL_HOOKS;
|
|
185
|
+
case "story":
|
|
186
|
+
return DRAFT_WORK_ITEM_OPERATIONAL_HOOKS;
|
|
187
|
+
default:
|
|
188
|
+
return DRAFT_WORK_ITEM_OPERATIONAL_HOOKS;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function normalizeDraftPhase(phase) {
|
|
192
|
+
return phase.trim().toLowerCase().replaceAll("-", "_");
|
|
193
|
+
}
|
|
122
194
|
export function buildSpecBody(laneCommand, workItem, phase = "implementation") {
|
|
123
195
|
const prompt = laneCommand.body.replaceAll("{workItemId}", workItem.id);
|
|
124
196
|
return `---
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loucompanion/forge-bridge",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3-dev.2c3feac3af65",
|
|
4
4
|
"description": "Forge runner bridge CLI for connecting local developer machines to Forge dispatch.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|