@haystackeditor/cli 0.25.1 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/README.md +39 -463
  2. package/dist/capture/app-config.js +30 -6
  3. package/dist/commands/capture-brief.js +45 -35
  4. package/dist/commands/capture-contract.js +4 -4
  5. package/dist/commands/crawl-report.js +196 -0
  6. package/dist/commands/db-profile-upload.js +3 -3
  7. package/dist/commands/feedback.js +66 -0
  8. package/dist/commands/init-telemetry.js +55 -9
  9. package/dist/commands/init.js +8 -3
  10. package/dist/commands/lockfile-pin.js +307 -0
  11. package/dist/commands/telemetry-token.js +3 -3
  12. package/dist/commands/tokens.js +4 -4
  13. package/dist/commands/verify-explore.js +3 -3
  14. package/dist/commands/verify-history.js +2 -2
  15. package/dist/commands/verify-onboarding.js +13 -16
  16. package/dist/commands/verify-precompute.js +3 -4
  17. package/dist/commands/verify.js +31 -118
  18. package/dist/index.js +55 -968
  19. package/dist/schema.js +4 -10
  20. package/dist/utils/haystack-api.js +8 -36
  21. package/package.json +1 -5
  22. package/schemas/feedback.v1.json +13 -0
  23. package/schemas/pre-verify.v2.json +240 -0
  24. package/schemas/verify-raw.v1.json +1132 -0
  25. package/schemas/verify.v2.json +655 -0
  26. package/dist/assets/hooks/agent-context/detect.ts +0 -316
  27. package/dist/assets/hooks/agent-context/format.ts +0 -100
  28. package/dist/assets/hooks/agent-context/index.ts +0 -41
  29. package/dist/assets/hooks/agent-context/parsers/claude.ts +0 -262
  30. package/dist/assets/hooks/agent-context/parsers/codex.ts +0 -416
  31. package/dist/assets/hooks/agent-context/parsers/gemini.ts +0 -155
  32. package/dist/assets/hooks/agent-context/parsers/opencode.ts +0 -174
  33. package/dist/assets/hooks/agent-context/tsconfig.json +0 -14
  34. package/dist/assets/hooks/agent-context/types.ts +0 -58
  35. package/dist/assets/hooks/llm-rules-template.md +0 -59
  36. package/dist/assets/hooks/package-lock.json +0 -598
  37. package/dist/assets/hooks/package.json +0 -12
  38. package/dist/assets/hooks/scripts/commit-msg.sh +0 -5
  39. package/dist/assets/hooks/scripts/post-commit.sh +0 -5
  40. package/dist/assets/hooks/scripts/pre-commit.sh +0 -175
  41. package/dist/assets/hooks/scripts/pre-push.sh +0 -25
  42. package/dist/assets/hooks/scripts/prepare-commit-msg.sh +0 -5
  43. package/dist/assets/hooks/truncation-checker/ast-analyzer.ts +0 -528
  44. package/dist/assets/hooks/truncation-checker/index.ts +0 -595
  45. package/dist/assets/hooks/truncation-checker/tsconfig.json +0 -13
  46. package/dist/assets/skills/map-cloud-verifier-universe/SKILL.md +0 -2051
  47. package/dist/assets/skills/map-cloud-verifier-universe/agents/openai.yaml +0 -4
  48. package/dist/assets/skills/map-cloud-verifier-universe/references/output-contract.md +0 -3411
  49. package/dist/assets/skills/map-your-system.md +0 -143
  50. package/dist/assets/skills/submit.md +0 -200
  51. package/dist/commands/ask.js +0 -20
  52. package/dist/commands/cloud-verifier-behaviors.js +0 -218
  53. package/dist/commands/cloud-verifier-data-store-census.js +0 -539
  54. package/dist/commands/cloud-verifier-data-store-drift.js +0 -158
  55. package/dist/commands/cloud-verifier-identity-census.js +0 -4060
  56. package/dist/commands/cloud-verifier-materialization.js +0 -704
  57. package/dist/commands/cloud-verifier-pascal-selector-census.js +0 -1382
  58. package/dist/commands/cloud-verifier-python-manifest-selector-census.js +0 -2015
  59. package/dist/commands/cloud-verifier-specialized-operational-census.js +0 -11432
  60. package/dist/commands/cloud-verifier-universe.js +0 -10178
  61. package/dist/commands/config.js +0 -549
  62. package/dist/commands/design-verify.js +0 -311
  63. package/dist/commands/dismiss.js +0 -159
  64. package/dist/commands/hooks.js +0 -226
  65. package/dist/commands/inbox.js +0 -137
  66. package/dist/commands/mcp.js +0 -201
  67. package/dist/commands/policy.js +0 -371
  68. package/dist/commands/pr-status.js +0 -207
  69. package/dist/commands/pr.js +0 -105
  70. package/dist/commands/prepare-universe-review.js +0 -1092
  71. package/dist/commands/production-source-deny-policy.js +0 -100
  72. package/dist/commands/request-review.js +0 -74
  73. package/dist/commands/review.js +0 -191
  74. package/dist/commands/rules.js +0 -98
  75. package/dist/commands/scaffold-provisional-universe.js +0 -806
  76. package/dist/commands/setup.js +0 -1170
  77. package/dist/commands/skills.js +0 -447
  78. package/dist/commands/status.js +0 -35
  79. package/dist/commands/submit.js +0 -745
  80. package/dist/commands/system-map.js +0 -228
  81. package/dist/commands/triage.js +0 -598
  82. package/dist/commands/webhooks.js +0 -241
  83. package/dist/states.js +0 -46
  84. package/dist/tools/detect.js +0 -832
  85. package/dist/triage/astra.js +0 -202
  86. package/dist/triage/prompts.js +0 -188
  87. package/dist/triage/runner.js +0 -200
  88. package/dist/triage/types.js +0 -7
  89. package/dist/types.js +0 -326
  90. package/dist/utils/action-output.js +0 -26
  91. package/dist/utils/analysis-api.js +0 -416
  92. package/dist/utils/config.js +0 -54
  93. package/dist/utils/design-verifier-api.js +0 -294
  94. package/dist/utils/design-verifier-history.js +0 -79
  95. package/dist/utils/design-verifier-result.js +0 -424
  96. package/dist/utils/github-api.js +0 -324
  97. package/dist/utils/pending-state.js +0 -86
  98. package/dist/utils/pr-ref.js +0 -56
  99. package/dist/utils/prompter.js +0 -328
  100. package/schemas/action.v1.json +0 -22
  101. package/schemas/ask.v1.json +0 -40
  102. package/schemas/inbox.v1.json +0 -27
  103. package/schemas/pr-status.v1.json +0 -61
  104. package/schemas/pr.v1.json +0 -97
  105. package/schemas/pr.v3.json +0 -45
  106. package/schemas/setup.v1.json +0 -75
  107. package/schemas/submit.v1.json +0 -90
  108. package/schemas/triage.v1.json +0 -103
  109. package/schemas/triage.v2.json +0 -64
