@haystackeditor/cli 0.25.1 → 0.26.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 (97) hide show
  1. package/README.md +39 -455
  2. package/dist/capture/app-config.js +25 -1
  3. package/dist/commands/capture-brief.js +41 -32
  4. package/dist/commands/feedback.js +66 -0
  5. package/dist/commands/init-telemetry.js +53 -7
  6. package/dist/commands/init.js +7 -2
  7. package/dist/commands/lockfile-pin.js +307 -0
  8. package/dist/commands/verify.js +68 -13
  9. package/dist/index.js +27 -923
  10. package/dist/schema.js +4 -10
  11. package/dist/utils/haystack-api.js +0 -36
  12. package/package.json +1 -5
  13. package/schemas/feedback.v1.json +13 -0
  14. package/schemas/pre-verify.v2.json +240 -0
  15. package/schemas/verify-raw.v1.json +1132 -0
  16. package/schemas/verify.v2.json +655 -0
  17. package/dist/assets/hooks/agent-context/detect.ts +0 -316
  18. package/dist/assets/hooks/agent-context/format.ts +0 -100
  19. package/dist/assets/hooks/agent-context/index.ts +0 -41
  20. package/dist/assets/hooks/agent-context/parsers/claude.ts +0 -262
  21. package/dist/assets/hooks/agent-context/parsers/codex.ts +0 -416
  22. package/dist/assets/hooks/agent-context/parsers/gemini.ts +0 -155
  23. package/dist/assets/hooks/agent-context/parsers/opencode.ts +0 -174
  24. package/dist/assets/hooks/agent-context/tsconfig.json +0 -14
  25. package/dist/assets/hooks/agent-context/types.ts +0 -58
  26. package/dist/assets/hooks/llm-rules-template.md +0 -59
  27. package/dist/assets/hooks/package-lock.json +0 -598
  28. package/dist/assets/hooks/package.json +0 -12
  29. package/dist/assets/hooks/scripts/commit-msg.sh +0 -5
  30. package/dist/assets/hooks/scripts/post-commit.sh +0 -5
  31. package/dist/assets/hooks/scripts/pre-commit.sh +0 -175
  32. package/dist/assets/hooks/scripts/pre-push.sh +0 -25
  33. package/dist/assets/hooks/scripts/prepare-commit-msg.sh +0 -5
  34. package/dist/assets/hooks/truncation-checker/ast-analyzer.ts +0 -528
  35. package/dist/assets/hooks/truncation-checker/index.ts +0 -595
  36. package/dist/assets/hooks/truncation-checker/tsconfig.json +0 -13
  37. package/dist/assets/skills/map-cloud-verifier-universe/SKILL.md +0 -2051
  38. package/dist/assets/skills/map-cloud-verifier-universe/agents/openai.yaml +0 -4
  39. package/dist/assets/skills/map-cloud-verifier-universe/references/output-contract.md +0 -3411
  40. package/dist/assets/skills/map-your-system.md +0 -143
  41. package/dist/assets/skills/submit.md +0 -200
  42. package/dist/commands/ask.js +0 -20
  43. package/dist/commands/cloud-verifier-behaviors.js +0 -218
  44. package/dist/commands/cloud-verifier-data-store-census.js +0 -539
  45. package/dist/commands/cloud-verifier-data-store-drift.js +0 -158
  46. package/dist/commands/cloud-verifier-identity-census.js +0 -4060
  47. package/dist/commands/cloud-verifier-materialization.js +0 -704
  48. package/dist/commands/cloud-verifier-pascal-selector-census.js +0 -1382
  49. package/dist/commands/cloud-verifier-python-manifest-selector-census.js +0 -2015
  50. package/dist/commands/cloud-verifier-specialized-operational-census.js +0 -11432
  51. package/dist/commands/cloud-verifier-universe.js +0 -10178
  52. package/dist/commands/config.js +0 -549
  53. package/dist/commands/design-verify.js +0 -311
  54. package/dist/commands/dismiss.js +0 -159
  55. package/dist/commands/hooks.js +0 -226
  56. package/dist/commands/inbox.js +0 -137
  57. package/dist/commands/mcp.js +0 -201
  58. package/dist/commands/policy.js +0 -371
  59. package/dist/commands/pr-status.js +0 -207
  60. package/dist/commands/pr.js +0 -105
  61. package/dist/commands/prepare-universe-review.js +0 -1092
  62. package/dist/commands/production-source-deny-policy.js +0 -100
  63. package/dist/commands/request-review.js +0 -74
  64. package/dist/commands/review.js +0 -191
  65. package/dist/commands/rules.js +0 -98
  66. package/dist/commands/scaffold-provisional-universe.js +0 -806
  67. package/dist/commands/setup.js +0 -1170
  68. package/dist/commands/skills.js +0 -447
  69. package/dist/commands/submit.js +0 -745
  70. package/dist/commands/system-map.js +0 -228
  71. package/dist/commands/triage.js +0 -598
  72. package/dist/commands/webhooks.js +0 -241
  73. package/dist/states.js +0 -46
  74. package/dist/tools/detect.js +0 -832
  75. package/dist/triage/astra.js +0 -202
  76. package/dist/triage/prompts.js +0 -188
  77. package/dist/triage/runner.js +0 -200
  78. package/dist/triage/types.js +0 -7
  79. package/dist/utils/action-output.js +0 -26
  80. package/dist/utils/analysis-api.js +0 -416
  81. package/dist/utils/design-verifier-api.js +0 -294
  82. package/dist/utils/design-verifier-history.js +0 -79
  83. package/dist/utils/design-verifier-result.js +0 -424
  84. package/dist/utils/github-api.js +0 -324
  85. package/dist/utils/pending-state.js +0 -86
  86. package/dist/utils/pr-ref.js +0 -56
  87. package/dist/utils/prompter.js +0 -328
  88. package/schemas/action.v1.json +0 -22
  89. package/schemas/ask.v1.json +0 -40
  90. package/schemas/inbox.v1.json +0 -27
  91. package/schemas/pr-status.v1.json +0 -61
  92. package/schemas/pr.v1.json +0 -97
  93. package/schemas/pr.v3.json +0 -45
  94. package/schemas/setup.v1.json +0 -75
  95. package/schemas/submit.v1.json +0 -90
  96. package/schemas/triage.v1.json +0 -103
  97. 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
- }