@haystackeditor/cli 0.8.1 → 0.10.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.
Files changed (63) hide show
  1. package/README.md +93 -87
  2. package/dist/assets/hooks/llm-rules-template.md +21 -0
  3. package/dist/assets/hooks/package.json +2 -2
  4. package/dist/assets/hooks/scripts/pre-push.sh +20 -0
  5. package/dist/assets/skills/prepare-haystack.md +323 -0
  6. package/dist/assets/skills/secrets.md +164 -0
  7. package/dist/assets/skills/setup-external-sandbox.md +243 -0
  8. package/dist/assets/skills/setup-haystack.md +639 -0
  9. package/dist/assets/skills/submit.md +154 -0
  10. package/dist/assets/templates/CLAUDE.md.snippet +42 -0
  11. package/dist/assets/templates/haystack.yml +193 -0
  12. package/dist/commands/check-pending.d.ts +19 -0
  13. package/dist/commands/check-pending.js +217 -0
  14. package/dist/commands/config.d.ts +13 -21
  15. package/dist/commands/config.js +278 -92
  16. package/dist/commands/dismiss.d.ts +17 -0
  17. package/dist/commands/dismiss.js +201 -0
  18. package/dist/commands/init.js +25 -28
  19. package/dist/commands/install-session-hooks.d.ts +16 -0
  20. package/dist/commands/install-session-hooks.js +302 -0
  21. package/dist/commands/login.js +1 -1
  22. package/dist/commands/policy.d.ts +31 -0
  23. package/dist/commands/policy.js +365 -0
  24. package/dist/commands/pr-status.d.ts +16 -0
  25. package/dist/commands/pr-status.js +188 -0
  26. package/dist/commands/setup.d.ts +13 -0
  27. package/dist/commands/setup.js +496 -0
  28. package/dist/commands/skills.d.ts +2 -2
  29. package/dist/commands/skills.js +51 -186
  30. package/dist/commands/submit.d.ts +23 -0
  31. package/dist/commands/submit.js +456 -0
  32. package/dist/commands/triage.d.ts +16 -0
  33. package/dist/commands/triage.js +354 -0
  34. package/dist/index.d.ts +7 -0
  35. package/dist/index.js +344 -4
  36. package/dist/tools/detect.d.ts +50 -0
  37. package/dist/tools/detect.js +853 -0
  38. package/dist/tools/fixtures.d.ts +38 -0
  39. package/dist/tools/fixtures.js +199 -0
  40. package/dist/tools/setup.d.ts +43 -0
  41. package/dist/tools/setup.js +597 -0
  42. package/dist/triage/prompts.d.ts +31 -0
  43. package/dist/triage/prompts.js +296 -0
  44. package/dist/triage/runner.d.ts +21 -0
  45. package/dist/triage/runner.js +339 -0
  46. package/dist/triage/traces.d.ts +20 -0
  47. package/dist/triage/traces.js +305 -0
  48. package/dist/triage/types.d.ts +47 -0
  49. package/dist/triage/types.js +7 -0
  50. package/dist/types.d.ts +1387 -191
  51. package/dist/types.js +254 -2
  52. package/dist/utils/analysis-api.d.ts +108 -0
  53. package/dist/utils/analysis-api.js +194 -0
  54. package/dist/utils/config.js +1 -1
  55. package/dist/utils/git.d.ts +80 -0
  56. package/dist/utils/git.js +302 -0
  57. package/dist/utils/github-api.d.ts +83 -0
  58. package/dist/utils/github-api.js +266 -0
  59. package/dist/utils/pending-state.d.ts +40 -0
  60. package/dist/utils/pending-state.js +86 -0
  61. package/dist/utils/secrets.js +3 -3
  62. package/dist/utils/skill.js +257 -0
  63. package/package.json +11 -9
package/dist/index.js CHANGED
@@ -7,8 +7,15 @@
7
7
  *
8
8
  * Usage:
9
9
  * npx @haystackeditor/cli init # Set up .haystack.json
10
+ * npx @haystackeditor/cli setup # Interactive onboarding wizard
10
11
  * npx @haystackeditor/cli status # Check configuration
