@delegance/claude-autopilot 5.2.0 → 5.2.2

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.
@@ -1,9 +1,24 @@
1
1
  // src/cli/autoregress-bridge.ts
2
2
  import * as path from 'node:path';
3
+ import * as fs from 'node:fs';
3
4
  import { spawnSync } from 'node:child_process';
4
- import { fileURLToPath } from 'node:url';
5
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
- const SCRIPT = path.resolve(__dirname, '../../scripts/autoregress.ts');
5
+ import { findPackageRoot } from "./_pkg-root.js";
6
+ // Resolve autoregress.ts via the canonical package root walker — works under
7
+ // both source (src/cli/...) and compiled (dist/src/cli/...) layouts. The prior
8
+ // `path.resolve(__dirname, '../../scripts/autoregress.ts')` worked from source
9
+ // but resolved to `dist/scripts/autoregress.ts` under the compiled layout (the
10
+ // shipped scripts/ lives at the package root, not under dist/), so global
11
+ // installs got ERR_MODULE_NOT_FOUND on every `claude-autopilot autoregress`.
12
+ function resolveScript() {
13
+ const root = findPackageRoot(import.meta.url);
14
+ if (root) {
15
+ const p = path.join(root, 'scripts', 'autoregress.ts');
16
+ if (fs.existsSync(p))
17
+ return p;
18
+ }
19
+ // Last-resort fallback for non-standard layouts (linked dev installs, etc.)
20
+ return 'scripts/autoregress.ts';
21
+ }
7
22
  const VALID_MODES = ['run', 'update', 'generate', 'diff'];
