@delegance/claude-autopilot 7.7.0 → 7.9.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 +62 -0
- package/README.md +22 -6
- package/bin/_launcher.js +310 -6
- package/dist/src/cli/dashboard/missing-package.d.ts +48 -0
- package/dist/src/cli/dashboard/missing-package.js +85 -0
- package/dist/src/cli/dashboard/upload.d.ts +1 -1
- package/dist/src/cli/dashboard/upload.js +20 -1
- package/dist/src/cli/help-text.d.ts +1 -1
- package/dist/src/cli/help-text.js +11 -1
- package/dist/src/cli/index.js +30 -0
- package/dist/src/cli/tsx-resolver.d.ts +42 -0
- package/dist/src/cli/tsx-resolver.js +376 -0
- package/package.json +4 -7
- package/skills/autopilot/SKILL.md +188 -127
- package/scripts/autoregress.ts +0 -402
- package/scripts/snapshots/impact-selector.ts +0 -60
- package/scripts/snapshots/import-scanner.ts +0 -44
- package/scripts/snapshots/serializer.ts +0 -24
- package/tests/snapshots/baselines/.gitkeep +0 -0
- package/tests/snapshots/baselines/src-formatters-sarif.json +0 -210
- package/tests/snapshots/baselines/src-snapshots-impact-selector.json +0 -32
- package/tests/snapshots/baselines/src-snapshots-import-scanner.json +0 -21
- package/tests/snapshots/baselines/src-snapshots-serializer.json +0 -39
- package/tests/snapshots/import-map.json +0 -138
- package/tests/snapshots/index.json +0 -14
- package/tests/snapshots/src-formatters-sarif.snap.ts +0 -132
- package/tests/snapshots/src-snapshots-impact-selector.snap.ts +0 -95
- package/tests/snapshots/src-snapshots-import-scanner.snap.ts +0 -126
- package/tests/snapshots/src-snapshots-serializer.snap.ts +0 -64
package/dist/src/cli/index.js
CHANGED
|
@@ -74,6 +74,36 @@ process.on('uncaughtException', err => {
|
|
|
74
74
|
process.exit(exit);
|
|
75
75
|
});
|
|
76
76
|
const args = process.argv.slice(2);
|
|
77
|
+
// v7.8.0 — `--tsx-source <bundled|project|path>` resolution-override flag.
|
|
78
|
+
// The launcher (bin/_launcher.js) already consumed this to pick the tsx
|
|
79
|
+
// binary; here we validate the value, surface a clear error on bad input,
|
|
80
|
+
// and strip the tokens from argv before subcommand dispatch so downstream
|
|
81
|
+
// parsers don't choke on an unknown flag. See
|
|
82
|
+
// docs/specs/v7.8.0-decouple-runtime-deps.md (amendment A2).
|
|
83
|
+
const TSX_SOURCE_VALID = ['bundled', 'project', 'path'];
|
|
84
|
+
{
|
|
85
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
86
|
+
const a = args[i];
|
|
87
|
+
if (a === undefined)
|
|
88
|
+
continue;
|
|
89
|
+
if (a === '--tsx-source' || a.startsWith('--tsx-source=')) {
|
|
90
|
+
const value = a.startsWith('--tsx-source=') ? a.slice('--tsx-source='.length) : args[i + 1];
|
|
91
|
+
if (!value || !TSX_SOURCE_VALID.includes(value)) {
|
|
92
|
+
process.stderr.write(`\x1b[31m[claude-autopilot] Invalid --tsx-source value '${value ?? ''}'. ` +
|
|
93
|
+
`Expected ${TSX_SOURCE_VALID.join(', ')}.\x1b[0m\n`);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
// Strip the consumed tokens from argv so subcommands don't see them.
|
|
97
|
+
if (a.startsWith('--tsx-source=')) {
|
|
98
|
+
args.splice(i, 1);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
args.splice(i, 2);
|
|
102
|
+
}
|
|
103
|
+
i -= 1;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
77
107
|
// Version flag — read package.json via the shared package-root helper. Works
|
|
78
108
|
// under both source (src/cli/index.ts) and compiled (dist/src/cli/index.js)
|
|
79
109
|
// layouts since findPackageRoot walks up to the canonical package root.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type TsxSource = 'project-local' | 'path' | 'bundled';
|
|
2
|
+
export interface TsxResolution {
|
|
3
|
+
/** Command to spawn (absolute path, normally `process.execPath`). */
|
|
4
|
+
command: string;
|
|
5
|
+
/** Args prepended before the user script path. */
|
|
6
|
+
args: string[];
|
|
7
|
+
/** Where tsx came from — drives the deprecation warning. */
|
|
8
|
+
source: TsxSource;
|
|
9
|
+
/** Override origin if env var or CLI flag forced this resolution. */
|
|
10
|
+
forcedBy?: 'env' | 'flag';
|
|
11
|
+
/**
|
|
12
|
+
* If true, callers MUST spawn this resolution with `shell: true` (or via
|
|
13
|
+
* cmd.exe). Set on Windows PATH hits that resolve to a `.cmd`/`.bat` shim —
|
|
14
|
+
* Node's `spawn()` cannot execute those reliably without a shell.
|
|
15
|
+
* Bundled / project-local hits run `process.execPath <bin.js>` directly and
|
|
16
|
+
* don't need a shell.
|
|
17
|
+
*/
|
|
18
|
+
shell?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface ResolveOpts {
|
|
21
|
+
projectRoot: string;
|
|
22
|
+
/** Value of CLAUDE_AUTOPILOT_TSX env var, if set. */
|
|
23
|
+
envOverride?: string;
|
|
24
|
+
/** Value of --tsx-source CLI flag, if passed. */
|
|
25
|
+
flagOverride?: 'bundled' | 'project' | 'path';
|
|
26
|
+
/** Test helper — suppress deprecation warning even on bundled fallthrough. */
|
|
27
|
+
suppressWarning?: boolean;
|
|
28
|
+
/** Test helper — inject process.env (defaults to process.env). */
|
|
29
|
+
env?: NodeJS.ProcessEnv;
|
|
30
|
+
/** Test helper — inject process.platform (defaults to process.platform). */
|
|
31
|
+
platform?: NodeJS.Platform;
|
|
32
|
+
}
|
|
33
|
+
export declare function resolveTsx(opts: ResolveOpts): TsxResolution;
|
|
34
|
+
/**
|
|
35
|
+
* Compute the state dir for warning dedup (A7).
|
|
36
|
+
* 1. CLAUDE_AUTOPILOT_STATE_DIR explicit override
|
|
37
|
+
* 2. POSIX + XDG_STATE_HOME set → $XDG_STATE_HOME/claude-autopilot
|
|
38
|
+
* 3. fallback → ~/.claude-autopilot
|
|
39
|
+
*/
|
|
40
|
+
export declare function stateDir(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): string;
|
|
41
|
+
export declare const __TSX_DEPRECATION_MESSAGE: string;
|
|
42
|
+
//# sourceMappingURL=tsx-resolver.d.ts.map
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
// src/cli/tsx-resolver.ts
|
|
2
|
+
//
|
|
3
|
+
// Cross-platform tsx resolver used by the CLI launcher when the dev/source
|
|
4
|
+
// path is taken (running .ts files via `node --import tsx`). Returns a
|
|
5
|
+
// `{command, args, source}` triple so spawning is portable (Windows uses
|
|
6
|
+
// `.cmd` shims; spawn via process.execPath + the resolved JS entry to avoid
|
|
7
|
+
// shebang / PATHEXT quirks).
|
|
8
|
+
//
|
|
9
|
+
// Precedence (default):
|
|
10
|
+
// 1. project-local — <projectRoot>/node_modules/tsx (via createRequire)
|
|
11
|
+
// 2. PATH — hand-rolled cross-platform lookup (no `which` dep)
|
|
12
|
+
// 3. bundled — this package's own bundled tsx (with a once-per-day
|
|
13
|
+
// deprecation warning; tsx is scheduled to be removed from runtime
|
|
14
|
+
// deps in v8.0.0).
|
|
15
|
+
//
|
|
16
|
+
// Escape hatches (in priority order, both skip precedence):
|
|
17
|
+
// --tsx-source=<bundled|project|path> (CLI flag, "forcedBy: flag")
|
|
18
|
+
// CLAUDE_AUTOPILOT_TSX=<bundled|project|path> (env, "forcedBy: env")
|
|
19
|
+
//
|
|
20
|
+
// Deprecation warning is silenced by CLAUDE_AUTOPILOT_NO_TSX_DEPRECATION=1.
|
|
21
|
+
// Filesystem dedup writes (state dir) are non-fatal — even if the state
|
|
22
|
+
// dir can't be created, the warning still prints (intentional: readonly
|
|
23
|
+
// homedir in CI should not change semantics).
|
|
24
|
+
//
|
|
25
|
+
// Amendments from codex pass 2 wired in:
|
|
26
|
+
// A1 — `createRequire(import.meta.url)` at module scope (ESM-safe)
|
|
27
|
+
// A3 — PATH-resolved bin that lives inside our own node_modules is
|
|
28
|
+
// treated as `bundled` so the warning still fires
|
|
29
|
+
// A6 — hand-rolled PATH lookup (drops the `which` dep)
|
|
30
|
+
// A7 — XDG_STATE_HOME / CLAUDE_AUTOPILOT_STATE_DIR for warning dedup
|
|
31
|
+
import { createRequire } from 'node:module';
|
|
32
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync, realpathSync, readdirSync } from 'node:fs';
|
|
33
|
+
import * as os from 'node:os';
|
|
34
|
+
import * as path from 'node:path';
|
|
35
|
+
// A1: createRequire at module scope so `require.resolve('tsx/package.json')`
|
|
36
|
+
// works in ESM-compiled output (the package is `"type": "module"`).
|
|
37
|
+
const require = createRequire(import.meta.url);
|
|
38
|
+
const VALID_SOURCES = [
|
|
39
|
+
'bundled',
|
|
40
|
+
'project',
|
|
41
|
+
'path',
|
|
42
|
+
];
|
|
43
|
+
export function resolveTsx(opts) {
|
|
44
|
+
const { projectRoot, envOverride, flagOverride } = opts;
|
|
45
|
+
const env = opts.env ?? process.env;
|
|
46
|
+
const platform = opts.platform ?? process.platform;
|
|
47
|
+
// --- Escape hatch: --tsx-source flag (highest priority) ---
|
|
48
|
+
if (flagOverride) {
|
|
49
|
+
return resolveFromSource(flagOverride, projectRoot, 'flag', { env, platform });
|
|
50
|
+
}
|
|
51
|
+
// --- Escape hatch: CLAUDE_AUTOPILOT_TSX env var ---
|
|
52
|
+
if (envOverride && VALID_SOURCES.includes(envOverride)) {
|
|
53
|
+
return resolveFromSource(envOverride, projectRoot, 'env', { env, platform });
|
|
54
|
+
}
|
|
55
|
+
// --- Normal precedence ladder ---
|
|
56
|
+
const projectResolved = tryProjectLocal(projectRoot);
|
|
57
|
+
if (projectResolved)
|
|
58
|
+
return projectResolved;
|
|
59
|
+
const pathResolved = tryPath({ env, platform });
|
|
60
|
+
if (pathResolved)
|
|
61
|
+
return pathResolved;
|
|
62
|
+
// Fall back to bundled tsx + (maybe) deprecation warning.
|
|
63
|
+
const bundled = resolveBundled();
|
|
64
|
+
if (!opts.suppressWarning) {
|
|
65
|
+
emitTsxDeprecationWarningSafe({ env, platform });
|
|
66
|
+
}
|
|
67
|
+
return bundled;
|
|
68
|
+
}
|
|
69
|
+
function tryProjectLocal(projectRoot) {
|
|
70
|
+
// Only classify as "project-local" if the CONSUMER explicitly declared
|
|
71
|
+
// `tsx` in their package.json. npm hoists @delegance/claude-autopilot's
|
|
72
|
+
// own tsx dep to the consumer root `node_modules/tsx`, so a bare
|
|
73
|
+
// `require.resolve('tsx/package.json')` from the consumer root would
|
|
74
|
+
// succeed even when the consumer never declared tsx themselves. Without
|
|
75
|
+
// this gate we mislabel the hoisted bundled tsx as project-local,
|
|
76
|
+
// suppressing the deprecation warning that's supposed to drive the
|
|
77
|
+
// v8.0.0 migration.
|
|
78
|
+
if (!consumerDeclaresTsx(projectRoot))
|
|
79
|
+
return null;
|
|
80
|
+
try {
|
|
81
|
+
const projectRequire = createRequire(path.join(projectRoot, 'package.json'));
|
|
82
|
+
const pkgPath = projectRequire.resolve('tsx/package.json');
|
|
83
|
+
return buildResolutionFromPkgJson(pkgPath, 'project-local');
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Reads the consumer's `package.json` and returns true iff `tsx` appears in
|
|
91
|
+
* `dependencies`, `devDependencies`, or `peerDependencies`. Missing or
|
|
92
|
+
* malformed package.json → false (conservative: better to fall through to
|
|
93
|
+
* PATH/bundled than to silently mislabel hoisted deps as project-local).
|
|
94
|
+
*/
|
|
95
|
+
function consumerDeclaresTsx(projectRoot) {
|
|
96
|
+
try {
|
|
97
|
+
const pkgPath = path.join(projectRoot, 'package.json');
|
|
98
|
+
const raw = readFileSync(pkgPath, 'utf8');
|
|
99
|
+
const pkg = JSON.parse(raw);
|
|
100
|
+
return (!!pkg.dependencies && 'tsx' in pkg.dependencies
|
|
101
|
+
? true
|
|
102
|
+
: !!pkg.devDependencies && 'tsx' in pkg.devDependencies
|
|
103
|
+
? true
|
|
104
|
+
: !!pkg.peerDependencies && 'tsx' in pkg.peerDependencies);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Walk upward from `filePath` looking for `<dir>/node_modules/<pkgName>`.
|
|
112
|
+
* Returns the absolute path to the package root, or null if not found.
|
|
113
|
+
*
|
|
114
|
+
* Used by the A3 self-pointer guard. Replaces the previous hardcoded
|
|
115
|
+
* `path.dirname(path.dirname(binAbs))` walk which assumed tsx's bin lived
|
|
116
|
+
* exactly 2 directories deep inside its package — fragile when tsx ships
|
|
117
|
+
* its bin at `dist/cli.mjs` (3 deep) or future layouts.
|
|
118
|
+
*
|
|
119
|
+
* PARITY: keep in sync with bin/_launcher.js packageRootContaining().
|
|
120
|
+
*/
|
|
121
|
+
function packageRootContaining(filePath, pkgName) {
|
|
122
|
+
let dir = path.dirname(path.resolve(filePath));
|
|
123
|
+
while (dir !== path.dirname(dir)) {
|
|
124
|
+
if (path.basename(dir) === pkgName && path.basename(path.dirname(dir)) === 'node_modules') {
|
|
125
|
+
return dir;
|
|
126
|
+
}
|
|
127
|
+
dir = path.dirname(dir);
|
|
128
|
+
}
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Case-insensitive lookup for `tsx` + each PATHEXT entry inside `dir`.
|
|
133
|
+
* Real Windows NTFS is case-insensitive, so `tsx.cmd`, `TSX.CMD`, and
|
|
134
|
+
* `Tsx.Cmd` all resolve to the same file. Linux ext4 (and most CI
|
|
135
|
+
* containers) is case-sensitive, so the previous `existsSync(tsx + ext)`
|
|
136
|
+
* lookup mismatched when test fixtures wrote `tsx.cmd` (lowercase) and
|
|
137
|
+
* PATHEXT was iterated in uppercase form (`.CMD`).
|
|
138
|
+
*
|
|
139
|
+
* Returns the actual filename found in `dir` (with on-disk casing) joined
|
|
140
|
+
* onto `dir`, or null if no match.
|
|
141
|
+
*/
|
|
142
|
+
function findTsxCaseInsensitive(dir, exts) {
|
|
143
|
+
let entries;
|
|
144
|
+
try {
|
|
145
|
+
entries = readdirSync(dir);
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
// Build set of acceptable lowercase filenames: `tsx`, `tsx.exe`, `tsx.cmd`, ...
|
|
151
|
+
const wanted = new Set(exts.map((ext) => `tsx${ext}`.toLowerCase()));
|
|
152
|
+
for (const entry of entries) {
|
|
153
|
+
if (wanted.has(entry.toLowerCase())) {
|
|
154
|
+
return path.join(dir, entry);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Hand-rolled PATH lookup (A6 — drops the `which` dep). On Windows, walk
|
|
161
|
+
* PATHEXT for each entry with a case-insensitive filename match; on POSIX,
|
|
162
|
+
* look for the bare `tsx` filename (case-sensitive).
|
|
163
|
+
*/
|
|
164
|
+
function tryPath(opts) {
|
|
165
|
+
const PATH = opts.env.PATH ?? opts.env.Path ?? '';
|
|
166
|
+
if (!PATH)
|
|
167
|
+
return null;
|
|
168
|
+
const isWin = opts.platform === 'win32';
|
|
169
|
+
const PATHEXT = isWin
|
|
170
|
+
? (opts.env.PATHEXT ?? '.EXE;.CMD;.BAT;.COM').split(';').filter(Boolean)
|
|
171
|
+
: [''];
|
|
172
|
+
const sep = isWin ? ';' : ':';
|
|
173
|
+
// A3 prep: figure out our bundled tsx package ROOT so we can detect a
|
|
174
|
+
// PATH entry that actually points back at our own node_modules. The
|
|
175
|
+
// previous implementation compared bin-dir prefixes (e.g.
|
|
176
|
+
// `node_modules/tsx/dist/`), but PATH hits are typically
|
|
177
|
+
// `node_modules/.bin/tsx` — a symlink (Unix) or `.cmd` shim (Windows)
|
|
178
|
+
// that lives in a SIBLING directory. `startsWith()` never matched.
|
|
179
|
+
// Compare package roots after `realpathSync` resolves the symlink/shim
|
|
180
|
+
// back to the underlying tsx package.
|
|
181
|
+
let bundledPkgRoot = null;
|
|
182
|
+
try {
|
|
183
|
+
const bundled = resolveBundled();
|
|
184
|
+
// `bundled.args[0]` is the absolute path to tsx's bin (e.g.
|
|
185
|
+
// `node_modules/tsx/dist/cli.mjs`). Walk upward to find the tsx
|
|
186
|
+
// package root rather than assuming a fixed depth — the previous
|
|
187
|
+
// `path.dirname(path.dirname(binAbs))` baked in 2 levels and broke
|
|
188
|
+
// on layouts where the bin lives 3+ directories deep inside the pkg.
|
|
189
|
+
const binAbs = bundled.args[0] ?? '';
|
|
190
|
+
if (binAbs) {
|
|
191
|
+
bundledPkgRoot = packageRootContaining(binAbs, 'tsx');
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
// If we can't even resolve the bundled tsx (shouldn't happen — it's a
|
|
196
|
+
// dep), continue without the self-pointer guard. The worst case is we
|
|
197
|
+
// miss the warning on a corner-case install.
|
|
198
|
+
bundledPkgRoot = null;
|
|
199
|
+
}
|
|
200
|
+
for (const rawDir of PATH.split(sep)) {
|
|
201
|
+
const dir = rawDir.trim();
|
|
202
|
+
if (!dir)
|
|
203
|
+
continue;
|
|
204
|
+
// On Windows, NTFS is case-insensitive — match filename case-insensitively
|
|
205
|
+
// against PATHEXT. On POSIX, ext4/btrfs are case-sensitive and PATHEXT is
|
|
206
|
+
// a single empty string, so the case-sensitive existsSync path is fine.
|
|
207
|
+
const candidate = isWin
|
|
208
|
+
? findTsxCaseInsensitive(dir, PATHEXT)
|
|
209
|
+
: (() => {
|
|
210
|
+
const c = path.join(dir, 'tsx');
|
|
211
|
+
return existsSync(c) ? c : null;
|
|
212
|
+
})();
|
|
213
|
+
if (!candidate)
|
|
214
|
+
continue;
|
|
215
|
+
// A3 — PATH hit that is actually inside our bundled node_modules
|
|
216
|
+
// is not really "user-supplied tsx on PATH"; skip this candidate and
|
|
217
|
+
// keep searching subsequent PATH entries. If no real user tsx exists
|
|
218
|
+
// anywhere on PATH, the loop falls off the end and the caller
|
|
219
|
+
// (resolveTsx) falls through to bundled with the deprecation warning.
|
|
220
|
+
//
|
|
221
|
+
// Critical: this MUST be `continue`, not `return null`. npm prepends
|
|
222
|
+
// `node_modules/.bin` to PATH when running scripts, so the bundled-
|
|
223
|
+
// pointing entry is hit first. A `return null` here would abort the
|
|
224
|
+
// entire PATH search, hiding a user's globally-installed tsx that
|
|
225
|
+
// appears later in PATH and triggering a spurious deprecation warning.
|
|
226
|
+
//
|
|
227
|
+
// Resolve symlinks via realpath because `node_modules/.bin/tsx` is
|
|
228
|
+
// typically a symlink to `../tsx/dist/cli.mjs` on Unix (and a .cmd
|
|
229
|
+
// shim on Windows that textually references the package directory).
|
|
230
|
+
if (bundledPkgRoot && isInBundledPackageRoot(candidate, bundledPkgRoot)) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
// For a PATH-resolved bin, spawn it directly. On Windows the .cmd
|
|
234
|
+
// shim handles dispatching to node; on Unix the shebang does.
|
|
235
|
+
//
|
|
236
|
+
// Critical: Node's `spawn()` on Windows cannot launch `.cmd`/`.bat`
|
|
237
|
+
// files directly — the OS exec syscalls require `cmd.exe`. Mark
|
|
238
|
+
// those hits so callers pass `shell: true` to `spawn()`. POSIX
|
|
239
|
+
// shebang hits don't need a shell.
|
|
240
|
+
const needsShell = isWin && /\.(cmd|bat)$/i.test(candidate);
|
|
241
|
+
return {
|
|
242
|
+
command: candidate,
|
|
243
|
+
args: [],
|
|
244
|
+
source: 'path',
|
|
245
|
+
...(needsShell ? { shell: true } : {}),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
function resolveBundled() {
|
|
251
|
+
const pkgPath = require.resolve('tsx/package.json');
|
|
252
|
+
return buildResolutionFromPkgJson(pkgPath, 'bundled');
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* True iff `candidatePath`, after realpath, lives inside `bundledPkgRoot`.
|
|
256
|
+
* Used by the A3 self-pointer guard. PATH hits typically resolve to
|
|
257
|
+
* `node_modules/.bin/tsx`, which is a SYMLINK (Unix) or `.cmd` shim
|
|
258
|
+
* (Windows) pointing into the tsx package. realpathSync resolves the
|
|
259
|
+
* symlink to the real entry under `node_modules/tsx/...`, so the
|
|
260
|
+
* package-root prefix check succeeds. Without realpath, `.bin/tsx` and
|
|
261
|
+
* `tsx/dist/cli.mjs` are in different directories and the previous
|
|
262
|
+
* `startsWith()` comparison silently failed.
|
|
263
|
+
*/
|
|
264
|
+
function isInBundledPackageRoot(candidatePath, bundledPkgRoot) {
|
|
265
|
+
try {
|
|
266
|
+
const realCandidate = path.resolve(realpathSync(candidatePath));
|
|
267
|
+
const realRoot = path.resolve(bundledPkgRoot);
|
|
268
|
+
return realCandidate === realRoot || realCandidate.startsWith(realRoot + path.sep);
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
// realpath failed (broken symlink? race?). Fall back to a textual
|
|
272
|
+
// prefix check — this still catches the rare "PATH points directly
|
|
273
|
+
// at the package dir" case.
|
|
274
|
+
return path.resolve(candidatePath).startsWith(path.resolve(bundledPkgRoot) + path.sep);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Read tsx's package.json `bin` (string or object) and produce a
|
|
279
|
+
* resolution that spawns the bin via `node <bin-js>`. This avoids
|
|
280
|
+
* shebang/PATHEXT quirks across platforms.
|
|
281
|
+
*/
|
|
282
|
+
function buildResolutionFromPkgJson(pkgPath, source) {
|
|
283
|
+
const pkgDir = path.dirname(pkgPath);
|
|
284
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
285
|
+
let binRel;
|
|
286
|
+
if (typeof pkg.bin === 'string') {
|
|
287
|
+
binRel = pkg.bin;
|
|
288
|
+
}
|
|
289
|
+
else if (pkg.bin && typeof pkg.bin === 'object') {
|
|
290
|
+
binRel = pkg.bin.tsx;
|
|
291
|
+
}
|
|
292
|
+
if (!binRel) {
|
|
293
|
+
throw new Error(`tsx package.json at ${pkgPath} has no bin.tsx entry`);
|
|
294
|
+
}
|
|
295
|
+
const binAbs = path.resolve(pkgDir, binRel);
|
|
296
|
+
if (!existsSync(binAbs)) {
|
|
297
|
+
throw new Error(`tsx bin ${binAbs} declared in package.json but missing on disk`);
|
|
298
|
+
}
|
|
299
|
+
// Spawn the bin .js via node directly. Portable across Windows/Unix —
|
|
300
|
+
// no .cmd shim required, no PATHEXT lookups, no shebang dependency.
|
|
301
|
+
return { command: process.execPath, args: [binAbs], source };
|
|
302
|
+
}
|
|
303
|
+
function resolveFromSource(source, projectRoot, forcedBy, pathOpts) {
|
|
304
|
+
let result = null;
|
|
305
|
+
if (source === 'project')
|
|
306
|
+
result = tryProjectLocal(projectRoot);
|
|
307
|
+
else if (source === 'path')
|
|
308
|
+
result = tryPath(pathOpts);
|
|
309
|
+
else
|
|
310
|
+
result = resolveBundled();
|
|
311
|
+
if (!result) {
|
|
312
|
+
throw new Error(`tsx source=${source} requested via ${forcedBy} but resolution failed. ` +
|
|
313
|
+
`Install tsx in your project or unset the override.`);
|
|
314
|
+
}
|
|
315
|
+
return { ...result, forcedBy };
|
|
316
|
+
}
|
|
317
|
+
// ---------------------------------------------------------------------------
|
|
318
|
+
// Deprecation warning + state dir (A7)
|
|
319
|
+
// ---------------------------------------------------------------------------
|
|
320
|
+
const TSX_DEPRECATION_MESSAGE = '\n' +
|
|
321
|
+
'[deprecation] @delegance/claude-autopilot is using its bundled `tsx` to run\n' +
|
|
322
|
+
' your TypeScript scripts. In v8.0.0, `tsx` will be removed from\n' +
|
|
323
|
+
' runtime deps and you will need to install it yourself:\n' +
|
|
324
|
+
'\n' +
|
|
325
|
+
' npm install -D tsx\n' +
|
|
326
|
+
'\n' +
|
|
327
|
+
' To silence this warning now and prepare for v8.0.0:\n' +
|
|
328
|
+
' 1. Add `tsx` to your project devDependencies, OR\n' +
|
|
329
|
+
' 2. Set `CLAUDE_AUTOPILOT_NO_TSX_DEPRECATION=1` in your env.\n' +
|
|
330
|
+
'\n' +
|
|
331
|
+
' Override resolution: `CLAUDE_AUTOPILOT_TSX=bundled|project|path`\n' +
|
|
332
|
+
' See docs/specs/v7.8.0-decouple-runtime-deps.md for details.\n';
|
|
333
|
+
/**
|
|
334
|
+
* Compute the state dir for warning dedup (A7).
|
|
335
|
+
* 1. CLAUDE_AUTOPILOT_STATE_DIR explicit override
|
|
336
|
+
* 2. POSIX + XDG_STATE_HOME set → $XDG_STATE_HOME/claude-autopilot
|
|
337
|
+
* 3. fallback → ~/.claude-autopilot
|
|
338
|
+
*/
|
|
339
|
+
export function stateDir(env = process.env, platform = process.platform) {
|
|
340
|
+
if (env.CLAUDE_AUTOPILOT_STATE_DIR)
|
|
341
|
+
return env.CLAUDE_AUTOPILOT_STATE_DIR;
|
|
342
|
+
if (platform !== 'win32' && env.XDG_STATE_HOME) {
|
|
343
|
+
return path.join(env.XDG_STATE_HOME, 'claude-autopilot');
|
|
344
|
+
}
|
|
345
|
+
return path.join(os.homedir(), '.claude-autopilot');
|
|
346
|
+
}
|
|
347
|
+
function emitTsxDeprecationWarningSafe(opts) {
|
|
348
|
+
// Opt-out for CI / log-hygiene.
|
|
349
|
+
if (opts.env.CLAUDE_AUTOPILOT_NO_TSX_DEPRECATION === '1')
|
|
350
|
+
return;
|
|
351
|
+
const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD (UTC)
|
|
352
|
+
const dedupPath = path.join(stateDir(opts.env, opts.platform), '.tsx-deprecation-shown');
|
|
353
|
+
try {
|
|
354
|
+
if (existsSync(dedupPath)) {
|
|
355
|
+
const lastShown = readFileSync(dedupPath, 'utf8').trim();
|
|
356
|
+
if (lastShown === today)
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
// unreadable — fall through and print (better than silently swallowing)
|
|
362
|
+
}
|
|
363
|
+
try {
|
|
364
|
+
mkdirSync(path.dirname(dedupPath), { recursive: true });
|
|
365
|
+
writeFileSync(dedupPath, today);
|
|
366
|
+
}
|
|
367
|
+
catch {
|
|
368
|
+
// Non-fatal: readonly homedir / sandbox / etc. We still print the
|
|
369
|
+
// warning below — the user might see it twice in a row, but that's
|
|
370
|
+
// preferable to silently swallowing in CI.
|
|
371
|
+
}
|
|
372
|
+
process.stderr.write(TSX_DEPRECATION_MESSAGE);
|
|
373
|
+
}
|
|
374
|
+
// Exported for testing.
|
|
375
|
+
export const __TSX_DEPRECATION_MESSAGE = TSX_DEPRECATION_MESSAGE;
|
|
376
|
+
//# sourceMappingURL=tsx-resolver.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@delegance/claude-autopilot",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.9.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"tag": "next"
|
|
@@ -51,10 +51,7 @@
|
|
|
51
51
|
"presets/",
|
|
52
52
|
"skills/",
|
|
53
53
|
"scripts/test-runner.mjs",
|
|
54
|
-
"scripts/autoregress.ts",
|
|
55
54
|
"scripts/post-build-rewrite-imports.mjs",
|
|
56
|
-
"scripts/snapshots/",
|
|
57
|
-
"tests/snapshots/",
|
|
58
55
|
"CHANGELOG.md",
|
|
59
56
|
"README.md"
|
|
60
57
|
],
|
|
@@ -64,14 +61,13 @@
|
|
|
64
61
|
"test:rls": "node --test --import=tsx tests/rls/*.test.ts",
|
|
65
62
|
"typecheck": "tsc --noEmit",
|
|
66
63
|
"build": "tsc -p tsconfig.build.json && node scripts/post-build-rewrite-imports.mjs",
|
|
67
|
-
"prepublishOnly": "npm run build && npm test",
|
|
68
|
-
"
|
|
64
|
+
"prepublishOnly": "npm run audit:supabase && npm run build && npm test",
|
|
65
|
+
"audit:supabase": "tsx scripts/audit-supabase-imports.ts",
|
|
69
66
|
"db:start": "bash scripts/db/start-supabase.sh",
|
|
70
67
|
"db:stop": "bash scripts/db/stop-supabase.sh",
|
|
71
68
|
"db:reset": "bash scripts/db/reset-supabase.sh"
|
|
72
69
|
},
|
|
73
70
|
"dependencies": {
|
|
74
|
-
"@supabase/supabase-js": "^2.97.0",
|
|
75
71
|
"ajv": "^8",
|
|
76
72
|
"ajv-formats": "^3.0.1",
|
|
77
73
|
"canonicalize": "^3.0.0",
|
|
@@ -87,6 +83,7 @@
|
|
|
87
83
|
"@anthropic-ai/sdk": "^0.91.1",
|
|
88
84
|
"@google/generative-ai": "^0.24.1",
|
|
89
85
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
86
|
+
"@supabase/supabase-js": "^2.97.0",
|
|
90
87
|
"openai": ">=4"
|
|
91
88
|
},
|
|
92
89
|
"devDependencies": {
|