11
12
  * npx @haystackeditor/cli login # Authenticate with GitHub
13
+ * npx @haystackeditor/cli submit # Create a PR (auto-merge or review)
14
+ * npx @haystackeditor/cli check-pending # Check analysis status
15
+ * npx @haystackeditor/cli triage 123 # View analysis results for a PR
16
+ * npx @haystackeditor/cli dismiss 123 # Dismiss findings for a PR
17
+ * npx @haystackeditor/cli mark-reviewed 123 # Mark review as not needed
18
+ * npx @haystackeditor/cli pr-status 123 # Show PR status in Haystack pipeline
12
19
  * npx @haystackeditor/cli secrets list # List stored secrets
13
20
  */
14
21
  import { Command } from 'commander';
@@ -16,14 +23,22 @@ import { statusCommand } from './commands/status.js';
16
23
  import { initCommand } from './commands/init.js';
17
24
  import { loginCommand, logoutCommand } from './commands/login.js';
18
25
  import { listSecrets, setSecret, getSecret, getSecretsForRepo, deleteSecret } from './commands/secrets.js';
19
- import { handleSandbox, handleAgenticTool } from './commands/config.js';
26
+ import { handleSandbox, handleAgenticTool, handleAutoMerge, handleWaitForReviewers } from './commands/config.js';
20
27
  import { installSkills, listSkills } from './commands/skills.js';
21
28
  import { hooksInstall, hooksStatus, hooksUpdate } from './commands/hooks.js';
29
+ import { submitCommand } from './commands/submit.js';
30
+ import { checkPendingCommand } from './commands/check-pending.js';
31
+ import { installSessionHooks, sessionHooksStatus } from './commands/install-session-hooks.js';
32
+ import { listPolicies, addPolicy, removePolicy, initPolicies, addInstruction } from './commands/policy.js';
33
+ import { triageCommand } from './commands/triage.js';
34
+ import { dismissCommand, markReviewedCommand, undismissCommand } from './commands/dismiss.js';
35
+ import { prStatusCommand } from './commands/pr-status.js';
36
+ import { setupCommand } from './commands/setup.js';
22
37
  const program = new Command();
23
38
  program
24
39
  .name('haystack')
25
40
  .description('Set up Haystack verification for your project')
26
- .version('0.3.0');
41
+ .version('0.9.0');
27
42
  program
28
43
  .command('init')
29
44
  .description('Create .haystack.json configuration')
@@ -34,10 +49,29 @@ This creates a .haystack.json file with auto-detected settings:
34
49
  • Services (for monorepos)
35
50
  • Auth bypass for sandbox environments
36
51
 
37
- After running, use /setup-haystack in Claude Code (or give your AI agent
38
- .agents/skills/setup-haystack.md) to add verification flows.
52
+ After running, use \`haystack setup\` to scan your repos for rules,
53
+ CI signals, and review policies.
39
54
  `)
40
55
  .action(initCommand);
56
+ program
57
+ .command('setup')
58
+ .description('Interactive onboarding wizard — scan repos for rules, signals, and policies')
59
+ .addHelpText('after', `
60
+ This walks you through Haystack setup step by step:
61
+
62
+ 1. Select GitHub repositories to configure
63
+ 2. Scan for coding rules (conventions your team follows)
64
+ 3. Scan for CI/bot signals (checks to wait for before merging)
65
+ 4. Scan for review policies (who should review what)
66
+ 5. Review and toggle discovered items
67
+ 6. Write .haystack.json to your repos
68
+
69
+ Requires authentication — run \`haystack login\` first.
70
+
71
+ Examples:
72
+ haystack setup
73
+ `)
74
+ .action(setupCommand);
41
75
  program
42
76
  .command('status')
43
77
  .description('Check if .haystack.json exists and is valid')
@@ -50,6 +84,191 @@ program
50
84
  .command('logout')
51
85
  .description('Remove stored credentials')
52
86
  .action(logoutCommand);