8
23
  export function buildAutoregressArgs(args) {
9
24
  const mode = args[0] && VALID_MODES.includes(args[0]) ? args[0] : 'run';
@@ -12,10 +27,11 @@ export function buildAutoregressArgs(args) {
12
27
  }
13
28
  export function runAutoregress(args) {
14
29
  const resolvedArgs = buildAutoregressArgs(args);
15
- const result = spawnSync(process.execPath, ['--import', 'tsx', SCRIPT, ...resolvedArgs], { stdio: 'inherit', cwd: process.cwd() });
30
+ const script = resolveScript();
31
+ const result = spawnSync(process.execPath, ['--import', 'tsx', script, ...resolvedArgs], { stdio: 'inherit', cwd: process.cwd() });
16
32
  if (result.error) {
17
33
  console.error(`[autoregress] failed to launch: ${result.error.message}`);
18
- console.error(` script: ${SCRIPT}`);
34
+ console.error(` script: ${script}`);
19
35
  return 1;
20
36
  }
21
37
  return result.status ?? 1;
@@ -22,7 +22,8 @@ function fmtTokens(n) {
22
22
  export async function runCosts(cwd = process.cwd()) {
23
23
  const log = readCostLog(cwd);
24
24
  if (log.length === 0) {
25
- console.log(fmt('yellow', '[costs] No run history found — run `guardrail run` first.'));
25
+ console.log(fmt('yellow', `[costs] No run history found in ${cwd} — run \`guardrail run\` first.`));
26
+ console.log(fmt('dim', ` (Costs are scoped per-project. \`cd\` to the project before checking.)`));
26
27
  return 0;
27
28
  }
28
29
  // 7-day window
@@ -33,12 +34,13 @@ export async function runCosts(cwd = process.cwd()) {
33
34
  const totalInput = log.reduce((s, e) => s + e.inputTokens, 0);
34
35
  const totalOutput = log.reduce((s, e) => s + e.outputTokens, 0);
35
36
  const recentCost = recent.reduce((s, e) => s + e.costUSD, 0);
36
- console.log(`\n${fmt('bold', '[costs]')}\n`);
37
+ console.log(`\n${fmt('bold', '[costs]')} ${fmt('dim', cwd)}\n`);
37
38
  // Summary row
38
39
  console.log(fmt('bold', 'Summary'));
39
40
  console.log(` All-time runs: ${log.length}`);
40
41
  console.log(` All-time cost: ${fmtUSD(totalCost)} (${fmtTokens(totalInput)} in / ${fmtTokens(totalOutput)} out)`);
41
42
  console.log(` Last 7 days: ${fmtUSD(recentCost)} (${recent.length} run${recent.length !== 1 ? 's' : ''})`);
43
+ console.log(fmt('dim', ` (per-project — scoped to ${cwd}/.guardrail-cache/costs.jsonl)`));
42
44
  console.log('');
43
45
  // Last 10 runs table
44
46
  console.log(fmt('bold', `Recent runs (last ${last10.length})`));
@@ -25,9 +25,15 @@ function gitFetch(remote, ref, cwd) {
25
25
  export async function runPr(options = {}) {
26
26
  const cwd = options.cwd ?? process.cwd();
27
27
  const configPath = options.configPath ?? path.join(cwd, 'guardrail.config.yaml');
28
+ // 5.2.2 — pr previously hard-failed when guardrail.config.yaml was missing
29
+ // ("not found at <path>"). The first-run UX surprised users running
30
+ // `claude-autopilot pr <n>` on a fresh repo: setup hadn't been invoked, and
31
+ // the error didn't say to invoke it. Now matches `run`'s behavior — defer
32
+ // config-loading to the underlying runCommand, which auto-detects stack +
33
+ // testCommand when the file is missing.
28
34
  if (!fs.existsSync(configPath)) {
29
- console.error(fmt('red', `[pr] guardrail.config.yaml not found at ${configPath}`));
30
- return 1;
35
+ console.log(fmt('dim', `[pr] guardrail.config.yaml not found auto-detecting from stack signals.`));
36
+ console.log(fmt('dim', ` Run \`claude-autopilot setup\` first to commit a config.`));
31
37
  }
32
38
  // Resolve PR number
33
39
  let prNumber = options.prNumber;
@@ -3,6 +3,14 @@ import * as path from 'node:path';
3
3
  const CACHE_DIR = '.guardrail-cache';
4
4
  const LOG_FILE = 'costs.jsonl';
5
5
  export function appendCostLog(cwd, entry) {
6
+ // Skip no-op entries that only pollute the report — runs that didn't
7
+ // actually invoke an LLM (dry-runs, no-findings paths, "no code files at
8
+ // path" early returns). Without this filter, randai's costs.jsonl picked up
9
+ // 6 zero-token zero-duration entries from setup-flow scans, drowning the
10
+ // 4 real review entries in `claude-autopilot costs` output.
11
+ if (entry.inputTokens === 0 && entry.outputTokens === 0 && entry.costUSD === 0) {
12
+ return;
13
+ }
6
14
  // Cost log is observability, not a contract. A failed write (read-only FS,
7
15
  // full disk, permission error) must NEVER block the caller — every callsite
8
16
  // calls this *after* its primary output is emitted, and a throw here would
@@ -1,4 +1,22 @@
1
1
  import { execSync } from 'node:child_process';
2
+ import * as fs from 'node:fs';
3
+ import * as path from 'node:path';
4
+ // Detects a Python venv next to the project and prepends its bin/ to PATH so
5
+ // commands like `pytest -q` resolve to the venv's binary instead of failing
6
+ // with "command not found" or hitting an unrelated system Python. Surfaced by
7
+ // the 5.0.8 e2e test on randai-johnson — claude-autopilot pr ran the test
8
+ // command from PATH and reported "tests failed" even though the venv-installed
9
+ // pytest passed cleanly.
10
+ function venvAwareEnv(cwd) {
11
+ const root = cwd ?? process.cwd();
12
+ for (const candidate of ['.venv', 'venv', 'env']) {
13
+ const binDir = path.join(root, candidate, 'bin');
14
+ if (fs.existsSync(path.join(binDir, 'python')) || fs.existsSync(path.join(binDir, 'python3'))) {
15
+ return { ...process.env, PATH: `${binDir}${path.delimiter}${process.env.PATH ?? ''}` };
16
+ }
17
+ }
18
+ return process.env;
19
+ }
2
20
  export async function runTestsPhase(input) {
3
21
  const start = Date.now();
4
22
  if (!input.testCommand) {
@@ -13,6 +31,7 @@ export async function runTestsPhase(input) {
13
31
  timeout: 120000,
14
32
  shell: process.env.SHELL ?? "/bin/sh",
15
33
  stdio: ['ignore', 'pipe', 'pipe'],
34
+ env: venvAwareEnv(input.cwd),
16
35
  });
17
36
  }
18
37
  catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delegance/claude-autopilot",
3
- "version": "5.2.0",
3
+ "version": "5.2.2",
4
4
  "type": "module",
5
5
  "description": "Autonomous development pipeline for Claude Code: brainstorm → spec → plan → implement → migrate → validate → PR → review → merge. Multi-model, local-first, every phase a skill you can intervene in.",
6
6
  "keywords": [
@@ -22,3 +22,14 @@ chunking:
22
22
  pipeline:
23
23
  runReviewOnStaticFail: true
24
24
  runReviewOnTestFail: true
25
+ # Optional: multi-model council. Uncomment + set ANTHROPIC_API_KEY and/or
26
+ # OPENAI_API_KEY. Both APIs supported — chat-completions and Responses API
27
+ # for codex/o-series models (auto-detected by name).
28
+ # Usage: claude-autopilot council --prompt "..." --context-file <path>
29
+ # council:
30
+ # models:
31
+ # - { adapter: claude, model: claude-opus-4-7, label: opus }
32
+ # - { adapter: openai, model: gpt-5.3-codex, label: codex }
33
+ # synthesizer: { adapter: claude, model: claude-sonnet-4-6, label: synth }
34
+ # timeout_ms: 30000
35
+ # min_successful_responses: 1