@delegance/claude-autopilot 2.5.0 → 5.0.0-alpha.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.
Files changed (129) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/README.md +169 -106
  3. package/bin/_launcher.js +77 -0
  4. package/bin/claude-autopilot.js +3 -0
  5. package/bin/guardrail.js +3 -0
  6. package/package.json +23 -9
  7. package/presets/generic/guardrail.config.yaml +35 -0
  8. package/presets/generic/stack.md +40 -0
  9. package/presets/nextjs-supabase/{autopilot.config.yaml → guardrail.config.yaml} +7 -0
  10. package/scripts/autoregress.ts +27 -11
  11. package/skills/autopilot/SKILL.md +170 -0
  12. package/skills/claude-autopilot.md +80 -0
  13. package/skills/guardrail.md +39 -0
  14. package/skills/migrate/SKILL.md +83 -0
  15. package/src/adapters/council/claude.ts +41 -0
  16. package/src/adapters/council/openai.ts +40 -0
  17. package/src/adapters/council/types.ts +7 -0
  18. package/src/adapters/loader.ts +7 -7
  19. package/src/adapters/review-engine/auto.ts +2 -2
  20. package/src/adapters/review-engine/claude.ts +9 -11
  21. package/src/adapters/review-engine/codex.ts +9 -11
  22. package/src/adapters/review-engine/gemini.ts +9 -11
  23. package/src/adapters/review-engine/openai-compatible.ts +10 -12
  24. package/src/adapters/review-engine/parse-output.ts +32 -6
  25. package/src/adapters/review-engine/prompt-builder.ts +19 -0
  26. package/src/adapters/review-engine/types.ts +1 -1
  27. package/src/adapters/vcs-host/commit-status.ts +39 -0
  28. package/src/adapters/vcs-host/github.ts +2 -2
  29. package/src/cli/baseline.ts +125 -0
  30. package/src/cli/ci.ts +11 -8
  31. package/src/cli/costs.ts +2 -2
  32. package/src/cli/council.ts +96 -0
  33. package/src/cli/detector.ts +21 -5
  34. package/src/cli/explain.ts +197 -0
  35. package/src/cli/fix.ts +173 -111
  36. package/src/cli/hook.ts +72 -27
  37. package/src/cli/ignore-helper.ts +116 -0
  38. package/src/cli/index.ts +355 -31
  39. package/src/cli/init.ts +12 -12
  40. package/src/cli/lsp.ts +200 -0
  41. package/src/cli/mcp.ts +206 -0
  42. package/src/cli/pr-comment.ts +5 -5
  43. package/src/cli/pr-desc.ts +168 -0
  44. package/src/cli/pr-review-comments.ts +3 -3
  45. package/src/cli/pr.ts +76 -0
  46. package/src/cli/preflight.ts +109 -32
  47. package/src/cli/report.ts +186 -0
  48. package/src/cli/run.ts +140 -36
  49. package/src/cli/scan.ts +233 -0
  50. package/src/cli/setup.ts +121 -15
  51. package/src/cli/test-gen.ts +125 -0
  52. package/src/cli/triage.ts +137 -0
  53. package/src/cli/watch.ts +52 -31
  54. package/src/cli/worker.ts +109 -0
  55. package/src/core/cache/review-cache.ts +2 -2
  56. package/src/core/chunking/index.ts +2 -2
  57. package/src/core/config/loader.ts +10 -10
  58. package/src/core/config/preset-resolver.ts +6 -6
  59. package/src/core/config/schema.ts +103 -2
  60. package/src/core/config/types.ts +57 -2
  61. package/src/core/council/config.ts +71 -0
  62. package/src/core/council/context.ts +17 -0
  63. package/src/core/council/runner.ts +83 -0
  64. package/src/core/council/types.ts +45 -0
  65. package/src/core/detect/llm-key.ts +89 -0
  66. package/src/core/detect/workspaces.ts +103 -0
  67. package/src/core/errors.ts +4 -4
  68. package/src/core/fix/generator.ts +149 -0
  69. package/src/core/ignore/index.ts +4 -4
  70. package/src/core/mcp/concurrency.ts +16 -0
  71. package/src/core/mcp/handlers/fix-finding.ts +126 -0
  72. package/src/core/mcp/handlers/get-capabilities.ts +62 -0
  73. package/src/core/mcp/handlers/get-findings.ts +36 -0
  74. package/src/core/mcp/handlers/review-diff.ts +65 -0
  75. package/src/core/mcp/handlers/scan-files.ts +65 -0
  76. package/src/core/mcp/handlers/validate-fix.ts +41 -0
  77. package/src/core/mcp/run-store.ts +85 -0
  78. package/src/core/mcp/workspace.ts +35 -0
  79. package/src/core/persist/baseline.ts +112 -0
  80. package/src/core/persist/cost-log.ts +1 -1
  81. package/src/core/persist/findings-cache.ts +1 -1
  82. package/src/core/persist/triage.ts +112 -0
  83. package/src/core/phases/static-rules.ts +18 -5
  84. package/src/core/pipeline/review-phase.ts +65 -26
  85. package/src/core/pipeline/run.ts +42 -10
  86. package/src/core/runtime/lock.ts +2 -2
  87. package/src/core/runtime/state.ts +2 -2
  88. package/src/core/schema-alignment/detector.ts +59 -0
  89. package/src/core/schema-alignment/extractor/index.ts +24 -0
  90. package/src/core/schema-alignment/extractor/prisma.ts +21 -0
  91. package/src/core/schema-alignment/extractor/sql.ts +99 -0
  92. package/src/core/schema-alignment/llm-check.ts +91 -0
  93. package/src/core/schema-alignment/scanner.ts +107 -0
  94. package/src/core/schema-alignment/types.ts +43 -0
  95. package/src/core/shell.ts +3 -3
  96. package/src/core/static-rules/registry.ts +17 -8
  97. package/src/core/static-rules/rules/brand-tokens.ts +145 -0
  98. package/src/core/static-rules/rules/hardcoded-secrets.ts +27 -1
  99. package/src/core/static-rules/rules/insecure-redirect.ts +67 -0
  100. package/src/core/static-rules/rules/missing-auth.ts +70 -0
  101. package/src/core/static-rules/rules/schema-alignment.ts +132 -0
  102. package/src/core/static-rules/rules/sql-injection.ts +71 -0
  103. package/src/core/static-rules/rules/ssrf.ts +63 -0
  104. package/src/core/static-rules/tailwind-extractor.ts +38 -0
  105. package/src/core/test-gen/coverage-analyzer.ts +93 -0
  106. package/src/core/test-gen/framework-detector.ts +21 -0
  107. package/src/core/test-gen/test-writer.ts +33 -0
  108. package/src/core/ui/design-context-loader.ts +87 -0
  109. package/src/core/worker/client.ts +46 -0
  110. package/src/core/worker/lockfile.ts +38 -0
  111. package/src/core/worker/server.ts +81 -0
  112. package/src/formatters/junit.ts +52 -0
  113. package/src/formatters/sarif.ts +2 -2
  114. package/src/index.ts +1 -2
  115. package/tests/snapshots/baselines/src-formatters-sarif.json +4 -4
  116. package/tests/snapshots/index.json +3 -3
  117. package/tests/snapshots/src-formatters-sarif.snap.ts +1 -1
  118. package/tests/snapshots/src-snapshots-impact-selector.snap.ts +3 -3
  119. package/tests/snapshots/src-snapshots-import-scanner.snap.ts +3 -3
  120. package/tests/snapshots/src-snapshots-serializer.snap.ts +2 -2
  121. package/bin/autopilot.js +0 -20
  122. package/skills/autopilot.md +0 -157
  123. /package/presets/go/{autopilot.config.yaml → guardrail.config.yaml} +0 -0
  124. /package/presets/python-fastapi/{autopilot.config.yaml → guardrail.config.yaml} +0 -0
  125. /package/presets/rails-postgres/{autopilot.config.yaml → guardrail.config.yaml} +0 -0
  126. /package/presets/t3/{autopilot.config.yaml → guardrail.config.yaml} +0 -0
  127. /package/{src → scripts}/snapshots/impact-selector.ts +0 -0
  128. /package/{src → scripts}/snapshots/import-scanner.ts +0 -0
  129. /package/{src → scripts}/snapshots/serializer.ts +0 -0
