@loucompanion/forge-bridge 0.1.1-dev.2ac58b6f94ce → 0.1.1-dev.2d1a116ca78d
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.d.ts +1 -0
- package/dist/bridge/bridge.service.js +146 -17
- package/dist/bridge/bridge.types.d.ts +4 -2
- package/dist/cli/cli.service.js +225 -10
- 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 +19 -5
- package/dist/session/phase-completion.js +1092 -26
- package/dist/session/provider-cli/provider-cli.base.d.ts +3 -1
- package/dist/session/provider-cli/provider-cli.service.d.ts +16 -1
- package/dist/session/provider-cli/provider-cli.service.js +1108 -216
- package/dist/session/provider-cli/provider-cli.types.d.ts +5 -0
- package/dist/session/session.service.js +43 -4
- package/dist/session/session.types.d.ts +77 -0
- package/dist/session/session.types.js +143 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worktree/artifact-injector.d.ts +1 -0
- package/dist/worktree/artifact-injector.js +117 -1
- package/dist/worktree/worktree.service.d.ts +1 -0
- package/dist/worktree/worktree.service.js +132 -6
- package/dist/worktree/worktree.types.d.ts +8 -1
- package/package.json +8 -3
|
@@ -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,15 +27,39 @@ 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,
|
|
33
50
|
baseBranch: dispatch.cycleRun.baseBranch || dispatch.cycleRun.requestedBase,
|
|
51
|
+
worktreeKey: dispatch.lease.worktreeKey,
|
|
52
|
+
ownerType: dispatch.cycleRun.ownerType ?? dispatch.lease.ownerType ?? null,
|
|
53
|
+
ownerId: dispatch.cycleRun.ownerId ?? dispatch.lease.ownerId ?? null,
|
|
54
|
+
lane: dispatch.execution.lane,
|
|
55
|
+
phase: dispatch.cycleRun.phase,
|
|
34
56
|
allowedRepoRoot: dispatch.serverLocal.repoRoot,
|
|
35
57
|
cleanupWorktrees: dispatch.serverLocal.cleanupWorktrees,
|
|
36
58
|
artifacts: dispatch.artifacts,
|
|
59
|
+
qaRework: dispatch.qaRework,
|
|
37
60
|
warmPathOverrides: dispatch.warmPathOverrides
|
|
38
61
|
});
|
|
62
|
+
const providerDispatch = withPreparedQaReworkPacket(dispatch, worktree.qaReworkPacketPath);
|
|
39
63
|
throwIfAborted(signal);
|
|
40
64
|
await emit({
|
|
41
65
|
role: "runner",
|
|
@@ -51,18 +75,21 @@ export class BridgeSessionService {
|
|
|
51
75
|
const lane = dispatch.execution.lane.toLowerCase();
|
|
52
76
|
const role = dispatch.execution.role.toLowerCase();
|
|
53
77
|
const isQa = role === "qa" || lane === "qa";
|
|
78
|
+
const isArchitect = role === "architect" || lane === "architect" || dispatch.cycleRun.kind.toLowerCase() === "architect";
|
|
54
79
|
const isConflictResolve = role === "conflict_resolve" || lane === "conflict_resolve";
|
|
55
80
|
const context = {
|
|
56
|
-
dispatch,
|
|
81
|
+
dispatch: providerDispatch,
|
|
57
82
|
worktree,
|
|
58
83
|
emit,
|
|
59
84
|
signal
|
|
60
85
|
};
|
|
61
86
|
const run = isQa
|
|
62
87
|
? await this.providerCli.runQa(context)
|
|
63
|
-
:
|
|
64
|
-
? await this.providerCli.
|
|
65
|
-
:
|
|
88
|
+
: isArchitect
|
|
89
|
+
? await this.providerCli.runArchitect(context)
|
|
90
|
+
: isConflictResolve
|
|
91
|
+
? await this.providerCli.runConflictResolve(context)
|
|
92
|
+
: await this.providerCli.runImplementation(context);
|
|
66
93
|
throwIfAborted(signal);
|
|
67
94
|
const phaseArtifacts = run.artifacts?.length ? run.artifacts : run.artifact ? [run.artifact] : [];
|
|
68
95
|
return redactSecrets({
|
|
@@ -107,6 +134,18 @@ function validateApiKeySecretGrant(dispatch) {
|
|
|
107
134
|
function exactSecrets(dispatch) {
|
|
108
135
|
return dispatch.apiKeySecretGrant?.secret ? [dispatch.apiKeySecretGrant.secret] : [];
|
|
109
136
|
}
|
|
137
|
+
function withPreparedQaReworkPacket(dispatch, packetPath) {
|
|
138
|
+
if (!dispatch.qaRework || !packetPath) {
|
|
139
|
+
return dispatch;
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
...dispatch,
|
|
143
|
+
qaRework: {
|
|
144
|
+
...dispatch.qaRework,
|
|
145
|
+
packetPath
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
110
149
|
async function emitDependencyWarmEvents(warm, emit) {
|
|
111
150
|
if (warm.linked.length > 0) {
|
|
112
151
|
await emit({
|
|
@@ -14,6 +14,17 @@ export interface RunOnceDispatchPacket {
|
|
|
14
14
|
readonly warmPathOverrides?: readonly string[];
|
|
15
15
|
readonly qaBrowser?: DispatchQaBrowserContext | null;
|
|
16
16
|
readonly qaRework?: DispatchQaReworkContext | null;
|
|
17
|
+
readonly architectRevision?: DispatchArchitectRevisionContext | null;
|
|
18
|
+
readonly storyDevelop?: DispatchStoryDevelopContext | null;
|
|
19
|
+
readonly storyQa?: DispatchStoryQaContext | null;
|
|
20
|
+
readonly draft?: DispatchDraftPrompt | null;
|
|
21
|
+
}
|
|
22
|
+
export interface DispatchDraftPrompt {
|
|
23
|
+
readonly kind: string;
|
|
24
|
+
readonly systemPrompt: string;
|
|
25
|
+
readonly userPrompt: string;
|
|
26
|
+
readonly projectName?: string | null;
|
|
27
|
+
readonly repoName?: string | null;
|
|
17
28
|
}
|
|
18
29
|
export interface DispatchQaBrowserContext {
|
|
19
30
|
readonly required: boolean;
|
|
@@ -37,6 +48,67 @@ export interface DispatchQaReworkContext {
|
|
|
37
48
|
readonly findingIds: readonly string[];
|
|
38
49
|
readonly findings: readonly Record<string, unknown>[];
|
|
39
50
|
}
|
|
51
|
+
export interface DispatchArchitectRevisionContext {
|
|
52
|
+
readonly priorPlanId: string;
|
|
53
|
+
readonly priorContentHash: string;
|
|
54
|
+
readonly feedback: string;
|
|
55
|
+
}
|
|
56
|
+
export interface DispatchStoryDevelopContext {
|
|
57
|
+
readonly storyWorkItemId: string;
|
|
58
|
+
readonly planId: string;
|
|
59
|
+
readonly planContentHash: string;
|
|
60
|
+
readonly architectTaskId: string;
|
|
61
|
+
readonly phaseId: string;
|
|
62
|
+
readonly dependencyWorkItemIds: readonly string[];
|
|
63
|
+
}
|
|
64
|
+
export interface DispatchStoryQaContext {
|
|
65
|
+
readonly storyWorkItemId: string;
|
|
66
|
+
readonly planId: string;
|
|
67
|
+
readonly planContentHash: string;
|
|
68
|
+
readonly worktreeKey: string;
|
|
69
|
+
readonly branch: string;
|
|
70
|
+
readonly implementationPlanJson?: string | null;
|
|
71
|
+
readonly qaPlanJson?: string | null;
|
|
72
|
+
readonly finalQaChecks: readonly string[];
|
|
73
|
+
readonly childTasks: readonly DispatchStoryQaChildTaskContext[];
|
|
74
|
+
readonly dependencies: readonly DispatchStoryQaDependencyContext[];
|
|
75
|
+
}
|
|
76
|
+
export interface DispatchStoryQaChildTaskContext {
|
|
77
|
+
readonly workItemId: string;
|
|
78
|
+
readonly title: string;
|
|
79
|
+
readonly architectTaskId: string;
|
|
80
|
+
readonly phaseId: string;
|
|
81
|
+
readonly phaseOrder: number;
|
|
82
|
+
readonly dependencyWorkItemIds: readonly string[];
|
|
83
|
+
readonly spec?: DispatchStoryQaChildSpecContext | null;
|
|
84
|
+
readonly implementationArtifact?: DispatchStoryQaArtifactContext | null;
|
|
85
|
+
readonly selfReviewArtifact?: DispatchStoryQaArtifactContext | null;
|
|
86
|
+
readonly validationArtifact?: DispatchStoryQaArtifactContext | null;
|
|
87
|
+
readonly qaArtifact?: DispatchStoryQaArtifactContext | null;
|
|
88
|
+
}
|
|
89
|
+
export interface DispatchStoryQaChildSpecContext {
|
|
90
|
+
readonly version: number;
|
|
91
|
+
readonly status: string;
|
|
92
|
+
readonly path: string;
|
|
93
|
+
readonly contentHash: string;
|
|
94
|
+
readonly body: string;
|
|
95
|
+
}
|
|
96
|
+
export interface DispatchStoryQaArtifactContext {
|
|
97
|
+
readonly artifactId: string;
|
|
98
|
+
readonly cycleRunId: string;
|
|
99
|
+
readonly phase: string;
|
|
100
|
+
readonly status: string;
|
|
101
|
+
readonly producedAt: string;
|
|
102
|
+
readonly path?: string | null;
|
|
103
|
+
readonly verdictJson?: string | null;
|
|
104
|
+
readonly evidenceJson?: string | null;
|
|
105
|
+
readonly findingsJson?: string | null;
|
|
106
|
+
readonly report?: string | null;
|
|
107
|
+
}
|
|
108
|
+
export interface DispatchStoryQaDependencyContext {
|
|
109
|
+
readonly fromWorkItemId: string;
|
|
110
|
+
readonly toWorkItemId: string;
|
|
111
|
+
}
|
|
40
112
|
export interface DispatchPullRequestContext {
|
|
41
113
|
readonly number: number;
|
|
42
114
|
readonly provider: string;
|
|
@@ -151,6 +223,11 @@ export interface RunnerDispatchArtifact {
|
|
|
151
223
|
readonly repoPath: string;
|
|
152
224
|
readonly path: string;
|
|
153
225
|
readonly verdictJson: string;
|
|
226
|
+
readonly planJson?: string | null;
|
|
227
|
+
readonly evidenceJson?: string | null;
|
|
228
|
+
readonly findingsJson?: string | null;
|
|
229
|
+
readonly implementationPlanJson?: string | null;
|
|
230
|
+
readonly qaPlanJson?: string | null;
|
|
154
231
|
readonly report: string;
|
|
155
232
|
readonly producedBy: string;
|
|
156
233
|
readonly status: string;
|
|
@@ -20,7 +20,11 @@ export function parseRunOnceDispatchPacket(value) {
|
|
|
20
20
|
artifacts: parseArtifacts(packet.artifacts),
|
|
21
21
|
warmPathOverrides: parseWarmPathOverrides(packet.warmPathOverrides),
|
|
22
22
|
qaBrowser: parseQaBrowser(packet.qaBrowser),
|
|
23
|
-
qaRework: parseQaRework(packet.qaRework)
|
|
23
|
+
qaRework: parseQaRework(packet.qaRework),
|
|
24
|
+
architectRevision: parseArchitectRevision(packet.architectRevision),
|
|
25
|
+
storyDevelop: parseStoryDevelop(packet.storyDevelop),
|
|
26
|
+
storyQa: parseStoryQa(packet.storyQa),
|
|
27
|
+
draft: parseDraftPrompt(packet.draft)
|
|
24
28
|
};
|
|
25
29
|
}
|
|
26
30
|
export function failureResult(summary, events = []) {
|
|
@@ -106,6 +110,17 @@ function parseExecution(value) {
|
|
|
106
110
|
runnerRepoLocalPath: optionalString(execution, "runnerRepoLocalPath")
|
|
107
111
|
};
|
|
108
112
|
}
|
|
113
|
+
function parseDraftPrompt(value) {
|
|
114
|
+
if (value === undefined || value === null) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
const draft = asObject(value, "draft");
|
|
118
|
+
return {
|
|
119
|
+
kind: requiredString(draft, "kind"),
|
|
120
|
+
systemPrompt: requiredString(draft, "systemPrompt"),
|
|
121
|
+
userPrompt: requiredString(draft, "userPrompt")
|
|
122
|
+
};
|
|
123
|
+
}
|
|
109
124
|
function parseApiKeySecretGrant(value) {
|
|
110
125
|
if (value === undefined || value === null) {
|
|
111
126
|
return null;
|
|
@@ -234,6 +249,133 @@ function parseQaRework(value) {
|
|
|
234
249
|
findings: findingsValue.map((entry, index) => asObject(entry, `qaRework.findings[${index}]`))
|
|
235
250
|
};
|
|
236
251
|
}
|
|
252
|
+
function parseArchitectRevision(value) {
|
|
253
|
+
if (value === undefined || value === null) {
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
const revision = asObject(value, "architectRevision");
|
|
257
|
+
return {
|
|
258
|
+
priorPlanId: requiredString(revision, "priorPlanId"),
|
|
259
|
+
priorContentHash: requiredString(revision, "priorContentHash"),
|
|
260
|
+
feedback: requiredString(revision, "feedback")
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
function parseStoryDevelop(value) {
|
|
264
|
+
if (value === undefined || value === null) {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
const storyDevelop = asObject(value, "storyDevelop");
|
|
268
|
+
const dependencyWorkItemIds = storyDevelop.dependencyWorkItemIds;
|
|
269
|
+
if (!Array.isArray(dependencyWorkItemIds)) {
|
|
270
|
+
throw new Error("storyDevelop.dependencyWorkItemIds must be an array.");
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
storyWorkItemId: requiredString(storyDevelop, "storyWorkItemId"),
|
|
274
|
+
planId: requiredString(storyDevelop, "planId"),
|
|
275
|
+
planContentHash: requiredString(storyDevelop, "planContentHash"),
|
|
276
|
+
architectTaskId: requiredString(storyDevelop, "architectTaskId"),
|
|
277
|
+
phaseId: requiredString(storyDevelop, "phaseId"),
|
|
278
|
+
dependencyWorkItemIds: dependencyWorkItemIds.map((entry, index) => {
|
|
279
|
+
if (typeof entry !== "string" || entry.trim().length === 0) {
|
|
280
|
+
throw new Error(`storyDevelop.dependencyWorkItemIds[${index}] must be a non-empty string.`);
|
|
281
|
+
}
|
|
282
|
+
return entry.trim();
|
|
283
|
+
})
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
function parseStoryQa(value) {
|
|
287
|
+
if (value === undefined || value === null) {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
const storyQa = asObject(value, "storyQa");
|
|
291
|
+
return {
|
|
292
|
+
storyWorkItemId: requiredString(storyQa, "storyWorkItemId"),
|
|
293
|
+
planId: requiredString(storyQa, "planId"),
|
|
294
|
+
planContentHash: requiredString(storyQa, "planContentHash"),
|
|
295
|
+
worktreeKey: requiredString(storyQa, "worktreeKey"),
|
|
296
|
+
branch: requiredString(storyQa, "branch"),
|
|
297
|
+
implementationPlanJson: optionalString(storyQa, "implementationPlanJson"),
|
|
298
|
+
qaPlanJson: optionalString(storyQa, "qaPlanJson"),
|
|
299
|
+
finalQaChecks: parseStringArray(storyQa.finalQaChecks, "storyQa.finalQaChecks"),
|
|
300
|
+
childTasks: parseStoryQaChildTasks(storyQa.childTasks),
|
|
301
|
+
dependencies: parseStoryQaDependencies(storyQa.dependencies)
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
function parseStoryQaChildTasks(value) {
|
|
305
|
+
if (!Array.isArray(value)) {
|
|
306
|
+
throw new Error("storyQa.childTasks must be an array.");
|
|
307
|
+
}
|
|
308
|
+
return value.map((entry, index) => {
|
|
309
|
+
const child = asObject(entry, `storyQa.childTasks[${index}]`);
|
|
310
|
+
return {
|
|
311
|
+
workItemId: requiredString(child, "workItemId"),
|
|
312
|
+
title: requiredString(child, "title"),
|
|
313
|
+
architectTaskId: requiredString(child, "architectTaskId"),
|
|
314
|
+
phaseId: requiredString(child, "phaseId"),
|
|
315
|
+
phaseOrder: requiredNumber(child, "phaseOrder"),
|
|
316
|
+
dependencyWorkItemIds: parseStringArray(child.dependencyWorkItemIds, `storyQa.childTasks[${index}].dependencyWorkItemIds`),
|
|
317
|
+
spec: parseStoryQaSpec(child.spec, `storyQa.childTasks[${index}].spec`),
|
|
318
|
+
implementationArtifact: parseStoryQaArtifact(child.implementationArtifact, `storyQa.childTasks[${index}].implementationArtifact`),
|
|
319
|
+
selfReviewArtifact: parseStoryQaArtifact(child.selfReviewArtifact, `storyQa.childTasks[${index}].selfReviewArtifact`),
|
|
320
|
+
validationArtifact: parseStoryQaArtifact(child.validationArtifact, `storyQa.childTasks[${index}].validationArtifact`),
|
|
321
|
+
qaArtifact: parseStoryQaArtifact(child.qaArtifact, `storyQa.childTasks[${index}].qaArtifact`)
|
|
322
|
+
};
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
function parseStoryQaSpec(value, label) {
|
|
326
|
+
if (value === undefined || value === null) {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
const spec = asObject(value, label);
|
|
330
|
+
return {
|
|
331
|
+
version: requiredNumber(spec, "version"),
|
|
332
|
+
status: requiredString(spec, "status"),
|
|
333
|
+
path: requiredString(spec, "path"),
|
|
334
|
+
contentHash: requiredString(spec, "contentHash"),
|
|
335
|
+
body: requiredString(spec, "body")
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
function parseStoryQaArtifact(value, label) {
|
|
339
|
+
if (value === undefined || value === null) {
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
const artifact = asObject(value, label);
|
|
343
|
+
return {
|
|
344
|
+
artifactId: requiredString(artifact, "artifactId"),
|
|
345
|
+
cycleRunId: requiredString(artifact, "cycleRunId"),
|
|
346
|
+
phase: requiredString(artifact, "phase"),
|
|
347
|
+
status: requiredString(artifact, "status"),
|
|
348
|
+
producedAt: requiredString(artifact, "producedAt"),
|
|
349
|
+
path: optionalString(artifact, "path"),
|
|
350
|
+
verdictJson: optionalString(artifact, "verdictJson"),
|
|
351
|
+
evidenceJson: optionalString(artifact, "evidenceJson"),
|
|
352
|
+
findingsJson: optionalString(artifact, "findingsJson"),
|
|
353
|
+
report: optionalString(artifact, "report")
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
function parseStoryQaDependencies(value) {
|
|
357
|
+
if (!Array.isArray(value)) {
|
|
358
|
+
throw new Error("storyQa.dependencies must be an array.");
|
|
359
|
+
}
|
|
360
|
+
return value.map((entry, index) => {
|
|
361
|
+
const dependency = asObject(entry, `storyQa.dependencies[${index}]`);
|
|
362
|
+
return {
|
|
363
|
+
fromWorkItemId: requiredString(dependency, "fromWorkItemId").trim(),
|
|
364
|
+
toWorkItemId: requiredString(dependency, "toWorkItemId").trim()
|
|
365
|
+
};
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
function parseStringArray(value, label) {
|
|
369
|
+
if (!Array.isArray(value)) {
|
|
370
|
+
throw new Error(`${label} must be an array.`);
|
|
371
|
+
}
|
|
372
|
+
return value.map((entry, index) => {
|
|
373
|
+
if (typeof entry !== "string" || entry.trim().length === 0) {
|
|
374
|
+
throw new Error(`${label}[${index}] must be a non-empty string.`);
|
|
375
|
+
}
|
|
376
|
+
return entry.trim();
|
|
377
|
+
});
|
|
378
|
+
}
|
|
237
379
|
function parseArtifacts(value) {
|
|
238
380
|
if (value === undefined || value === null) {
|
|
239
381
|
return [];
|
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.2d1a116ca78d";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const forgeBridgeVersion = "0.1.1-dev.
|
|
1
|
+
export const forgeBridgeVersion = "0.1.1-dev.2d1a116ca78d";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { DispatchResolvedArtifact } from "../session/session.types.js";
|
|
2
2
|
export declare function injectWorktreeArtifacts(worktreePath: string, artifacts: readonly DispatchResolvedArtifact[]): Promise<void>;
|
|
3
|
+
export declare function filterArtifactsForLane(artifacts: readonly DispatchResolvedArtifact[], lane: string, phase: string): readonly DispatchResolvedArtifact[];
|
|
3
4
|
export declare function writeArtifact(worktreePath: string, artifact: DispatchResolvedArtifact): Promise<void>;
|
|
4
5
|
export declare function selectLaneCommand(artifacts: readonly DispatchResolvedArtifact[], lane: string, phase: string): DispatchResolvedArtifact | undefined;
|
|
5
6
|
export declare function resolveCommandSlug(lane: string, phase: string): string;
|
|
@@ -1,11 +1,73 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
const DEBUG_OPERATIONAL_SKILLS = new Set(["debug-repro", "debug-root-cause", "debug-fix", "debug-regression"]);
|
|
5
|
+
const DEBUG_OPERATIONAL_HOOKS = new Set(["debug-gate", "debug-tool-hook", "debug-rework"]);
|
|
6
|
+
const ARCHITECT_OPERATIONAL_SKILLS = new Set([
|
|
7
|
+
"architect-repo-map",
|
|
8
|
+
"architect-acceptance-map",
|
|
9
|
+
"architect-contract-designer",
|
|
10
|
+
"architect-dag-planner",
|
|
11
|
+
"architect-test-plan-designer",
|
|
12
|
+
"architect-risk-review",
|
|
13
|
+
"architect-integration-strategy",
|
|
14
|
+
"architect-handoff-writer",
|
|
15
|
+
"architect-mcp-contract",
|
|
16
|
+
"architect-semantic-validator"
|
|
17
|
+
]);
|
|
18
|
+
const ARCHITECT_OPERATIONAL_HOOKS = new Set([
|
|
19
|
+
"architect-preflight",
|
|
20
|
+
"architect-context",
|
|
21
|
+
"architect-repo-map",
|
|
22
|
+
"architect-acceptance-map",
|
|
23
|
+
"architect-contracts",
|
|
24
|
+
"architect-plan",
|
|
25
|
+
"architect-qa-seed",
|
|
26
|
+
"architect-integration",
|
|
27
|
+
"architect-mcp-contract",
|
|
28
|
+
"architect-validate-plan",
|
|
29
|
+
"architect-risk-gate",
|
|
30
|
+
"architect-mcp-complete",
|
|
31
|
+
"architect-complete",
|
|
32
|
+
"architect-revise"
|
|
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-work-order-plan",
|
|
40
|
+
"draft-critic",
|
|
41
|
+
"draft-proposal"
|
|
42
|
+
]);
|
|
43
|
+
const DRAFT_OPERATIONAL_HOOKS = new Set([
|
|
44
|
+
"draft-intent",
|
|
45
|
+
"draft-repo-context",
|
|
46
|
+
"draft-graph-context",
|
|
47
|
+
"draft-product-scope",
|
|
48
|
+
"draft-work-order-plan",
|
|
49
|
+
"draft-critic",
|
|
50
|
+
"draft-proposal"
|
|
51
|
+
]);
|
|
4
52
|
export async function injectWorktreeArtifacts(worktreePath, artifacts) {
|
|
5
53
|
for (const artifact of artifacts) {
|
|
6
54
|
await writeArtifact(worktreePath, artifact);
|
|
7
55
|
}
|
|
8
56
|
}
|
|
57
|
+
export function filterArtifactsForLane(artifacts, lane, phase) {
|
|
58
|
+
const normalizedLane = lane.trim().toLowerCase();
|
|
59
|
+
const commandSlug = resolveCommandSlug(lane, phase).toLowerCase();
|
|
60
|
+
if (normalizedLane === "debug") {
|
|
61
|
+
return artifacts.filter((artifact) => shouldInjectDebugArtifact(artifact, commandSlug));
|
|
62
|
+
}
|
|
63
|
+
if (normalizedLane === "architect") {
|
|
64
|
+
return artifacts.filter((artifact) => shouldInjectArchitectArtifact(artifact, commandSlug));
|
|
65
|
+
}
|
|
66
|
+
if (normalizedLane === "draft") {
|
|
67
|
+
return artifacts.filter((artifact) => shouldInjectDraftArtifact(artifact, commandSlug));
|
|
68
|
+
}
|
|
69
|
+
return artifacts;
|
|
70
|
+
}
|
|
9
71
|
export async function writeArtifact(worktreePath, artifact) {
|
|
10
72
|
const writtenHash = sha256(artifact.body);
|
|
11
73
|
if (writtenHash !== artifact.contentHash.toLowerCase()) {
|
|
@@ -29,11 +91,17 @@ export function selectLaneCommand(artifacts, lane, phase) {
|
|
|
29
91
|
return artifacts.find((artifact) => normalizedArtifactKind(artifact.kind) === "command" && artifact.slug.trim().toLowerCase() === slug);
|
|
30
92
|
}
|
|
31
93
|
export function resolveCommandSlug(lane, phase) {
|
|
94
|
+
const normalizedLane = lane.trim().toLowerCase();
|
|
95
|
+
if (normalizedLane === "debug") {
|
|
96
|
+
return "debug-prompt";
|
|
97
|
+
}
|
|
32
98
|
const normalizedPhase = phase.trim().toLowerCase().replace(/-/g, "_");
|
|
33
99
|
if (normalizedPhase === "rework") {
|
|
34
100
|
return "rework-prompt";
|
|
35
101
|
}
|
|
36
|
-
switch (
|
|
102
|
+
switch (normalizedLane) {
|
|
103
|
+
case "draft":
|
|
104
|
+
return "draft-prompt";
|
|
37
105
|
case "architect":
|
|
38
106
|
return "architect-prompt";
|
|
39
107
|
case "qa":
|
|
@@ -42,6 +110,54 @@ export function resolveCommandSlug(lane, phase) {
|
|
|
42
110
|
return "dev-prompt";
|
|
43
111
|
}
|
|
44
112
|
}
|
|
113
|
+
function shouldInjectDebugArtifact(artifact, commandSlug) {
|
|
114
|
+
if (artifact.scope.trim().toLowerCase() !== "operational") {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
const slug = artifact.slug.trim().toLowerCase();
|
|
118
|
+
switch (normalizedArtifactKind(artifact.kind)) {
|
|
119
|
+
case "command":
|
|
120
|
+
return slug === commandSlug;
|
|
121
|
+
case "skill":
|
|
122
|
+
return DEBUG_OPERATIONAL_SKILLS.has(slug);
|
|
123
|
+
case "hook":
|
|
124
|
+
return DEBUG_OPERATIONAL_HOOKS.has(slug);
|
|
125
|
+
default:
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function shouldInjectArchitectArtifact(artifact, commandSlug) {
|
|
130
|
+
if (artifact.scope.trim().toLowerCase() !== "operational") {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
const slug = artifact.slug.trim().toLowerCase();
|
|
134
|
+
switch (normalizedArtifactKind(artifact.kind)) {
|
|
135
|
+
case "command":
|
|
136
|
+
return slug === commandSlug;
|
|
137
|
+
case "skill":
|
|
138
|
+
return ARCHITECT_OPERATIONAL_SKILLS.has(slug);
|
|
139
|
+
case "hook":
|
|
140
|
+
return ARCHITECT_OPERATIONAL_HOOKS.has(slug);
|
|
141
|
+
default:
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function shouldInjectDraftArtifact(artifact, commandSlug) {
|
|
146
|
+
if (artifact.scope.trim().toLowerCase() !== "operational") {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
const slug = artifact.slug.trim().toLowerCase();
|
|
150
|
+
switch (normalizedArtifactKind(artifact.kind)) {
|
|
151
|
+
case "command":
|
|
152
|
+
return slug === commandSlug;
|
|
153
|
+
case "skill":
|
|
154
|
+
return DRAFT_OPERATIONAL_SKILLS.has(slug);
|
|
155
|
+
case "hook":
|
|
156
|
+
return DRAFT_OPERATIONAL_HOOKS.has(slug);
|
|
157
|
+
default:
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
45
161
|
export function buildSpecBody(laneCommand, workItem, phase = "implementation") {
|
|
46
162
|
const prompt = laneCommand.body.replaceAll("{workItemId}", workItem.id);
|
|
47
163
|
return `---
|
|
@@ -11,6 +11,7 @@ export declare class GitWorktreeService implements WorktreeService {
|
|
|
11
11
|
private resolveMatchingRepo;
|
|
12
12
|
private ensureBaseBranch;
|
|
13
13
|
private gitRefExists;
|
|
14
|
+
private currentBranch;
|
|
14
15
|
private ensureGitIdentity;
|
|
15
16
|
}
|
|
16
17
|
export declare function normalizeBranchName(value: string | null | undefined, fallback: string): string;
|