@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.
@@ -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-dev.f599637d804e";
1
+ export declare const forgeBridgeVersion = "0.1.3-dev.2c3feac3af65";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const forgeBridgeVersion = "0.1.1-dev.f599637d804e";
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.1-dev.f599637d804e",
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",