87
+ program
88
+ .command('submit')
89
+ .description('Create a PR from current changes')
90
+ .option('--title <title>', 'PR title (default: last commit message)')
91
+ .option('--body <body>', 'PR body (default: commit messages since base)')
92
+ .option('--body-file <path>', 'Read PR body from file (use "-" for stdin)')
93
+ .option('--base <branch>', 'Base branch (default: main or master)')
94
+ .option('--draft', 'Create as draft PR')
95
+ .option('--review [reviewer]', 'Request human review (BLOCKS auto-merge — only use when explicitly asked)')
96
+ .option('--force', 'Skip pre-PR triage checks')
97
+ .option('--auto-fix', 'Auto-fix analysis issues in a sandbox before surfacing to Feed')
98
+ .option('--no-wait', 'Skip waiting for analysis results')
99
+ .addHelpText('after', `
100
+ This command is designed for AI coding agents to submit PRs.
101
+
102
+ 1. Runs pre-PR triage (code review, rules, intent drift) via sub-agents
103
+ 2. Pushes the current branch to origin
104
+ 3. Creates a pull request on GitHub
105
+ 4. Waits for Haystack analysis results (triggered via GitHub App webhook)
106
+
107
+ Pre-PR Triage:
108
+ Before creating the PR, haystack spawns parallel sub-agents to check for:
109
+ • Code review bugs (logic errors, null crashes, security issues)
110
+ • Rule violations (from .haystack/pr-rules.yml)
111
+ • Intent drift (AI agent deviations from user instructions)
112
+
113
+ Use --force to skip triage entirely.
114
+ Use --no-wait to skip waiting for analysis results.
115
+ Use --auto-fix to auto-fix analysis issues before surfacing to Feed.
116
+
117
+ Review Routing:
118
+ By default, PRs enter the auto-merge queue and merge automatically once
119
+ Haystack analysis passes. This is the correct default for most PRs.
120
+
121
+ ⚠ Only use --review when the user EXPLICITLY asks for human review.
122
+ --review BLOCKS auto-merge — the PR will NOT merge until a human approves,
123
+ which delays merging. Do not add --review "just to be safe".
124
+
125
+ • --review Labels the PR "haystack:needs-review" for the
126
+ needs-assignment queue (a teammate will pick it up)
127
+ • --review <username> Same label, plus requests review from that GitHub user
128
+
129
+ Examples:
130
+ haystack submit --auto-fix # Recommended: triage, create PR, auto-fix issues
131
+ haystack submit # Triage, create PR, auto-merge queue (no auto-fix)
132
+ haystack submit --force # Skip triage
133
+ haystack submit --no-wait # Don't wait for analysis
134
+ haystack submit --title "Fix auth" # Custom PR title
135
+ haystack submit --body "## Summary" # Custom PR body (inline)
136
+ haystack submit --body-file body.md # Read body from file (supports markdown)
137
+ echo "## Summary" | haystack submit --body-file - # Read body from stdin
138
+ haystack submit --base develop # Target a different base branch
139
+ haystack submit --draft # Create as draft PR
140
+ haystack submit --review # ⚠ Blocks auto-merge, needs human approval
141
+ haystack submit --review octocat # ⚠ Blocks auto-merge, requests review from octocat
142
+ `)
143
+ .action(submitCommand);
144
+ program
145
+ .command('check-pending')
146
+ .description('Check status of pending Haystack analysis')
147
+ .option('--json', 'Output as JSON')
148
+ .option('--hook', 'Minimal output for session-start hooks')
149
+ .option('--clear', 'Clear pending submit state')
150
+ .addHelpText('after', `
151
+ After \`haystack submit\`, this checks whether the analysis finished
152
+ and shows a two-state verdict:
153
+
154
+ Good to merge — No issues found, safe to merge
155
+ Needs your input — Bugs or rule violations found
156
+
157
+ This runs automatically on CLI session start if session hooks are installed.
158
+
159
+ Examples:
160
+ haystack check-pending # Rich terminal output
161
+ haystack check-pending --hook # Minimal output (for hooks)
162
+ haystack check-pending --json # Machine-readable JSON
163
+ haystack check-pending --clear # Clear pending state
164
+ `)
165
+ .action(checkPendingCommand);
166
+ program
167
+ .command('triage <pr>')
168
+ .description('View Haystack analysis results for a PR')
169
+ .option('--json', 'Output as JSON')
170
+ .option('--no-wait', 'Exit immediately if analysis is still pending')
171
+ .addHelpText('after', `
172
+ Look up triage results (bugs, rule violations, verdict) for any PR.
173
+
174
+ PR identifier formats:
175
+ 123 PR number (uses current repo)
176
+ #123 PR number with hash
177
+ owner/repo#123 Fully qualified
178
+ https://github.com/owner/repo/pull/123 GitHub URL
179
+
180
+ By default, if analysis is still in progress, the command will poll
181
+ for up to 5 minutes. Use --no-wait to exit immediately instead.
182
+
183
+ Examples:
184
+ haystack triage 42 # Current repo, PR #42
185
+ haystack triage acme/widgets#99 # Specific repo
186
+ haystack triage https://github.com/o/r/pull/1 # From URL
187
+ haystack triage 42 --json # Machine-readable output
188
+ haystack triage 42 --no-wait # Don't wait if pending
189
+ `)
190
+ .action(triageCommand);
191
+ program
192
+ .command('dismiss <pr>')
193
+ .description('Dismiss analysis findings for a PR')
194
+ .addHelpText('after', `
195
+ Dismiss analysis findings for a PR, moving it from "Issues Found" to
196
+ "Good to Merge" in the Haystack feed.
197
+
198
+ The override is tied to the PR's current HEAD commit. If a new commit is
199
+ pushed, the override is invalidated and you'll need to dismiss again.
200
+
201
+ PR identifier formats:
202
+ 123 PR number (uses current repo)
203
+ #123 PR number with hash
204
+ owner/repo#123 Fully qualified
205
+ https://github.com/owner/repo/pull/123 GitHub URL
206
+
207
+ Examples:
208
+ haystack dismiss 42 # Dismiss findings for PR #42
209
+ haystack dismiss acme/widgets#99 # Dismiss for specific repo
210
+ `)
211
+ .action(dismissCommand);
212
+ program
213
+ .command('mark-reviewed <pr>')
214
+ .description('Mark human review as not needed for a PR')
215
+ .addHelpText('after', `
216
+ Mark human review as not needed for a PR, moving it from "Needs Review"
217
+ to "Good to Merge" in the Haystack feed.
218
+
219
+ The override is tied to the PR's current HEAD commit. If a new commit is
220
+ pushed, the override is invalidated and you'll need to mark it again.
221
+
222
+ PR identifier formats:
223
+ 123 PR number (uses current repo)
224
+ #123 PR number with hash
225
+ owner/repo#123 Fully qualified
226
+ https://github.com/owner/repo/pull/123 GitHub URL
227
+
228
+ Examples:
229
+ haystack mark-reviewed 42 # Mark review not needed for PR #42
230
+ haystack mark-reviewed acme/widgets#99 # Mark for specific repo
231
+ `)
232
+ .action(markReviewedCommand);
233
+ program
234
+ .command('undismiss <pr>')
235
+ .description('Undo a dismiss or mark-reviewed override for a PR')
236
+ .addHelpText('after', `
237
+ Clear all overrides (dismissed findings and/or review-not-needed) for a PR,
238
+ returning it to its original feed bucket.
239
+
240
+ PR identifier formats:
241
+ 123 PR number (uses current repo)
242
+ #123 PR number with hash
243
+ owner/repo#123 Fully qualified
244
+ https://github.com/owner/repo/pull/123 GitHub URL
245
+
246
+ Examples:
247
+ haystack undismiss 42 # Undo overrides for PR #42
248
+ haystack undismiss acme/widgets#99 # Undo for specific repo
249
+ `)
250
+ .action(undismissCommand);
251
+ program
252
+ .command('pr-status <pr>')
253
+ .description('Show the current Haystack status of a PR')
254
+ .option('--json', 'Output as JSON')
255
+ .addHelpText('after', `
256
+ Show what bucket a PR is in within the Haystack pipeline:
257
+ analyzing, auto-fixing, good-to-merge, issues, needs-assignment, etc.
258
+
259
+ PR identifier formats:
260
+ 123 PR number (uses current repo)
261
+ #123 PR number with hash
262
+ owner/repo#123 Fully qualified
263
+ https://github.com/owner/repo/pull/123 GitHub URL
264
+
265
+ Examples:
266
+ haystack pr-status 42 # Current repo, PR #42
267
+ haystack pr-status acme/widgets#99 # Specific repo
268
+ haystack pr-status https://github.com/o/r/pull/1 # From URL
269
+ haystack pr-status 42 --json # Machine-readable output
270
+ `)
271
+ .action(prStatusCommand);
53
272
  // Secrets subcommands