package/src/cli/index.ts CHANGED
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * autopilot CLI — entry point
3
+ * guardrail CLI — entry point
4
4
  *
5
5
  * Usage:
6
- * autopilot init scaffold autopilot.config.yaml from a preset
7
- * autopilot run run the pipeline on git-changed files
8
- * autopilot run --base main diff against a specific branch
9
- * autopilot run --dry-run show what would run, no execution
10
- * autopilot watch re-run pipeline on every file save (debounced)
11
- * autopilot doctor check prerequisites (alias: preflight)
6
+ * guardrail run review git-changed files
7
+ * guardrail scan src/auth/ review any path (no git required)
8
+ * guardrail scan --ask "..." ask a targeted question about code
9
+ * guardrail ci opinionated CI entrypoint
10
+ * guardrail watch re-run on every file save
11
+ * guardrail doctor check prerequisites
12
12
  */
13
13
  import { runCommand } from './run.ts';
14
14
  import { runWatch } from './watch.ts';
@@ -16,6 +16,17 @@ import { runSetup } from './setup.ts';
16
16
  import { runDoctor } from './preflight.ts';
17
17
  import { runCi } from './ci.ts';
18
18
  import { runFix } from './fix.ts';
19
+ import { runScan } from './scan.ts';
20
+ import { runReport } from './report.ts';
21
+ import { runExplain } from './explain.ts';
22
+ import { runIgnore } from './ignore-helper.ts';
23
+ import { runPr } from './pr.ts';
24
+ import { runBaseline } from './baseline.ts';
25
+ import { runTriage } from './triage.ts';
26
+ import { runLsp } from './lsp.ts';
27
+ import { runWorker } from './worker.ts';
28
+ import { runTestGen } from './test-gen.ts';
29
+ import { runCouncilCmd } from './council.ts';
19
30
 
