@momentiq/dark-factory-cli 2.1.0 → 2.2.0
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/adapters/static-schema-lint.js +1 -1
- package/dist/cli.js +3 -3
- package/dist/cli.js.map +1 -1
- package/dist/commands/onboard.d.ts +64 -1
- package/dist/commands/onboard.d.ts.map +1 -1
- package/dist/commands/onboard.js +265 -53
- package/dist/commands/onboard.js.map +1 -1
- package/dist/doctor.d.ts.map +1 -1
- package/dist/doctor.js +20 -0
- package/dist/doctor.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +2 -0
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/onboard.d.ts +6 -0
- package/dist/mcp/tools/onboard.d.ts.map +1 -0
- package/dist/mcp/tools/onboard.js +115 -0
- package/dist/mcp/tools/onboard.js.map +1 -0
- package/dist/onboard/apply-plan.d.ts +29 -0
- package/dist/onboard/apply-plan.d.ts.map +1 -0
- package/dist/onboard/apply-plan.js +80 -0
- package/dist/onboard/apply-plan.js.map +1 -0
- package/dist/onboard/auto-profile.d.ts +3 -0
- package/dist/onboard/auto-profile.d.ts.map +1 -0
- package/dist/onboard/auto-profile.js +20 -0
- package/dist/onboard/auto-profile.js.map +1 -0
- package/dist/onboard/generate-plan.d.ts +18 -0
- package/dist/onboard/generate-plan.d.ts.map +1 -0
- package/dist/onboard/generate-plan.js +68 -0
- package/dist/onboard/generate-plan.js.map +1 -0
- package/dist/onboard/llm-client.d.ts +45 -0
- package/dist/onboard/llm-client.d.ts.map +1 -0
- package/dist/onboard/llm-client.js +84 -0
- package/dist/onboard/llm-client.js.map +1 -0
- package/dist/onboard/prompts/scaffold.md +93 -0
- package/dist/onboard/prompts.d.ts +13 -0
- package/dist/onboard/prompts.d.ts.map +1 -0
- package/dist/onboard/prompts.js +54 -0
- package/dist/onboard/prompts.js.map +1 -0
- package/dist/onboard/scaffold-schema.d.ts +194 -0
- package/dist/onboard/scaffold-schema.d.ts.map +1 -0
- package/dist/onboard/scaffold-schema.js +73 -0
- package/dist/onboard/scaffold-schema.js.map +1 -0
- package/dist/onboard/schema.d.ts.map +1 -1
- package/dist/onboard/schema.js +7 -1
- package/dist/onboard/schema.js.map +1 -1
- package/dist/onboard/seeders/adr.d.ts +3 -0
- package/dist/onboard/seeders/adr.d.ts.map +1 -0
- package/dist/onboard/seeders/adr.js +212 -0
- package/dist/onboard/seeders/adr.js.map +1 -0
- package/dist/onboard/seeders/agent-review-config/cloud.canonical.json +22 -0
- package/dist/onboard/seeders/agent-review-config/local.canonical.json +21 -0
- package/dist/onboard/seeders/agent-review-config.d.ts +3 -0
- package/dist/onboard/seeders/agent-review-config.d.ts.map +1 -0
- package/dist/onboard/seeders/agent-review-config.js +38 -0
- package/dist/onboard/seeders/agent-review-config.js.map +1 -0
- package/dist/onboard/seeders/cycle1-bootstrap.d.ts +3 -0
- package/dist/onboard/seeders/cycle1-bootstrap.d.ts.map +1 -0
- package/dist/onboard/seeders/cycle1-bootstrap.js +76 -0
- package/dist/onboard/seeders/cycle1-bootstrap.js.map +1 -0
- package/dist/onboard/seeders/index.d.ts +47 -0
- package/dist/onboard/seeders/index.d.ts.map +1 -0
- package/dist/onboard/seeders/index.js +37 -0
- package/dist/onboard/seeders/index.js.map +1 -0
- package/dist/onboard/seeders/runbook.d.ts +13 -0
- package/dist/onboard/seeders/runbook.d.ts.map +1 -0
- package/dist/onboard/seeders/runbook.js +85 -0
- package/dist/onboard/seeders/runbook.js.map +1 -0
- package/dist/onboard/seeders/templates/adr.md.tmpl +26 -0
- package/dist/onboard/seeders/templates/cycle1-bootstrap.md.tmpl +64 -0
- package/dist/onboard/seeders/templates/runbook.md.tmpl +26 -0
- package/dist/onboard/template-loader.d.ts +20 -0
- package/dist/onboard/template-loader.d.ts.map +1 -0
- package/dist/onboard/template-loader.js +143 -0
- package/dist/onboard/template-loader.js.map +1 -0
- package/dist/onboard/template-ref.d.ts +15 -0
- package/dist/onboard/template-ref.d.ts.map +1 -0
- package/dist/onboard/template-ref.js +45 -0
- package/dist/onboard/template-ref.js.map +1 -0
- package/dist/onboard/validate.d.ts +19 -0
- package/dist/onboard/validate.d.ts.map +1 -0
- package/dist/onboard/validate.js +127 -0
- package/dist/onboard/validate.js.map +1 -0
- package/dist/onboard/writers/dry-run-renderer.d.ts +6 -0
- package/dist/onboard/writers/dry-run-renderer.d.ts.map +1 -0
- package/dist/onboard/writers/dry-run-renderer.js +88 -0
- package/dist/onboard/writers/dry-run-renderer.js.map +1 -0
- package/dist/onboard/writers/emit.d.ts +6 -0
- package/dist/onboard/writers/emit.d.ts.map +1 -0
- package/dist/onboard/writers/emit.js +29 -0
- package/dist/onboard/writers/emit.js.map +1 -0
- package/dist/onboard/writers/merge.d.ts +16 -0
- package/dist/onboard/writers/merge.d.ts.map +1 -0
- package/dist/onboard/writers/merge.js +142 -0
- package/dist/onboard/writers/merge.js.map +1 -0
- package/dist/onboard/writers/pr-writer.d.ts +44 -0
- package/dist/onboard/writers/pr-writer.d.ts.map +1 -0
- package/dist/onboard/writers/pr-writer.js +104 -0
- package/dist/onboard/writers/pr-writer.js.map +1 -0
- package/dist/onboard/writers/skip.d.ts +9 -0
- package/dist/onboard/writers/skip.d.ts.map +1 -0
- package/dist/onboard/writers/skip.js +9 -0
- package/dist/onboard/writers/skip.js.map +1 -0
- package/package.json +4 -1
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// packages/cli/src/mcp/tools/onboard.ts
|
|
2
|
+
//
|
|
3
|
+
// Cycle 15 Phase C — `df_onboard {target, mode}` MCP tool.
|
|
4
|
+
//
|
|
5
|
+
// Delegates EVERYTHING beyond analysis-only to `cmdOnboard()` from
|
|
6
|
+
// `src/commands/onboard.ts` (Task 4.5) so the MCP path returns the SAME
|
|
7
|
+
// merged plan as the CLI path. The MCP tool MUST NOT re-assemble a plan
|
|
8
|
+
// from seeders alone — that would skip Phase B's LLM-emitted CLAUDE.md
|
|
9
|
+
// and break Task 5 metric 1 through the MCP path.
|
|
10
|
+
//
|
|
11
|
+
// Per-mode `destructiveHint` annotation is set on the response (NOT on
|
|
12
|
+
// the tool registration) because it varies per call:
|
|
13
|
+
// - analysis-only / dry-run → false (read-only / in-memory)
|
|
14
|
+
// - apply / pr → true (writes to working tree / opens PR)
|
|
15
|
+
//
|
|
16
|
+
// Tool-registration `openWorldHint: true` applies to ALL modes —
|
|
17
|
+
// analysis reads the filesystem; pr reaches GitHub via gh.
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
import { analyze } from "../../onboard/analyze.js";
|
|
20
|
+
import { cmdOnboard } from "../../commands/onboard.js";
|
|
21
|
+
function isDestructive(mode) {
|
|
22
|
+
return mode === "apply" || mode === "pr";
|
|
23
|
+
}
|
|
24
|
+
function renderMarkdownSummary(input) {
|
|
25
|
+
if ("analysis" in input.sc) {
|
|
26
|
+
const a = input.sc.analysis;
|
|
27
|
+
return [
|
|
28
|
+
`**df_onboard** (mode=analysis-only)`,
|
|
29
|
+
`- canonicalName: ${a.canonicalName || "(unknown)"}`,
|
|
30
|
+
`- stacks: ${a.stacks.map((s) => s.language).join(", ") || "(none)"}`,
|
|
31
|
+
`- services: ${a.services.length}`,
|
|
32
|
+
`- decisions: ${a.decisions.length}`,
|
|
33
|
+
`- analyzerErrors: ${a.analyzerErrors.length}`,
|
|
34
|
+
].join("\n");
|
|
35
|
+
}
|
|
36
|
+
const p = input.sc;
|
|
37
|
+
return [
|
|
38
|
+
`**df_onboard** (mode=${input.mode}, dryRun=${p.dryRun}, applied=${p.applied})`,
|
|
39
|
+
`- files in plan: ${p.plan.files.length}`,
|
|
40
|
+
p.branchName ? `- branch: ${p.branchName}` : null,
|
|
41
|
+
]
|
|
42
|
+
.filter((line) => Boolean(line))
|
|
43
|
+
.join("\n");
|
|
44
|
+
}
|
|
45
|
+
export function registerOnboardTool(server, _opts = {}) {
|
|
46
|
+
server.registerTool("df_onboard", {
|
|
47
|
+
title: "Dark Factory onboard",
|
|
48
|
+
description: "Run `df onboard` on a target repo. Modes: analysis-only (Phase A — emit RepoAnalysis only), " +
|
|
49
|
+
"dry-run (Phase B+C — emit merged ScaffoldPlan without writing), apply (Phase B+C — write files " +
|
|
50
|
+
"to working tree), pr (Phase B+C — branch + commit + open a PR via gh). 'apply' and 'pr' mutate " +
|
|
51
|
+
"the working tree; their per-call response carries `annotations.destructiveHint: true`.",
|
|
52
|
+
inputSchema: {
|
|
53
|
+
target: z
|
|
54
|
+
.string()
|
|
55
|
+
.describe("Absolute path to the target repo's working tree."),
|
|
56
|
+
mode: z
|
|
57
|
+
.enum(["analysis-only", "dry-run", "apply", "pr"])
|
|
58
|
+
.describe("Operation mode. 'analysis-only' is read-only; 'dry-run' computes a plan but writes nothing; " +
|
|
59
|
+
"'apply' writes files in place; 'pr' writes to a fresh branch and opens a PR."),
|
|
60
|
+
},
|
|
61
|
+
outputSchema: {
|
|
62
|
+
analysis: z
|
|
63
|
+
.unknown()
|
|
64
|
+
.optional()
|
|
65
|
+
.describe("Present when mode='analysis-only' — the RepoAnalysis JSON."),
|
|
66
|
+
plan: z
|
|
67
|
+
.unknown()
|
|
68
|
+
.optional()
|
|
69
|
+
.describe("Present when mode != 'analysis-only' — the merged ScaffoldPlan."),
|
|
70
|
+
dryRun: z.boolean().optional(),
|
|
71
|
+
applied: z.boolean().optional(),
|
|
72
|
+
branchName: z.string().nullable().optional(),
|
|
73
|
+
},
|
|
74
|
+
annotations: {
|
|
75
|
+
// openWorldHint applies to ALL modes — analysis reads the
|
|
76
|
+
// filesystem; pr reaches GitHub via gh. destructiveHint is NOT
|
|
77
|
+
// set at registration because it varies per-mode (set per-call
|
|
78
|
+
// on the response below).
|
|
79
|
+
openWorldHint: true,
|
|
80
|
+
},
|
|
81
|
+
}, async (args) => {
|
|
82
|
+
const target = args.target;
|
|
83
|
+
const mode = args.mode;
|
|
84
|
+
const destructiveHint = isDestructive(mode);
|
|
85
|
+
if (mode === "analysis-only") {
|
|
86
|
+
const analysis = await analyze(target);
|
|
87
|
+
const result = { analysis };
|
|
88
|
+
return {
|
|
89
|
+
structuredContent: result,
|
|
90
|
+
content: [{ type: "text", text: renderMarkdownSummary({ mode, sc: result }) }],
|
|
91
|
+
// Per-call destructive flag — analysis-only is read-only.
|
|
92
|
+
annotations: { destructiveHint },
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
// dry-run / apply / pr — delegate to cmdOnboard. `json: true` is
|
|
96
|
+
// structurally irrelevant in the orchestrator's contract today
|
|
97
|
+
// (the orchestrator always returns the structured plan; the CLI
|
|
98
|
+
// shim handles rendered diff output), but pass it explicitly so
|
|
99
|
+
// the intent ("MCP wants the structured plan, not a rendered
|
|
100
|
+
// ANSI diff") is documented at the call site.
|
|
101
|
+
const out = (await cmdOnboard({ target, mode }));
|
|
102
|
+
const result = {
|
|
103
|
+
plan: out.plan,
|
|
104
|
+
dryRun: mode === "dry-run",
|
|
105
|
+
applied: out.applied,
|
|
106
|
+
branchName: out.branchName,
|
|
107
|
+
};
|
|
108
|
+
return {
|
|
109
|
+
structuredContent: result,
|
|
110
|
+
content: [{ type: "text", text: renderMarkdownSummary({ mode, sc: result }) }],
|
|
111
|
+
annotations: { destructiveHint },
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=onboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onboard.js","sourceRoot":"","sources":["../../../src/mcp/tools/onboard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type MergeResult } from "./writers/merge.js";
|
|
2
|
+
import { type SkipResult } from "./writers/skip.js";
|
|
3
|
+
import type { ScaffoldPlan } from "./scaffold-schema.js";
|
|
4
|
+
export type ApplyMode = "dry-run" | "apply";
|
|
5
|
+
export interface EmitResult {
|
|
6
|
+
path: string;
|
|
7
|
+
action: "emit";
|
|
8
|
+
wrote: boolean;
|
|
9
|
+
}
|
|
10
|
+
export type ApplyFileResult = EmitResult | MergeResult | SkipResult;
|
|
11
|
+
export interface ApplyResult {
|
|
12
|
+
mode: ApplyMode;
|
|
13
|
+
results: ApplyFileResult[];
|
|
14
|
+
rendered?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface ApplyOptions {
|
|
17
|
+
mode: ApplyMode;
|
|
18
|
+
force?: boolean;
|
|
19
|
+
color?: boolean;
|
|
20
|
+
stderr?: (s: string) => void;
|
|
21
|
+
}
|
|
22
|
+
export declare class ScaffoldApplyError extends Error {
|
|
23
|
+
written: string[];
|
|
24
|
+
notWritten: string[];
|
|
25
|
+
cause: unknown;
|
|
26
|
+
constructor(message: string, written: string[], notWritten: string[], cause: unknown);
|
|
27
|
+
}
|
|
28
|
+
export declare function applyPlan(rootDir: string, plan: ScaffoldPlan, opts: ApplyOptions): Promise<ApplyResult>;
|
|
29
|
+
//# sourceMappingURL=apply-plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply-plan.d.ts","sourceRoot":"","sources":["../../src/onboard/apply-plan.ts"],"names":[],"mappings":"AAcA,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAG/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5C,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,WAAW,GAAG,UAAU,CAAC;AAEpE,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,EAAE,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;gBACZ,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO;CAOrF;AAED,wBAAsB,SAAS,CAC7B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,WAAW,CAAC,CAuDtB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// packages/cli/src/onboard/apply-plan.ts
|
|
2
|
+
//
|
|
3
|
+
// Dispatcher over the three writers + the dry-run renderer. The PR writer
|
|
4
|
+
// is a separate entry point (pr-writer.ts) because it spans subprocess
|
|
5
|
+
// orchestration (gh auth check + branch + commit + gh pr create) that
|
|
6
|
+
// doesn't belong in this hot path.
|
|
7
|
+
//
|
|
8
|
+
// Partial-failure contract (per B-D7): on any write error mid-loop, collect
|
|
9
|
+
// the written-so-far list + the not-written list and re-throw a
|
|
10
|
+
// ScaffoldApplyError. Idempotency: callers can re-run --apply after fixing
|
|
11
|
+
// the disk/permission issue; written files are overwritten with identical
|
|
12
|
+
// content; merge files re-detect the BEGIN/END block.
|
|
13
|
+
import { writeEmit } from "./writers/emit.js";
|
|
14
|
+
import { writeMerge } from "./writers/merge.js";
|
|
15
|
+
import { writeSkip } from "./writers/skip.js";
|
|
16
|
+
import { renderDryRun } from "./writers/dry-run-renderer.js";
|
|
17
|
+
export class ScaffoldApplyError extends Error {
|
|
18
|
+
written;
|
|
19
|
+
notWritten;
|
|
20
|
+
cause;
|
|
21
|
+
constructor(message, written, notWritten, cause) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = "ScaffoldApplyError";
|
|
24
|
+
this.written = written;
|
|
25
|
+
this.notWritten = notWritten;
|
|
26
|
+
this.cause = cause;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export async function applyPlan(rootDir, plan, opts) {
|
|
30
|
+
if (opts.mode !== "dry-run" && opts.mode !== "apply") {
|
|
31
|
+
throw new Error(`df onboard: applyPlan does not handle mode "${opts.mode}". ` +
|
|
32
|
+
"For pr mode, use runPrMode(plan, ...) from pr-writer.ts.");
|
|
33
|
+
}
|
|
34
|
+
if (opts.mode === "dry-run") {
|
|
35
|
+
const renderOpts = opts.color !== undefined ? { color: opts.color } : {};
|
|
36
|
+
const rendered = await renderDryRun(rootDir, plan, renderOpts);
|
|
37
|
+
const results = plan.files.map((f) => {
|
|
38
|
+
if (f.action === "skip")
|
|
39
|
+
return { path: f.path, action: "skip", rationale: f.rationale, wrote: false };
|
|
40
|
+
if (f.action === "emit")
|
|
41
|
+
return { path: f.path, action: "emit", wrote: false };
|
|
42
|
+
return { path: f.path, action: "merge", wrote: false, skipped: false };
|
|
43
|
+
});
|
|
44
|
+
return { mode: "dry-run", results, rendered };
|
|
45
|
+
}
|
|
46
|
+
// apply mode
|
|
47
|
+
const results = [];
|
|
48
|
+
const written = [];
|
|
49
|
+
for (let i = 0; i < plan.files.length; i++) {
|
|
50
|
+
const file = plan.files[i];
|
|
51
|
+
try {
|
|
52
|
+
if (file.action === "skip") {
|
|
53
|
+
results.push(await writeSkip(rootDir, file));
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (file.action === "emit") {
|
|
57
|
+
await writeEmit(rootDir, file, { force: opts.force ?? false });
|
|
58
|
+
results.push({ path: file.path, action: "emit", wrote: true });
|
|
59
|
+
written.push(file.path);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
// merge
|
|
63
|
+
const mergeOpts = opts.stderr !== undefined ? { stderr: opts.stderr } : {};
|
|
64
|
+
const r = await writeMerge(rootDir, file, mergeOpts);
|
|
65
|
+
results.push(r);
|
|
66
|
+
if (r.wrote)
|
|
67
|
+
written.push(file.path);
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
const notWritten = plan.files.slice(i).map((f) => f.path);
|
|
71
|
+
throw new ScaffoldApplyError(`df onboard: write failed at ${file.path} (${err instanceof Error ? err.message : String(err)}). ` +
|
|
72
|
+
`Files written before the failure: ${written.length ? written.join(", ") : "(none)"}. ` +
|
|
73
|
+
`Files NOT written: ${notWritten.join(", ")}. ` +
|
|
74
|
+
`Re-run \`df onboard --apply\` after addressing the disk/perm issue; ` +
|
|
75
|
+
`written files will be overwritten with the same content (idempotent).`, written, notWritten, err);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return { mode: "apply", results };
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=apply-plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply-plan.js","sourceRoot":"","sources":["../../src/onboard/apply-plan.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,0EAA0E;AAC1E,uEAAuE;AACvE,sEAAsE;AACtE,mCAAmC;AACnC,EAAE;AACF,4EAA4E;AAC5E,gEAAgE;AAChE,2EAA2E;AAC3E,0EAA0E;AAC1E,sDAAsD;AAEtD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAoB,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,SAAS,EAAmB,MAAM,mBAAmB,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA2B7D,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAC3C,OAAO,CAAW;IAClB,UAAU,CAAW;IACZ,KAAK,CAAU;IACxB,YAAY,OAAe,EAAE,OAAiB,EAAE,UAAoB,EAAE,KAAc;QAClF,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,OAAe,EACf,IAAkB,EAClB,IAAkB;IAElB,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CACb,+CAA+C,IAAI,CAAC,IAAc,KAAK;YACrE,0DAA0D,CAC7D,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC5B,MAAM,UAAU,GAAwB,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9F,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;QAC/D,MAAM,OAAO,GAAsB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACtD,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM;gBAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YACvG,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM;gBAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAC/E,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QACzE,CAAC,CAAC,CAAC;QACH,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;IAChD,CAAC;IAED,aAAa;IACb,MAAM,OAAO,GAAsB,EAAE,CAAC;IACtC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC;QAC5B,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC3B,OAAO,CAAC,IAAI,CAAC,MAAM,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;gBAC7C,SAAS;YACX,CAAC;YACD,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;gBAC3B,MAAM,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,KAAK,EAAE,CAAC,CAAC;gBAC/D,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC/D,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACxB,SAAS;YACX,CAAC;YACD,QAAQ;YACR,MAAM,SAAS,GAAqC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7G,MAAM,CAAC,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;YACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAChB,IAAI,CAAC,CAAC,KAAK;gBAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC1D,MAAM,IAAI,kBAAkB,CAC1B,+BAA+B,IAAI,CAAC,IAAI,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK;gBAChG,qCAAqC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI;gBACvF,sBAAsB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;gBAC/C,sEAAsE;gBACtE,uEAAuE,EACzE,OAAO,EACP,UAAU,EACV,GAAG,CACJ,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-profile.d.ts","sourceRoot":"","sources":["../../src/onboard/auto-profile.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,wBAAgB,WAAW,CAAC,QAAQ,EAAE,YAAY,GAAG,OAAO,GAAG,OAAO,CAGrE"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// packages/cli/src/onboard/auto-profile.ts
|
|
2
|
+
//
|
|
3
|
+
// Resolve the critic profile (local | cloud) from a RepoAnalysis when the
|
|
4
|
+
// operator did not pass --profile explicitly.
|
|
5
|
+
//
|
|
6
|
+
// This module is shipped as its own file (not inline in commands/onboard.ts)
|
|
7
|
+
// so Phase C's deterministic .agent-review/config.json seeder can import the
|
|
8
|
+
// same function via `import { autoProfile } from "../auto-profile.js"`. The
|
|
9
|
+
// single-source-of-truth keeps Phase B + Phase C aligned on the heuristic
|
|
10
|
+
// without either side re-implementing it.
|
|
11
|
+
//
|
|
12
|
+
// Heuristic (B-D8): a repo that already has a DF cli-pin AND a cloud PR
|
|
13
|
+
// workflow is one that already runs the cloud quartet — keep them on
|
|
14
|
+
// `cloud`. Everything else defaults to `local`. Explicit `--profile` always
|
|
15
|
+
// wins; this function is only the fallback.
|
|
16
|
+
export function autoProfile(analysis) {
|
|
17
|
+
const dfWired = analysis.dfPresence.cliPin !== null && analysis.dfPresence.prWorkflow;
|
|
18
|
+
return dfWired ? "cloud" : "local";
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=auto-profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-profile.js","sourceRoot":"","sources":["../../src/onboard/auto-profile.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,EAAE;AACF,0EAA0E;AAC1E,8CAA8C;AAC9C,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,4EAA4E;AAC5E,0EAA0E;AAC1E,0CAA0C;AAC1C,EAAE;AACF,wEAAwE;AACxE,qEAAqE;AACrE,4EAA4E;AAC5E,4CAA4C;AAI5C,MAAM,UAAU,WAAW,CAAC,QAAsB;IAChD,MAAM,OAAO,GAAG,QAAQ,CAAC,UAAU,CAAC,MAAM,KAAK,IAAI,IAAI,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC;IACtF,OAAO,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;AACrC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ScaffoldPlan } from "./scaffold-schema.js";
|
|
2
|
+
import { type LlmInputs, type LlmResult } from "./llm-client.js";
|
|
3
|
+
import type { RepoAnalysis } from "./schema.js";
|
|
4
|
+
import type { Template } from "./template-loader.js";
|
|
5
|
+
export interface GeneratePlanOptions {
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
modelId: string;
|
|
8
|
+
/** Resolved critic profile (B-D8). Flows into the scaffold prompt so the LLM
|
|
9
|
+
* emits a profile-correct `.agent-review/config.json`. The CLI resolves the
|
|
10
|
+
* profile (explicit `--profile` flag, else `autoProfile(analysis)`) before
|
|
11
|
+
* calling generatePlan. */
|
|
12
|
+
profile: "local" | "cloud";
|
|
13
|
+
callLlm?: (inputs: LlmInputs) => Promise<LlmResult>;
|
|
14
|
+
}
|
|
15
|
+
export interface GeneratePlanResult extends Omit<ScaffoldPlan, never> {
|
|
16
|
+
}
|
|
17
|
+
export declare function generatePlan(analysis: RepoAnalysis, template: Template, opts: GeneratePlanOptions): Promise<ScaffoldPlan>;
|
|
18
|
+
//# sourceMappingURL=generate-plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-plan.d.ts","sourceRoot":"","sources":["../../src/onboard/generate-plan.ts"],"names":[],"mappings":"AAUA,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAEL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB;;;gCAG4B;IAC5B,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC;IAE3B,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,kBAAmB,SAAQ,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC;CAEpE;AAID,wBAAsB,YAAY,CAChC,QAAQ,EAAE,YAAY,EACtB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,YAAY,CAAC,CAsDvB"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// packages/cli/src/onboard/generate-plan.ts
|
|
2
|
+
//
|
|
3
|
+
// Stage B entry point. Composes the prompt renderer, the LLM client, and
|
|
4
|
+
// the ScaffoldPlanSchema validator into one async function. One retry on
|
|
5
|
+
// validation failure (the model gets a chance to fix its own JSON when
|
|
6
|
+
// shown the Zod error); zero retries on transient LLM errors (the LLM
|
|
7
|
+
// client wrapper already handles those).
|
|
8
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
9
|
+
import { ScaffoldPlanSchema, SCAFFOLD_PLAN_BYTE_BUDGET, } from "./scaffold-schema.js";
|
|
10
|
+
import { renderScaffoldPrompt } from "./prompts.js";
|
|
11
|
+
import { callScaffoldLlm, } from "./llm-client.js";
|
|
12
|
+
const TOOL_NAME = "emit_scaffold_plan";
|
|
13
|
+
export async function generatePlan(analysis, template, opts) {
|
|
14
|
+
const { systemPrompt, userMessage } = await renderScaffoldPrompt(analysis, template.files, { profile: opts.profile });
|
|
15
|
+
const inputSchema = zodToJsonSchema(ScaffoldPlanSchema, "ScaffoldPlan");
|
|
16
|
+
const callLlm = opts.callLlm ?? ((i) => callScaffoldLlm(i));
|
|
17
|
+
// exactOptionalPropertyTypes forbids assigning `undefined` to an optional
|
|
18
|
+
// property; conditionally spread `apiKey` only when defined so the llm-client
|
|
19
|
+
// falls through to its ANTHROPIC_API_KEY env-var lookup.
|
|
20
|
+
const baseInputs = {
|
|
21
|
+
systemPrompt,
|
|
22
|
+
toolName: TOOL_NAME,
|
|
23
|
+
toolInputSchema: inputSchema,
|
|
24
|
+
modelId: opts.modelId,
|
|
25
|
+
...(opts.apiKey !== undefined ? { apiKey: opts.apiKey } : {}),
|
|
26
|
+
};
|
|
27
|
+
// Attempt 1
|
|
28
|
+
let result = await callLlm({ ...baseInputs, userMessage });
|
|
29
|
+
let parsed = ScaffoldPlanSchema.safeParse(stampTemplateRef(result.planJson, template));
|
|
30
|
+
if (!parsed.success) {
|
|
31
|
+
// Capture the first-attempt issues BEFORE the retry overwrites `parsed`,
|
|
32
|
+
// so the final error can cite both attempts (per W3 finding round-1).
|
|
33
|
+
const firstAttemptIssues = parsed.error.issues;
|
|
34
|
+
// Retry once with the validation error appended — the model often self-corrects.
|
|
35
|
+
const retryMessage = `${userMessage}\n\n## Previous attempt rejected\n` +
|
|
36
|
+
"Your last tool call did not validate. Issues:\n" +
|
|
37
|
+
firstAttemptIssues.map((i) => `- ${i.path.join(".")}: ${i.message}`).join("\n") +
|
|
38
|
+
"\n\nEmit `emit_scaffold_plan` again, fixing each issue above.";
|
|
39
|
+
result = await callLlm({ ...baseInputs, userMessage: retryMessage });
|
|
40
|
+
parsed = ScaffoldPlanSchema.safeParse(stampTemplateRef(result.planJson, template));
|
|
41
|
+
if (!parsed.success) {
|
|
42
|
+
throw new Error(`df onboard: scaffold plan validation failed after retry. ` +
|
|
43
|
+
`First-attempt issues: ${formatZodIssues(firstAttemptIssues)}; ` +
|
|
44
|
+
`second-attempt issues: ${formatZodIssues(parsed.error.issues)}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Byte-budget backstop (per B-D2). Per-array caps and per-field maxLength
|
|
48
|
+
// catch most overflows; this is the final guardrail before the writer.
|
|
49
|
+
const serialized = JSON.stringify(parsed.data);
|
|
50
|
+
if (serialized.length > SCAFFOLD_PLAN_BYTE_BUDGET) {
|
|
51
|
+
throw new Error(`df onboard: ScaffoldPlan exceeds ${SCAFFOLD_PLAN_BYTE_BUDGET}-byte (64 KB) budget: ` +
|
|
52
|
+
`produced ${serialized.length} bytes. Tighten per-file tailored_content or files[] count.`);
|
|
53
|
+
}
|
|
54
|
+
return parsed.data;
|
|
55
|
+
}
|
|
56
|
+
// stampTemplateRef forces the plan's templateRef to be the loader's canonicalRef,
|
|
57
|
+
// not the LLM's value — the LLM could echo a stale or fabricated ref, and the
|
|
58
|
+
// plan's provenance must be ground-truth.
|
|
59
|
+
function stampTemplateRef(planJson, template) {
|
|
60
|
+
if (planJson && typeof planJson === "object" && !Array.isArray(planJson)) {
|
|
61
|
+
return { ...planJson, templateRef: template.canonicalRef };
|
|
62
|
+
}
|
|
63
|
+
return planJson;
|
|
64
|
+
}
|
|
65
|
+
function formatZodIssues(issues) {
|
|
66
|
+
return issues.map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`).join("; ");
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=generate-plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-plan.js","sourceRoot":"","sources":["../../src/onboard/generate-plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export interface LlmInputs {
|
|
2
|
+
systemPrompt: string;
|
|
3
|
+
userMessage: string;
|
|
4
|
+
toolName: string;
|
|
5
|
+
toolInputSchema: object;
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
modelId: string;
|
|
8
|
+
maxTokens?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface LlmResult {
|
|
11
|
+
planJson: unknown;
|
|
12
|
+
modelId: string;
|
|
13
|
+
inputTokens: number;
|
|
14
|
+
outputTokens: number;
|
|
15
|
+
attempts: number;
|
|
16
|
+
}
|
|
17
|
+
export interface LlmClientLike {
|
|
18
|
+
messages: {
|
|
19
|
+
create(params: unknown): Promise<{
|
|
20
|
+
id: string;
|
|
21
|
+
model: string;
|
|
22
|
+
content: Array<{
|
|
23
|
+
type: "tool_use";
|
|
24
|
+
name: string;
|
|
25
|
+
input: unknown;
|
|
26
|
+
} | {
|
|
27
|
+
type: "text";
|
|
28
|
+
text: string;
|
|
29
|
+
} | {
|
|
30
|
+
type: string;
|
|
31
|
+
}>;
|
|
32
|
+
usage: {
|
|
33
|
+
input_tokens: number;
|
|
34
|
+
output_tokens: number;
|
|
35
|
+
};
|
|
36
|
+
stop_reason: string;
|
|
37
|
+
}>;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export type LlmClientFactory = (apiKey: string) => LlmClientLike;
|
|
41
|
+
export interface CallOptions {
|
|
42
|
+
createClient?: LlmClientFactory;
|
|
43
|
+
}
|
|
44
|
+
export declare function callScaffoldLlm(inputs: LlmInputs, opts?: CallOptions): Promise<LlmResult>;
|
|
45
|
+
//# sourceMappingURL=llm-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-client.d.ts","sourceRoot":"","sources":["../../src/onboard/llm-client.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE;QACR,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC;YAC/B,EAAE,EAAE,MAAM,CAAC;YACX,KAAK,EAAE,MAAM,CAAC;YACd,OAAO,EAAE,KAAK,CACV;gBAAE,IAAI,EAAE,UAAU,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAC;gBAAC,KAAK,EAAE,OAAO,CAAA;aAAE,GAClD;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAA;aAAE,GAC9B;gBAAE,IAAI,EAAE,MAAM,CAAA;aAAE,CACnB,CAAC;YACF,KAAK,EAAE;gBAAE,YAAY,EAAE,MAAM,CAAC;gBAAC,aAAa,EAAE,MAAM,CAAA;aAAE,CAAC;YACvD,WAAW,EAAE,MAAM,CAAC;SACrB,CAAC,CAAC;KACJ,CAAC;CACH;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,aAAa,CAAC;AAkBjE,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,SAAS,EACjB,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,SAAS,CAAC,CA2DpB"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// packages/cli/src/onboard/llm-client.ts
|
|
2
|
+
//
|
|
3
|
+
// Anthropic Messages-API wrapper with tool-use structured output.
|
|
4
|
+
//
|
|
5
|
+
// Why a wrapper instead of calling the SDK directly: the call shape is
|
|
6
|
+
// shared by generatePlan() (Phase B) AND a future Phase B.1 prompt-test
|
|
7
|
+
// harness, and the retry classification (transient vs permanent) is
|
|
8
|
+
// security-critical (a 401 must never silently retry). Centralizing the
|
|
9
|
+
// shape here means a new caller can't accidentally drift from the policy.
|
|
10
|
+
//
|
|
11
|
+
// Tool-use binding: declare a single tool `emit_scaffold_plan` whose
|
|
12
|
+
// `input_schema` is the JSON-Schema rendering of ScaffoldPlanSchema; set
|
|
13
|
+
// tool_choice to force the model into a tool call; read the tool-use
|
|
14
|
+
// block's `input` as the candidate JSON. The SDK validates the JSON
|
|
15
|
+
// shape at the API boundary; downstream Zod validation re-checks the
|
|
16
|
+
// semantics (discriminated-union, byte budgets).
|
|
17
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
18
|
+
const DEFAULT_FACTORY = (apiKey) => new Anthropic({ apiKey });
|
|
19
|
+
const TRANSIENT_STATUSES = new Set([429, 500, 502, 503, 504]);
|
|
20
|
+
function isTransient(err) {
|
|
21
|
+
if (!(err instanceof Error))
|
|
22
|
+
return false;
|
|
23
|
+
const status = err.status;
|
|
24
|
+
if (typeof status === "number")
|
|
25
|
+
return TRANSIENT_STATUSES.has(status);
|
|
26
|
+
const code = err.code;
|
|
27
|
+
if (typeof code === "string" && /ECONNRESET|ETIMEDOUT|ENETUNREACH|EAI_AGAIN/.test(code)) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
export async function callScaffoldLlm(inputs, opts = {}) {
|
|
33
|
+
const apiKey = inputs.apiKey ?? process.env["ANTHROPIC_API_KEY"];
|
|
34
|
+
if (!apiKey || !apiKey.trim()) {
|
|
35
|
+
throw new Error("df onboard: ANTHROPIC_API_KEY is required for Phase B's scaffold-generation LLM call. " +
|
|
36
|
+
"Set the env var (or pass --api-key) and re-run.");
|
|
37
|
+
}
|
|
38
|
+
const factory = opts.createClient ?? DEFAULT_FACTORY;
|
|
39
|
+
const client = factory(apiKey);
|
|
40
|
+
const request = {
|
|
41
|
+
model: inputs.modelId,
|
|
42
|
+
max_tokens: inputs.maxTokens ?? 8192,
|
|
43
|
+
system: inputs.systemPrompt,
|
|
44
|
+
tools: [
|
|
45
|
+
{
|
|
46
|
+
name: inputs.toolName,
|
|
47
|
+
description: "Emit the tailored ScaffoldPlan JSON. The input schema is enforced strictly; " +
|
|
48
|
+
"fields not in the schema are rejected.",
|
|
49
|
+
input_schema: inputs.toolInputSchema,
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
tool_choice: { type: "tool", name: inputs.toolName },
|
|
53
|
+
messages: [{ role: "user", content: inputs.userMessage }],
|
|
54
|
+
};
|
|
55
|
+
let attempts = 0;
|
|
56
|
+
let lastErr = null;
|
|
57
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
58
|
+
attempts++;
|
|
59
|
+
try {
|
|
60
|
+
const r = await client.messages.create(request);
|
|
61
|
+
const toolBlock = r.content.find((b) => b.type === "tool_use" &&
|
|
62
|
+
b.name === inputs.toolName);
|
|
63
|
+
if (!toolBlock) {
|
|
64
|
+
throw new Error(`df onboard: no tool_use block found in LLM response (got stop_reason=${r.stop_reason}). ` +
|
|
65
|
+
"The model did not honor tool_choice; check the prompt and model selection.");
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
planJson: toolBlock.input,
|
|
69
|
+
modelId: r.model,
|
|
70
|
+
inputTokens: r.usage.input_tokens,
|
|
71
|
+
outputTokens: r.usage.output_tokens,
|
|
72
|
+
attempts,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
lastErr = err;
|
|
77
|
+
if (attempt === 0 && isTransient(err))
|
|
78
|
+
continue;
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=llm-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm-client.js","sourceRoot":"","sources":["../../src/onboard/llm-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
<!-- packages/cli/src/onboard/prompts/scaffold.md -->
|
|
2
|
+
# Dark Factory `df onboard` — Stage B scaffold prompt (cycle 15 Phase B)
|
|
3
|
+
|
|
4
|
+
You are the "Stage B" scaffold-tailoring agent inside `@momentiq/dark-factory-cli`'s
|
|
5
|
+
`df onboard` command. Your role is to consume a **structured `RepoAnalysis`** (the
|
|
6
|
+
deterministic Stage A output) and a **sage-blueprint template directory** (the raw
|
|
7
|
+
markdown/JSON/YAML files that define the agent-context-set scaffolding), then
|
|
8
|
+
emit a **`ScaffoldPlan`** describing how to tailor the template to the target repo.
|
|
9
|
+
|
|
10
|
+
## Operating contract
|
|
11
|
+
|
|
12
|
+
You MUST emit your response via the `emit_scaffold_plan` tool call. Do NOT emit
|
|
13
|
+
prose text outside of the tool call. The tool's `input_schema` is strict; fields
|
|
14
|
+
outside the schema are rejected, and the action discriminator forces an unambiguous
|
|
15
|
+
choice per file.
|
|
16
|
+
|
|
17
|
+
For every file in the input template, choose exactly ONE action:
|
|
18
|
+
|
|
19
|
+
| Action | Use when | Required fields |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| `emit` | The target repo does NOT have this file yet, OR the existing file is empty/trivial and should be replaced. | `path`, `rationale`, `tailored_content` |
|
|
22
|
+
| `merge` | The target repo HAS a non-trivial version of this file and the LLM-tailored content should be APPENDED additively (NOT replace). Reserve for `CLAUDE.md` and `AGENTS.md`. | `path`, `rationale`, `tailored_content` |
|
|
23
|
+
| `skip` | The target repo already covers this file's intent, OR the file is irrelevant to the target's stack/services. | `path`, `rationale` (NO `tailored_content` — the schema rejects it) |
|
|
24
|
+
|
|
25
|
+
### Tailoring rules
|
|
26
|
+
|
|
27
|
+
1. **Cite the analysis, never invent.** Every claim in `tailored_content` must be
|
|
28
|
+
grounded in a field of `RepoAnalysis`. If you reference a service, it MUST
|
|
29
|
+
appear in `analysis.services[]`. If you cite a stack, it MUST appear in
|
|
30
|
+
`analysis.stacks[]`. If you describe a deploy story, cite `analysis.ci.deployStory`.
|
|
31
|
+
Hallucinating a non-existent service or stack is the worst possible failure
|
|
32
|
+
mode of this pipeline.
|
|
33
|
+
|
|
34
|
+
2. **Substitute `{{ }}` placeholders.** The template files contain
|
|
35
|
+
`{{ project_name }}`, `{{ stack }}`, `{{ services }}` and similar Copier-style
|
|
36
|
+
placeholders. Replace each with the concrete value from `RepoAnalysis`:
|
|
37
|
+
- `{{ project_name }}` → `analysis.canonicalName.split("/")[1]`
|
|
38
|
+
- `{{ owner }}` → `analysis.canonicalName.split("/")[0]`
|
|
39
|
+
- `{{ stack }}` → `analysis.stacks[0]?.language` (or "polyglot" if multiple)
|
|
40
|
+
- `{{ services }}` → bulleted list from `analysis.services[]`
|
|
41
|
+
- `{{ default_branch }}` → `analysis.git.defaultBranch`
|
|
42
|
+
The bodies you emit must contain NO leftover `{{ }}` after substitution.
|
|
43
|
+
|
|
44
|
+
3. **Use `merge` only for `CLAUDE.md` and `AGENTS.md`.** Other files don't have
|
|
45
|
+
the marker-comment infrastructure; use `emit` or `skip` for everything else.
|
|
46
|
+
When `analysis.docs.hasClaudeMd === true`, use `merge` for `CLAUDE.md`;
|
|
47
|
+
otherwise use `emit`. Same for `AGENTS.md`.
|
|
48
|
+
|
|
49
|
+
4. **Skip when the target already covers it.** If `analysis.dfPresence.configJson === true`,
|
|
50
|
+
skip `.agent-review/config.json` — don't fight the existing gate. If
|
|
51
|
+
`analysis.dfPresence.prWorkflow === true`, skip `dark-factory-pr.yml`. Cite
|
|
52
|
+
the dfPresence field in the rationale.
|
|
53
|
+
|
|
54
|
+
4a. **ALWAYS SKIP `.agent-review/config.json` (the Phase C seeder owns this path).** Emit a single `skip` entry for `.agent-review/config.json` with rationale "phase C seeder owns this path; phase B does not emit config.json"; do NOT include `tailored_content`. This applies regardless of `analysis.dfPresence.configJson`'s value and supersedes rule 4 for this specific path.
|
|
55
|
+
|
|
56
|
+
5. **`merge` content rules.** When you emit `tailored_content` for a `merge`
|
|
57
|
+
action, that content is APPENDED after the existing file (with marker
|
|
58
|
+
comments managed by the CLI). Therefore:
|
|
59
|
+
- Do NOT include the user's existing headings.
|
|
60
|
+
- START with a top-level H2 section (e.g. `## Dark Factory onboarding`) so the
|
|
61
|
+
append reads naturally.
|
|
62
|
+
- Do NOT include `<!-- df onboard: inserted-by-cycle-15 -->` markers — the
|
|
63
|
+
CLI's merge writer adds them.
|
|
64
|
+
|
|
65
|
+
6. **`rationale` is one sentence.** Plain English, ≤ 800 chars; reference the
|
|
66
|
+
specific analysis fields that drove the action choice.
|
|
67
|
+
|
|
68
|
+
7. **`summary` is one paragraph.** ≤ 800 chars; what the LLM tailored, why, and
|
|
69
|
+
any caveats the operator should know.
|
|
70
|
+
|
|
71
|
+
## Inputs
|
|
72
|
+
|
|
73
|
+
### Resolved critic profile
|
|
74
|
+
|
|
75
|
+
`{{CRITIC_PROFILE}}`
|
|
76
|
+
|
|
77
|
+
(The phase-c seeder reads this resolved profile to emit the matching .agent-review/config.json; phase B's role is to thread it, not to emit the JSON.)
|
|
78
|
+
|
|
79
|
+
### RepoAnalysis
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{{ANALYSIS_JSON}}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Template file list (path: sizeBytes)
|
|
86
|
+
|
|
87
|
+
{{TEMPLATE_FILE_LIST}}
|
|
88
|
+
|
|
89
|
+
### Template file bodies
|
|
90
|
+
|
|
91
|
+
{{TEMPLATE_FILE_BODIES}}
|
|
92
|
+
|
|
93
|
+
## Now emit the `emit_scaffold_plan` tool call.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RepoAnalysis } from "./schema.js";
|
|
2
|
+
import type { TemplateFile } from "./template-loader.js";
|
|
3
|
+
export interface RenderedPrompt {
|
|
4
|
+
systemPrompt: string;
|
|
5
|
+
userMessage: string;
|
|
6
|
+
}
|
|
7
|
+
export interface RenderScaffoldPromptOptions {
|
|
8
|
+
/** Resolved critic profile (B-D8). Substituted into the system prompt's `{{CRITIC_PROFILE}}` placeholder so the LLM's `.agent-review/config.json` tailoring uses the right fleet. */
|
|
9
|
+
profile: "local" | "cloud";
|
|
10
|
+
}
|
|
11
|
+
export declare function renderScaffoldPromptSync(asset: string, analysis: RepoAnalysis, templateFiles: TemplateFile[], opts: RenderScaffoldPromptOptions): RenderedPrompt;
|
|
12
|
+
export declare function renderScaffoldPrompt(analysis: RepoAnalysis, templateFiles: TemplateFile[], opts: RenderScaffoldPromptOptions): Promise<RenderedPrompt>;
|
|
13
|
+
//# sourceMappingURL=prompts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/onboard/prompts.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAYzD,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,2BAA2B;IAC1C,qLAAqL;IACrL,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC;CAC5B;AAcD,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,YAAY,EACtB,aAAa,EAAE,YAAY,EAAE,EAC7B,IAAI,EAAE,2BAA2B,GAChC,cAAc,CAsBhB;AAED,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,YAAY,EACtB,aAAa,EAAE,YAAY,EAAE,EAC7B,IAAI,EAAE,2BAA2B,GAChC,OAAO,CAAC,cAAc,CAAC,CAGzB"}
|