54
273
  const secrets = program
55
274
  .command('secrets')
@@ -123,6 +342,52 @@ Examples:
123
342
  haystack config agentic-tool codex # Use your ChatGPT
124
343
  `)
125
344
  .action(handleAgenticTool);
345
+ config
346
+ .command('auto-merge [action]')
347
+ .description('Auto-merge safe PRs submitted via haystack submit (on|off|status)')
348
+ .addHelpText('after', `
349
+ Actions:
350
+ on, enable Enable auto-merge for safe PRs
351
+ off, disable Disable auto-merge
352
+ status Show current status (default)
353
+
354
+ When enabled, PRs submitted via \`haystack submit\` will be
355
+ automatically merged if Haystack analysis finds no issues
356
+ (safe to merge). PRs with bugs or rule violations still
357
+ require manual review.
358
+
359
+ Examples:
360
+ haystack config auto-merge # Show current status
361
+ haystack config auto-merge on # Enable auto-merge
362
+ haystack config auto-merge off # Disable auto-merge
363
+ `)
364
+ .action(handleAutoMerge);
365
+ config
366
+ .command('wait-for-reviewers [action] [reviewers...]')
367
+ .description('Configure which AI reviewers the merge queue waits for')
368
+ .addHelpText('after', `
369
+ Actions:
370
+ list Show current expected reviewers (default)
371
+ add <names> Add one or more AI reviewer sources
372
+ remove <names> Remove one or more AI reviewer sources
373
+ clear Remove all expected reviewers
374
+
375
+ AI reviewer sources are stored in .haystack.json under
376
+ babysitter.expected_ai_reviewers. The merge queue will
377
+ wait for these reviewers to post before starting the
378
+ grace period.
379
+
380
+ Run \`haystack config wait-for-reviewers list\` to see
381
+ all available reviewer sources and which are enabled.
382
+
383
+ Examples:
384
+ haystack config wait-for-reviewers # Show status
385
+ haystack config wait-for-reviewers add cursor # Wait for Cursor BugBot
386
+ haystack config wait-for-reviewers add cursor coderabbit # Add multiple
387
+ haystack config wait-for-reviewers remove cursor # Stop waiting for Cursor
388
+ haystack config wait-for-reviewers clear # Wait for none
389
+ `)
390
+ .action(handleWaitForReviewers);
126
391
  // Skills subcommands