20
31
  const args = process.argv.slice(2);
21
32
 
@@ -29,8 +40,113 @@ if (args[0] === '--version' || args[0] === '-v') {
29
40
  process.exit(0);
30
41
  }
31
42
 
32
- const SUBCOMMANDS = ['init', 'run', 'ci', 'fix', 'costs', 'watch', 'hook', 'autoregress', 'doctor', 'preflight', 'setup', 'help', '--help', '-h'] as const;
33
- const VALUE_FLAGS = ['base', 'config', 'files', 'format', 'output', 'debounce'];
43
+ // Help flag route to help handler explicitly before subcommand defaulting.
44
+ // Without this, `--help` falls through the "args[0].startsWith('--')" check below
45
+ // and defaults to `run`, which is surprising and a v4 regression we preserve no longer.
46
+ if (args[0] === '--help' || args[0] === '-h') {
47
+ args.unshift('help');
48
+ args.splice(1, 1); // remove the original --help/-h token
49
+ }
50
+
51
+ // Verb grouping (new in alpha.2): `claude-autopilot review <verb>` and
52
+ // `claude-autopilot advanced <verb>` are dispatcher prefixes that route to the
53
+ // same flat handlers. Legacy flat invocation (`claude-autopilot run`) is unchanged
54
+ // — the grouped form is purely additive.
55
+ //
56
+ // Scope gates enforce that only the documented verb sets work under each prefix,
57
+ // so `claude-autopilot review doctor` is rejected with a clear error instead of
58
+ // silently routing to the doctor handler (which would confuse the mental model).
59
+ const REVIEW_VERBS = new Set(['run', 'scan', 'ci', 'fix', 'baseline', 'explain', 'watch', 'report']);
60
+ // `detector` is a library used by setup/run, not a CLI subcommand — leave it out.
61
+ const ADVANCED_VERBS = new Set(['lsp', 'mcp', 'worker', 'autoregress', 'test-gen', 'hook', 'ignore']);
62
+ if (args[0] === 'review') {
63
+ const sub = args[1];
64
+ if (!sub || sub === '--help' || sub === '-h') {
65
+ console.log(`
66
+ Usage: claude-autopilot review <verb> [options]
67
+
68
+ Review-phase verbs:
69
+ run Review git-changed files (default)
70
+ scan Review any path — no git required
71
+ ci Opinionated CI entrypoint (post comments + SARIF)
72
+ fix Auto-fix cached findings using the configured LLM
73
+ baseline Manage the committed findings baseline
74
+ explain Deep-dive explanation + remediation for a specific finding
75
+ watch Watch for file changes and re-run on each save
76
+ report Render cached findings as a markdown report
77
+
78
+ These are aliases for the flat subcommands — \`claude-autopilot run\` and
79
+ \`claude-autopilot review run\` are equivalent.
80
+ `);
81
+ process.exit(0);
82
+ }
83
+ if (!REVIEW_VERBS.has(sub)) {
84
+ console.error(`\x1b[31m[claude-autopilot] "${sub}" is not a review-phase verb.\x1b[0m`);
85
+ console.error(`\x1b[2m Valid: ${[...REVIEW_VERBS].join(', ')}\x1b[0m`);
86
+ console.error(`\x1b[2m Did you mean: claude-autopilot ${sub} ...?\x1b[0m`);
87
+ process.exit(1);
88
+ }
89
+ args.shift(); // drop 'review', leave the flat subcommand at args[0]
90
+ }
91
+ if (args[0] === 'advanced') {
92
+ const sub = args[1];
93
+ if (!sub || sub === '--help' || sub === '-h') {
94
+ console.log(`
95
+ Usage: claude-autopilot advanced <verb> [options]
96
+
97
+ Advanced / niche verbs (hidden from top-level --help to keep it readable):
98
+ lsp Language server — publishes findings as LSP diagnostics
99
+ mcp MCP server for Claude / ChatGPT integration
100
+ worker Persistent review daemon (start|stop|status)
101
+ autoregress Snapshot regression tests
102
+ test-gen Generate test cases for uncovered exports
103
+ hook Install / remove the pre-push git hook
104
+ ignore Edit the findings ignore list
105
+
106
+ These are aliases for the flat subcommands; they still work without the 'advanced' prefix.
107
+ `);
108
+ process.exit(0);
109
+ }
110
+ if (!ADVANCED_VERBS.has(sub)) {
111
+ console.error(`\x1b[31m[claude-autopilot] "${sub}" is not an advanced verb.\x1b[0m`);
112
+ console.error(`\x1b[2m Valid: ${[...ADVANCED_VERBS].join(', ')}\x1b[0m`);
113
+ process.exit(1);
114
+ }
115
+ args.shift(); // drop 'advanced'
116
+ }
117
+
118
+ const SUBCOMMANDS = ['init', 'run', 'scan', 'report', 'explain', 'ignore', 'ci', 'pr', 'fix', 'costs', 'watch', 'hook', 'autoregress', 'baseline', 'triage', 'lsp', 'worker', 'mcp', 'test-gen', 'pr-desc', 'doctor', 'preflight', 'setup', 'council', 'help', '--help', '-h'] as const;
119
+ const VALUE_FLAGS = ['base', 'config', 'files', 'format', 'output', 'debounce', 'ask', 'focus', 'fail-on', 'note', 'reason', 'expires', 'profile', 'severity', 'prompt', 'context-file'];
120
+
121
+ // Bare invocation — no subcommand, no flags → show welcome guide
122
+ if (args.length === 0) {
123
+ const hasKey = !!(process.env.ANTHROPIC_API_KEY || process.env.GEMINI_API_KEY ||
124
+ process.env.GOOGLE_API_KEY || process.env.OPENAI_API_KEY || process.env.GROQ_API_KEY);
125
+ const keyLine = hasKey
126
+ ? '\x1b[32m✓\x1b[0m LLM API key detected'
127
+ : '\x1b[33m!\x1b[0m No LLM API key found — set one of:\n ANTHROPIC_API_KEY https://console.anthropic.com/\n OPENAI_API_KEY https://platform.openai.com/api-keys\n GEMINI_API_KEY https://aistudio.google.com/app/apikey\n GROQ_API_KEY https://console.groq.com/keys (fast free tier)';
128
+ console.log(`
129
+ \x1b[1m@delegance/guardrail\x1b[0m — LLM-powered code review for your PR diffs
130
+
131
+ ${keyLine}
132
+
133
+ \x1b[1mQuick start:\x1b[0m
134
+
135
+ \x1b[36mnpx guardrail run --base main\x1b[0m Review files changed vs main
136
+ \x1b[36mnpx guardrail scan src/auth/\x1b[0m Scan any path (no git required)
137
+ \x1b[36mnpx guardrail scan --ask "SQL injection?" src/db/\x1b[0m
138
+ \x1b[36mnpx guardrail fix\x1b[0m Auto-fix cached findings
139
+ \x1b[36mnpx guardrail pr-desc\x1b[0m Generate PR description from current diff
140
+
141
+ \x1b[1mSetup:\x1b[0m
142
+
143
+ \x1b[36mnpx guardrail setup\x1b[0m Auto-detect stack, write config, install hook
144
+ \x1b[36mnpx guardrail doctor\x1b[0m Check prerequisites
145
+
146
+ Run \x1b[36mnpx guardrail --help\x1b[0m for full command reference.
147
+ `);
148
+ process.exit(0);
149
+ }
34
150
 
