@haystackeditor/cli 0.15.18 → 0.15.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +86 -1
- package/dist/assets/skills/map-your-system.md +143 -0
- package/dist/commands/ask.js +2 -2
- package/dist/commands/config.d.ts +9 -0
- package/dist/commands/config.js +46 -14
- package/dist/commands/dismiss.d.ts +15 -3
- package/dist/commands/dismiss.js +66 -116
- package/dist/commands/mcp.js +96 -1
- package/dist/commands/policy.js +31 -12
- package/dist/commands/pr-status.d.ts +125 -2
- package/dist/commands/pr-status.js +88 -73
- package/dist/commands/pr.d.ts +1 -7
- package/dist/commands/pr.js +10 -21
- package/dist/commands/request-review.d.ts +12 -1
- package/dist/commands/request-review.js +42 -65
- package/dist/commands/review.d.ts +25 -0
- package/dist/commands/review.js +191 -0
- package/dist/commands/setup.js +4 -1
- package/dist/commands/skills.js +4 -0
- package/dist/commands/submit.d.ts +3 -0
- package/dist/commands/submit.js +178 -38
- package/dist/commands/system-map.d.ts +42 -0
- package/dist/commands/system-map.js +228 -0
- package/dist/commands/tokens.js +3 -2
- package/dist/commands/traces.js +2 -2
- package/dist/commands/triage.d.ts +1 -6
- package/dist/commands/triage.js +51 -55
- package/dist/index.js +116 -19
- package/dist/schema.d.ts +4 -0
- package/dist/schema.js +4 -0
- package/dist/states.d.ts +29 -0
- package/dist/states.js +46 -0
- package/dist/triage/runner.d.ts +7 -0
- package/dist/triage/runner.js +2 -2
- package/dist/utils/action-output.d.ts +24 -0
- package/dist/utils/action-output.js +26 -0
- package/dist/utils/analysis-api.d.ts +44 -0
- package/dist/utils/analysis-api.js +137 -6
- package/dist/utils/auth.js +7 -0
- package/dist/utils/git.d.ts +4 -1
- package/dist/utils/git.js +17 -7
- package/dist/utils/haystack-api.d.ts +15 -0
- package/dist/utils/haystack-api.js +58 -7
- package/dist/utils/pr-ref.d.ts +27 -0
- package/dist/utils/pr-ref.js +56 -0
- package/package.json +1 -1
- package/schemas/action.v1.json +22 -0
- package/schemas/error.v1.json +14 -0
- package/schemas/pr-status.v1.json +61 -0
- package/schemas/pr.v1.json +80 -17
- package/schemas/setup.v1.json +66 -12
- package/schemas/submit.v1.json +88 -0
- package/schemas/triage.v1.json +71 -18
package/dist/index.js
CHANGED
|
@@ -25,6 +25,7 @@ import { dirname, join } from 'node:path';
|
|
|
25
25
|
import chalk from 'chalk';
|
|
26
26
|
import { Command, Option } from 'commander';
|
|
27
27
|
import { statusCommand } from './commands/status.js';
|
|
28
|
+
import { withSchema } from './schema.js';
|
|
28
29
|
import { initCommand } from './commands/init.js';
|
|
29
30
|
import { authListCommand, authUseCommand, loginCommand, logoutCommand, whoamiCommand, } from './commands/login.js';
|
|
30
31
|
import { handleAgenticTool, handleAutoMerge, handleAutoFix, handleWaitForReviewers, isAutoMergeEnabled, isAutoFixEnabled } from './commands/config.js';
|
|
@@ -36,6 +37,7 @@ import { listPolicies, addPolicy, removePolicy, initPolicies, addInstruction } f
|
|
|
36
37
|
import { triageCommand } from './commands/triage.js';
|
|
37
38
|
import { dismissCommand, markReviewedCommand, undismissCommand } from './commands/dismiss.js';
|
|
38
39
|
import { requestReviewCommand } from './commands/request-review.js';
|
|
40
|
+
import { reviewCommand } from './commands/review.js';
|
|
39
41
|
import { prStatusCommand } from './commands/pr-status.js';
|
|
40
42
|
import { prReadCommand } from './commands/pr.js';
|
|
41
43
|
import { inboxListCommand } from './commands/inbox.js';
|
|
@@ -45,6 +47,7 @@ import { setupCommand } from './commands/setup.js';
|
|
|
45
47
|
import { schemaCommand, listSchemas } from './commands/schema-cmd.js';
|
|
46
48
|
import { registerWebhook, listWebhooks, rotateWebhookSecret, setWebhookEnabled, listDeliveries, replayDelivery, } from './commands/webhooks.js';
|
|
47
49
|
import { editRulesCommand, validateRulesCommand } from './commands/rules.js';
|
|
50
|
+
import { systemMapValidateCommand } from './commands/system-map.js';
|
|
48
51
|
import { headlessLoginCommand, headlessLogoutCommand, listTokensCommand, revokeTokenCommand, } from './commands/tokens.js';
|
|
49
52
|
// `mcp` is imported lazily inside its command action (below), NOT at the top level.
|
|
50
53
|
// It is the only module that pulls in `@modelcontextprotocol/sdk`; keeping it out of
|
|
@@ -52,12 +55,19 @@ import { headlessLoginCommand, headlessLogoutCommand, listTokensCommand, revokeT
|
|
|
52
55
|
// like `triage`/`submit`. (0.15.12 shipped `mcp.js` but published its package.json
|
|
53
56
|
// WITHOUT `@modelcontextprotocol/sdk`, so the eager import here hard-crashed every
|
|
54
57
|
// command at startup with ERR_MODULE_NOT_FOUND.)
|
|
55
|
-
async function runPublicCommand(action) {
|
|
58
|
+
async function runPublicCommand(action, json) {
|
|
56
59
|
try {
|
|
57
60
|
await action();
|
|
58
61
|
}
|
|
59
62
|
catch (err) {
|
|
60
|
-
|
|
63
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
64
|
+
// In --json mode stdout must still carry one parseable document — emit the
|
|
65
|
+
// versioned error envelope (`haystack schema error`) instead of leaving
|
|
66
|
+
// stdout empty with prose on stderr.
|
|
67
|
+
if (json) {
|
|
68
|
+
process.stdout.write(JSON.stringify(withSchema('error', { status: 'error', error: message }), null, 2) + '\n');
|
|
69
|
+
}
|
|
70
|
+
console.error(chalk.red(message));
|
|
61
71
|
process.exitCode = 1;
|
|
62
72
|
}
|
|
63
73
|
}
|
|
@@ -111,7 +121,7 @@ program
|
|
|
111
121
|
.description('Onboarding wizard — scan repos for rules/policies and write Haystack config')
|
|
112
122
|
.option('--json', 'Agent mode: emit NDJSON events on stdout, read answers on stdin (no TTY prompts)')
|
|
113
123
|
.option('--repo <repo...>', 'Repo(s) to configure (owner/name); skips the repo picker')
|
|
114
|
-
.option('-y, --yes', 'Accept defaults non-interactively (keep all discovered items, confirm write)')
|
|
124
|
+
.option('-y, --yes', 'Accept defaults non-interactively (keep all discovered items, confirm write, ENABLE auto-merge — pass --no-auto-merge to opt out)')
|
|
115
125
|
// Defining --no-auto-merge also accepts --auto-merge; default is "ask" unless
|
|
116
126
|
// one is explicitly passed (detected via getOptionValueSource below).
|
|
117
127
|
.option('--no-auto-merge', 'Set auto-merge in the written config (--auto-merge / --no-auto-merge; omit to be asked)')
|
|
@@ -246,7 +256,9 @@ program
|
|
|
246
256
|
.option('--force', 'Skip pre-PR triage checks')
|
|
247
257
|
.addOption(new Option('--auto-fix', 'Alpha auto-fix for entitled repositories').hideHelp())
|
|
248
258
|
.option('--auto-merge', 'Apply auto-merge label (default: from .haystack.json, --no-auto-merge to disable)')
|
|
259
|
+
.option('--no-auto-merge', 'Do not apply the auto-merge label')
|
|
249
260
|
.option('--no-wait', 'Skip waiting for analysis results')
|
|
261
|
+
.option('--json', 'Machine-readable: one JSON document on stdout (see `haystack schema submit`), progress on stderr')
|
|
250
262
|
.option('--max-turns <n>', 'Max agentic turns per triage checker (overrides .haystack.json triage.maxTurns)', (v) => parseInt(v, 10))
|
|
251
263
|
.option('--triage-timeout <seconds>', 'Wall-clock timeout per triage checker in seconds (overrides .haystack.json triage.timeoutMs)', (v) => parseInt(v, 10))
|
|
252
264
|
.addHelpText('after', `
|
|
@@ -295,8 +307,22 @@ Review Routing:
|
|
|
295
307
|
needs-assignment queue (a teammate will pick it up)
|
|
296
308
|
• --review <username> Same label, plus requests review from that GitHub user
|
|
297
309
|
|
|
310
|
+
Machine-readable output (--json):
|
|
311
|
+
stdout carries exactly one JSON document (schema: \`haystack schema submit\`);
|
|
312
|
+
all progress goes to stderr. On success the payload includes the PR ref,
|
|
313
|
+
the resolved title/body and their sources, auto-merge/auto-fix state with
|
|
314
|
+
source, and the analysis outcome. On failure it is an error envelope
|
|
315
|
+
{ status: "error", error: "..." }.
|
|
316
|
+
|
|
317
|
+
Exit codes:
|
|
318
|
+
0 PR created/updated (including verdict "needs_input" — the PR exists;
|
|
319
|
+
read the verdict from the JSON or from \`haystack triage\`)
|
|
320
|
+
1 Hard failure (bad flags, auth, push, PR creation, blocking triage
|
|
321
|
+
findings, or the analysis status check itself failing)
|
|
322
|
+
|
|
298
323
|
Examples:
|
|
299
324
|
haystack submit # Recommended: triage, create PR, auto-merge queue
|
|
325
|
+
haystack submit --json # Agent mode: JSON on stdout, progress on stderr
|
|
300
326
|
haystack submit --force # Skip triage
|
|
301
327
|
haystack submit --no-wait # Don't wait for analysis
|
|
302
328
|
haystack submit --title "Fix auth" # Custom PR title
|
|
@@ -311,17 +337,23 @@ Examples:
|
|
|
311
337
|
haystack submit --max-turns 12 # Raise triage turn cap to 12 per checker
|
|
312
338
|
haystack submit --triage-timeout 300 # Raise triage wall-clock to 5 minutes
|
|
313
339
|
`)
|
|
314
|
-
.action(async (options) => {
|
|
315
|
-
// Resolve --auto-merge
|
|
316
|
-
|
|
340
|
+
.action(async (options, command) => {
|
|
341
|
+
// Resolve --auto-merge / --auto-fix defaults from .haystack.json when not
|
|
342
|
+
// explicitly set, and record WHERE each value came from so submit can
|
|
343
|
+
// announce the source (silent config-driven defaults are invisible to
|
|
344
|
+
// the caller otherwise).
|
|
345
|
+
const autoMergeFromCli = command.getOptionValueSource('autoMerge') === 'cli';
|
|
346
|
+
const autoFixFromCli = command.getOptionValueSource('autoFix') === 'cli';
|
|
347
|
+
if (!autoMergeFromCli) {
|
|
317
348
|
options.autoMerge = await isAutoMergeEnabled();
|
|
318
349
|
}
|
|
319
|
-
// Resolve --auto-fix default from .haystack.json (preferences.auto_fix).
|
|
320
350
|
// Auto-fix remains alpha — the explicit --auto-fix flag still surfaces
|
|
321
351
|
// discouragement warnings; opting in via repo config is the supported path.
|
|
322
|
-
if (
|
|
352
|
+
if (!autoFixFromCli) {
|
|
323
353
|
options.autoFix = await isAutoFixEnabled();
|
|
324
354
|
}
|
|
355
|
+
options.autoMergeSource = autoMergeFromCli ? 'flag' : 'config';
|
|
356
|
+
options.autoFixSource = autoFixFromCli ? 'flag' : 'config';
|
|
325
357
|
return submitCommand(options);
|
|
326
358
|
});
|
|
327
359
|
program
|
|
@@ -363,35 +395,36 @@ inbox
|
|
|
363
395
|
.command('list')
|
|
364
396
|
.description('List PRs in your Haystack inbox')
|
|
365
397
|
.option('--json', 'Output as JSON')
|
|
366
|
-
.action((options) => runPublicCommand(() => inboxListCommand(options)));
|
|
398
|
+
.action((options) => runPublicCommand(() => inboxListCommand(options), options.json));
|
|
367
399
|
const prProgram = program.command('pr').description('Inspect one pull request');
|
|
368
400
|
prProgram
|
|
369
401
|
.command('get <ref>')
|
|
370
402
|
.description('Get triage, merge blockers, and trace availability')
|
|
371
403
|
.option('--json', 'Output as JSON')
|
|
372
|
-
.action((ref, options) => runPublicCommand(() => prReadCommand(ref, options)));
|
|
404
|
+
.action((ref, options) => runPublicCommand(() => prReadCommand(ref, options), options.json));
|
|
373
405
|
program
|
|
374
406
|
.command('ask <ref> <question>')
|
|
375
407
|
.description('Ask Haystack about a pull request')
|
|
376
408
|
.option('--json', 'Output the answer and every consulted customer-facing source as JSON')
|
|
377
409
|
.option('--session <id>', 'Continue a previous Ask Haystack session')
|
|
378
|
-
.action((ref, question, options) => runPublicCommand(() => askHaystackCommand(ref, question, options)));
|
|
410
|
+
.action((ref, question, options) => runPublicCommand(() => askHaystackCommand(ref, question, options), options.json));
|
|
379
411
|
const traces = program.command('traces').description('Inspect customer-owned Entire traces');
|
|
380
412
|
traces
|
|
381
413
|
.command('list <ref>')
|
|
382
414
|
.description('List retained checkpoints for a pull request')
|
|
383
415
|
.option('--json', 'Output as JSON')
|
|
384
|
-
.action((ref, options) => runPublicCommand(() => tracesListCommand(ref, options)));
|
|
416
|
+
.action((ref, options) => runPublicCommand(() => tracesListCommand(ref, options), options.json));
|
|
385
417
|
traces
|
|
386
418
|
.command('get <ref> <checkpoint>')
|
|
387
419
|
.description('Get retained transcript chunks for one checkpoint')
|
|
388
420
|
.option('--cursor <cursor>', 'Zero-based chunk cursor')
|
|
389
421
|
.option('--limit <count>', 'Chunks per page', '20')
|
|
390
422
|
.option('--json', 'Output as JSON')
|
|
391
|
-
.action((ref, checkpoint, options) => runPublicCommand(() => tracesGetCommand(ref, checkpoint, options)));
|
|
423
|
+
.action((ref, checkpoint, options) => runPublicCommand(() => tracesGetCommand(ref, checkpoint, options), options.json));
|
|
392
424
|
program
|
|
393
425
|
.command('dismiss <pr>')
|
|
394
426
|
.description('Dismiss analysis findings for a PR')
|
|
427
|
+
.option('--json', 'Machine-readable output (see `haystack schema action`)')
|
|
395
428
|
.addHelpText('after', `
|
|
396
429
|
Dismiss analysis findings for a PR, moving it from "Issues Found" to
|
|
397
430
|
"Good to Merge" in the Haystack feed.
|
|
@@ -413,6 +446,7 @@ Examples:
|
|
|
413
446
|
program
|
|
414
447
|
.command('mark-reviewed <pr>')
|
|
415
448
|
.description('Mark human review as not needed for a PR')
|
|
449
|
+
.option('--json', 'Machine-readable output (see `haystack schema action`)')
|
|
416
450
|
.addHelpText('after', `
|
|
417
451
|
Mark human review as not needed for a PR, moving it from "Needs Review"
|
|
418
452
|
to "Good to Merge" in the Haystack feed.
|
|
@@ -434,6 +468,7 @@ Examples:
|
|
|
434
468
|
program
|
|
435
469
|
.command('undismiss <pr>')
|
|
436
470
|
.description('Undo a dismiss or mark-reviewed override for a PR')
|
|
471
|
+
.option('--json', 'Machine-readable output (see `haystack schema action`)')
|
|
437
472
|
.addHelpText('after', `
|
|
438
473
|
Clear all overrides (dismissed findings and/or review-not-needed) for a PR,
|
|
439
474
|
returning it to its original feed bucket.
|
|
@@ -449,9 +484,39 @@ Examples:
|
|
|
449
484
|
haystack undismiss acme/widgets#99 # Undo for specific repo
|
|
450
485
|
`)
|
|
451
486
|
.action(undismissCommand);
|
|
487
|
+
program
|
|
488
|
+
.command('review [pr]')
|
|
489
|
+
.description('Trigger a fresh Haystack analysis for a PR (machine analysis — for HUMAN review use request-review)')
|
|
490
|
+
.option('--no-wait', 'Exit after triggering instead of waiting for results')
|
|
491
|
+
.option('--json', 'Machine-readable output (see `haystack schema action`)')
|
|
492
|
+
.addHelpText('after', `
|
|
493
|
+
Re-run full Haystack analysis on a PR's current head.
|
|
494
|
+
|
|
495
|
+
Haystack analyzes a PR once when it's opened (or marked ready); later
|
|
496
|
+
pushes only get a resolution check against the existing findings. Use
|
|
497
|
+
this command when new commits deserve fresh detection, or any time you
|
|
498
|
+
want a second opinion.
|
|
499
|
+
|
|
500
|
+
When called without a PR identifier, uses the last submitted PR.
|
|
501
|
+
|
|
502
|
+
PR identifier formats:
|
|
503
|
+
(none) Last submitted PR
|
|
504
|
+
123 PR number (uses current repo)
|
|
505
|
+
#123 PR number with hash
|
|
506
|
+
owner/repo#123 Fully qualified
|
|
507
|
+
https://github.com/owner/repo/pull/123 GitHub URL
|
|
508
|
+
|
|
509
|
+
Examples:
|
|
510
|
+
haystack review # Last submitted PR
|
|
511
|
+
haystack review 42 # Current repo, PR #42
|
|
512
|
+
haystack review acme/widgets#99 # Specific repo
|
|
513
|
+
haystack review 42 --no-wait # Trigger and exit
|
|
514
|
+
`)
|
|
515
|
+
.action(reviewCommand);
|
|
452
516
|
program
|
|
453
517
|
.command('request-review <pr> [reviewer]')
|
|
454
518
|
.description('Tag a PR as needing human review')
|
|
519
|
+
.option('--json', 'Machine-readable output (see `haystack schema action`)')
|
|
455
520
|
.addHelpText('after', `
|
|
456
521
|
Tag any PR with "needs human review", even after it was created.
|
|
457
522
|
Adds the haystack:needs-review label to the PR, moving it from
|
|
@@ -522,9 +587,9 @@ config
|
|
|
522
587
|
.description('Auto-merge safe PRs submitted via haystack submit (on|off|status)')
|
|
523
588
|
.addHelpText('after', `
|
|
524
589
|
Actions:
|
|
525
|
-
on, enable
|
|
526
|
-
off, disable Disable auto-merge
|
|
527
|
-
status
|
|
590
|
+
on, enable, true Enable auto-merge for safe PRs
|
|
591
|
+
off, disable, false Disable auto-merge
|
|
592
|
+
status Show current status (default)
|
|
528
593
|
|
|
529
594
|
When enabled, PRs submitted via \`haystack submit\` will be
|
|
530
595
|
automatically merged if Haystack analysis finds no issues
|
|
@@ -542,9 +607,9 @@ config
|
|
|
542
607
|
.description('(Alpha) Auto-fix mechanical issues on PRs submitted via haystack submit (on|off|status)')
|
|
543
608
|
.addHelpText('after', `
|
|
544
609
|
Actions:
|
|
545
|
-
on, enable
|
|
546
|
-
off, disable Disable auto-fix
|
|
547
|
-
status
|
|
610
|
+
on, enable, true Enable auto-fix for mechanical issues
|
|
611
|
+
off, disable, false Disable auto-fix
|
|
612
|
+
status Show current status (default)
|
|
548
613
|
|
|
549
614
|
⚠ Auto-fix is alpha. When enabled, PRs submitted via \`haystack submit\`
|
|
550
615
|
are labeled haystack:auto-fix. The Haystack analysis pipeline then
|
|
@@ -853,6 +918,38 @@ rules
|
|
|
853
918
|
process.exit(1);
|
|
854
919
|
}
|
|
855
920
|
});
|
|
921
|
+
// ─── system-map ──────────────────────────────────────────────────────────────
|
|
922
|
+
const systemMap = program
|
|
923
|
+
.command('system-map')
|
|
924
|
+
.description('Manage .haystack/system.yml — runtime facts that help Haystack QA run your system');
|
|
925
|
+
systemMap
|
|
926
|
+
.command('validate')
|
|
927
|
+
.description('Check .haystack/system.yml against the rules Haystack QA will apply')
|
|
928
|
+
.option('--json', 'Machine-readable result')
|
|
929
|
+
.addHelpText('after', `
|
|
930
|
+
The system map holds VERIFIED facts about how your system works — install/
|
|
931
|
+
build commands, how services boot and signal readiness, seed/reset commands,
|
|
932
|
+
dev-only test logins, external services that must never be touched live, and
|
|
933
|
+
prebuilt CI artifacts. Haystack QA uses these facts to run more of what it
|
|
934
|
+
already wants to test; the file never selects tests.
|
|
935
|
+
|
|
936
|
+
Author it with the /map-your-system skill (haystack skills install), then run
|
|
937
|
+
this before committing. Checks: valid YAML + version, no test-selection keys,
|
|
938
|
+
no secret values, non-empty boot/login/run commands, artifact declarations.
|
|
939
|
+
|
|
940
|
+
Examples:
|
|
941
|
+
haystack system-map validate
|
|
942
|
+
haystack system-map validate --json
|
|
943
|
+
`)
|
|
944
|
+
.action(async (opts) => {
|
|
945
|
+
try {
|
|
946
|
+
await systemMapValidateCommand(opts);
|
|
947
|
+
}
|
|
948
|
+
catch (err) {
|
|
949
|
+
console.error(chalk.red('validate failed:'), err instanceof Error ? err.message : err);
|
|
950
|
+
process.exit(1);
|
|
951
|
+
}
|
|
952
|
+
});
|
|
856
953
|
// ─── mcp ─────────────────────────────────────────────────────────────────────
|
|
857
954
|
program
|
|
858
955
|
.command('mcp')
|
package/dist/schema.d.ts
CHANGED
|
@@ -13,9 +13,13 @@ export declare const SCHEMA_VERSIONS: {
|
|
|
13
13
|
readonly triage: "2.0.0";
|
|
14
14
|
readonly setup: "1.0.0";
|
|
15
15
|
readonly pr: "2.0.0";
|
|
16
|
+
readonly 'pr-status': "1.0.0";
|
|
16
17
|
readonly inbox: "1.0.0";
|
|
17
18
|
readonly ask: "1.0.0";
|
|
18
19
|
readonly traces: "1.0.0";
|
|
20
|
+
readonly submit: "1.0.0";
|
|
21
|
+
readonly action: "1.0.0";
|
|
22
|
+
readonly error: "1.0.0";
|
|
19
23
|
};
|
|
20
24
|
export type SchemaName = keyof typeof SCHEMA_VERSIONS;
|
|
21
25
|
/** Wrap a payload with the `schema_version` envelope. */
|
package/dist/schema.js
CHANGED
|
@@ -13,9 +13,13 @@ export const SCHEMA_VERSIONS = {
|
|
|
13
13
|
triage: '2.0.0',
|
|
14
14
|
setup: '1.0.0',
|
|
15
15
|
pr: '2.0.0',
|
|
16
|
+
'pr-status': '1.0.0',
|
|
16
17
|
inbox: '1.0.0',
|
|
17
18
|
ask: '1.0.0',
|
|
18
19
|
traces: '1.0.0',
|
|
20
|
+
submit: '1.0.0',
|
|
21
|
+
action: '1.0.0',
|
|
22
|
+
error: '1.0.0',
|
|
19
23
|
};
|
|
20
24
|
/** Wrap a payload with the `schema_version` envelope. */
|
|
21
25
|
export function withSchema(name, payload) {
|
package/dist/states.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical machine-readable PR-state vocabulary for every CLI JSON surface.
|
|
3
|
+
*
|
|
4
|
+
* One concept, one spelling: all `--json` output uses snake_case state tokens.
|
|
5
|
+
* Before this module existed the same state appeared as `good_to_merge`
|
|
6
|
+
* (triage/pr get/inbox) and `good-to-merge` (pr-status, raw server buckets),
|
|
7
|
+
* so no single parser worked across commands. Server responses that use
|
|
8
|
+
* kebab-case are normalized at the edge via `normalizeFeedBucket`.
|
|
9
|
+
*/
|
|
10
|
+
/** Public triage verdict — emitted by `triage`, `pr get`, `submit`, `pr-status`. */
|
|
11
|
+
export declare const TRIAGE_VERDICTS: readonly ["good_to_merge", "needs_review", "needs_input"];
|
|
12
|
+
export type TriageVerdict = (typeof TRIAGE_VERDICTS)[number];
|
|
13
|
+
/**
|
|
14
|
+
* Map the server's internal analysis verdict ('clean' | 'has-issues' |
|
|
15
|
+
* 'needs-review', with a rating-based fallback for pre-verdict analyses) to
|
|
16
|
+
* the public triage verdict. This mapping used to be duplicated inline in
|
|
17
|
+
* triage.ts and pr.ts.
|
|
18
|
+
*/
|
|
19
|
+
export declare function toPublicVerdict(analysisVerdict: string | null | undefined, haystackRating?: number | null): TriageVerdict;
|
|
20
|
+
/**
|
|
21
|
+
* Known feed buckets in canonical snake_case form. The server may introduce
|
|
22
|
+
* new buckets; `normalizeFeedBucket` converts mechanically so unknown buckets
|
|
23
|
+
* pass through in the same convention instead of crashing or leaking kebab-case.
|
|
24
|
+
*/
|
|
25
|
+
export declare const FEED_BUCKETS: readonly ["analyzing", "auto_fixing", "good_to_merge", "failed_when_merging", "issues", "needs_assignment", "needs_shepherding", "needs_fixing", "awaiting_review", "review_requested", "snoozed"];
|
|
26
|
+
export type FeedBucket = (typeof FEED_BUCKETS)[number];
|
|
27
|
+
/** Normalize a server feed bucket (kebab-case) to the canonical snake_case form. */
|
|
28
|
+
export declare function normalizeFeedBucket(bucket: string): string;
|
|
29
|
+
export declare function normalizeFeedBucket(bucket: string | null | undefined): string | null;
|
package/dist/states.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical machine-readable PR-state vocabulary for every CLI JSON surface.
|
|
3
|
+
*
|
|
4
|
+
* One concept, one spelling: all `--json` output uses snake_case state tokens.
|
|
5
|
+
* Before this module existed the same state appeared as `good_to_merge`
|
|
6
|
+
* (triage/pr get/inbox) and `good-to-merge` (pr-status, raw server buckets),
|
|
7
|
+
* so no single parser worked across commands. Server responses that use
|
|
8
|
+
* kebab-case are normalized at the edge via `normalizeFeedBucket`.
|
|
9
|
+
*/
|
|
10
|
+
/** Public triage verdict — emitted by `triage`, `pr get`, `submit`, `pr-status`. */
|
|
11
|
+
export const TRIAGE_VERDICTS = ['good_to_merge', 'needs_review', 'needs_input'];
|
|
12
|
+
/**
|
|
13
|
+
* Map the server's internal analysis verdict ('clean' | 'has-issues' |
|
|
14
|
+
* 'needs-review', with a rating-based fallback for pre-verdict analyses) to
|
|
15
|
+
* the public triage verdict. This mapping used to be duplicated inline in
|
|
16
|
+
* triage.ts and pr.ts.
|
|
17
|
+
*/
|
|
18
|
+
export function toPublicVerdict(analysisVerdict, haystackRating) {
|
|
19
|
+
const verdict = analysisVerdict ?? ((haystackRating ?? 0) >= 5 ? 'clean' : 'has-issues');
|
|
20
|
+
if (verdict === 'clean')
|
|
21
|
+
return 'good_to_merge';
|
|
22
|
+
if (verdict === 'needs-review')
|
|
23
|
+
return 'needs_review';
|
|
24
|
+
return 'needs_input';
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Known feed buckets in canonical snake_case form. The server may introduce
|
|
28
|
+
* new buckets; `normalizeFeedBucket` converts mechanically so unknown buckets
|
|
29
|
+
* pass through in the same convention instead of crashing or leaking kebab-case.
|
|
30
|
+
*/
|
|
31
|
+
export const FEED_BUCKETS = [
|
|
32
|
+
'analyzing',
|
|
33
|
+
'auto_fixing',
|
|
34
|
+
'good_to_merge',
|
|
35
|
+
'failed_when_merging',
|
|
36
|
+
'issues',
|
|
37
|
+
'needs_assignment',
|
|
38
|
+
'needs_shepherding',
|
|
39
|
+
'needs_fixing',
|
|
40
|
+
'awaiting_review',
|
|
41
|
+
'review_requested',
|
|
42
|
+
'snoozed',
|
|
43
|
+
];
|
|
44
|
+
export function normalizeFeedBucket(bucket) {
|
|
45
|
+
return bucket == null ? null : bucket.replace(/-/g, '_');
|
|
46
|
+
}
|
package/dist/triage/runner.d.ts
CHANGED
|
@@ -16,6 +16,13 @@ export interface RunTriageOptions {
|
|
|
16
16
|
* Returns the first one found, or null if none available.
|
|
17
17
|
*/
|
|
18
18
|
export declare function detectAgenticCLI(): AgenticCLI | null;
|
|
19
|
+
/**
|
|
20
|
+
* Build the CLI command + args for the detected agentic tool.
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildCommand(cli: AgenticCLI, prompt: string, maxTurns: number): {
|
|
23
|
+
command: string;
|
|
24
|
+
args: string[];
|
|
25
|
+
};
|
|
19
26
|
/**
|
|
20
27
|
* Run pre-PR triage checks using parallel sub-agent processes.
|
|
21
28
|
*
|
package/dist/triage/runner.js
CHANGED
|
@@ -86,7 +86,7 @@ export function detectAgenticCLI() {
|
|
|
86
86
|
/**
|
|
87
87
|
* Build the CLI command + args for the detected agentic tool.
|
|
88
88
|
*/
|
|
89
|
-
function buildCommand(cli, prompt, maxTurns) {
|
|
89
|
+
export function buildCommand(cli, prompt, maxTurns) {
|
|
90
90
|
switch (cli) {
|
|
91
91
|
case 'claude':
|
|
92
92
|
return {
|
|
@@ -101,7 +101,7 @@ function buildCommand(cli, prompt, maxTurns) {
|
|
|
101
101
|
case 'codex':
|
|
102
102
|
return {
|
|
103
103
|
command: 'codex',
|
|
104
|
-
args: ['exec', prompt, '--full-auto'
|
|
104
|
+
args: ['exec', prompt, '--full-auto'],
|
|
105
105
|
};
|
|
106
106
|
case 'gemini':
|
|
107
107
|
return {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared machine-readable envelope for the mutating commands
|
|
3
|
+
* (dismiss, mark-reviewed, undismiss, request-review, review).
|
|
4
|
+
*
|
|
5
|
+
* One schema for all of them (`haystack schema action`): every payload
|
|
6
|
+
* carries `action`, `status`, and `ref`, so an agent can drive any state
|
|
7
|
+
* change with a single parser. Success payloads go to stdout as the only
|
|
8
|
+
* stdout content; human/progress output stays on stderr in --json mode.
|
|
9
|
+
*/
|
|
10
|
+
export type ActionName = 'dismiss' | 'mark_reviewed' | 'undismiss' | 'request_review' | 'trigger_review' | 'clear_pending';
|
|
11
|
+
export interface ActionPayload extends Record<string, unknown> {
|
|
12
|
+
action: ActionName;
|
|
13
|
+
status: 'ok';
|
|
14
|
+
ref: string;
|
|
15
|
+
/** Human-readable statement of what changed. */
|
|
16
|
+
detail: string;
|
|
17
|
+
}
|
|
18
|
+
/** Print the success envelope (JSON mode only — human output is the caller's). */
|
|
19
|
+
export declare function emitActionPayload(payload: ActionPayload): void;
|
|
20
|
+
/**
|
|
21
|
+
* Print a failure (JSON error envelope on stdout when --json, red text on
|
|
22
|
+
* stderr always) and exit 1.
|
|
23
|
+
*/
|
|
24
|
+
export declare function failAction(json: boolean | undefined, action: ActionName, ref: string | null, message: string): never;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared machine-readable envelope for the mutating commands
|
|
3
|
+
* (dismiss, mark-reviewed, undismiss, request-review, review).
|
|
4
|
+
*
|
|
5
|
+
* One schema for all of them (`haystack schema action`): every payload
|
|
6
|
+
* carries `action`, `status`, and `ref`, so an agent can drive any state
|
|
7
|
+
* change with a single parser. Success payloads go to stdout as the only
|
|
8
|
+
* stdout content; human/progress output stays on stderr in --json mode.
|
|
9
|
+
*/
|
|
10
|
+
import chalk from 'chalk';
|
|
11
|
+
import { withSchema } from '../schema.js';
|
|
12
|
+
/** Print the success envelope (JSON mode only — human output is the caller's). */
|
|
13
|
+
export function emitActionPayload(payload) {
|
|
14
|
+
console.log(JSON.stringify(withSchema('action', payload), null, 2));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Print a failure (JSON error envelope on stdout when --json, red text on
|
|
18
|
+
* stderr always) and exit 1.
|
|
19
|
+
*/
|
|
20
|
+
export function failAction(json, action, ref, message) {
|
|
21
|
+
if (json) {
|
|
22
|
+
process.stdout.write(JSON.stringify(withSchema('action', { action, status: 'error', ref, error: message }), null, 2) + '\n');
|
|
23
|
+
}
|
|
24
|
+
console.error(chalk.red(`\n${message}\n`));
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
@@ -110,6 +110,39 @@ export type AnalysisReadyResult = {
|
|
|
110
110
|
status: 'error';
|
|
111
111
|
message: string;
|
|
112
112
|
};
|
|
113
|
+
export type TriggerAnalysisResult = {
|
|
114
|
+
status: 'started';
|
|
115
|
+
requestId: string;
|
|
116
|
+
} | {
|
|
117
|
+
status: 'already_processing';
|
|
118
|
+
requestId: string;
|
|
119
|
+
} | {
|
|
120
|
+
status: 'rate_limited';
|
|
121
|
+
message: string;
|
|
122
|
+
retryAfterSeconds?: number;
|
|
123
|
+
} | {
|
|
124
|
+
status: 'denied';
|
|
125
|
+
message: string;
|
|
126
|
+
} | {
|
|
127
|
+
status: 'error';
|
|
128
|
+
message: string;
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Trigger a fresh full analysis for a PR via the auth-worker
|
|
132
|
+
* (`/api/analysis-trigger/{owner}/{repo}/{pr}`). Requires auth — triggers
|
|
133
|
+
* start a real analysis run. Returns the request id to wait on.
|
|
134
|
+
*/
|
|
135
|
+
export declare function triggerAnalysis(owner: string, repo: string, prNumber: number, token: string, headSha?: string): Promise<TriggerAnalysisResult>;
|
|
136
|
+
export type TriggerRunStatus = 'pending' | 'COMPLETED' | 'FAILED' | 'SUPERSEDED' | 'error';
|
|
137
|
+
/**
|
|
138
|
+
* Poll the status of a triggered run by request id. The PR-level result
|
|
139
|
+
* endpoint can't distinguish a fresh re-run on the same head from the
|
|
140
|
+
* previous completed analysis — the request id can.
|
|
141
|
+
*/
|
|
142
|
+
export declare function fetchTriggerStatus(requestId: string, token: string): Promise<{
|
|
143
|
+
status: TriggerRunStatus;
|
|
144
|
+
message?: string;
|
|
145
|
+
}>;
|
|
113
146
|
/**
|
|
114
147
|
* Check if analysis results are available for a PR by polling the result endpoint.
|
|
115
148
|
* Returns 'ready' if complete, 'pending' if not yet available, or 'error' for server failures.
|
|
@@ -139,5 +172,16 @@ export interface AutoFixManifest {
|
|
|
139
172
|
timestamp: string;
|
|
140
173
|
resultSha?: string;
|
|
141
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* Null means "artifact does not exist (yet)" — 404s and transient
|
|
177
|
+
* network/parse failures. Auth failures (401/403) THROW a classified
|
|
178
|
+
* HaystackApiError instead: collapsing them into null used to make an
|
|
179
|
+
* expired token read as "not available yet — try again in a moment",
|
|
180
|
+
* sending agents into a retry loop when the fix was `haystack login`.
|
|
181
|
+
*/
|
|
142
182
|
export declare function fetchAutoFixManifest(owner: string, repo: string, prNumber: number, token?: string): Promise<AutoFixManifest | null>;
|
|
183
|
+
/**
|
|
184
|
+
* Null means "synthesis does not exist (yet)". Auth failures (401/403) THROW
|
|
185
|
+
* a classified HaystackApiError — see fetchAutoFixManifest for why.
|
|
186
|
+
*/
|
|
143
187
|
export declare function fetchRatingSynthesis(owner: string, repo: string, prNumber: number, token?: string): Promise<RatingSynthesis | null>;
|