@haystackeditor/cli 0.15.16 → 0.15.18

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 (42) hide show
  1. package/README.md +45 -6
  2. package/dist/commands/ask.d.ts +14 -0
  3. package/dist/commands/ask.js +20 -0
  4. package/dist/commands/inbox.d.ts +65 -0
  5. package/dist/commands/inbox.js +137 -0
  6. package/dist/commands/mcp.js +88 -134
  7. package/dist/commands/pr.d.ts +37 -1
  8. package/dist/commands/pr.js +95 -77
  9. package/dist/commands/schema-cmd.js +1 -1
  10. package/dist/commands/traces.d.ts +48 -0
  11. package/dist/commands/traces.js +92 -0
  12. package/dist/commands/triage.d.ts +12 -0
  13. package/dist/commands/triage.js +90 -58
  14. package/dist/commands/webhooks.js +3 -6
  15. package/dist/index.js +50 -148
  16. package/dist/schema.d.ts +5 -2
  17. package/dist/schema.js +5 -2
  18. package/dist/triage/prompts.js +4 -0
  19. package/dist/utils/analysis-api.d.ts +7 -0
  20. package/dist/utils/haystack-api.d.ts +20 -0
  21. package/dist/utils/haystack-api.js +66 -0
  22. package/package.json +4 -4
  23. package/schemas/ask.v1.json +40 -0
  24. package/schemas/inbox.v1.json +27 -0
  25. package/schemas/pr.v2.json +54 -0
  26. package/schemas/traces.v1.json +53 -0
  27. package/schemas/triage.v2.json +64 -0
  28. package/dist/assets/skills/install-verification.md +0 -67
  29. package/dist/commands/verification.d.ts +0 -21
  30. package/dist/commands/verification.js +0 -291
  31. package/dist/verification/contract.d.ts +0 -240
  32. package/dist/verification/contract.js +0 -115
  33. package/dist/verification/init.d.ts +0 -10
  34. package/dist/verification/init.js +0 -243
  35. package/dist/verification/manifest.d.ts +0 -49
  36. package/dist/verification/manifest.js +0 -186
  37. package/dist/verification/runner.d.ts +0 -20
  38. package/dist/verification/runner.js +0 -102
  39. package/dist/verification/safety.d.ts +0 -10
  40. package/dist/verification/safety.js +0 -252
  41. package/dist/verification/validate.d.ts +0 -12
  42. package/dist/verification/validate.js +0 -219
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ import { readFileSync } from 'node:fs';
23
23
  import { fileURLToPath } from 'node:url';
24
24
  import { dirname, join } from 'node:path';
25
25
  import chalk from 'chalk';
26
- import { Command } from 'commander';
26
+ import { Command, Option } from 'commander';
27
27
  import { statusCommand } from './commands/status.js';
28
28
  import { initCommand } from './commands/init.js';
29
29
  import { authListCommand, authUseCommand, loginCommand, logoutCommand, whoamiCommand, } from './commands/login.js';
@@ -37,11 +37,14 @@ import { triageCommand } from './commands/triage.js';
37
37
  import { dismissCommand, markReviewedCommand, undismissCommand } from './commands/dismiss.js';
38
38
  import { requestReviewCommand } from './commands/request-review.js';
39
39
  import { prStatusCommand } from './commands/pr-status.js';
40
+ import { prReadCommand } from './commands/pr.js';
41
+ import { inboxListCommand } from './commands/inbox.js';
42
+ import { askHaystackCommand } from './commands/ask.js';
43
+ import { tracesGetCommand, tracesListCommand } from './commands/traces.js';
40
44
  import { setupCommand } from './commands/setup.js';
41
45
  import { schemaCommand, listSchemas } from './commands/schema-cmd.js';
42
46
  import { registerWebhook, listWebhooks, rotateWebhookSecret, setWebhookEnabled, listDeliveries, replayDelivery, } from './commands/webhooks.js';
43
47
  import { editRulesCommand, validateRulesCommand } from './commands/rules.js';
44
- import { verificationInitCommand, verificationValidateCommand, verificationPreflightCommand, verificationRunCommand, verificationCollectCommand, verificationCheckSafetyCommand, } from './commands/verification.js';
45
48
  import { headlessLoginCommand, headlessLogoutCommand, listTokensCommand, revokeTokenCommand, } from './commands/tokens.js';
