@delegance/claude-autopilot 7.2.1 → 7.4.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 +41 -0
- package/dist/src/cli/help-text.js +1 -1
- package/dist/src/cli/index.js +19 -2
- package/dist/src/cli/scaffold/node.d.ts +20 -0
- package/dist/src/cli/scaffold/node.js +162 -0
- package/dist/src/cli/scaffold/python.d.ts +71 -0
- package/dist/src/cli/scaffold/python.js +338 -0
- package/dist/src/cli/scaffold/types.d.ts +68 -0
- package/dist/src/cli/scaffold/types.js +6 -0
- package/dist/src/cli/scaffold.d.ts +43 -29
- package/dist/src/cli/scaffold.js +245 -174
- package/dist/src/index.d.ts +13 -0
- package/dist/src/index.js +33 -1
- package/package.json +3 -2
package/dist/src/cli/scaffold.js
CHANGED
|
@@ -1,60 +1,64 @@
|
|
|
1
|
-
// v7.
|
|
1
|
+
// v7.4.0 — `claude-autopilot scaffold --from-spec <path>` (per-stack).
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
3
|
+
// History:
|
|
4
|
+
// v7.2.0 — initial verb, Node ESM only.
|
|
5
|
+
// v7.4.0 — split per-stack scaffolders into ./scaffold/{node,python}.ts.
|
|
6
|
+
// This file remains the public entry point + stack detector +
|
|
7
|
+
// dispatcher; src/index.ts continues to re-export `runScaffold`,
|
|
8
|
+
// `parseSpecFiles`, `buildStarterPackageJson`, plus the
|
|
9
|
+
// `ScaffoldOptions` / `ScaffoldResult` types from here so library
|
|
10
|
+
// consumers don't break.
|
|
9
11
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
12
|
+
// Stack detection lives in `detectStack()`. Per the spec ("Stack detection"
|
|
13
|
+
// section), precedence is:
|
|
14
|
+
// 1. explicit --stack flag (validated; unknown -> exit 3)
|
|
15
|
+
// 2. FastAPI (path + 'fastapi' mention) — checked BEFORE generic Python so
|
|
16
|
+
// a FastAPI spec listing pyproject.toml isn't mis-classified
|
|
17
|
+
// 3. Python (pyproject.toml or requirements.txt)
|
|
18
|
+
// 4. Node (package.json)
|
|
19
|
+
// 5. Detected-but-unsupported (go.mod / Cargo.toml / Gemfile) -> exit 3
|
|
20
|
+
// 6. Fallback: Node ESM (preserves v7.2.0 default for ambiguous specs)
|
|
16
21
|
//
|
|
17
|
-
//
|
|
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).
|
|
22
|
+
// Polyglot guard: package.json AND pyproject.toml together without --stack
|
|
23
|
+
// -> exit 3 with "polyglot spec — pass --stack to disambiguate".
|
|
42
24
|
//
|
|
43
25
|
// Exit codes:
|
|
44
|
-
// 0 — scaffolded
|
|
45
|
-
// 1 — spec file missing
|
|
46
|
-
// 2 — spec missing
|
|
26
|
+
// 0 — scaffolded
|
|
27
|
+
// 1 — spec file missing
|
|
28
|
+
// 2 — spec missing `## Files` section
|
|
29
|
+
// 3 (NEW v7.4.0) — `--stack` value not recognized, detected-but-unsupported
|
|
30
|
+
// stack (Go/Rust/Ruby), or polyglot spec without --stack
|
|
47
31
|
import * as fs from 'node:fs';
|
|
48
32
|
import * as fsAsync from 'node:fs/promises';
|
|
49
33
|
import * as path from 'node:path';
|
|
50
|
-
|
|
51
|
-
|
|
34
|
+
import { scaffoldNode, buildStarterPackageJson } from "./scaffold/node.js";
|
|
35
|
+
import { scaffoldPython } from "./scaffold/python.js";
|
|
52
36
|
const BOLD = (t) => `\x1b[1m${t}\x1b[0m`;
|
|
53
37
|
const DIM = (t) => `\x1b[2m${t}\x1b[0m`;
|
|
38
|
+
// Re-export types + the legacy buildStarterPackageJson so `src/index.ts` and
|
|
39
|
+
// the existing tests/scaffold.test.ts (which imports from this module) keep
|
|
40
|
+
// compiling without changes.
|
|
41
|
+
export { buildStarterPackageJson };
|
|
42
|
+
/** Valid `--stack` argument values. v7.5+ adds 'go', 'rust', 'ruby'. */
|
|
43
|
+
export const SUPPORTED_STACKS = ['node', 'python', 'fastapi'];
|
|
44
|
+
/** Stacks we DETECT-but-don't-support yet. Mapped to spec exit-3 messages. */
|
|
45
|
+
export const UNSUPPORTED_STACK_FILES = {
|
|
46
|
+
go: 'go.mod',
|
|
47
|
+
rust: 'Cargo.toml',
|
|
48
|
+
ruby: 'Gemfile',
|
|
49
|
+
};
|
|
54
50
|
/**
|
|
55
51
|
* Parse the `## Files` (or `## files`) section of a spec markdown file.
|
|
56
52
|
* Tolerant: missing section returns `null`; malformed bullets are skipped
|
|
57
53
|
* silently. Returns extracted file paths + best-effort package-hint blob.
|
|
54
|
+
*
|
|
55
|
+
* v7.4.0 also extracts:
|
|
56
|
+
* - `stackHint` — first prose mention of `fastapi` / `python` / `node`
|
|
57
|
+
* (case-insensitive). Used as a tie-breaker when path heuristics are
|
|
58
|
+
* ambiguous between Python and FastAPI.
|
|
59
|
+
* - `pythonDeps` — narrow extraction per spec ("Dependency hint
|
|
60
|
+
* extraction"): explicit `dependencies: [...]` block, backticked
|
|
61
|
+
* package names with extras, and the phrase `depends on <name>`.
|
|
58
62
|
*/
|
|
59
63
|
export function parseSpecFiles(markdown) {
|
|
60
64
|
const filesSectionRe = /^##\s+files\s*$/im;
|
|
@@ -78,12 +82,20 @@ export function parseSpecFiles(markdown) {
|
|
|
78
82
|
continue;
|
|
79
83
|
const raw = captured.trim();
|
|
80
84
|
// Skip prose-y entries by requiring path-shape: contains `/` or
|
|
81
|
-
// ends in known ext, OR is a known root-level file.
|
|
82
|
-
|
|
85
|
+
// ends in known ext, OR is a known root-level file. v7.4.0 adds
|
|
86
|
+
// `pyproject.toml`, `requirements.txt`, `Cargo.toml`, `Gemfile`,
|
|
87
|
+
// `go.mod` to the root-file allowlist so the stack detector can see
|
|
88
|
+
// them.
|
|
89
|
+
if (/[/.](?:js|ts|tsx|jsx|md|json|yaml|yml|sh|py|rs|go|rb|sql|toml)$/i.test(raw) ||
|
|
83
90
|
raw === 'package.json' ||
|
|
84
91
|
raw === 'tsconfig.json' ||
|
|
85
92
|
raw === 'README.md' ||
|
|
86
|
-
raw === '.gitignore'
|
|
93
|
+
raw === '.gitignore' ||
|
|
94
|
+
raw === 'pyproject.toml' ||
|
|
95
|
+
raw === 'requirements.txt' ||
|
|
96
|
+
raw === 'Cargo.toml' ||
|
|
97
|
+
raw === 'Gemfile' ||
|
|
98
|
+
raw === 'go.mod') {
|
|
87
99
|
paths.push(raw);
|
|
88
100
|
}
|
|
89
101
|
}
|
|
@@ -104,12 +116,14 @@ export function parseSpecFiles(markdown) {
|
|
|
104
116
|
if (Object.keys(entries).length > 0)
|
|
105
117
|
packageHints.bin = entries;
|
|
106
118
|
}
|
|
107
|
-
// dependencies: { foo: ^1 }
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
119
|
+
// dependencies: { foo: ^1 } — Node-shape (object form).
|
|
120
|
+
// Skip if it looks like an array (Python-shape `dependencies: [...]`)
|
|
121
|
+
// — that's handled below as a Python dep block.
|
|
122
|
+
const depObjMatch = /dependencies\s*:\s*\{\s*([^}]+)\s*\}/i.exec(sectionBody);
|
|
123
|
+
const depObjBody = depObjMatch?.[1];
|
|
124
|
+
if (depObjBody) {
|
|
111
125
|
const entries = {};
|
|
112
|
-
for (const part of
|
|
126
|
+
for (const part of depObjBody.split(',')) {
|
|
113
127
|
const [name, version] = part.split(':').map((s) => s.trim().replace(/['"`]/g, ''));
|
|
114
128
|
if (name && version)
|
|
115
129
|
entries[name] = version;
|
|
@@ -133,60 +147,133 @@ export function parseSpecFiles(markdown) {
|
|
|
133
147
|
if (Object.keys(entries).length > 0)
|
|
134
148
|
packageHints.scripts = entries;
|
|
135
149
|
}
|
|
150
|
+
// v7.4.0 — Python dep extraction (narrow contract, codex W6).
|
|
151
|
+
// Pattern 1: explicit `dependencies: [foo, bar, baz]` array form.
|
|
152
|
+
// We deliberately accept the Python-style array AFTER the Node-style
|
|
153
|
+
// object check above so a Node spec with `dependencies: { foo: ^1 }`
|
|
154
|
+
// still flows into packageHints.dependencies.
|
|
155
|
+
const pythonDeps = [];
|
|
156
|
+
const depArrayMatch = /dependencies\s*:\s*\[\s*([^\]]+)\s*\]/i.exec(sectionBody);
|
|
157
|
+
const depArrayBody = depArrayMatch?.[1];
|
|
158
|
+
if (depArrayBody) {
|
|
159
|
+
for (const raw of depArrayBody.split(',')) {
|
|
160
|
+
const cleaned = raw.trim().replace(/^[`'"]/, '').replace(/[`'"]$/, '');
|
|
161
|
+
if (cleaned)
|
|
162
|
+
pythonDeps.push(cleaned);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Pattern 2: backticked package names with extras. We look for
|
|
166
|
+
// backticks containing `name[extra]` (with or without a version
|
|
167
|
+
// suffix). This is intentionally narrow — `foo` alone in backticks
|
|
168
|
+
// could be anything (filename, prose), so we require the `[extra]`
|
|
169
|
+
// shape to fire this pattern.
|
|
170
|
+
const extrasRe = /`([A-Za-z][A-Za-z0-9._-]*\[[^\]`]+\][^`]*)`/g;
|
|
171
|
+
let em;
|
|
172
|
+
while ((em = extrasRe.exec(sectionBody)) !== null) {
|
|
173
|
+
const value = em[1]?.trim();
|
|
174
|
+
if (value)
|
|
175
|
+
pythonDeps.push(value);
|
|
176
|
+
}
|
|
177
|
+
// Pattern 3: phrase `depends on <name>`. We capture the next
|
|
178
|
+
// identifier-shaped token (PEP 508 names: letters / digits / `._-`).
|
|
179
|
+
const dependsOnRe = /depends\s+on\s+`?([A-Za-z][A-Za-z0-9._-]*(?:\[[^\]]+\])?(?:[<>=!~][^\s`]+)?)`?/gi;
|
|
180
|
+
let dm;
|
|
181
|
+
while ((dm = dependsOnRe.exec(sectionBody)) !== null) {
|
|
182
|
+
const name = dm[1]?.trim();
|
|
183
|
+
if (name)
|
|
184
|
+
pythonDeps.push(name);
|
|
185
|
+
}
|
|
186
|
+
if (pythonDeps.length > 0)
|
|
187
|
+
packageHints.pythonDeps = pythonDeps;
|
|
188
|
+
// v7.4.0 — stack hint extraction. First-match wins, FastAPI checked
|
|
189
|
+
// before generic Python (codex C1) so prose like "FastAPI app on
|
|
190
|
+
// Python 3.12" classifies as fastapi, not python.
|
|
191
|
+
if (/\bfastapi\b/i.test(sectionBody)) {
|
|
192
|
+
packageHints.stackHint = 'fastapi';
|
|
193
|
+
}
|
|
194
|
+
else if (/\bpython\b/i.test(sectionBody)) {
|
|
195
|
+
packageHints.stackHint = 'python';
|
|
196
|
+
}
|
|
197
|
+
else if (/\bnode(?:\.js)?\s+\d+\b/i.test(sectionBody)) {
|
|
198
|
+
packageHints.stackHint = 'node';
|
|
199
|
+
}
|
|
136
200
|
return { paths, packageHints };
|
|
137
201
|
}
|
|
138
202
|
/**
|
|
139
|
-
*
|
|
140
|
-
*
|
|
203
|
+
* Apply the precedence ladder documented at the top of this file. Pure
|
|
204
|
+
* function — no I/O — so it's directly unit-testable.
|
|
205
|
+
*/
|
|
206
|
+
export function detectStack(parsed, explicit) {
|
|
207
|
+
// Step 1: explicit override always wins.
|
|
208
|
+
if (explicit)
|
|
209
|
+
return { kind: 'resolved', stack: explicit };
|
|
210
|
+
const paths = parsed.paths;
|
|
211
|
+
const has = (name) => paths.includes(name);
|
|
212
|
+
const hasMainPy = paths.some(p => p === 'main.py' ||
|
|
213
|
+
p === 'app/main.py' ||
|
|
214
|
+
/^src\/[^/]+\/main\.py$/.test(p));
|
|
215
|
+
const hasFastapiMention = parsed.packageHints.stackHint === 'fastapi';
|
|
216
|
+
const hasPythonMarker = has('pyproject.toml') || has('requirements.txt');
|
|
217
|
+
const hasNodeMarker = has('package.json');
|
|
218
|
+
// Polyglot guard (codex W3) — Node + Python without --stack.
|
|
219
|
+
if (hasNodeMarker && hasPythonMarker) {
|
|
220
|
+
return {
|
|
221
|
+
kind: 'polyglot',
|
|
222
|
+
message: 'polyglot spec — pass --stack to disambiguate',
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
// Step 2: FastAPI (BEFORE generic Python — codex C1).
|
|
226
|
+
if (hasMainPy && hasFastapiMention) {
|
|
227
|
+
return { kind: 'resolved', stack: 'fastapi' };
|
|
228
|
+
}
|
|
229
|
+
// Edge: spec lists pyproject.toml AND mentions FastAPI in prose but
|
|
230
|
+
// doesn't list main.py — still classify as FastAPI; we generate
|
|
231
|
+
// main.py ourselves anyway.
|
|
232
|
+
if (hasPythonMarker && hasFastapiMention) {
|
|
233
|
+
return { kind: 'resolved', stack: 'fastapi' };
|
|
234
|
+
}
|
|
235
|
+
// Step 3: Python.
|
|
236
|
+
if (hasPythonMarker)
|
|
237
|
+
return { kind: 'resolved', stack: 'python' };
|
|
238
|
+
// Step 4: Node.
|
|
239
|
+
if (hasNodeMarker)
|
|
240
|
+
return { kind: 'resolved', stack: 'node' };
|
|
241
|
+
// Step 5: detected-but-unsupported (codex W2).
|
|
242
|
+
for (const [stack, file] of Object.entries(UNSUPPORTED_STACK_FILES)) {
|
|
243
|
+
if (has(file)) {
|
|
244
|
+
return {
|
|
245
|
+
kind: 'unsupported',
|
|
246
|
+
stack,
|
|
247
|
+
message: `${stack} detected but not supported until v7.5`,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
// Step 6: fallback — Node ESM (preserves v7.2.0 default for ambiguous
|
|
252
|
+
// specs that listed only paths with no root-marker file).
|
|
253
|
+
return { kind: 'resolved', stack: 'node' };
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Print the `--list-stacks` output (codex NOTE #2). Three sections:
|
|
257
|
+
* Supported, Auto-detected, Recognized-but-unsupported.
|
|
141
258
|
*/
|
|
142
|
-
export function
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
pkg.devDependencies = hints.devDependencies;
|
|
160
|
-
return pkg;
|
|
259
|
+
export function printStackList() {
|
|
260
|
+
console.log('');
|
|
261
|
+
console.log(BOLD('Supported (--stack accepts these):'));
|
|
262
|
+
console.log(' node Node 22 ESM (package.json + tsconfig.json)');
|
|
263
|
+
console.log(' python Python 3.11+ (pyproject.toml + hatchling + pytest)');
|
|
264
|
+
console.log(' fastapi Python + FastAPI (auto-includes fastapi + uvicorn[standard])');
|
|
265
|
+
console.log('');
|
|
266
|
+
console.log(BOLD('Auto-detected from `## Files`:'));
|
|
267
|
+
console.log(' node when `package.json` is listed');
|
|
268
|
+
console.log(' python when `pyproject.toml` or `requirements.txt` is listed');
|
|
269
|
+
console.log(' fastapi when `main.py` is listed AND a bullet mentions `fastapi`');
|
|
270
|
+
console.log('');
|
|
271
|
+
console.log(BOLD('Recognized-but-unsupported (exit 3):'));
|
|
272
|
+
console.log(' go v7.5 (would detect via go.mod)');
|
|
273
|
+
console.log(' rust v7.5 (would detect via Cargo.toml)');
|
|
274
|
+
console.log(' ruby v7.5+ (would detect via Gemfile)');
|
|
275
|
+
console.log('');
|
|
161
276
|
}
|
|
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
277
|
export async function runScaffold(opts) {
|
|
191
278
|
const cwd = opts.cwd ?? process.cwd();
|
|
192
279
|
const specAbs = path.isAbsolute(opts.specPath) ? opts.specPath : path.join(cwd, opts.specPath);
|
|
@@ -200,88 +287,72 @@ export async function runScaffold(opts) {
|
|
|
200
287
|
process.stderr.write(`[scaffold] spec missing a "## Files" section: ${specAbs}\n`);
|
|
201
288
|
process.exit(2);
|
|
202
289
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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);
|
|
290
|
+
// Validate explicit --stack value. The CLI dispatch in src/cli/index.ts
|
|
291
|
+
// also validates, but doing it here too means library consumers calling
|
|
292
|
+
// `runScaffold({ stack: 'python' })` get the same guard.
|
|
293
|
+
if (opts.stack && !SUPPORTED_STACKS.includes(opts.stack)) {
|
|
294
|
+
process.stderr.write(`[scaffold] --stack "${opts.stack}" not recognized — supported: ${SUPPORTED_STACKS.join(', ')}\n`);
|
|
295
|
+
process.exit(3);
|
|
216
296
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (!opts.dryRun)
|
|
222
|
-
await fsAsync.mkdir(abs, { recursive: true });
|
|
223
|
-
dirsCreated.push(d);
|
|
224
|
-
console.log(` ${PASS} mkdir ${DIM(d + '/')}`);
|
|
297
|
+
const detection = detectStack(parsed, opts.stack);
|
|
298
|
+
if (detection.kind === 'unsupported') {
|
|
299
|
+
process.stderr.write(`[scaffold] ${detection.message}\n`);
|
|
300
|
+
process.exit(3);
|
|
225
301
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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)}`);
|
|
302
|
+
if (detection.kind === 'polyglot') {
|
|
303
|
+
process.stderr.write(`[scaffold] ${detection.message}\n`);
|
|
304
|
+
process.exit(3);
|
|
244
305
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
306
|
+
const stack = detection.stack;
|
|
307
|
+
console.log(`\n${BOLD('[scaffold]')} ${DIM(specAbs)} ${DIM(`(stack: ${stack})`)}\n`);
|
|
308
|
+
// codex W5 — when --stack <python|fastapi> is explicit and the spec
|
|
309
|
+
// ALSO lists Node files, warn + filter them out so the Python
|
|
310
|
+
// scaffolder doesn't try to touch them.
|
|
311
|
+
let ignoredOtherStackFiles;
|
|
312
|
+
let parsedForStack = parsed;
|
|
313
|
+
if (opts.stack && (stack === 'python' || stack === 'fastapi')) {
|
|
314
|
+
const NODE_FILES = new Set(['package.json', 'tsconfig.json']);
|
|
315
|
+
const ignored = parsed.paths.filter(p => NODE_FILES.has(p));
|
|
316
|
+
if (ignored.length > 0) {
|
|
317
|
+
ignoredOtherStackFiles = ignored;
|
|
318
|
+
console.log(` ${DIM(`! ignoring Node files (--stack ${stack}): ${ignored.join(', ')}`)}`);
|
|
319
|
+
parsedForStack = {
|
|
320
|
+
...parsed,
|
|
321
|
+
paths: parsed.paths.filter(p => !NODE_FILES.has(p)),
|
|
322
|
+
};
|
|
259
323
|
}
|
|
260
324
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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})`)}`);
|
|
325
|
+
else if (opts.stack === 'node') {
|
|
326
|
+
// Symmetric: when --stack node is forced and the spec also lists
|
|
327
|
+
// Python markers, drop them so we don't touch them as placeholders.
|
|
328
|
+
const PYTHON_FILES = new Set(['pyproject.toml', 'requirements.txt']);
|
|
329
|
+
const ignored = parsed.paths.filter(p => PYTHON_FILES.has(p));
|
|
330
|
+
if (ignored.length > 0) {
|
|
331
|
+
ignoredOtherStackFiles = ignored;
|
|
332
|
+
console.log(` ${DIM(`! ignoring Python files (--stack node): ${ignored.join(', ')}`)}`);
|
|
333
|
+
parsedForStack = {
|
|
334
|
+
...parsed,
|
|
335
|
+
paths: parsed.paths.filter(p => !PYTHON_FILES.has(p)),
|
|
336
|
+
};
|
|
280
337
|
}
|
|
281
338
|
}
|
|
282
|
-
|
|
339
|
+
const ctx = { cwd, parsed: parsedForStack, dryRun: !!opts.dryRun };
|
|
340
|
+
let result;
|
|
341
|
+
if (stack === 'python') {
|
|
342
|
+
result = await scaffoldPython(ctx, { isFastapi: false });
|
|
343
|
+
}
|
|
344
|
+
else if (stack === 'fastapi') {
|
|
345
|
+
result = await scaffoldPython(ctx, { isFastapi: true });
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
result = await scaffoldNode(ctx);
|
|
349
|
+
}
|
|
350
|
+
result.stack = stack;
|
|
351
|
+
if (ignoredOtherStackFiles)
|
|
352
|
+
result.ignoredOtherStackFiles = ignoredOtherStackFiles;
|
|
353
|
+
console.log(`\n${BOLD('Done.')} ${DIM(`${result.dirsCreated.length} dirs, ${result.filesCreated.length} files created, ${result.filesSkippedExisting.length} skipped.`)}\n`);
|
|
283
354
|
if (opts.dryRun)
|
|
284
355
|
console.log(DIM(`(--dry-run: no files were written)\n`));
|
|
285
|
-
return
|
|
356
|
+
return result;
|
|
286
357
|
}
|
|
287
358
|
//# sourceMappingURL=scaffold.js.map
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
1
|
export type { Finding, Severity, FindingSource } from './core/findings/types.js';
|
|
2
2
|
export type { RunResult, RunInput, PhaseResult } from './core/pipeline/run.js';
|
|
3
3
|
export type { GuardrailConfig, AdapterRef, AdapterReference } from './core/config/types.js';
|
|
4
|
+
export { runScan } from './cli/scan.js';
|
|
5
|
+
export { runScaffold } from './cli/scaffold.js';
|
|
6
|
+
export { runValidate } from './cli/validate.js';
|
|
7
|
+
export { runFix } from './cli/fix.js';
|
|
8
|
+
export { runCosts } from './cli/costs.js';
|
|
9
|
+
export { runReport } from './cli/report.js';
|
|
10
|
+
export { runDoctor } from './cli/preflight.js';
|
|
11
|
+
export { runSetup } from './cli/setup.js';
|
|
12
|
+
export { runDeploy, runDeployStatus, runDeployRollback } from './cli/deploy.js';
|
|
13
|
+
export { detectProject } from './cli/detector.js';
|
|
14
|
+
export type { DetectionResult } from './cli/detector.js';
|
|
15
|
+
export type { ScaffoldOptions, ScaffoldResult } from './cli/scaffold.js';
|
|
16
|
+
export type { SetupOptions, ProfileName } from './cli/setup.js';
|
|
4
17
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/index.js
CHANGED
|
@@ -1,2 +1,34 @@
|
|
|
1
|
-
|
|
1
|
+
// v7.3.0 — Curated library API for in-process consumers (notably the v8
|
|
2
|
+
// daemon, which imports these instead of spawning the CLI subprocess).
|
|
3
|
+
//
|
|
4
|
+
// ## Stability contract
|
|
5
|
+
//
|
|
6
|
+
// Anything re-exported below is part of the supported library API. Changes
|
|
7
|
+
// to function signatures here are SemVer-major. Internal refactors that
|
|
8
|
+
// don't change the exported shape are SemVer-minor or patch.
|
|
9
|
+
//
|
|
10
|
+
// Functions deliberately NOT re-exported (still callable via direct
|
|
11
|
+
// `@delegance/claude-autopilot/cli/*` imports if you really need them, but
|
|
12
|
+
// no compatibility guarantee):
|
|
13
|
+
// - JSON-envelope wrappers (`runUnderJsonMode`, `runAutopilotWithJsonEnvelope`)
|
|
14
|
+
// — those are CLI-shape helpers, not library shape.
|
|
15
|
+
// - Internal `_*` helpers and test seams.
|
|
16
|
+
// - The `runs` / `runs-watch` group — engine introspection is a separate
|
|
17
|
+
// v8 prerequisite (`@delegance/claude-autopilot/run-state` will export
|
|
18
|
+
// it once it's stable).
|
|
19
|
+
//
|
|
20
|
+
// See docs/library-api.md for the full surface + usage examples.
|
|
21
|
+
// Pipeline verbs (read-only / discovery).
|
|
22
|
+
export { runScan } from './cli/scan.js';
|
|
23
|
+
export { runScaffold } from './cli/scaffold.js';
|
|
24
|
+
export { runValidate } from './cli/validate.js';
|
|
25
|
+
export { runFix } from './cli/fix.js';
|
|
26
|
+
export { runCosts } from './cli/costs.js';
|
|
27
|
+
export { runReport } from './cli/report.js';
|
|
28
|
+
export { runDoctor } from './cli/preflight.js';
|
|
29
|
+
export { runSetup } from './cli/setup.js';
|
|
30
|
+
// Pipeline verbs (side-effecting — daemon must wrap these in policy gates).
|
|
31
|
+
export { runDeploy, runDeployStatus, runDeployRollback } from './cli/deploy.js';
|
|
32
|
+
// Helpers.
|
|
33
|
+
export { detectProject } from './cli/detector.js';
|
|
2
34
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@delegance/claude-autopilot",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.4.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"tag": "next"
|
|
@@ -36,7 +36,8 @@
|
|
|
36
36
|
"types": "./dist/src/index.d.ts",
|
|
37
37
|
"exports": {
|
|
38
38
|
".": {
|
|
39
|
-
"types": "./dist/src/index.d.ts"
|
|
39
|
+
"types": "./dist/src/index.d.ts",
|
|
40
|
+
"default": "./dist/src/index.js"
|
|
40
41
|
},
|
|
41
42
|
"./bin/claude-autopilot.js": "./bin/claude-autopilot.js",
|
|
42
43
|
"./bin/guardrail.js": "./bin/guardrail.js",
|