127
392
  const skills = program
128
393
  .command('skills')
@@ -186,6 +451,81 @@ hooks
186
451
  .command('update')
187
452
  .description('Update Entire CLI to the latest version')
188
453
  .action(hooksUpdate);
454
+ hooks
455
+ .command('install-session')
456
+ .description('Install session-start hooks for coding CLIs')
457
+ .option('--cli <name>', 'Target CLI: claude, codex, gemini, or all')
458
+ .addHelpText('after', `
459
+ Configures your coding CLI to run \`haystack check-pending\` on session start.
460
+ This shows pending PR analysis results when you open a new terminal session.
461
+
462
+ Claude Code: Native SessionStart hook (.claude/settings.json)
463
+ Codex CLI: AGENTS.md instructions
464
+ Gemini CLI: GEMINI.md instructions
465
+
466
+ Examples:
467
+ haystack hooks install-session # Auto-detect CLIs
468
+ haystack hooks install-session --cli claude # Claude Code only
469
+ haystack hooks install-session --cli all # All detected CLIs
470
+ `)
471
+ .action(installSessionHooks);
472
+ hooks
473
+ .command('session-status')
474
+ .description('Check session hook installation status')
475
+ .action(sessionHooksStatus);
476
+ // Policy subcommands
477
+ const policy = program
478
+ .command('policy')
479
+ .description('Manage review policies (.haystack/review-policy.md)');
480
+ policy
481
+ .command('list')
482
+ .description('List all review policies')
483
+ .action(listPolicies);
484
+ policy
485
+ .command('add [name]')
486
+ .description('Add a new review policy interactively')
487
+ .addHelpText('after', `
488
+ This command prompts for:
489
+ • Policy name (e.g., "Infrastructure changes")
490
+ • File patterns (comma-separated globs, e.g., "terraform/**,*.tf")
491
+ • Severity (critical, high, medium, low)
492
+ • Reason (why this requires human review)
493
+
494
+ Examples:
495
+ haystack policy add # Interactive add
496
+ haystack policy add "Database changes" # Start with name
497
+ `)
498
+ .action(addPolicy);
499
+ policy
500
+ .command('remove <name>')
501
+ .description('Remove a review policy by name')
502
+ .action(removePolicy);
503
+ policy
504
+ .command('add-instruction [text]')
505
+ .description('Add a semantic review instruction')
506
+ .addHelpText('after', `
507
+ Instructions are natural language directives that override default review behavior.
508
+ They apply to all PRs (not file-pattern-gated).
509
+
510
+ Examples:
511
+ haystack policy add-instruction "Never flag weak test coverage as needing review"
512
+ haystack policy add-instruction # Interactive prompt
513
+ `)
514
+ .action(addInstruction);
515
+ policy
516
+ .command('init')
517
+ .description('Create initial review-policy.md with example policies')
518
+ .option('-f, --force', 'Overwrite existing file')
519
+ .addHelpText('after', `
520
+ Creates .haystack/review-policy.md with sensible defaults:
521
+ • Infrastructure changes (terraform, pulumi, cdk)
522
+ • Secret files (*.secret*, *.env*, credentials)
523
+ • CI/CD pipelines (GitHub Actions, GitLab CI, etc.)
524
+
525
+ The generated policies appear in Haystack's "Human Review Needed"
526
+ section when a PR touches matching files.
527
+ `)
528
+ .action(initPolicies);
189
529
  // Show help if no command provided