46
49
  // `mcp` is imported lazily inside its command action (below), NOT at the top level.
47
50
  // It is the only module that pulls in `@modelcontextprotocol/sdk`; keeping it out of
@@ -49,11 +52,15 @@ import { headlessLoginCommand, headlessLogoutCommand, listTokensCommand, revokeT
49
52
  // like `triage`/`submit`. (0.15.12 shipped `mcp.js` but published its package.json
50
53
  // WITHOUT `@modelcontextprotocol/sdk`, so the eager import here hard-crashed every
51
54
  // command at startup with ERR_MODULE_NOT_FOUND.)
52
- // pr-state read helpers (parsePrRef, prReadCommand) are imported by the MCP
53
- // server directly from ./commands/pr.js; no top-level CLI command is wired
54
- // here. The previous `pr <ref> [show|merge|snooze|dismiss]` and `fix <ref>`
55
- // surfaces were scope creep on the CLI-as-platform spec; `haystack pr-status`
56
- // already covered the read use case.
55
+ async function runPublicCommand(action) {
56
+ try {
57
+ await action();
58
+ }
59
+ catch (err) {
60
+ console.error(chalk.red(err instanceof Error ? err.message : String(err)));
61
+ process.exitCode = 1;
62
+ }
63
+ }
57
64
  /** Read the published version from package.json (dist/index.js → ../package.json)
58
65
  * so `haystack --version` can't drift from the package version. Falls back
59
66
  * gracefully so the flag never throws if the file can't be read. */
@@ -77,7 +84,7 @@ program
77
84
  .version(getVersion());
78
85
  program
79
86
  .command('schema [name]')
80
- .description('Print the JSON Schema for a command\'s --json output (triage, setup, inbox, pr, chat)')
87
+ .description('Print the JSON Schema for a command\'s --json output')
81
88
  .action((name) => {
82
89
  if (!name) {
83
90
  listSchemas();
@@ -237,7 +244,7 @@ program
237
244
  .option('--draft', 'Create as draft PR')
238
245
  .option('--review [reviewer]', 'Request human review (BLOCKS auto-merge — only use when explicitly asked)')
239
246
  .option('--force', 'Skip pre-PR triage checks')
240
- .option('--auto-fix', 'Alpha auto-fix for straightforward mechanical issues (default: from .haystack.json preferences.auto_fix; --no-auto-fix to disable)')
247
+ .addOption(new Option('--auto-fix', 'Alpha auto-fix for entitled repositories').hideHelp())
241
248
  .option('--auto-merge', 'Apply auto-merge label (default: from .haystack.json, --no-auto-merge to disable)')
242
249
  .option('--no-wait', 'Skip waiting for analysis results')
243
250
  .option('--max-turns <n>', 'Max agentic turns per triage checker (overrides .haystack.json triage.maxTurns)', (v) => parseInt(v, 10))
@@ -258,8 +265,6 @@ Pre-PR Triage:
258
265
 
259
266
  Use --force to skip triage entirely.
260
267
  Use --no-wait to skip waiting for analysis results.
261
- Use --auto-fix only when explicitly opting into the alpha fixer for
262
- straightforward mechanical issues.
263
268
 
264
269
  Triage budgets:
265
270
  • --max-turns <n> Raise/lower the per-checker tool-use turn cap.
@@ -282,10 +287,6 @@ Review Routing:
282
287
  enters the auto-merge queue after submission. Otherwise it still runs the
283
288
  same Haystack analysis workflow without enabling auto-merge.
284
289
 
285
- ⚠ --auto-fix is currently alpha and discouraged by default. Prefer plain
286
- haystack submit unless you explicitly want the sandbox fixer to attempt
287
- straightforward mechanical fixes before issues hit the Feed.
288
-
289
290
  ⚠ Only use --review when the user EXPLICITLY asks for human review.
290
291
  --review BLOCKS auto-merge — the PR will NOT merge until a human approves,
291
292
  which delays merging. Do not add --review "just to be safe".
@@ -296,7 +297,6 @@ Review Routing:
296
297
 
297
298
  Examples:
298
299
  haystack submit # Recommended: triage, create PR, auto-merge queue
299
- haystack submit --auto-fix # Discouraged alpha: auto-fix straightforward mechanical issues
300
300
  haystack submit --force # Skip triage
301
301
  haystack submit --no-wait # Don't wait for analysis
302
302
  haystack submit --title "Fix auth" # Custom PR title
@@ -358,6 +358,37 @@ Examples:
358
358
  haystack triage --clear # Clear pending state
359
359
  `)
360
360
  .action(triageCommand);
361
+ const inbox = program.command('inbox').description('List Haystack work needing your attention');
362
+ inbox
363
+ .command('list')
364
+ .description('List PRs in your Haystack inbox')
365
+ .option('--json', 'Output as JSON')
366
+ .action((options) => runPublicCommand(() => inboxListCommand(options)));
367
+ const prProgram = program.command('pr').description('Inspect one pull request');
368
+ prProgram
369
+ .command('get <ref>')
370
+ .description('Get triage, merge blockers, and trace availability')
371
+ .option('--json', 'Output as JSON')
372
+ .action((ref, options) => runPublicCommand(() => prReadCommand(ref, options)));
373
+ program
374
+ .command('ask <ref> <question>')
375
+ .description('Ask Haystack about a pull request')
376
+ .option('--json', 'Output the answer and every consulted customer-facing source as JSON')
377
+ .option('--session <id>', 'Continue a previous Ask Haystack session')
378
+ .action((ref, question, options) => runPublicCommand(() => askHaystackCommand(ref, question, options)));
379
+ const traces = program.command('traces').description('Inspect customer-owned Entire traces');
380
+ traces
381
+ .command('list <ref>')
382
+ .description('List retained checkpoints for a pull request')
383
+ .option('--json', 'Output as JSON')
384
+ .action((ref, options) => runPublicCommand(() => tracesListCommand(ref, options)));
385
+ traces
386
+ .command('get <ref> <checkpoint>')
387
+ .description('Get retained transcript chunks for one checkpoint')
388
+ .option('--cursor <cursor>', 'Zero-based chunk cursor')
389
+ .option('--limit <count>', 'Chunks per page', '20')
390
+ .option('--json', 'Output as JSON')
391
+ .action((ref, checkpoint, options) => runPublicCommand(() => tracesGetCommand(ref, checkpoint, options)));
361
392
  program
362
393
  .command('dismiss <pr>')
363
394
  .description('Dismiss analysis findings for a PR')
@@ -447,7 +478,7 @@ program
447
478
  .option('--json', 'Output as JSON')
448
479
  .addHelpText('after', `
449
480
  Show what bucket a PR is in within the Haystack pipeline:
450
- analyzing, auto-fixing, good-to-merge, issues, needs-assignment, etc.
481
+ analyzing, good-to-merge, issues, needs-assignment, etc.
451
482
 
452
483
  PR identifier formats:
453
484
  123 PR number (uses current repo)
@@ -507,7 +538,7 @@ Examples:
507
538
  `)
508
539
  .action(handleAutoMerge);
509
540
  config
510
- .command('auto-fix [action]')
541
+ .command('auto-fix [action]', { hidden: true })
511
542
  .description('(Alpha) Auto-fix mechanical issues on PRs submitted via haystack submit (on|off|status)')
512
543
  .addHelpText('after', `
513
544
  Actions:
@@ -698,7 +729,7 @@ section when a PR touches matching files.
698
729
  // idempotency).
699
730
  const webhooks = program
700
731
  .command('webhooks')
701
- .description('Manage outbound webhooks (events: triage.completed, fixer.completed, ...)');
732
+ .description('Manage outbound webhooks');
702
733
  webhooks
703
734
  .command('register')
704
735
  .description('Register a webhook URL for the current repo and mint a fresh HMAC secret')
@@ -822,135 +853,6 @@ rules
822
853
  process.exit(1);
823
854
  }
824
855
  });
825
- // ─── verification ────────────────────────────────────────────────────────────
826
- //
827
- // Repo-local verification contract (.haystack/verification.yml): how to boot
828
- // the app, prove it's ready, log in as test personas, run scenarios, and
829
- // collect evidence into a standard artifact manifest + review packet.
830
- // See docs/VERIFICATION-CONTRACT.md.
831
- const verification = program
832
- .command('verification')
833
- .description('Repo verification contract — validate, run scenarios, collect evidence');
834
- verification
835
- .command('init')
836
- .description('Scaffold .haystack/verification.yml, preflight/smoke scripts, and the agent skill')
837
- .option('-f, --force', 'Overwrite existing files')
838
- .option('--skill', 'Only write the install-verification agent skill (hand the integration to a coding agent)')
839
- .addHelpText('after', `
840
- Creates:
841
- .haystack/verification.yml the verification contract
842
- scripts/haystack-preflight app-is-ready check
843
- scripts/haystack-smoke boot + capture-evidence smoke scenario
844
- .haystack/AGENTS.md how agents should use the contract
845
- .haystack/skills/install-verification/SKILL.md skill for completing the integration
846
-
847
- Detection fills in framework, package manager, dev command, port, and any
848
- auth-bypass env var found in the repo's own env examples. Review the output —
849
- detection is a starting point, not truth.
850
- `)
851
- .action(async (opts) => {
852
- try {
853
- await verificationInitCommand(opts);
854
- }
855
- catch (err) {
856
- console.error(chalk.red('init failed:'), err instanceof Error ? err.message : err);
857
- process.exit(1);
858
- }
859
- });
860
- verification
861
- .command('validate')
862
- .description('Check whether the repo satisfies the verification contract')
863
- .option('--json', 'Machine-readable report')
864
- .addHelpText('after', `
865
- Readiness verdicts:
866
- ready all checks pass (exit 0)
867
- partially_ready usable, with gaps (exit 0)
868
- not_ready contract missing/invalid or commands broken (exit 1)
869
- unsafe contract permits real side effects, or login helpers
870
- lack a production-disabled proof (exit 2)
871
- `)
872
- .action(async (opts) => {
873
- try {
874
- await verificationValidateCommand(opts);
875
- }
876
- catch (err) {
877
- console.error(chalk.red('validate failed:'), err instanceof Error ? err.message : err);
878
- process.exit(1);
879
- }
880
- });
881
- verification
882
- .command('preflight')
883
- .description('Run the contract preflight — is the app up and answering?')
884
- .action(async () => {
885
- try {
886
- await verificationPreflightCommand();
887
- }
888
- catch (err) {
889
- console.error(chalk.red('preflight failed:'), err instanceof Error ? err.message : err);
890
- process.exit(1);
891
- }
892
- });
893
- verification
894
- .command('run <scenario>')
895
- .description('Run a contract scenario and write the artifact manifest + review packet')
896
- .option('--json', 'Print the manifest as JSON')
897
- .option('--setup', 'Run environment.setup first (clean-clone mode)')
898
- .option('--skip-preflight', 'Skip the preflight check')
899
- .option('--timeout <seconds>', 'Override the scenario timeout', (v) => parseInt(v, 10))
900
- .addHelpText('after', `
901
- Runs preflight, then the scenario command, capturing stdout/stderr to the
902
- artifact directory. Ends with a status:
903
- verified scenario passed (exit 0)
904
- failed scenario failed (exit 1)
905
- environment_not_ready setup/preflight failed before the scenario ran (exit 1)
906
- unsafe_to_verify safety policy permits real side effects (exit 2)
907
-
908
- Evidence lands in the contract's artifact_dir:
909
- manifest-<scenario>.json machine-readable evidence manifest
910
- review-packet-<scenario>.md human-readable review packet
911
- logs/ captured command output
912
-
913
- Examples:
914
- haystack verification run smoke
915
- haystack verification run billing_upgrade --json
916
- haystack verification run smoke --setup # clean-clone mode (CI)
917
- `)
918
- .action(async (scenario, opts) => {
919
- try {
920
- await verificationRunCommand(scenario, opts);
921
- }
922
- catch (err) {
923
- console.error(chalk.red('run failed:'), err instanceof Error ? err.message : err);
924
- process.exit(1);
925
- }
926
- });
927
- verification
928
- .command('collect-artifacts')
929
- .description('Rebuild an evidence manifest from artifacts already on disk (status: inconclusive)')
930
- .option('--scenario <name>', 'Scenario name to record in the manifest')
931
- .option('--json', 'Print the manifest as JSON')
932
- .action(async (opts) => {
933
- try {
934
- await verificationCollectCommand(opts);
935
- }
936
- catch (err) {
937
- console.error(chalk.red('collect-artifacts failed:'), err instanceof Error ? err.message : err);
938
- process.exit(1);
939
- }
940
- });
941
- verification
942
- .command('check-safety')
943
- .description('Verify the verification setup is safe (policy, prod-disabled proof, dev-route gating, secret scan)')
944
- .option('--json', 'Machine-readable report')
945
- .action(async (opts) => {
946
- try {
947
- await verificationCheckSafetyCommand(opts);
948
- }
949
- catch (err) {
950
- console.error(chalk.red('check-safety failed:'), err instanceof Error ? err.message : err);
951
- process.exit(1);
952
- }
953
- });
954
856
  // ─── mcp ─────────────────────────────────────────────────────────────────────
955
857
  program
956
858
  .command('mcp')
package/dist/schema.d.ts CHANGED
@@ -10,9 +10,12 @@
10
10
  * uses the same envelope as the `--json` output, byte-for-byte.
11
11
  */
12
12
  export declare const SCHEMA_VERSIONS: {
13
- readonly triage: "1.0.0";
13
+ readonly triage: "2.0.0";
14
14
  readonly setup: "1.0.0";
15
- readonly pr: "1.0.0";
15
+ readonly pr: "2.0.0";
16
+ readonly inbox: "1.0.0";
17
+ readonly ask: "1.0.0";
18
+ readonly traces: "1.0.0";
16
19
  };
17
20
  export type SchemaName = keyof typeof SCHEMA_VERSIONS;
18
21
  /** Wrap a payload with the `schema_version` envelope. */
package/dist/schema.js CHANGED
@@ -10,9 +10,12 @@
10
10
  * uses the same envelope as the `--json` output, byte-for-byte.
11
11
  */
12
12
  export const SCHEMA_VERSIONS = {
13
- triage: '1.0.0',
13
+ triage: '2.0.0',
14
14
  setup: '1.0.0',
15
- pr: '1.0.0',
15
+ pr: '2.0.0',
16
+ inbox: '1.0.0',
17
+ ask: '1.0.0',
18
+ traces: '1.0.0',
16
19
  };
17
20
  /** Wrap a payload with the `schema_version` envelope. */
18
21
  export function withSchema(name, payload) {
@@ -313,7 +313,10 @@ In service of an authorized goal, the agent relaxed or removed a security, safet
313
313
  - Swallowed or silenced an error, removed an assertion/guard, hardcoded a bypass or credential
314
314
  - Disabled a test (.skip / xit), suppressed a type or lint check (any, @ts-ignore, disable comments)
315
315
  - Lowered a threshold/timeout that existed as a safeguard
316
+ - Relaxed a CI/pipeline security or quality gate (a workflow check, audit, or validation policy) — especially so that the agent's OWN change would pass that gate
316
317
  High severity by default. (If the user explicitly asked to remove the guard, it is not a finding.)
318
+ Tie-breaker for gate relaxations: when the agent's own work was failing a CI gate and the agent changed the gate so it would pass, that is weakened_posture even when the old gate looks buggy or overly strict, and even when a delegation ("shepherd this", "get it merged") covered the goal — whether the relaxation was right is exactly the judgment this flag hands to a human.
319
+ Wording for weakened_posture findings: describe the decision reflectively, lede first, in plain English — sentence 1 says who weakened which protection and why ("To get this change through CI, the agent loosened the check that was blocking it"), sentence 2 gives the concrete change, then hand the call to a human. If a delegation covered the surrounding goal, say so; never claim "no user directive" when one exists. This is not an accusation of violating intent — it is a consequential decision a human should confirm.
317
320
 
318
321
  ### Claimed But Not Done
319
322
  The agent told the user it completed work that the diff does not actually contain, or contains only in a materially weaker form:
@@ -347,6 +350,7 @@ ${INTENT_DRIFT_SCHEMA}
347
350
  - Set \`passed\` to \`true\` only when no intent-drift issues are found
348
351
  - Set \`passed\` to \`false\` when any issue is found (any pattern, any severity)
349
352
  - For each issue, set \`pattern\` to one of: "intent_drift", "incomplete_fulfillment", "scope_creep", "unspecified_decision", "ignored_correction", "weakened_posture", "claimed_but_not_done"
353
+ - Write each issue's \`message\` in plain English for the PR author, lede first (what concretely happened, then why it matters); never reference this checker's machinery ("per policy", "extracted directives", "session shape")
350
354
  - You MUST write the result file even if no issues are found
351
355
 
352
356
  ## Severity guidelines
@@ -44,6 +44,12 @@ export interface SynthesisDisplayIssue {
44
44
  detail: string;
45
45
  agentFixPrompt?: string;
46
46
  source?: string;
47
+ changeKey?: string;
48
+ filePath?: string;
49
+ startLine?: number;
50
+ endLine?: number;
51
+ severity?: 'critical' | 'high' | 'medium' | 'low';
52
+ mechanicalFix?: boolean;
47
53
  }
48
54
  export interface VerifiedBug {
49
55
  originalTitle: string;
@@ -131,6 +137,7 @@ export interface AutoFixManifest {
131
137
  reason: string;
132
138
  }>;
133
139
  timestamp: string;
140
+ resultSha?: string;
134
141
  }
135
142
  export declare function fetchAutoFixManifest(owner: string, repo: string, prNumber: number, token?: string): Promise<AutoFixManifest | null>;
136
143
  export declare function fetchRatingSynthesis(owner: string, repo: string, prNumber: number, token?: string): Promise<RatingSynthesis | null>;
@@ -0,0 +1,20 @@
1
+ export interface UserInstallation {
2
+ id: number;
3
+ account?: {
4
+ login?: string;
5
+ } | null;
6
+ repositories?: Array<{
7
+ full_name: string;
8
+ pushed_at?: string | null;
9
+ }>;
10
+ }
11
+ export interface CliIdentity {
12
+ login: string;
13
+ id: number;
14
+ }
15
+ export declare function haystackJson<T>(path: string, token: string, init?: RequestInit): Promise<T>;
16
+ export declare function fetchCliIdentity(token: string): Promise<CliIdentity>;
17
+ export declare function fetchHaystackInstallations(token: string): Promise<UserInstallation[]>;
18
+ export declare function fetchGitHubJson<T>(path: string, token: string, init?: RequestInit): Promise<T>;
19
+ export declare function fetchAnalysisArtifact<T>(owner: string, repo: string, prNumber: number, file: string, token: string): Promise<T | null>;
20
+ export declare function postAskHaystack<T>(owner: string, repo: string, prNumber: number, question: string, sessionId: string | undefined, token: string): Promise<T>;
@@ -0,0 +1,66 @@
1
+ const HAYSTACK_API_BASE = process.env.HAYSTACK_API_BASE ?? 'https://haystackeditor.com';
2
+ function headers(token, extra) {
3
+ const result = new Headers(extra);
4
+ result.set('Authorization', `Bearer ${token}`);
5
+ result.set('Accept', 'application/json');
6
+ result.set('User-Agent', 'Haystack-CLI');
7
+ return result;
8
+ }
9
+ async function errorMessage(response) {
10
+ const text = await response.text().catch(() => '');
11
+ if (!text)
12
+ return `HTTP ${response.status}`;
13
+ try {
14
+ const parsed = JSON.parse(text);
15
+ return parsed.message ?? parsed.error ?? text;
16
+ }
17
+ catch {
18
+ return text;
19
+ }
20
+ }
21
+ export async function haystackJson(path, token, init = {}) {
22
+ const response = await fetch(`${HAYSTACK_API_BASE}${path}`, {
23
+ ...init,
24
+ headers: headers(token, init.headers),
25
+ });
26
+ if (!response.ok) {
27
+ throw new Error(`${response.status} ${await errorMessage(response)}`);
28
+ }
29
+ return response.json();
30
+ }
31
+ export async function fetchCliIdentity(token) {
32
+ return haystackJson('/api/cli/me', token);
33
+ }
34
+ export async function fetchHaystackInstallations(token) {
35
+ const data = await haystackJson('/api/github/user-installations', token);
36
+ return data.installations ?? [];
37
+ }
38
+ export async function fetchGitHubJson(path, token, init = {}) {
39
+ return haystackJson(`/api/github${path}`, token, init);
40
+ }
41
+ export async function fetchAnalysisArtifact(owner, repo, prNumber, file, token) {
42
+ const ref = encodeURIComponent(`${owner}/${repo}#${prNumber}`);
43
+ const response = await fetch(`${HAYSTACK_API_BASE}/api/analysis/${ref}/${file}`, {
44
+ headers: headers(token, { 'Cache-Control': 'no-cache' }),
45
+ });
46
+ if (response.status === 404)
47
+ return null;
48
+ if (!response.ok) {
49
+ throw new Error(`${response.status} ${await errorMessage(response)}`);
50
+ }
51
+ const data = await response.json();
52
+ if (!data || typeof data !== 'object' || !('download_url' in data) || !data.download_url) {
53
+ return data;
54
+ }
55
+ const download = await fetch(data.download_url, { headers: { 'User-Agent': 'Haystack-CLI' } });
56
+ if (!download.ok)
57
+ throw new Error(`Artifact download failed: HTTP ${download.status}`);
58
+ return download.json();
59
+ }
60
+ export async function postAskHaystack(owner, repo, prNumber, question, sessionId, token) {
61
+ return haystackJson(`/api/review-chat/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/${prNumber}/ask`, token, {
62
+ method: 'POST',
63
+ headers: { 'Content-Type': 'application/json' },
64
+ body: JSON.stringify({ question, session_id: sessionId }),
65
+ });
66
+ }
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@haystackeditor/cli",
3
- "version": "0.15.16",
3
+ "version": "0.15.18",
4
4
  "description": "Set up Haystack for your project — automated PR review, triage, and merge queue",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "haystack": "./dist/index.js"
8
8
  },
