@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.
- package/README.md +45 -6
- package/dist/commands/ask.d.ts +14 -0
- package/dist/commands/ask.js +20 -0
- package/dist/commands/inbox.d.ts +65 -0
- package/dist/commands/inbox.js +137 -0
- package/dist/commands/mcp.js +88 -134
- package/dist/commands/pr.d.ts +37 -1
- package/dist/commands/pr.js +95 -77
- package/dist/commands/schema-cmd.js +1 -1
- package/dist/commands/traces.d.ts +48 -0
- package/dist/commands/traces.js +92 -0
- package/dist/commands/triage.d.ts +12 -0
- package/dist/commands/triage.js +90 -58
- package/dist/commands/webhooks.js +3 -6
- package/dist/index.js +50 -148
- package/dist/schema.d.ts +5 -2
- package/dist/schema.js +5 -2
- package/dist/triage/prompts.js +4 -0
- package/dist/utils/analysis-api.d.ts +7 -0
- package/dist/utils/haystack-api.d.ts +20 -0
- package/dist/utils/haystack-api.js +66 -0
- package/package.json +4 -4
- package/schemas/ask.v1.json +40 -0
- package/schemas/inbox.v1.json +27 -0
- package/schemas/pr.v2.json +54 -0
- package/schemas/traces.v1.json +53 -0
- package/schemas/triage.v2.json +64 -0
- package/dist/assets/skills/install-verification.md +0 -67
- package/dist/commands/verification.d.ts +0 -21
- package/dist/commands/verification.js +0 -291
- package/dist/verification/contract.d.ts +0 -240
- package/dist/verification/contract.js +0 -115
- package/dist/verification/init.d.ts +0 -10
- package/dist/verification/init.js +0 -243
- package/dist/verification/manifest.d.ts +0 -49
- package/dist/verification/manifest.js +0 -186
- package/dist/verification/runner.d.ts +0 -20
- package/dist/verification/runner.js +0 -102
- package/dist/verification/safety.d.ts +0 -10
- package/dist/verification/safety.js +0 -252
- package/dist/verification/validate.d.ts +0 -12
- 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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
|
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
|
-
.
|
|
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,
|
|
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
|
|
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: "
|
|
13
|
+
readonly triage: "2.0.0";
|
|
14
14
|
readonly setup: "1.0.0";
|
|
15
|
-
readonly pr: "
|
|
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: '
|
|
13
|
+
triage: '2.0.0',
|
|
14
14
|
setup: '1.0.0',
|
|
15
|
-
pr: '
|
|
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) {
|
package/dist/triage/prompts.js
CHANGED
|
@@ -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.
|
|
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": "
|
|
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.
|
|
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
|
+
}
|