190
530
  if (process.argv.length === 2) {
191
531
  program.help();
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Auto-detection utilities for framework, monorepo, and service configuration.
3
+ * Used by the setup wizard to provide smart defaults.
4
+ */
5
+ export interface DetectedFramework {
6
+ name: string;
7
+ devCommand: string;
8
+ port: number;
9
+ readyPattern: string;
10
+ configFile: string;
11
+ }
12
+ export interface DetectedService {
13
+ name: string;
14
+ root: string;
15
+ type: 'server' | 'batch';
16
+ framework?: DetectedFramework;
17
+ /** Services this service depends on (detected from proxy config) */
18
+ depends_on?: string[];
19
+ }
20
+ export interface ProxyTarget {
21
+ /** Path pattern being proxied (e.g., "/api") */
22
+ path: string;
23
+ /** Target URL (e.g., "http://localhost:8787") */
24
+ target: string;
25
+ /** Port extracted from target */
26
+ port: number;
27
+ }
28
+ export interface ProjectDetection {
29
+ isMonorepo: boolean;
30
+ packageManager: 'npm' | 'pnpm' | 'yarn' | 'bun';
31
+ frameworks: DetectedFramework[];
32
+ services: DetectedService[];
33
+ /** Proxy targets detected from config files */
34
+ proxyTargets: ProxyTarget[];
35
+ suggestions: {
36
+ projectName: string;
37
+ devCommand: string;
38
+ port: number;
39
+ readyPattern: string;
40
+ authBypassEnv?: string;
41
+ };
42
+ }
43
+ /**
44
+ * Main detection function - analyzes a project directory
45
+ */
46
+ export declare function detectProject(rootDir?: string): ProjectDetection;
47
+ /**
48
+ * Format detection results for display to user
49
+ */
50
+ export declare function formatDetectionSummary(detection: ProjectDetection): string;