@@ -1,311 +0,0 @@
1
- /**
2
- * design-verify command — Kick off a blind design-by-execution verification
3
- * run for a PR.
4
- *
5
- * haystack design-verify <pr>
6
- *
7
- * Unlike `haystack review` (finding-oriented analysis of the diff), design
8
- * verification executes the change: it infers intent from the diff alone,
9
- * designs pre-registered behavioral test cases, runs control-before, before,
10
- * and after in isolated sandboxes, and reports evidence-backed anomaly
11
- * candidates outside the registered intent.
12
- *
13
- * The command waits by default and returns an agent-facing behavioral result.
14
- * --no-wait preserves the asynchronous trigger-only path.
15
- */
16
- import chalk from 'chalk';
17
- import { loadToken } from '../utils/auth.js';
18
- import { parsePrRef } from '../utils/pr-ref.js';
19
- import { getDesignVerifierHistory, getDesignVerifierUniverseAction, startDesignVerifierUniverseAction, triggerDesignVerifier, waitForDesignVerifier, waitForDesignVerifierUniverseAction, } from '../utils/design-verifier-api.js';
20
- import { buildDesignVerifierAgentReport, designVerifierStageLabel, formatDesignVerifierAgentReport, } from '../utils/design-verifier-result.js';
21
- import { formatDesignVerifierHistory } from '../utils/design-verifier-history.js';
22
- function emit(json, payload, human) {
23
- if (json) {
24
- console.log(JSON.stringify(payload));
25
- }
26
- else {
27
- human();
28
- }
29
- }
30
- function parseExecArgv(source) {
31
- let value;
32
- try {
33
- value = JSON.parse(source);
34
- }
35
- catch (error) {
36
- throw new Error(`--exec-json is not valid JSON: ${error.message}`);
37
- }
38
- if (!Array.isArray(value) || value.length === 0 ||
39
- value.some(argument => typeof argument !== 'string')) {
40
- throw new Error('--exec-json must be a non-empty JSON array of string arguments');
41
- }
42
- return value;
43
- }
44
- function terminalSafe(value) {
45
- // Preserve ordinary whitespace while removing C0/C1 controls, including
46
- // ESC/OSC introducers. JSON output retains the server-returned strings.
47
- let safe = '';
48
- for (const character of value) {
49
- const codePoint = character.codePointAt(0) ?? 0;
50
- const ordinaryWhitespace = codePoint === 9 || codePoint === 10 || codePoint === 13;
51
- if ((codePoint < 32 && !ordinaryWhitespace) ||
52
- (codePoint >= 127 && codePoint <= 159))
53
- continue;
54
- safe += character;
55
- }
56
- return safe;
57
- }
58
- function printUniverseAction(action) {
59
- const result = action.result;
60
- if (action.status === 'queued' || action.status === 'running') {
61
- console.log(chalk.dim(`${action.caseId}/${action.universe} ${action.action} is ${action.status}; action ${action.actionId}`));
62
- return;
63
- }
64
- if (action.status === 'failed') {
65
- console.error(chalk.red(`Universe ${action.action} failed: ${action.error || 'unknown error'}`));
66
- return;
67
- }
68
- if (!result) {
69
- console.error(chalk.red(`Universe ${action.action} completed without a result.`));
70
- return;
71
- }
72
- console.log(chalk.dim(`${action.caseId}/${action.universe} ${action.action} (${result.durationMs}ms, exit ${result.exitCode ?? 'unknown'})`));
73
- if (result.stdout) {
74
- const stdout = terminalSafe(result.stdout);
75
- process.stdout.write(stdout.endsWith('\n') ? stdout : `${stdout}\n`);
76
- }
77
- if (result.stderr) {
78
- const stderr = terminalSafe(result.stderr);
79
- process.stderr.write(stderr.endsWith('\n') ? stderr : `${stderr}\n`);
80
- }
81
- if (result.stdoutOmittedBytes || result.stderrOmittedBytes) {
82
- console.error(chalk.dim(`Output truncated: ${result.stdoutOmittedBytes} stdout bytes, ${result.stderrOmittedBytes} stderr bytes omitted.`));
83
- }
84
- }
85
- async function runUniverseOperation(pr, prLabel, options, token, pollIntervalSeconds, timeoutSeconds) {
86
- const requested = Boolean(options.action || options.case || options.universe ||
87
- options.replay || options.execJson || options.cwd || options.commandTimeout);
88
- if (!requested)
89
- return false;
90
- if (!options.run)
91
- throw new Error('universe exploration requires --run <run-id>');
92
- let actionId = options.action;
93
- if (actionId) {
94
- if (options.case || options.universe || options.replay || options.execJson ||
95
- options.cwd || options.commandTimeout) {
96
- throw new Error('--action cannot be combined with a new universe command');
97
- }
98
- if (options.wait === false) {
99
- const loaded = await getDesignVerifierUniverseAction(pr.owner, pr.repo, pr.prNumber, options.run, actionId, token);
100
- if (loaded.status !== 'ok')
101
- throw new Error(loaded.message);
102
- emit(options.json, { ok: true, pr: prLabel, ...loaded.action }, () => printUniverseAction(loaded.action));
103
- return true;
104
- }
105
- }
106
- else {
107
- if (!options.case || !/^case-\d{5}$/.test(options.case)) {
108
- throw new Error('a canonical --case case-00000 is required');
109
- }
110
- if (!options.universe || !['control-before', 'before', 'after'].includes(options.universe)) {
111
- throw new Error('--universe must be control-before, before, or after');
112
- }
113
- if (Boolean(options.replay) === Boolean(options.execJson)) {
114
- throw new Error('choose exactly one of --replay or --exec-json');
115
- }
116
- if (options.replay && (options.cwd || options.commandTimeout)) {
117
- throw new Error('--cwd and --command-timeout apply only to --exec-json');
118
- }
119
- const commandTimeoutMs = options.commandTimeout === undefined
120
- ? undefined
121
- : Number(options.commandTimeout) * 1000;
122
- if (commandTimeoutMs !== undefined &&
123
- !(Number.isInteger(commandTimeoutMs) && commandTimeoutMs >= 1_000 && commandTimeoutMs <= 300_000)) {
124
- throw new Error('--command-timeout must be between 1 and 300 seconds');
125
- }
126
- const started = await startDesignVerifierUniverseAction(pr.owner, pr.repo, pr.prNumber, options.run, token, {
127
- caseId: options.case,
128
- universe: options.universe,
129
- action: options.replay ? 'replay' : 'exec',
130
- ...(options.execJson ? { argv: parseExecArgv(options.execJson) } : {}),
131
- ...(options.cwd ? { cwd: options.cwd } : {}),
132
- ...(commandTimeoutMs === undefined ? {} : { timeoutMs: commandTimeoutMs }),
133
- });
134
- if (started.status !== 'ok')
135
- throw new Error(started.message);
136
- actionId = started.action.actionId;
137
- if (options.wait === false) {
138
- emit(options.json, { ok: true, pr: prLabel, ...started.action }, () => {
139
- console.log(chalk.green('✓ Universe action queued'));
140
- console.log(chalk.dim(` Action: ${actionId}`));
141
- console.log(chalk.dim(` Resume: haystack design-verify ${prLabel} --run ${options.run} --action ${actionId}`));
142
- });
143
- return true;
144
- }
145
- }
146
- const waited = await waitForDesignVerifierUniverseAction(pr.owner, pr.repo, pr.prNumber, options.run, actionId, token, { intervalMs: pollIntervalSeconds * 1000, timeoutMs: timeoutSeconds * 1000 });
147
- if (waited.status === 'timeout') {
148
- throw new Error(`Timed out waiting; resume with: haystack design-verify ${prLabel} --run ${options.run} --action ${actionId}`);
149
- }
150
- if (waited.status === 'error')
151
- throw new Error(waited.message);
152
- emit(options.json, {
153
- ok: waited.action.status === 'completed' && waited.action.result?.exitCode === 0,
154
- pr: prLabel,
155
- ...waited.action,
156
- }, () => printUniverseAction(waited.action));
157
- process.exitCode = waited.action.status === 'failed'
158
- ? 2
159
- : waited.action.result?.exitCode === 0 ? 0 : 1;
160
- return true;
161
- }
162
- export async function designVerifyCommand(identifier, options) {
163
- let pr;
164
- try {
165
- pr = parsePrRef(identifier, 'design-verify');
166
- }
167
- catch (err) {
168
- emit(options.json, { ok: false, error: err.message }, () => {
169
- console.error(chalk.red(err.message));
170
- });
171
- process.exitCode = 1;
172
- return;
173
- }
174
- const prLabel = `${pr.owner}/${pr.repo}#${pr.prNumber}`;
175
- let token = null;
176
- try {
177
- token = await loadToken({ owner: pr.owner, repo: pr.repo });
178
- }
179
- catch (err) {
180
- emit(options.json, { ok: false, pr: prLabel, error: `Could not load Haystack credentials: ${err.message}` }, () => {
181
- console.error(chalk.red(`Could not load Haystack credentials: ${err.message}`));
182
- });
183
- process.exitCode = 1;
184
- return;
185
- }
186
- if (!token) {
187
- emit(options.json, { ok: false, pr: prLabel, error: 'Authentication required. Run `haystack login` first.' }, () => {
188
- console.error(chalk.red('Design verification requires authentication. Run `haystack login` first.'));
189
- });
190
- process.exitCode = 1;
191
- return;
192
- }
193
- if (options.history || options.limit !== undefined) {
194
- try {
195
- if (!options.history)
196
- throw new Error('--limit requires --history');
197
- if (options.run || options.case || options.universe || options.replay || options.execJson ||
198
- options.cwd || options.commandTimeout || options.action || options.wait === false) {
199
- throw new Error('--history cannot be combined with run or universe operations');
200
- }
201
- const limit = Number(options.limit ?? '20');
202
- if (!Number.isSafeInteger(limit) || limit < 1 || limit > 100) {
203
- throw new Error('--limit must be between 1 and 100');
204
- }
205
- const loaded = await getDesignVerifierHistory(pr.owner, pr.repo, token, limit);
206
- if (loaded.status !== 'ok')
207
- throw new Error(loaded.message);
208
- emit(options.json, { ...loaded.history }, () => console.log(formatDesignVerifierHistory(loaded.history)));
209
- }
210
- catch (error) {
211
- emit(options.json, { ok: false, error: error.message }, () => {
212
- console.error(chalk.red(error.message));
213
- });
214
- process.exitCode = 2;
215
- }
216
- return;
217
- }
218
- const pollIntervalSeconds = Number(options.pollInterval || '3');
219
- const timeoutSeconds = Number(options.timeout || '7200');
220
- if (!(Number.isFinite(pollIntervalSeconds) && pollIntervalSeconds >= 1 && pollIntervalSeconds <= 60)) {
221
- emit(options.json, { ok: false, error: '--poll-interval must be between 1 and 60 seconds' }, () => {
222
- console.error(chalk.red('--poll-interval must be between 1 and 60 seconds'));
223
- });
224
- process.exitCode = 2;
225
- return;
226
- }
227
- if (!(Number.isFinite(timeoutSeconds) && timeoutSeconds >= 1 && timeoutSeconds <= 7200)) {
228
- emit(options.json, { ok: false, error: '--timeout must be between 1 and 7200 seconds' }, () => {
229
- console.error(chalk.red('--timeout must be between 1 and 7200 seconds'));
230
- });
231
- process.exitCode = 2;
232
- return;
233
- }
234
- try {
235
- if (await runUniverseOperation(pr, prLabel, options, token, pollIntervalSeconds, timeoutSeconds))
236
- return;
237
- }
238
- catch (error) {
239
- emit(options.json, { ok: false, pr: prLabel, error: error.message }, () => {
240
- console.error(chalk.red(error.message));
241
- });
242
- process.exitCode = 2;
243
- return;
244
- }
245
- if (!options.json) {
246
- console.log(chalk.dim(options.run
247
- ? `\nResuming design verification ${options.run}...`
248
- : `\nStarting design verification for ${prLabel}...`));
249
- }
250
- const result = options.run
251
- ? { status: 'duplicate', runId: options.run }
252
- : await triggerDesignVerifier(pr.owner, pr.repo, pr.prNumber, token);
253
- switch (result.status) {
254
- case 'started':
255
- case 'queued':
256
- case 'duplicate':
257
- if (options.wait === false) {
258
- emit(options.json, { ok: true, pr: prLabel, runId: result.runId, outcome: result.status }, () => {
259
- const suffix = result.status === 'queued'
260
- ? ' (at capacity — queued; it will start as a slot frees up)'
261
- : '';
262
- console.log(chalk.green(`✓ Design verification ${result.status}${suffix}`));
263
- console.log(chalk.dim(` Run: ${result.runId}`));
264
- console.log(chalk.dim(` Resume: haystack design-verify ${prLabel} --run ${result.runId}`));
265
- });
266
- return;
267
- }
268
- break;
269
- default:
270
- emit(options.json, { ok: false, pr: prLabel, error: result.message, kind: result.status }, () => {
271
- console.error(chalk.red(`✗ ${result.message}`));
272
- });
273
- process.exitCode = 1;
274
- return;
275
- }
276
- let lastProgress = '';
277
- const waited = await waitForDesignVerifier(pr.owner, pr.repo, pr.prNumber, result.runId, token, {
278
- intervalMs: pollIntervalSeconds * 1000,
279
- timeoutMs: timeoutSeconds * 1000,
280
- onProgress: (run) => {
281
- if (options.json)
282
- return;
283
- const label = designVerifierStageLabel(run.stage);
284
- if (label === lastProgress)
285
- return;
286
- lastProgress = label;
287
- console.error(chalk.dim(` ${label}...`));
288
- },
289
- });
290
- if (waited.status === 'timeout') {
291
- const message = `Timed out waiting; the run continues. Resume with: haystack design-verify ${prLabel} --run ${result.runId}`;
292
- emit(options.json, { ok: false, status: 'pending', runId: result.runId, error: message }, () => {
293
- console.error(chalk.yellow(message));
294
- });
295
- process.exitCode = 2;
296
- return;
297
- }
298
- if (waited.status === 'error') {
299
- emit(options.json, { ok: false, status: 'failed', runId: result.runId, error: waited.message }, () => {
300
- console.error(chalk.red(`Verification status failed: ${waited.message}`));
301
- });
302
- process.exitCode = 2;
303
- return;
304
- }
305
- const report = buildDesignVerifierAgentReport(waited.run);
306
- if (options.json)
307
- console.log(JSON.stringify(report));
308
- else
309
- console.log(`\n${formatDesignVerifierAgentReport(report)}`);
310
- process.exitCode = report.exitCode;
311
- }
@@ -1,159 +0,0 @@
1
- /**
2
- * dismiss & mark-reviewed commands — User overrides for PR findings.
3
- *
4
- * haystack dismiss <pr> — Dismiss analysis findings for a PR
5
- * haystack mark-reviewed <pr> — Mark human review as not needed for a PR
6
- *
7
- * Each posts a user override to the Haystack API so the PR moves toward
8
- * "Good to Merge" in the feed.
9
- *
10
- * Accepts a PR identifier in several formats:
11
- * 123 # Uses current repo's owner/repo
12
- * owner/repo#123 # Fully qualified
13
- * https://github.com/owner/repo/pull/123 # GitHub URL
14
- */
15
- import chalk from 'chalk';
16
- import { emitActionPayload, failAction } from '../utils/action-output.js';
17
- import { resolveAuthContext } from '../utils/auth.js';
18
- import { classifyHttpError } from '../utils/haystack-api.js';
19
- import { githubRestBaseFor } from '../utils/github-api.js';
20
- import { parsePrRef } from '../utils/pr-ref.js';
21
- // ============================================================================
22
- // GitHub API helpers
23
- // ============================================================================
24
- async function getPRHeadSha(owner, repo, prNumber, token) {
25
- const response = await fetch(`${githubRestBaseFor(token)}/repos/${owner}/${repo}/pulls/${prNumber}`, {
26
- headers: {
27
- Authorization: `Bearer ${token}`,
28
- Accept: 'application/vnd.github.v3+json',
29
- 'User-Agent': 'Haystack-CLI',
30
- },
31
- });
32
- if (!response.ok) {
33
- if (response.status === 404) {
34
- throw new Error(`PR #${prNumber} not found in ${owner}/${repo}`);
35
- }
36
- throw await classifyHttpError(response, `Fetching PR #${prNumber}`);
37
- }
38
- const data = await response.json();
39
- return data.head.sha;
40
- }
41
- // ============================================================================
42
- // User override API
43
- // ============================================================================
44
- const HAYSTACK_API = 'https://haystackeditor.com';
45
- async function postUserOverride(repoFullName, prNumber, commitSha, type, token) {
46
- const response = await fetch(`${HAYSTACK_API}/api/user-override`, {
47
- method: 'POST',
48
- headers: {
49
- Authorization: `Bearer ${token}`,
50
- 'Content-Type': 'application/json',
51
- 'User-Agent': 'Haystack-CLI',
52
- },
53
- body: JSON.stringify({ repoFullName, prNumber, commitSha, type }),
54
- });
55
- if (!response.ok) {
56
- throw await classifyHttpError(response, 'User override');
57
- }
58
- }
59
- async function deleteUserOverrides(repoFullName, prNumber, token) {
60
- const response = await fetch(`${HAYSTACK_API}/api/user-override`, {
61
- method: 'DELETE',
62
- headers: {
63
- Authorization: `Bearer ${token}`,
64
- 'Content-Type': 'application/json',
65
- 'User-Agent': 'Haystack-CLI',
66
- },
67
- body: JSON.stringify({ repoFullName, prNumber }),
68
- });
69
- if (!response.ok) {
70
- throw await classifyHttpError(response, 'Clearing user overrides');
71
- }
72
- }
73
- // ============================================================================
74
- // Pure payload functions (shared by the CLI commands and the MCP server —
75
- // they throw instead of exiting, and never write to stdout)
76
- // ============================================================================
77
- /** Post a user override and return the action payload. Throws on failure. */
78
- export async function performOverride(identifier, type, commandName) {
79
- const pr = parsePrRef(identifier, commandName);
80
- const authContext = await resolveAuthContext({ owner: pr.owner, repo: pr.repo });
81
- const repoFullName = `${pr.owner}/${pr.repo}`;
82
- const headSha = await getPRHeadSha(pr.owner, pr.repo, pr.prNumber, authContext.token);
83
- await postUserOverride(repoFullName, pr.prNumber, headSha, type, authContext.token);
84
- return {
85
- action: type === 'findings-dismissed' ? 'dismiss' : 'mark_reviewed',
86
- status: 'ok',
87
- ref: `${repoFullName}#${pr.prNumber}`,
88
- head_sha: headSha,
89
- detail: type === 'findings-dismissed'
90
- ? 'Findings dismissed; the PR moves to "Good to Merge". A new commit with net-new issues resurfaces it.'
91
- : 'Human review marked as not needed; the PR moves to "Good to Merge". A new commit invalidates the override.',
92
- };
93
- }
94
- /** Clear all overrides and return the action payload. Throws on failure. */
95
- export async function performUndismiss(identifier) {
96
- const pr = parsePrRef(identifier, 'undismiss');
97
- const authContext = await resolveAuthContext({ owner: pr.owner, repo: pr.repo });
98
- const repoFullName = `${pr.owner}/${pr.repo}`;
99
- await deleteUserOverrides(repoFullName, pr.prNumber, authContext.token);
100
- return {
101
- action: 'undismiss',
102
- status: 'ok',
103
- ref: `${repoFullName}#${pr.prNumber}`,
104
- detail: 'All overrides cleared; the PR returns to its original feed bucket.',
105
- };
106
- }
107
- async function runOverride(identifier, type, commandName, options) {
108
- const action = type === 'findings-dismissed' ? 'dismiss' : 'mark_reviewed';
109
- const actionLabel = type === 'findings-dismissed'
110
- ? 'Dismissing findings'
111
- : 'Marking review as not needed';
112
- if (!options.json)
113
- console.log(chalk.dim(`\n${actionLabel} for ${identifier}...`));
114
- let payload;
115
- try {
116
- payload = await performOverride(identifier, type, commandName);
117
- }
118
- catch (err) {
119
- failAction(options.json, action, null, err.message);
120
- return; // unreachable — satisfies TS narrowing
121
- }
122
- if (options.json) {
123
- emitActionPayload(payload);
124
- return;
125
- }
126
- if (type === 'findings-dismissed') {
127
- console.log(chalk.green(`\n Findings dismissed for ${payload.ref}`));
128
- }
129
- else {
130
- console.log(chalk.green(`\n Review marked as not needed for ${payload.ref}`));
131
- }
132
- console.log(chalk.dim(` Commit: ${payload.head_sha.slice(0, 7)}`));
133
- console.log(chalk.dim(' The PR will move to "Good to Merge" in the feed.'));
134
- console.log(chalk.dim(' If a new commit introduces net-new issues, the PR will resurface.\n'));
135
- }
136
- export async function dismissCommand(identifier, options = {}) {
137
- await runOverride(identifier, 'findings-dismissed', 'dismiss', options);
138
- }
139
- export async function markReviewedCommand(identifier, options = {}) {
140
- await runOverride(identifier, 'review-not-needed', 'mark-reviewed', options);
141
- }
142
- export async function undismissCommand(identifier, options = {}) {
143
- if (!options.json)
144
- console.log(chalk.dim(`\nClearing overrides for ${identifier}...`));
145
- let payload;
146
- try {
147
- payload = await performUndismiss(identifier);
148
- }
149
- catch (err) {
150
- failAction(options.json, 'undismiss', null, err.message);
151
- return; // unreachable — satisfies TS narrowing
152
- }
153
- if (options.json) {
154
- emitActionPayload(payload);
155
- return;
156
- }
157
- console.log(chalk.green(`\n Overrides cleared for ${payload.ref}`));
158
- console.log(chalk.dim(' The PR will return to its original feed bucket.\n'));
159
- }