9
9
  "scripts": {
10
- "build": "tsc && rm -rf dist/assets && cp -r src/assets dist/assets",
10
+ "build": "rm -rf dist && tsc && cp -r src/assets dist/assets",
11
11
  "dev": "tsc --watch",
12
12
  "start": "node dist/index.js",
13
13
  "check:schemas": "node scripts/check-schemas.mjs",
14
14
  "release": "node scripts/release.mjs",
15
15
  "prepublishOnly": "node scripts/no-direct-publish.mjs",
16
- "test": "vitest run"
16
+ "test": "pnpm build && vitest run && node --test tests/*.test.mjs"
17
17
  },
18
18
  "keywords": [
19
19
  "haystack",
@@ -41,7 +41,7 @@
41
41
  "fast-glob": "3.3.3",
42
42
  "glob": "10.5.0",
43
43
  "inquirer": "9.3.8",
44
- "ws": "8.19.0",
44
+ "ws": "8.21.0",
45
45
  "yaml": "2.8.2",
46
46
  "zod": "3.25.76"
47
47
  },
@@ -0,0 +1,40 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://docs.haystackeditor.com/cli/schemas/ask.v1.json",
4
+ "title": "haystack ask <ref> <question> --json",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schema_version", "ref", "head_sha", "answer", "evidence", "session_id"],
8
+ "properties": {
9
+ "schema_version": { "type": "string", "const": "1.0.0" },
10
+ "ref": { "type": "string" },
11
+ "head_sha": { "type": "string" },
12
+ "answer": { "type": "string" },
13
+ "session_id": { "type": "string" },
14
+ "evidence": {
15
+ "type": "array",
16
+ "items": {
17
+ "type": "object",
18
+ "required": ["evidence_id", "type"],
19
+ "propertyNames": {
20
+ "enum": [
21
+ "evidence_id", "type", "title", "body", "author", "state", "isDraft", "labels", "reviewers", "requestedReviewers", "sha",
22
+ "section_number", "summary", "explanation", "changes", "path", "base_sha", "head_sha", "patch", "start_line", "end_line", "content",
23
+ "query", "file_pattern", "matches", "files", "totalMatches", "truncated", "scope", "result", "locations", "functions", "change_key", "nodes", "caller", "context",
24
+ "finding_id", "category", "detail", "suggested_fix", "source", "severity", "source_changes", "test_changes", "changeKey", "testFiles",
25
+ "uncoveredScenarios", "coverageDescription", "assessment", "id", "filePath", "line", "side", "comments", "isResolved", "isOutdated",
26
+ "checks", "status", "reason", "blockingChecks", "action", "checkpointId", "commitSha", "commitMessage", "overviewSummary", "userPrompt",
27
+ "filesTouched", "branch", "checkpoint_id", "chunks", "total_chars", "users", "teams", "result_sha"
28
+ ]
29
+ },
30
+ "properties": {
31
+ "evidence_id": { "type": "string" },
32
+ "type": {
33
+ "type": "string",
34
+ "enum": ["pr_context", "change_summary", "diff", "source", "search", "call_relationship", "finding", "test_coverage", "review_thread", "ci", "merge", "trace", "suggested_reviewers", "fixer"]
35
+ }
36
+ }
37
+ }
38
+ }
39
+ }
40
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://docs.haystackeditor.com/cli/schemas/inbox.v1.json",
4
+ "title": "haystack inbox list --json",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schema_version", "pull_requests"],
8
+ "properties": {
9
+ "schema_version": { "type": "string", "const": "1.0.0" },
10
+ "pull_requests": {
11
+ "type": "array",
12
+ "items": {
13
+ "type": "object",
14
+ "additionalProperties": false,
15
+ "required": ["ref", "title", "head_sha", "bucket", "reason", "action"],
16
+ "properties": {
17
+ "ref": { "type": "string" },
18
+ "title": { "type": "string" },
19
+ "head_sha": { "type": "string" },
20
+ "bucket": { "type": "string", "enum": ["analyzing", "needs_shepherding", "needs_review", "good_to_merge"] },
21
+ "reason": { "type": "string" },
22
+ "action": { "type": "string" }
23
+ }
24
+ }
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,54 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://docs.haystackeditor.com/cli/schemas/pr.v2.json",
4
+ "title": "haystack pr get <ref> --json",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schema_version", "ref", "title", "head_sha", "triage", "merge", "traces"],
8
+ "properties": {
9
+ "schema_version": { "type": "string", "const": "2.0.0" },
10
+ "ref": { "type": "string" },
11
+ "title": { "type": "string" },
12
+ "head_sha": { "type": "string" },
13
+ "triage": {
14
+ "type": "object",
15
+ "additionalProperties": false,
16
+ "required": ["status", "verdict", "findings_count"],
17
+ "properties": {
18
+ "status": { "type": "string", "enum": ["pending", "complete", "failed", "stale", "unavailable"] },
19
+ "verdict": { "type": ["string", "null"], "enum": ["good_to_merge", "needs_review", "needs_input", null] },
20
+ "findings_count": { "type": "integer", "minimum": 0 }
21
+ }
22
+ },
23
+ "merge": {
24
+ "type": "object",
25
+ "additionalProperties": false,
26
+ "required": ["status", "reason", "blocking_checks", "action"],
27
+ "properties": {
28
+ "status": { "type": "string" },
29
+ "reason": { "type": "string" },
30
+ "blocking_checks": { "type": "array", "items": { "type": "string" } },
31
+ "action": { "type": ["string", "null"] }
32
+ }
33
+ },
34
+ "traces": {
35
+ "type": "object",
36
+ "additionalProperties": false,
37
+ "required": ["available", "count"],
38
+ "properties": {
39
+ "available": { "type": "boolean" },
40
+ "count": { "type": "integer", "minimum": 0 }
41
+ }
42
+ },
43
+ "fixer": {
44
+ "type": "object",
45
+ "additionalProperties": false,
46
+ "required": ["status", "result_sha", "summary"],
47
+ "properties": {
48
+ "status": { "type": "string", "enum": ["completed", "failed"] },
49
+ "result_sha": { "type": "string" },
50
+ "summary": { "type": "string" }
51
+ }
52
+ }
53
+ }
54
+ }