35
151
  // Detect first non-flag arg as subcommand, default to 'run'
36
152
  const subcommand = (args[0] && !args[0].startsWith('--')) ? args[0] : 'run';
@@ -41,7 +157,7 @@ function flag(name: string): string | undefined {
41
157
  if (idx < 0) return undefined;
42
158
  const val = args[idx + 1];
43
159
  if (val === undefined || val.startsWith('--')) {
44
- console.error(`\x1b[31m[autopilot] --${name} requires a value\x1b[0m`);
160
+ console.error(`\x1b[31m[guardrail] --${name} requires a value\x1b[0m`);
45
161
  process.exit(1);
46
162
  }
47
163
  return val;
@@ -53,18 +169,37 @@ function boolFlag(name: string): boolean {
53
169
 
54
170
  function printUsage(): void {
55
171
  console.log(`
56
- Usage: autopilot <command> [options]
172
+ Usage: guardrail <command> [options]
57
173
 
58
174
  Commands:
59
- run Run the pipeline on git-changed files (default)
60
- watch Watch for file changes and re-run pipeline on each save
61
- init Scaffold autopilot.config.yaml from a preset
62
- doctor Check prerequisites and show exact fix commands (alias: preflight)
63
- autoregress Run snapshot regression tests (run|diff|update|generate)
175
+ run Review git-changed files (default)
176
+ scan Review any path no git required
177
+ report Render cached findings as a markdown report
178
+ explain Deep-dive explanation + remediation for a specific finding
179
+ ignore Interactively add findings to .guardrail-ignore
180
+ watch Watch for file changes and re-run on each save
181
+ pr Review a specific PR by number (auto-detects if on PR branch)
182
+ fix Auto-fix cached findings using the configured LLM
183
+ costs Show per-run cost summary
184
+ ci Opinionated CI entrypoint (post comments + SARIF)
185
+ init Scaffold guardrail.config.yaml from a preset
186
+ setup Auto-detect stack, write config, install pre-push hook
187
+ doctor Check prerequisites (alias: preflight)
188
+ preflight Check prerequisites (alias: doctor)
189
+ hook Install / remove the pre-push git hook
190
+ baseline Manage the committed findings baseline (create|update|show|delete)
191
+ triage Mark individual findings as accepted/dismissed
192
+ pr-desc Generate a PR title / summary / test plan from the current diff
193
+ council Multi-model review — dispatch the diff to N models and synthesize consensus
194
+ mcp MCP server for Claude / ChatGPT integration
195
+ autoregress Snapshot regression tests (run|diff|update|generate)
196
+ lsp Language server — publishes findings as LSP diagnostics (stdin/stdout)
197
+ worker Persistent review daemon for multi-terminal parallel usage (start|stop|status)
198
+ test-gen Detect uncovered exports and generate test cases using the LLM
64
199
 
65
200
  Options (run):
66
201
  --base <ref> Git base ref for diff (default: HEAD~1)
67
- --config <path> Path to config file (default: ./autopilot.config.yaml)
202
+ --config <path> Path to config file (default: ./guardrail.config.yaml)
68
203
  --files <a,b,c> Explicit comma-separated file list (skips git detection)
69
204
  --dry-run Show what would run without executing
70
205
  --diff Send git diff hunks instead of full files (~70% fewer tokens)
@@ -74,8 +209,19 @@ Options (run):
74
209
  --format <text|sarif> Output format (default: text)
75
210
  --output <path> Output file path (required with --format sarif)
76
211
 
77
- fix Auto-fix cached findings using the configured LLM
78
- costs Show per-run cost summary from .autopilot-cache/costs.jsonl
212
+ Options (scan):
213
+ <path> [path...] Files or directories to scan (or --all for entire codebase)
214
+ --all Scan entire codebase
215
+ --ask <question> Targeted question to inject into the LLM review prompt
216
+ --focus <type> security | logic | performance (default: all)
217
+ --dry-run List files that would be scanned without running
218
+ --config <path> Path to config file
219
+
220
+ Options (pr):
221
+ <number> PR number to review (optional if on a PR branch)
222
+ --no-post-comments Skip posting/updating PR summary comment
223
+ --no-inline-comments Skip posting per-line inline annotations
224
+ --config <path> Path to config file
79
225
 
80
226
  Options (fix):
81
227
  --severity <critical|warning|all> Which findings to fix (default: critical)
@@ -83,7 +229,7 @@ Options (fix):
83
229
  --config <path> Path to config file
84
230
 
85
231
  Options (watch):
86
- --config <path> Path to config file (default: ./autopilot.config.yaml)
232
+ --config <path> Path to config file (default: ./guardrail.config.yaml)
87
233
  --debounce <ms> Debounce delay in ms (default: 300)
88
234
 
89
235
  Options (autoregress):
@@ -95,8 +241,32 @@ Options (autoregress):
95
241
  }
96
242
 
97
243
  switch (subcommand) {
244
+ case 'scan': {
245
+ const config = flag('config');
246
+ const ask = flag('ask');
247
+ const focusArg = flag('focus');
248
+ if (focusArg && !['security', 'logic', 'performance', 'brand', 'all'].includes(focusArg)) {
249
+ console.error(`\x1b[31m[guardrail] --focus must be "security", "logic", "performance", or "all"\x1b[0m`);
250
+ process.exit(1);
251
+ }
252
+ const dryRun = boolFlag('dry-run');
253
+ const all = boolFlag('all');
254
+ // Remaining non-flag args after 'scan' are paths
255
+ const targets = args.slice(1).filter(a => !a.startsWith('--') && a !== ask && a !== focusArg && a !== config);
256
+ const code = await runScan({
257
+ configPath: config,
258
+ targets: targets.length > 0 ? targets : undefined,
259
+ all,
260
+ ask,
261
+ focus: focusArg as 'security' | 'logic' | 'performance' | 'brand' | 'all' | undefined,
262
+ dryRun,
263
+ });
264
+ process.exit(code);
265
+ break;
266
+ }
267
+
98
268
  case 'init': {
99
- console.log('\x1b[33m[init] autopilot init is deprecated — use: npx autopilot setup\x1b[0m\n');
269
+ console.log('\x1b[33m[init] guardrail init is deprecated — use: npx guardrail setup\x1b[0m\n');
100
270
  const force = args.includes('--force');
101
271
  await runSetup({ force });
102
272
  break;
@@ -120,7 +290,7 @@ switch (subcommand) {
120
290
  const debounceArg = flag('debounce');
121
291
  const debounceMs = debounceArg ? parseInt(debounceArg, 10) : undefined;
122
292
  if (debounceArg && (isNaN(debounceMs!) || debounceMs! < 0)) {
123
- console.error(`\x1b[31m[autopilot] --debounce must be a non-negative integer\x1b[0m`);
293
+ console.error(`\x1b[31m[guardrail] --debounce must be a non-negative integer\x1b[0m`);
124
294
  process.exit(1);
125
295
  }
126
296
  await runWatch({ configPath: config, debounceMs });
@@ -134,19 +304,27 @@ switch (subcommand) {
134
304
  const dryRun = boolFlag('dry-run');
135
305
  const diff = boolFlag('diff');
136
306
  const delta = boolFlag('delta');
307
+ const staticOnly = args.includes('--static-only');
137
308
  const inlineComments = boolFlag('inline-comments');
138
309
  const postComments = boolFlag('post-comments');
139
310
  const formatArg = flag('format');
140
311
  const outputPath = flag('output');
141
312
 
142
- if (formatArg && formatArg !== 'text' && formatArg !== 'sarif') {
143
- console.error(`\x1b[31m[autopilot] --format must be "text" or "sarif"\x1b[0m`);
313
+ if (formatArg && formatArg !== 'text' && formatArg !== 'sarif' && formatArg !== 'junit') {
314
+ console.error(`\x1b[31m[guardrail] --format must be "text", "sarif", or "junit"\x1b[0m`);
315
+ process.exit(1);
316
+ }
317
+ if ((formatArg === 'sarif' || formatArg === 'junit') && !outputPath) {
318
+ console.error(`\x1b[31m[guardrail] --format ${formatArg} requires --output <path>\x1b[0m`);
144
319
  process.exit(1);
145
320
  }
146
- if (formatArg === 'sarif' && !outputPath) {
147
- console.error(`\x1b[31m[autopilot] --format sarif requires --output <path>\x1b[0m`);
321
+
322
+ const failOnArg = flag('fail-on');
323
+ if (failOnArg && !['critical', 'warning', 'note', 'none'].includes(failOnArg)) {
324
+ console.error(`\x1b[31m[guardrail] --fail-on must be "critical", "warning", "note", or "none"\x1b[0m`);
148
325
  process.exit(1);
149
326
  }
327
+ const newOnly = boolFlag('new-only');
150
328
 
151
329
  const code = await runCommand({
152
330
  base,
@@ -155,10 +333,13 @@ switch (subcommand) {
155
333
  dryRun,
156
334
  diff,
157
335
  delta,
336
+ newOnly,
337
+ failOn: failOnArg as 'critical' | 'warning' | 'note' | 'none' | undefined,
158
338
  inlineComments,
159
339
  postComments,
160
- format: formatArg as 'text' | 'sarif' | undefined,
340
+ format: formatArg as 'text' | 'sarif' | 'junit' | undefined,
161
341
  outputPath,
342
+ skipReview: staticOnly,
162
343
  });
163
344
  process.exit(code);
164
345
  break;
@@ -171,6 +352,8 @@ switch (subcommand) {
171
352
  const noPostComments = boolFlag('no-post-comments');
172
353
  const noInlineComments = boolFlag('no-inline-comments');
173
354
  const diff = boolFlag('diff');
355
+ const newOnly = boolFlag('new-only');
356
+ const failOnArg = flag('fail-on');
174
357
  const code = await runCi({
175
358
  configPath: config,
176
359
  base,
@@ -178,6 +361,33 @@ switch (subcommand) {
178
361
  postComments: noPostComments ? false : undefined,
179
362
  inlineComments: noInlineComments ? false : undefined,
180
363
  diff,
364
+ newOnly,
365
+ failOn: failOnArg as 'critical' | 'warning' | 'note' | 'none' | undefined,
366
+ });
367
+ process.exit(code);
368
+ break;
369
+ }
370
+
371
+ case 'baseline': {
372
+ const { runBaseline: rb } = await import('./baseline.ts');
373
+ const sub = args[1] ?? 'show';
374
+ const note = flag('note');
375
+ const config = flag('config');
376
+ const code = await rb(sub, { cwd: process.cwd(), note, baselinePath: config });
377
+ process.exit(code);
378
+ break;
379
+ }
380
+
381
+ case 'pr': {
382
+ const config = flag('config');
383
+ const noPostComments = boolFlag('no-post-comments');
384
+ const noInlineComments = boolFlag('no-inline-comments');
385
+ const prNumber = args.slice(1).find(a => !a.startsWith('--') && /^\d+$/.test(a));
386
+ const code = await runPr({
387
+ configPath: config,
388
+ prNumber,
389
+ noPostComments,
390
+ noInlineComments,
181
391
  });
182
392
  process.exit(code);
183
393
  break;
@@ -187,7 +397,11 @@ switch (subcommand) {
187
397
  const { runHook } = await import('./hook.ts');
188
398
  const hookSub = args[1] ?? 'status';
189
399
  const force = boolFlag('force');
190
- const code = await runHook(hookSub, { force });
400
+ const code = await runHook(hookSub, {
401
+ force,
402
+ preCommitOnly: args.includes('--pre-commit-only'),
403
+ prePushOnly: args.includes('--pre-push-only'),
404
+ });
191
405
  process.exit(code);
192
406
  break;
193
407
  }
@@ -203,19 +417,67 @@ switch (subcommand) {
203
417
  const config = flag('config');
204
418
  const severityArg = flag('severity');
205
419
  if (severityArg && !['critical', 'warning', 'all'].includes(severityArg)) {
206
- console.error(`\x1b[31m[autopilot] --severity must be "critical", "warning", or "all"\x1b[0m`);
420
+ console.error(`\x1b[31m[guardrail] --severity must be "critical", "warning", or "all"\x1b[0m`);
207
421
  process.exit(1);
208
422
  }
209
423
  const dryRun = boolFlag('dry-run');
424
+ const noVerify = boolFlag('no-verify');
210
425
  const code = await runFix({
211
426
  configPath: config,
212
427
  severity: severityArg as 'critical' | 'warning' | 'all' | undefined,
213
428
  dryRun,
429
+ noVerify,
430
+ });
431
+ process.exit(code);
432
+ break;
433
+ }
434
+
435
+ case 'triage': {
436
+ const sub = args[1];
437
+ const rest = args.slice(2);
438
+ const code = await runTriage(sub, rest);
439
+ process.exit(code);
440
+ break;
441
+ }
442
+
443
+ case 'test-gen': {
444
+ const config = flag('config');
445
+ const base = flag('base');
446
+ const dryRun = boolFlag('dry-run');
447
+ const verify = boolFlag('verify');
448
+ const targets = args.slice(1).filter(a => !a.startsWith('--') && a !== config && a !== base);
449
+ const code = await runTestGen({
450
+ cwd: process.cwd(),
451
+ configPath: config,
452
+ targets: targets.length > 0 ? targets : undefined,
453
+ base,
454
+ dryRun,
455
+ verify,
214
456
  });
215
457
  process.exit(code);
216
458
  break;
217
459
  }
218
460
 
461
+ case 'pr-desc': {
462
+ const { runPrDesc } = await import('./pr-desc.ts');
463
+ const baseIdx = args.indexOf('--base');
464
+ const base = baseIdx !== -1 ? args[baseIdx + 1] : undefined;
465
+ const outputIdx = args.indexOf('--output');
466
+ const output = outputIdx !== -1 ? args[outputIdx + 1] : undefined;
467
+ await runPrDesc({
468
+ base,
469
+ post: args.includes('--post'),
470
+ yes: args.includes('--yes'),
471
+ output,
472
+ });
473
+ break;
474
+ }
475
+
476
+ case 'lsp': {
477
+ await runLsp({ cwd: process.cwd() });
478
+ break;
479
+ }
480
+
219
481
  case 'costs': {
220
482
  const { runCosts } = await import('./costs.ts');
221
483
  const code = await runCosts();
@@ -223,14 +485,76 @@ switch (subcommand) {
223
485
  break;
224
486
  }
225
487
 
488
+ case 'report': {
489
+ const outputPath = flag('output');
490
+ const trend = boolFlag('trend');
491
+ const code = await runReport({ output: outputPath, trend });
492
+ process.exit(code);
493
+ break;
494
+ }
495
+
496
+ case 'explain': {
497
+ const config = flag('config');
498
+ // Target is the first non-flag arg after 'explain'
499
+ const target = args.slice(1).find(a => !a.startsWith('--'));
500
+ const code = await runExplain({ configPath: config, target });
501
+ process.exit(code);
502
+ break;
503
+ }
504
+
505
+ case 'ignore': {
506
+ const all = boolFlag('all');
507
+ const dryRun = boolFlag('dry-run');
508
+ const code = await runIgnore({ all, dryRun });
509
+ process.exit(code);
510
+ break;
511
+ }
512
+
226
513
  case 'setup': {
227
514
  const force = args.includes('--force');
228
- await runSetup({ force });
515
+ const profileArg = flag('profile');
516
+ if (profileArg && !['security-strict', 'team', 'solo'].includes(profileArg)) {
517
+ console.error(`\x1b[31m[guardrail] --profile must be "security-strict", "team", or "solo"\x1b[0m`);
518
+ process.exit(1);
519
+ }
520
+ await runSetup({ force, profile: profileArg as 'security-strict' | 'team' | 'solo' | undefined });
521
+ break;
522
+ }
523
+
524
+ case 'worker': {
525
+ const sub = args[1];
526
+ const config = flag('config');
527
+ const code = await runWorker(sub, { cwd: process.cwd(), configPath: config });
528
+ process.exit(code);
529
+ break;
530
+ }
531
+
532
+ case 'council': {
533
+ const config = flag('config');
534
+ const prompt = flag('prompt');
535
+ const contextFile = flag('context-file');
536
+ const dryRun = boolFlag('dry-run');
537
+ const noSynthesize = boolFlag('no-synthesize');
538
+ const code = await runCouncilCmd({
539
+ prompt,
540
+ contextFile,
541
+ configPath: config,
542
+ dryRun,
543
+ noSynthesize,
544
+ });
545
+ process.exit(code);
546
+ break;
547
+ }
548
+
549
+ case 'mcp': {
550
+ const { runMcp } = await import('./mcp.ts');
551
+ const configPath = flag('config');
552
+ await runMcp({ cwd: process.cwd(), configPath });
229
553
  break;
230
554
  }
231
555
 
232
556
  default:
233
- console.error(`\x1b[31m[autopilot] Unknown subcommand: "${subcommand}"\x1b[0m`);
557
+ console.error(`\x1b[31m[guardrail] Unknown subcommand: "${subcommand}"\x1b[0m`);
234
558
  printUsage();
235
559
  process.exit(1);
236
560
  }
package/src/cli/init.ts CHANGED
@@ -17,14 +17,14 @@ const PRESET_DESCRIPTIONS: Record<string, string> = {
17
17
  const PRESET_NAMES = Object.keys(PRESET_DESCRIPTIONS);
18
18
 
19
19
  export async function runInit(cwd: string = process.cwd()): Promise<void> {
20
- const dest = path.join(cwd, 'autopilot.config.yaml');
20
+ const dest = path.join(cwd, 'guardrail.config.yaml');
21
21
 
22
22
  if (fs.existsSync(dest)) {
23
- console.error(`\x1b[33m[init] autopilot.config.yaml already exists — remove it first to re-init\x1b[0m`);
23
+ console.error(`\x1b[33m[init] guardrail.config.yaml already exists — remove it first to re-init\x1b[0m`);
24
24
  process.exit(1);
25
25
  }
26
26
 
27
- console.log('\n\x1b[1m[autopilot init] Choose a preset:\x1b[0m\n');
27
+ console.log('\n\x1b[1m[guardrail init] Choose a preset:\x1b[0m\n');
28
28
  PRESET_NAMES.forEach((name, i) => {
29
29
  console.log(` ${i + 1}. ${name.padEnd(22)} ${PRESET_DESCRIPTIONS[name]}`);
30
30
  });
@@ -63,27 +63,27 @@ export async function runInit(cwd: string = process.cwd()): Promise<void> {
63
63
  const presetContent = await fsAsync.readFile(presetConfigPath, 'utf8');
64
64
  await fsAsync.writeFile(dest, presetContent, 'utf8');
65
65
 
66
- console.log(`\n\x1b[32m✓\x1b[0m Created autopilot.config.yaml from preset \x1b[1m${presetName}\x1b[0m`);
66
+ console.log(`\n\x1b[32m✓\x1b[0m Created guardrail.config.yaml from preset \x1b[1m${presetName}\x1b[0m`);
67
67
  console.log('\nNext steps:');
68
- console.log(' 1. Review autopilot.config.yaml and adjust testCommand / protectedPaths');
68
+ console.log(' 1. Review guardrail.config.yaml and adjust testCommand / protectedPaths');
69
69
  console.log(' 2. Set OPENAI_API_KEY in your environment (for Codex review)');
70
70
  console.log(' 3. Run your first pipeline to verify the setup:');
71
- console.log(' npx autopilot run');
71
+ console.log(' npx guardrail run');
72
72
  console.log(' 4. Generate snapshot baselines after your first feature lands:');
73
- console.log(' npx autopilot autoregress generate');
73
+ console.log(' npx guardrail autoregress generate');
74
74
  console.log(' 5. Install the pre-push git hook (enforces snapshots on push):');
75
- console.log(' npx autopilot hook install');
75
+ console.log(' npx guardrail hook install');
76
76
  console.log(' 6. (Optional) Add CI with GitHub Actions:');
77
- console.log(' uses: axledbetter/claude-autopilot/.github/actions/ci@main\n');
77
+ console.log(' uses: axledbetter/guardrail/.github/actions/ci@main\n');
78
78
  }
79
79
 
80
80
  function presetSearchPaths(name: string): string[] {
81
81
  // fileURLToPath handles encoded chars and Windows drive letters safely
82
82
  const pkgRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
83
83
  return [
84
- path.join(pkgRoot, 'presets', name, 'autopilot.config.yaml'),
85
- path.join(process.cwd(), 'presets', name, 'autopilot.config.yaml'),
86
- path.join(process.cwd(), 'node_modules', '@delegance', 'claude-autopilot', 'presets', name, 'autopilot.config.yaml'),
84
+ path.join(pkgRoot, 'presets', name, 'guardrail.config.yaml'),
85
+ path.join(process.cwd(), 'presets', name, 'guardrail.config.yaml'),
86
+ path.join(process.cwd(), 'node_modules', '@delegance', 'claude-autopilot', 'presets', name, 'guardrail.config.yaml'),
87
87
  ];
88
88
  }
89
89