@delegance/claude-autopilot 5.5.2 → 7.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/CHANGELOG.md +1776 -6
- package/README.md +65 -1
- package/bin/_launcher.js +38 -23
- package/dist/src/adapters/council/openai.js +12 -6
- package/dist/src/adapters/deploy/_http.d.ts +43 -0
- package/dist/src/adapters/deploy/_http.js +99 -0
- package/dist/src/adapters/deploy/fly.d.ts +206 -0
- package/dist/src/adapters/deploy/fly.js +696 -0
- package/dist/src/adapters/deploy/index.d.ts +2 -0
- package/dist/src/adapters/deploy/index.js +33 -0
- package/dist/src/adapters/deploy/render.d.ts +181 -0
- package/dist/src/adapters/deploy/render.js +550 -0
- package/dist/src/adapters/deploy/types.d.ts +67 -3
- package/dist/src/adapters/deploy/vercel.d.ts +17 -1
- package/dist/src/adapters/deploy/vercel.js +29 -49
- package/dist/src/adapters/pricing.d.ts +36 -0
- package/dist/src/adapters/pricing.js +40 -0
- package/dist/src/adapters/review-engine/codex.js +10 -7
- package/dist/src/cli/autopilot.d.ts +75 -0
- package/dist/src/cli/autopilot.js +750 -0
- package/dist/src/cli/brainstorm.d.ts +23 -0
- package/dist/src/cli/brainstorm.js +131 -0
- package/dist/src/cli/costs.d.ts +15 -1
- package/dist/src/cli/costs.js +99 -10
- package/dist/src/cli/dashboard/index.d.ts +5 -0
- package/dist/src/cli/dashboard/index.js +49 -0
- package/dist/src/cli/dashboard/login.d.ts +22 -0
- package/dist/src/cli/dashboard/login.js +260 -0
- package/dist/src/cli/dashboard/logout.d.ts +12 -0
- package/dist/src/cli/dashboard/logout.js +45 -0
- package/dist/src/cli/dashboard/status.d.ts +30 -0
- package/dist/src/cli/dashboard/status.js +65 -0
- package/dist/src/cli/dashboard/upload.d.ts +16 -0
- package/dist/src/cli/dashboard/upload.js +48 -0
- package/dist/src/cli/deploy.d.ts +3 -3
- package/dist/src/cli/deploy.js +34 -9
- package/dist/src/cli/engine-flag-deprecation.d.ts +14 -0
- package/dist/src/cli/engine-flag-deprecation.js +20 -0
- package/dist/src/cli/fix.d.ts +18 -0
- package/dist/src/cli/fix.js +105 -11
- package/dist/src/cli/help-text.d.ts +52 -0
- package/dist/src/cli/help-text.js +416 -0
- package/dist/src/cli/implement.d.ts +91 -0
- package/dist/src/cli/implement.js +196 -0
- package/dist/src/cli/index.d.ts +2 -1
- package/dist/src/cli/index.js +774 -245
- package/dist/src/cli/json-envelope.d.ts +187 -0
- package/dist/src/cli/json-envelope.js +270 -0
- package/dist/src/cli/json-mode.d.ts +33 -0
- package/dist/src/cli/json-mode.js +201 -0
- package/dist/src/cli/migrate.d.ts +111 -0
- package/dist/src/cli/migrate.js +305 -0
- package/dist/src/cli/plan.d.ts +81 -0
- package/dist/src/cli/plan.js +149 -0
- package/dist/src/cli/pr.d.ts +106 -0
- package/dist/src/cli/pr.js +191 -19
- package/dist/src/cli/preflight.js +26 -0
- package/dist/src/cli/review.d.ts +27 -0
- package/dist/src/cli/review.js +126 -0
- package/dist/src/cli/runs-watch-renderer.d.ts +45 -0
- package/dist/src/cli/runs-watch-renderer.js +275 -0
- package/dist/src/cli/runs-watch.d.ts +41 -0
- package/dist/src/cli/runs-watch.js +395 -0
- package/dist/src/cli/runs.d.ts +122 -0
- package/dist/src/cli/runs.js +902 -0
- package/dist/src/cli/scaffold.d.ts +39 -0
- package/dist/src/cli/scaffold.js +287 -0
- package/dist/src/cli/scan.d.ts +93 -0
- package/dist/src/cli/scan.js +166 -40
- package/dist/src/cli/setup.d.ts +30 -0
- package/dist/src/cli/setup.js +137 -0
- package/dist/src/cli/spec.d.ts +66 -0
- package/dist/src/cli/spec.js +132 -0
- package/dist/src/cli/validate.d.ts +29 -0
- package/dist/src/cli/validate.js +131 -0
- package/dist/src/core/config/schema.d.ts +9 -0
- package/dist/src/core/config/schema.js +7 -0
- package/dist/src/core/config/types.d.ts +11 -0
- package/dist/src/core/council/runner.d.ts +10 -1
- package/dist/src/core/council/runner.js +25 -3
- package/dist/src/core/council/types.d.ts +7 -0
- package/dist/src/core/errors.d.ts +1 -1
- package/dist/src/core/errors.js +11 -0
- package/dist/src/core/logging/redaction.d.ts +13 -0
- package/dist/src/core/logging/redaction.js +20 -0
- package/dist/src/core/migrate/schema-validator.js +15 -1
- package/dist/src/core/phases/static-rules.d.ts +5 -1
- package/dist/src/core/phases/static-rules.js +2 -5
- package/dist/src/core/run-state/budget.d.ts +88 -0
- package/dist/src/core/run-state/budget.js +141 -0
- package/dist/src/core/run-state/cli-internal.d.ts +21 -0
- package/dist/src/core/run-state/cli-internal.js +174 -0
- package/dist/src/core/run-state/events.d.ts +59 -0
- package/dist/src/core/run-state/events.js +512 -0
- package/dist/src/core/run-state/lock.d.ts +61 -0
- package/dist/src/core/run-state/lock.js +206 -0
- package/dist/src/core/run-state/phase-context.d.ts +60 -0
- package/dist/src/core/run-state/phase-context.js +108 -0
- package/dist/src/core/run-state/phase-registry.d.ts +137 -0
- package/dist/src/core/run-state/phase-registry.js +162 -0
- package/dist/src/core/run-state/phase-runner.d.ts +80 -0
- package/dist/src/core/run-state/phase-runner.js +447 -0
- package/dist/src/core/run-state/provider-readback.d.ts +130 -0
- package/dist/src/core/run-state/provider-readback.js +426 -0
- package/dist/src/core/run-state/replay-decision.d.ts +69 -0
- package/dist/src/core/run-state/replay-decision.js +144 -0
- package/dist/src/core/run-state/resolve-engine.d.ts +45 -0
- package/dist/src/core/run-state/resolve-engine.js +74 -0
- package/dist/src/core/run-state/resume-preflight.d.ts +66 -0
- package/dist/src/core/run-state/resume-preflight.js +116 -0
- package/dist/src/core/run-state/run-phase-with-lifecycle.d.ts +69 -0
- package/dist/src/core/run-state/run-phase-with-lifecycle.js +193 -0
- package/dist/src/core/run-state/runs.d.ts +57 -0
- package/dist/src/core/run-state/runs.js +288 -0
- package/dist/src/core/run-state/snapshot.d.ts +14 -0
- package/dist/src/core/run-state/snapshot.js +114 -0
- package/dist/src/core/run-state/state.d.ts +40 -0
- package/dist/src/core/run-state/state.js +164 -0
- package/dist/src/core/run-state/types.d.ts +284 -0
- package/dist/src/core/run-state/types.js +19 -0
- package/dist/src/core/run-state/ulid.d.ts +11 -0
- package/dist/src/core/run-state/ulid.js +95 -0
- package/dist/src/core/schema-alignment/extractor/index.d.ts +1 -1
- package/dist/src/core/schema-alignment/extractor/index.js +2 -2
- package/dist/src/core/schema-alignment/extractor/prisma.d.ts +13 -1
- package/dist/src/core/schema-alignment/extractor/prisma.js +65 -10
- package/dist/src/core/schema-alignment/git-history.d.ts +19 -0
- package/dist/src/core/schema-alignment/git-history.js +53 -0
- package/dist/src/core/static-rules/rules/brand-tokens.js +2 -2
- package/dist/src/core/static-rules/rules/schema-alignment.js +14 -4
- package/dist/src/dashboard/auto-upload.d.ts +26 -0
- package/dist/src/dashboard/auto-upload.js +107 -0
- package/dist/src/dashboard/config.d.ts +22 -0
- package/dist/src/dashboard/config.js +109 -0
- package/dist/src/dashboard/upload/canonical.d.ts +3 -0
- package/dist/src/dashboard/upload/canonical.js +16 -0
- package/dist/src/dashboard/upload/chain.d.ts +9 -0
- package/dist/src/dashboard/upload/chain.js +27 -0
- package/dist/src/dashboard/upload/snapshot.d.ts +23 -0
- package/dist/src/dashboard/upload/snapshot.js +66 -0
- package/dist/src/dashboard/upload/uploader.d.ts +54 -0
- package/dist/src/dashboard/upload/uploader.js +330 -0
- package/package.json +19 -3
- package/scripts/autoregress.ts +1 -1
- package/scripts/test-runner.mjs +4 -0
- package/skills/claude-autopilot.md +1 -1
- package/skills/make-interfaces-feel-better/SKILL.md +104 -0
- package/skills/simplify-ui/SKILL.md +103 -0
- package/skills/ui/SKILL.md +117 -0
- package/skills/ui-ux-pro-max/SKILL.md +90 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export interface ScaffoldOptions {
|
|
2
|
+
cwd?: string;
|
|
3
|
+
specPath: string;
|
|
4
|
+
/** When true, log what would happen but don't write anything. */
|
|
5
|
+
dryRun?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface ScaffoldResult {
|
|
8
|
+
filesCreated: string[];
|
|
9
|
+
dirsCreated: string[];
|
|
10
|
+
filesSkippedExisting: string[];
|
|
11
|
+
packageJsonAction: 'created' | 'merged' | 'skipped-exists';
|
|
12
|
+
tsconfigAction: 'created' | 'skipped-exists' | 'skipped-no-ts';
|
|
13
|
+
}
|
|
14
|
+
interface ParsedFiles {
|
|
15
|
+
/** Raw paths extracted from the `## Files` section bullets. */
|
|
16
|
+
paths: string[];
|
|
17
|
+
/** Loosely-parsed package.json hints found anywhere in the section. */
|
|
18
|
+
packageHints: {
|
|
19
|
+
bin?: Record<string, string>;
|
|
20
|
+
type?: 'module' | 'commonjs';
|
|
21
|
+
dependencies?: Record<string, string>;
|
|
22
|
+
devDependencies?: Record<string, string>;
|
|
23
|
+
scripts?: Record<string, string>;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Parse the `## Files` (or `## files`) section of a spec markdown file.
|
|
28
|
+
* Tolerant: missing section returns `null`; malformed bullets are skipped
|
|
29
|
+
* silently. Returns extracted file paths + best-effort package-hint blob.
|
|
30
|
+
*/
|
|
31
|
+
export declare function parseSpecFiles(markdown: string): ParsedFiles | null;
|
|
32
|
+
/**
|
|
33
|
+
* Build a minimal starter package.json. Caller passes in any explicit
|
|
34
|
+
* hints (parsed from spec); we layer Node 22 ESM defaults on top.
|
|
35
|
+
*/
|
|
36
|
+
export declare function buildStarterPackageJson(projectName: string, hints: ParsedFiles['packageHints']): Record<string, unknown>;
|
|
37
|
+
export declare function runScaffold(opts: ScaffoldOptions): Promise<ScaffoldResult>;
|
|
38
|
+
export {};
|
|
39
|
+
//# sourceMappingURL=scaffold.d.ts.map
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
// v7.2.0 — `claude-autopilot scaffold --from-spec <path>`
|
|
2
|
+
//
|
|
3
|
+
// Closes the biggest remaining day-1 friction the v7.1.6 blank-repo
|
|
4
|
+
// benchmark identified: even with auto-scaffolded CLAUDE.md and .gitignore
|
|
5
|
+
// (v7.1.7), a fresh repo still needs a hand-written package.json, tsconfig,
|
|
6
|
+
// and directory skeleton before any feature work happens. This verb reads
|
|
7
|
+
// a spec markdown file's `## Files` section and creates the listed
|
|
8
|
+
// directories + a starter package.json + tsconfig.json.
|
|
9
|
+
//
|
|
10
|
+
// Scope intentionally small:
|
|
11
|
+
// - Node ESM only (one-shot ship; per-stack expansion is v8 work).
|
|
12
|
+
// - Touches files, never overwrites (operator opted into autopilot, not
|
|
13
|
+
// into us nuking their package.json).
|
|
14
|
+
// - Inspects spec for `scripts:` / `dependencies:` / `devDependencies:`
|
|
15
|
+
// hints in plain prose; uses heuristics rather than a strict schema.
|
|
16
|
+
//
|
|
17
|
+
// Spec format expectations (matches v7.1.6 benchmark spec shape):
|
|
18
|
+
//
|
|
19
|
+
// ## Files
|
|
20
|
+
//
|
|
21
|
+
// * `package.json` — `type: module`, `bin: { foo: bin/foo.js }`,
|
|
22
|
+
// `dependencies: { @anthropic-ai/sdk: ^0.91 }`, ...
|
|
23
|
+
// * `bin/foo.js` — argv parser + main loop.
|
|
24
|
+
// * `src/baz.js` — pure function.
|
|
25
|
+
// * `tests/foo.test.js` — node:test cases.
|
|
26
|
+
// * `README.md` — usage + install.
|
|
27
|
+
//
|
|
28
|
+
// Heuristics:
|
|
29
|
+
// - Backtick-quoted paths in `## Files` bullets become directories
|
|
30
|
+
// (parent of the path) and empty placeholder files (the path itself).
|
|
31
|
+
// - JSON-ish tokens in the bullet description (`type: module`,
|
|
32
|
+
// `dependencies: { foo: ^1 }`) get parsed loosely and merged into
|
|
33
|
+
// a starter package.json.
|
|
34
|
+
// - tsconfig is a Node 22 ESM default with `allowJs+checkJs+noEmit`
|
|
35
|
+
// when the spec lists `.js` files (matches v7.1.6 benchmark project),
|
|
36
|
+
// or compiled NodeNext when it lists `.ts`.
|
|
37
|
+
//
|
|
38
|
+
// What this DELIBERATELY does NOT do:
|
|
39
|
+
// - Run `npm install`. The user can decide which package manager.
|
|
40
|
+
// - Pick a test runner if the spec doesn't say. Echoes `npm test`.
|
|
41
|
+
// - Generate the CLAUDE.md (that's v7.1.7's job).
|
|
42
|
+
//
|
|
43
|
+
// Exit codes:
|
|
44
|
+
// 0 — scaffolded (or all targets already existed; idempotent)
|
|
45
|
+
// 1 — spec file missing or not readable
|
|
46
|
+
// 2 — spec missing a `## Files` section
|
|
47
|
+
import * as fs from 'node:fs';
|
|
48
|
+
import * as fsAsync from 'node:fs/promises';
|
|
49
|
+
import * as path from 'node:path';
|
|
50
|
+
const PASS = '\x1b[32m✓\x1b[0m';
|
|
51
|
+
const SKIP = '\x1b[2m·\x1b[0m';
|
|
52
|
+
const BOLD = (t) => `\x1b[1m${t}\x1b[0m`;
|
|
53
|
+
const DIM = (t) => `\x1b[2m${t}\x1b[0m`;
|
|
54
|
+
/**
|
|
55
|
+
* Parse the `## Files` (or `## files`) section of a spec markdown file.
|
|
56
|
+
* Tolerant: missing section returns `null`; malformed bullets are skipped
|
|
57
|
+
* silently. Returns extracted file paths + best-effort package-hint blob.
|
|
58
|
+
*/
|
|
59
|
+
export function parseSpecFiles(markdown) {
|
|
60
|
+
const filesSectionRe = /^##\s+files\s*$/im;
|
|
61
|
+
const m = filesSectionRe.exec(markdown);
|
|
62
|
+
if (!m)
|
|
63
|
+
return null;
|
|
64
|
+
const startIdx = m.index + m[0].length;
|
|
65
|
+
// Section ends at next heading or EOF.
|
|
66
|
+
const tail = markdown.slice(startIdx);
|
|
67
|
+
const nextHeadingMatch = /^#{1,6}\s+\S/m.exec(tail);
|
|
68
|
+
const sectionBody = nextHeadingMatch
|
|
69
|
+
? tail.slice(0, nextHeadingMatch.index)
|
|
70
|
+
: tail;
|
|
71
|
+
const paths = [];
|
|
72
|
+
// Bullet line: `* \`path\` — desc` or `- \`path\` — desc`.
|
|
73
|
+
const bulletRe = /^[*-]\s+`([^`]+)`/gm;
|
|
74
|
+
let bm;
|
|
75
|
+
while ((bm = bulletRe.exec(sectionBody)) !== null) {
|
|
76
|
+
const captured = bm[1];
|
|
77
|
+
if (!captured)
|
|
78
|
+
continue;
|
|
79
|
+
const raw = captured.trim();
|
|
80
|
+
// Skip prose-y entries by requiring path-shape: contains `/` or
|
|
81
|
+
// ends in known ext, OR is a known root-level file.
|
|
82
|
+
if (/[/.](?:js|ts|tsx|jsx|md|json|yaml|yml|sh|py|rs|go|rb|sql)$/i.test(raw) ||
|
|
83
|
+
raw === 'package.json' ||
|
|
84
|
+
raw === 'tsconfig.json' ||
|
|
85
|
+
raw === 'README.md' ||
|
|
86
|
+
raw === '.gitignore') {
|
|
87
|
+
paths.push(raw);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Loose package.json hint extraction. Look for inline tokens.
|
|
91
|
+
const packageHints = {};
|
|
92
|
+
if (/`?type\s*:\s*['"`]?module['"`]?/.test(sectionBody))
|
|
93
|
+
packageHints.type = 'module';
|
|
94
|
+
// bin: { foo: bin/foo.js }
|
|
95
|
+
const binMatch = /bin\s*:\s*\{\s*([^}]+)\s*\}/.exec(sectionBody);
|
|
96
|
+
const binBody = binMatch?.[1];
|
|
97
|
+
if (binBody) {
|
|
98
|
+
const entries = {};
|
|
99
|
+
for (const part of binBody.split(',')) {
|
|
100
|
+
const [name, target] = part.split(':').map((s) => s.trim().replace(/['"`]/g, ''));
|
|
101
|
+
if (name && target)
|
|
102
|
+
entries[name] = target;
|
|
103
|
+
}
|
|
104
|
+
if (Object.keys(entries).length > 0)
|
|
105
|
+
packageHints.bin = entries;
|
|
106
|
+
}
|
|
107
|
+
// dependencies: { foo: ^1 }
|
|
108
|
+
const depMatch = /dependencies\s*:\s*\{\s*([^}]+)\s*\}/i.exec(sectionBody);
|
|
109
|
+
const depBody = depMatch?.[1];
|
|
110
|
+
if (depBody) {
|
|
111
|
+
const entries = {};
|
|
112
|
+
for (const part of depBody.split(',')) {
|
|
113
|
+
const [name, version] = part.split(':').map((s) => s.trim().replace(/['"`]/g, ''));
|
|
114
|
+
if (name && version)
|
|
115
|
+
entries[name] = version;
|
|
116
|
+
}
|
|
117
|
+
if (Object.keys(entries).length > 0)
|
|
118
|
+
packageHints.dependencies = entries;
|
|
119
|
+
}
|
|
120
|
+
// scripts: { test: "..." } (handles quoted values via a 2nd pass)
|
|
121
|
+
const scriptsMatch = /scripts\s*:\s*\{\s*([^}]+)\s*\}/i.exec(sectionBody);
|
|
122
|
+
const scriptsBody = scriptsMatch?.[1];
|
|
123
|
+
if (scriptsBody) {
|
|
124
|
+
const entries = {};
|
|
125
|
+
// Use looser splitter — colon inside quoted values is fine.
|
|
126
|
+
const partRe = /([a-z_-]+)\s*:\s*["']([^"']+)["']/gi;
|
|
127
|
+
let pm;
|
|
128
|
+
while ((pm = partRe.exec(scriptsBody)) !== null) {
|
|
129
|
+
const [, key, value] = pm;
|
|
130
|
+
if (key && value)
|
|
131
|
+
entries[key] = value;
|
|
132
|
+
}
|
|
133
|
+
if (Object.keys(entries).length > 0)
|
|
134
|
+
packageHints.scripts = entries;
|
|
135
|
+
}
|
|
136
|
+
return { paths, packageHints };
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Build a minimal starter package.json. Caller passes in any explicit
|
|
140
|
+
* hints (parsed from spec); we layer Node 22 ESM defaults on top.
|
|
141
|
+
*/
|
|
142
|
+
export function buildStarterPackageJson(projectName, hints) {
|
|
143
|
+
const pkg = {
|
|
144
|
+
name: projectName,
|
|
145
|
+
version: '0.1.0',
|
|
146
|
+
private: true,
|
|
147
|
+
type: hints.type ?? 'module',
|
|
148
|
+
engines: { node: '>=22' },
|
|
149
|
+
scripts: {
|
|
150
|
+
test: 'node --test tests/*.test.js',
|
|
151
|
+
...hints.scripts,
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
if (hints.bin)
|
|
155
|
+
pkg.bin = hints.bin;
|
|
156
|
+
if (hints.dependencies)
|
|
157
|
+
pkg.dependencies = hints.dependencies;
|
|
158
|
+
if (hints.devDependencies)
|
|
159
|
+
pkg.devDependencies = hints.devDependencies;
|
|
160
|
+
return pkg;
|
|
161
|
+
}
|
|
162
|
+
const STARTER_TSCONFIG_JS = {
|
|
163
|
+
compilerOptions: {
|
|
164
|
+
target: 'ES2022',
|
|
165
|
+
module: 'NodeNext',
|
|
166
|
+
moduleResolution: 'NodeNext',
|
|
167
|
+
allowJs: true,
|
|
168
|
+
checkJs: true,
|
|
169
|
+
noEmit: true,
|
|
170
|
+
strict: true,
|
|
171
|
+
esModuleInterop: true,
|
|
172
|
+
skipLibCheck: true,
|
|
173
|
+
types: ['node'],
|
|
174
|
+
},
|
|
175
|
+
include: ['bin/**/*', 'src/**/*', 'tests/**/*'],
|
|
176
|
+
};
|
|
177
|
+
const STARTER_TSCONFIG_TS = {
|
|
178
|
+
compilerOptions: {
|
|
179
|
+
target: 'ES2022',
|
|
180
|
+
module: 'NodeNext',
|
|
181
|
+
moduleResolution: 'NodeNext',
|
|
182
|
+
outDir: 'dist',
|
|
183
|
+
strict: true,
|
|
184
|
+
esModuleInterop: true,
|
|
185
|
+
skipLibCheck: true,
|
|
186
|
+
types: ['node'],
|
|
187
|
+
},
|
|
188
|
+
include: ['bin/**/*', 'src/**/*', 'tests/**/*'],
|
|
189
|
+
};
|
|
190
|
+
export async function runScaffold(opts) {
|
|
191
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
192
|
+
const specAbs = path.isAbsolute(opts.specPath) ? opts.specPath : path.join(cwd, opts.specPath);
|
|
193
|
+
if (!fs.existsSync(specAbs)) {
|
|
194
|
+
process.stderr.write(`[scaffold] spec file not found: ${specAbs}\n`);
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
const md = await fsAsync.readFile(specAbs, 'utf8');
|
|
198
|
+
const parsed = parseSpecFiles(md);
|
|
199
|
+
if (!parsed) {
|
|
200
|
+
process.stderr.write(`[scaffold] spec missing a "## Files" section: ${specAbs}\n`);
|
|
201
|
+
process.exit(2);
|
|
202
|
+
}
|
|
203
|
+
console.log(`\n${BOLD('[scaffold]')} ${DIM(specAbs)}\n`);
|
|
204
|
+
const projectName = path.basename(cwd);
|
|
205
|
+
const filesCreated = [];
|
|
206
|
+
const filesSkippedExisting = [];
|
|
207
|
+
const dirsCreated = [];
|
|
208
|
+
let packageJsonAction = 'skipped-exists';
|
|
209
|
+
let tsconfigAction = 'skipped-no-ts';
|
|
210
|
+
// 1) Create directories first.
|
|
211
|
+
const dirs = new Set();
|
|
212
|
+
for (const p of parsed.paths) {
|
|
213
|
+
const d = path.dirname(p);
|
|
214
|
+
if (d && d !== '.')
|
|
215
|
+
dirs.add(d);
|
|
216
|
+
}
|
|
217
|
+
for (const d of dirs) {
|
|
218
|
+
const abs = path.join(cwd, d);
|
|
219
|
+
if (fs.existsSync(abs))
|
|
220
|
+
continue;
|
|
221
|
+
if (!opts.dryRun)
|
|
222
|
+
await fsAsync.mkdir(abs, { recursive: true });
|
|
223
|
+
dirsCreated.push(d);
|
|
224
|
+
console.log(` ${PASS} mkdir ${DIM(d + '/')}`);
|
|
225
|
+
}
|
|
226
|
+
// 2) Create placeholder files (skip ones we'll handle specially).
|
|
227
|
+
const SPECIAL = new Set(['package.json', 'tsconfig.json']);
|
|
228
|
+
for (const p of parsed.paths) {
|
|
229
|
+
if (SPECIAL.has(p))
|
|
230
|
+
continue;
|
|
231
|
+
const abs = path.join(cwd, p);
|
|
232
|
+
if (fs.existsSync(abs)) {
|
|
233
|
+
filesSkippedExisting.push(p);
|
|
234
|
+
console.log(` ${SKIP} exists ${DIM(p)}`);
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
if (!opts.dryRun) {
|
|
238
|
+
await fsAsync.mkdir(path.dirname(abs), { recursive: true });
|
|
239
|
+
// Touch — empty file. Real content is the agent's job.
|
|
240
|
+
await fsAsync.writeFile(abs, '', 'utf8');
|
|
241
|
+
}
|
|
242
|
+
filesCreated.push(p);
|
|
243
|
+
console.log(` ${PASS} touch ${DIM(p)}`);
|
|
244
|
+
}
|
|
245
|
+
// 3) package.json — only if the spec lists it.
|
|
246
|
+
if (parsed.paths.includes('package.json')) {
|
|
247
|
+
const pkgAbs = path.join(cwd, 'package.json');
|
|
248
|
+
if (fs.existsSync(pkgAbs)) {
|
|
249
|
+
packageJsonAction = 'skipped-exists';
|
|
250
|
+
console.log(` ${SKIP} exists ${DIM('package.json (preserved)')}`);
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
const pkg = buildStarterPackageJson(projectName, parsed.packageHints);
|
|
254
|
+
if (!opts.dryRun) {
|
|
255
|
+
await fsAsync.writeFile(pkgAbs, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
|
|
256
|
+
}
|
|
257
|
+
packageJsonAction = 'created';
|
|
258
|
+
console.log(` ${PASS} write ${DIM('package.json (Node 22 ESM starter)')}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// 4) tsconfig.json — only if the spec lists it. JS-flavor when the
|
|
262
|
+
// other paths are predominantly .js, TS-flavor for .ts.
|
|
263
|
+
if (parsed.paths.includes('tsconfig.json')) {
|
|
264
|
+
const tsAbs = path.join(cwd, 'tsconfig.json');
|
|
265
|
+
if (fs.existsSync(tsAbs)) {
|
|
266
|
+
tsconfigAction = 'skipped-exists';
|
|
267
|
+
console.log(` ${SKIP} exists ${DIM('tsconfig.json (preserved)')}`);
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
const otherPaths = parsed.paths.filter((p) => !SPECIAL.has(p));
|
|
271
|
+
const tsCount = otherPaths.filter((p) => /\.tsx?$/.test(p)).length;
|
|
272
|
+
const jsCount = otherPaths.filter((p) => /\.jsx?$/.test(p)).length;
|
|
273
|
+
const config = tsCount > jsCount ? STARTER_TSCONFIG_TS : STARTER_TSCONFIG_JS;
|
|
274
|
+
tsconfigAction = 'created';
|
|
275
|
+
if (!opts.dryRun) {
|
|
276
|
+
await fsAsync.writeFile(tsAbs, JSON.stringify(config, null, 2) + '\n', 'utf8');
|
|
277
|
+
}
|
|
278
|
+
const flavor = config === STARTER_TSCONFIG_TS ? 'compiled TS to dist/' : 'JS w/ JSDoc + checkJs';
|
|
279
|
+
console.log(` ${PASS} write ${DIM(`tsconfig.json (${flavor})`)}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
console.log(`\n${BOLD('Done.')} ${DIM(`${dirsCreated.length} dirs, ${filesCreated.length} files created, ${filesSkippedExisting.length} skipped.`)}\n`);
|
|
283
|
+
if (opts.dryRun)
|
|
284
|
+
console.log(DIM(`(--dry-run: no files were written)\n`));
|
|
285
|
+
return { filesCreated, dirsCreated, filesSkippedExisting, packageJsonAction, tsconfigAction };
|
|
286
|
+
}
|
|
287
|
+
//# sourceMappingURL=scaffold.js.map
|
package/dist/src/cli/scan.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { ReviewEngine } from '../adapters/review-engine/types.ts';
|
|
2
|
+
import type { GuardrailConfig } from '../core/config/types.ts';
|
|
3
|
+
import { type RunPhase } from '../core/run-state/phase-runner.ts';
|
|
1
4
|
export interface ScanCommandOptions {
|
|
2
5
|
cwd?: string;
|
|
3
6
|
configPath?: string;
|
|
@@ -6,6 +9,96 @@ export interface ScanCommandOptions {
|
|
|
6
9
|
ask?: string;
|
|
7
10
|
focus?: 'security' | 'logic' | 'performance' | 'brand' | 'all';
|
|
8
11
|
dryRun?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* v6.0.1 — engine knob inputs. The CLI dispatcher gathers these and the
|
|
14
|
+
* resolver picks a winner against (cli > env > config > built-in default).
|
|
15
|
+
* Both fields are optional; an absent CLI flag + absent env value falls
|
|
16
|
+
* through to the loaded config and then to the built-in default (off in v6.0).
|
|
17
|
+
*/
|
|
18
|
+
cliEngine?: boolean;
|
|
19
|
+
envEngine?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Test-only seam — injects a pre-built ReviewEngine so tests can exercise
|
|
22
|
+
* the engine-wrap path without hitting `loadAdapter()` (and therefore
|
|
23
|
+
* without needing an LLM API key in the environment). Production callers
|
|
24
|
+
* MUST NOT pass this; the CLI dispatcher does not expose a flag that sets
|
|
25
|
+
* it. Underscore-prefixed to make the test-seam intent obvious in code
|
|
26
|
+
* search.
|
|
27
|
+
*/
|
|
28
|
+
__testReviewEngine?: ReviewEngine;
|
|
9
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Input handed to the wrapped `RunPhase<ScanInput, ScanOutput>` body. Captures
|
|
32
|
+
* everything the phase needs that's already been resolved by the outer scope
|
|
33
|
+
* (file list, engine, focus hint, etc.) so the phase body itself is a thin
|
|
34
|
+
* await on `runReviewPhase` + finding post-processing.
|
|
35
|
+
*
|
|
36
|
+
* Exported (along with `ScanOutput`) so the v6.2.0 orchestrator's phase
|
|
37
|
+
* registry (`src/core/run-state/phase-registry.ts`) can carry the typed
|
|
38
|
+
* I/O shape on its `PhaseRegistration<ScanInput, ScanOutput>` slot.
|
|
39
|
+
*/
|
|
40
|
+
export interface ScanInput {
|
|
41
|
+
files: string[];
|
|
42
|
+
relFiles: string[];
|
|
43
|
+
cwd: string;
|
|
44
|
+
config: GuardrailConfig;
|
|
45
|
+
engine: ReviewEngine;
|
|
46
|
+
focusHint: string;
|
|
47
|
+
ask?: string;
|
|
48
|
+
focusLabel: string | null;
|
|
49
|
+
all: boolean;
|
|
50
|
+
}
|
|
51
|
+
/** What the wrapped phase returns. The outer scope translates this back to
|
|
52
|
+
* the legacy stdout banner + exit code path. Keeping the shape JSON-
|
|
53
|
+
* serializable means runPhase persists it into phases/<name>.json so a
|
|
54
|
+
* future skip-already-applied can restore it without re-running the LLM. */
|
|
55
|
+
export interface ScanOutput {
|
|
56
|
+
/** Total files actually sent to the review engine. */
|
|
57
|
+
fileCount: number;
|
|
58
|
+
/** Findings after ignore-rule filtering. Tagged with severity so a JSON
|
|
59
|
+
* consumer can reason about pass/fail without re-reading the cache. */
|
|
60
|
+
findings: Array<{
|
|
61
|
+
severity: 'critical' | 'warning' | 'note';
|
|
62
|
+
message: string;
|
|
63
|
+
file?: string;
|
|
64
|
+
line?: number;
|
|
65
|
+
suggestion?: string;
|
|
66
|
+
}>;
|
|
67
|
+
costUSD?: number;
|
|
68
|
+
durationMs: number;
|
|
69
|
+
/** Pass-through of the LLM's raw text outputs when --ask returned prose
|
|
70
|
+
* rather than structured findings — replicates the legacy "Answer:" path. */
|
|
71
|
+
rawOutputs?: string[];
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* v6.2.0 — sentinel returned by `buildScanPhase` when the verb's pre-flight
|
|
75
|
+
* (no targets, no files found, dry-run, missing LLM key, …) decided it can
|
|
76
|
+
* exit early without running the engine. The CLI dispatcher treats this as
|
|
77
|
+
* "render-and-exit"; the orchestrator skips registry dispatch and returns
|
|
78
|
+
* the exit code straight through. Mirrors the legacy in-place `return N`
|
|
79
|
+
* branches inside `runScan` byte-for-byte.
|
|
80
|
+
*/
|
|
81
|
+
export interface BuildScanPhaseEarlyExit {
|
|
82
|
+
kind: 'early-exit';
|
|
83
|
+
exitCode: number;
|
|
84
|
+
}
|
|
85
|
+
export interface BuildScanPhaseResult {
|
|
86
|
+
kind: 'phase';
|
|
87
|
+
phase: RunPhase<ScanInput, ScanOutput>;
|
|
88
|
+
input: ScanInput;
|
|
89
|
+
config: GuardrailConfig;
|
|
90
|
+
renderResult: (output: ScanOutput) => number;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* v6.2.0 — extract the `RunPhase<ScanInput, ScanOutput>` construction out of
|
|
94
|
+
* `runScan(options)` so the new top-level `autopilot` orchestrator can drive
|
|
95
|
+
* `runPhase` itself with a shared `phaseIdx` against the same run dir.
|
|
96
|
+
*
|
|
97
|
+
* The legacy `runScan(options)` keeps calling this builder internally (then
|
|
98
|
+
* `runPhaseWithLifecycle` for single-phase runs) so direct CLI behavior is
|
|
99
|
+
* byte-for-byte identical to v6.1. Parity is asserted by
|
|
100
|
+
* `tests/cli/scan-builder-parity.test.ts`.
|
|
101
|
+
*/
|
|
102
|
+
export declare function buildScanPhase(options: ScanCommandOptions): Promise<BuildScanPhaseResult | BuildScanPhaseEarlyExit>;
|
|
10
103
|
export declare function runScan(options?: ScanCommandOptions): Promise<number>;
|
|
11
104
|
//# sourceMappingURL=scan.d.ts.map
|