@haystackeditor/cli 0.8.0 → 0.9.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.
- package/README.md +105 -12
- package/dist/assets/hooks/agent-context/detect.ts +136 -0
- package/dist/assets/hooks/agent-context/format.ts +99 -0
- package/dist/assets/hooks/agent-context/index.ts +39 -0
- package/dist/assets/hooks/agent-context/parsers/claude.ts +253 -0
- package/dist/assets/hooks/agent-context/parsers/gemini.ts +155 -0
- package/dist/assets/hooks/agent-context/parsers/opencode.ts +174 -0
- package/dist/assets/hooks/agent-context/tsconfig.json +13 -0
- package/dist/assets/hooks/agent-context/types.ts +58 -0
- package/dist/assets/hooks/llm-rules-template.md +56 -0
- package/dist/assets/hooks/package.json +11 -0
- package/dist/assets/hooks/scripts/commit-msg.sh +4 -0
- package/dist/assets/hooks/scripts/post-commit.sh +4 -0
- package/dist/assets/hooks/scripts/pre-commit.sh +92 -0
- package/dist/assets/hooks/scripts/pre-push.sh +25 -0
- package/dist/assets/hooks/scripts/prepare-commit-msg.sh +3 -0
- package/dist/assets/hooks/truncation-checker/ast-analyzer.ts +528 -0
- package/dist/assets/hooks/truncation-checker/index.ts +595 -0
- package/dist/assets/hooks/truncation-checker/tsconfig.json +13 -0
- package/dist/assets/skills/prepare-haystack.md +323 -0
- package/dist/assets/skills/secrets.md +164 -0
- package/dist/assets/skills/setup-external-sandbox.md +243 -0
- package/dist/assets/skills/setup-haystack.md +639 -0
- package/dist/assets/skills/submit.md +154 -0
- package/dist/assets/templates/CLAUDE.md.snippet +42 -0
- package/dist/assets/templates/haystack.yml +193 -0
- package/dist/commands/check-pending.d.ts +19 -0
- package/dist/commands/check-pending.js +217 -0
- package/dist/commands/config.d.ts +18 -12
- package/dist/commands/config.js +327 -52
- package/dist/commands/hooks.d.ts +17 -0
- package/dist/commands/hooks.js +269 -0
- package/dist/commands/install-session-hooks.d.ts +16 -0
- package/dist/commands/install-session-hooks.js +302 -0
- package/dist/commands/login.js +1 -1
- package/dist/commands/policy.d.ts +31 -0
- package/dist/commands/policy.js +365 -0
- package/dist/commands/skills.d.ts +8 -0
- package/dist/commands/skills.js +80 -0
- package/dist/commands/submit.d.ts +22 -0
- package/dist/commands/submit.js +428 -0
- package/dist/commands/triage.d.ts +16 -0
- package/dist/commands/triage.js +354 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +317 -2
- package/dist/tools/detect.d.ts +50 -0
- package/dist/tools/detect.js +853 -0
- package/dist/tools/fixtures.d.ts +38 -0
- package/dist/tools/fixtures.js +199 -0
- package/dist/tools/setup.d.ts +43 -0
- package/dist/tools/setup.js +597 -0
- package/dist/triage/prompts.d.ts +31 -0
- package/dist/triage/prompts.js +266 -0
- package/dist/triage/runner.d.ts +21 -0
- package/dist/triage/runner.js +325 -0
- package/dist/triage/traces.d.ts +20 -0
- package/dist/triage/traces.js +305 -0
- package/dist/triage/types.d.ts +47 -0
- package/dist/triage/types.js +7 -0
- package/dist/types.d.ts +1387 -191
- package/dist/types.js +254 -2
- package/dist/utils/analysis-api.d.ts +108 -0
- package/dist/utils/analysis-api.js +194 -0
- package/dist/utils/config.js +1 -1
- package/dist/utils/git.d.ts +80 -0
- package/dist/utils/git.js +302 -0
- package/dist/utils/github-api.d.ts +83 -0
- package/dist/utils/github-api.js +266 -0
- package/dist/utils/hooks.d.ts +26 -0
- package/dist/utils/hooks.js +226 -0
- package/dist/utils/pending-state.d.ts +38 -0
- package/dist/utils/pending-state.js +86 -0
- package/dist/utils/secrets.js +3 -3
- package/dist/utils/skill.d.ts +1 -1
- package/dist/utils/skill.js +658 -1
- package/package.json +5 -3
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* triage command — View Haystack analysis results for any PR.
|
|
3
|
+
*
|
|
4
|
+
* Fetches the full rating synthesis (same data shown on the Haystack feed)
|
|
5
|
+
* and displays verdict, findings, verified bugs, and agent fix prompts.
|
|
6
|
+
*
|
|
7
|
+
* Accepts a PR identifier in several formats:
|
|
8
|
+
* haystack triage 123 # Uses current repo's owner/repo
|
|
9
|
+
* haystack triage owner/repo#123 # Fully qualified
|
|
10
|
+
* haystack triage https://github.com/owner/repo/pull/123
|
|
11
|
+
*/
|
|
12
|
+
import chalk from 'chalk';
|
|
13
|
+
import { checkAnalysisReady, fetchRatingSynthesis } from '../utils/analysis-api.js';
|
|
14
|
+
import { parseRemoteUrl } from '../utils/git.js';
|
|
15
|
+
import { loadToken } from './login.js';
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// PR identifier parsing
|
|
18
|
+
// ============================================================================
|
|
19
|
+
/**
|
|
20
|
+
* Parse a PR identifier from various formats:
|
|
21
|
+
* 123 -> uses current repo
|
|
22
|
+
* #123 -> uses current repo
|
|
23
|
+
* owner/repo#123 -> fully qualified
|
|
24
|
+
* https://github.com/owner/repo/pull/123 -> GitHub URL
|
|
25
|
+
*/
|
|
26
|
+
function parsePRIdentifier(identifier) {
|
|
27
|
+
// GitHub URL: https://github.com/owner/repo/pull/123
|
|
28
|
+
const urlMatch = identifier.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)\/?$/);
|
|
29
|
+
if (urlMatch) {
|
|
30
|
+
return { owner: urlMatch[1], repo: urlMatch[2], prNumber: parseInt(urlMatch[3], 10) };
|
|
31
|
+
}
|
|
32
|
+
// Fully qualified: owner/repo#123
|
|
33
|
+
const qualifiedMatch = identifier.match(/^([^/]+)\/([^#]+)#(\d+)$/);
|
|
34
|
+
if (qualifiedMatch) {
|
|
35
|
+
return { owner: qualifiedMatch[1], repo: qualifiedMatch[2], prNumber: parseInt(qualifiedMatch[3], 10) };
|
|
36
|
+
}
|
|
37
|
+
// Bare number: 123 or #123
|
|
38
|
+
const numberMatch = identifier.match(/^#?(\d+)$/);
|
|
39
|
+
if (numberMatch) {
|
|
40
|
+
const prNumber = parseInt(numberMatch[1], 10);
|
|
41
|
+
// Infer owner/repo from git remote
|
|
42
|
+
try {
|
|
43
|
+
const remote = parseRemoteUrl('origin');
|
|
44
|
+
return { owner: remote.owner, repo: remote.repo, prNumber };
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
throw new Error(`Cannot determine repository for PR #${prNumber}.\n` +
|
|
48
|
+
`Use the full format: haystack triage owner/repo#${prNumber}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
throw new Error(`Invalid PR identifier: "${identifier}"\n\n` +
|
|
52
|
+
`Accepted formats:\n` +
|
|
53
|
+
` haystack triage 123\n` +
|
|
54
|
+
` haystack triage owner/repo#123\n` +
|
|
55
|
+
` haystack triage https://github.com/owner/repo/pull/123`);
|
|
56
|
+
}
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// Output formatting
|
|
59
|
+
// ============================================================================
|
|
60
|
+
function ratingLabel(rating) {
|
|
61
|
+
switch (rating) {
|
|
62
|
+
case 5: return chalk.green.bold('5/5') + chalk.green(' — Safe to merge');
|
|
63
|
+
case 4: return chalk.green('4/5') + chalk.dim(' — Low risk');
|
|
64
|
+
case 3: return chalk.yellow('3/5') + chalk.dim(' — Moderate risk');
|
|
65
|
+
case 2: return chalk.red('2/5') + chalk.dim(' — High risk');
|
|
66
|
+
case 1: return chalk.red.bold('1/5') + chalk.red(' — Critical issues');
|
|
67
|
+
default: return chalk.dim(`${rating}/5`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function verdictLabel(verdict) {
|
|
71
|
+
switch (verdict) {
|
|
72
|
+
case 'clean': return chalk.green.bold('Clean');
|
|
73
|
+
case 'has-issues': return chalk.yellow.bold('Has Issues');
|
|
74
|
+
default: return chalk.dim('Unknown');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function severityIcon(severity) {
|
|
78
|
+
switch (severity) {
|
|
79
|
+
case 'critical': return chalk.red.bold('✗✗');
|
|
80
|
+
case 'high': return chalk.red('✗');
|
|
81
|
+
case 'medium': return chalk.yellow('!');
|
|
82
|
+
case 'low': return chalk.blue('·');
|
|
83
|
+
default: return chalk.dim('-');
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function printRichOutput(pr, synthesis) {
|
|
87
|
+
const reviewUrl = `https://haystackeditor.com/review/${pr.owner}/${pr.repo}/${pr.prNumber}`;
|
|
88
|
+
console.log(chalk.bold('\n' + '━'.repeat(60)));
|
|
89
|
+
console.log(chalk.bold(` Haystack Analysis: ${pr.owner}/${pr.repo}#${pr.prNumber}`));
|
|
90
|
+
console.log(chalk.bold('━'.repeat(60) + '\n'));
|
|
91
|
+
// Rating & verdict
|
|
92
|
+
console.log(` ${chalk.dim('Rating:')} ${ratingLabel(synthesis.haystackRating)}`);
|
|
93
|
+
console.log(` ${chalk.dim('Verdict:')} ${verdictLabel(synthesis.analysisVerdict)}`);
|
|
94
|
+
if (synthesis.phase) {
|
|
95
|
+
const phaseLabel = synthesis.phase === 2 ? 'Final (post-verification)' : 'Preliminary (pre-verification)';
|
|
96
|
+
console.log(` ${chalk.dim('Phase:')} ${chalk.dim(phaseLabel)}`);
|
|
97
|
+
}
|
|
98
|
+
if (synthesis.verificationConfidence) {
|
|
99
|
+
const conf = synthesis.verificationConfidence;
|
|
100
|
+
const levelColor = conf.level === 'high' ? chalk.green : conf.level === 'medium' ? chalk.yellow : chalk.red;
|
|
101
|
+
console.log(` ${chalk.dim('Verified:')} ${levelColor(conf.level)} — ${chalk.dim(conf.summary)}`);
|
|
102
|
+
}
|
|
103
|
+
if (synthesis.reviewRecommendation?.canAutoMerge) {
|
|
104
|
+
console.log(` ${chalk.dim('Merge:')} ${chalk.green('Safe to auto-merge')}`);
|
|
105
|
+
}
|
|
106
|
+
// Verification results (build/test/lint)
|
|
107
|
+
if (synthesis.verification) {
|
|
108
|
+
const v = synthesis.verification;
|
|
109
|
+
const parts = [];
|
|
110
|
+
if (v.build)
|
|
111
|
+
parts.push(v.build.success ? chalk.green('build ✓') : chalk.red('build ✗'));
|
|
112
|
+
if (v.test)
|
|
113
|
+
parts.push(v.test.success ? chalk.green('test ✓') : chalk.red('test ✗'));
|
|
114
|
+
if (v.lint)
|
|
115
|
+
parts.push(v.lint.success ? chalk.green('lint ✓') : chalk.red('lint ✗'));
|
|
116
|
+
if (parts.length > 0) {
|
|
117
|
+
console.log(` ${chalk.dim('Checks:')} ${parts.join(' ')}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Findings (synthesisDisplay) — the main actionable content
|
|
121
|
+
if (synthesis.synthesisDisplay && synthesis.synthesisDisplay.length > 0) {
|
|
122
|
+
console.log(chalk.bold('\n Findings\n'));
|
|
123
|
+
for (let i = 0; i < synthesis.synthesisDisplay.length; i++) {
|
|
124
|
+
const finding = synthesis.synthesisDisplay[i];
|
|
125
|
+
const sourceTag = finding.source ? chalk.dim(` (via ${finding.source})`) : '';
|
|
126
|
+
console.log(` ${chalk.yellow(`${i + 1}.`)} ${chalk.bold(finding.category)}${sourceTag}`);
|
|
127
|
+
console.log(` ${finding.summary}`);
|
|
128
|
+
if (finding.detail) {
|
|
129
|
+
// Indent multi-line detail
|
|
130
|
+
const detailLines = finding.detail.split('\n');
|
|
131
|
+
for (const line of detailLines) {
|
|
132
|
+
console.log(` ${chalk.dim(line)}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
console.log('');
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// Verified bugs
|
|
139
|
+
const confirmedBugs = (synthesis.verifiedBugs || []).filter(b => b.verified);
|
|
140
|
+
if (confirmedBugs.length > 0) {
|
|
141
|
+
console.log(chalk.bold(' Verified Bugs\n'));
|
|
142
|
+
for (const bug of confirmedBugs) {
|
|
143
|
+
const sourceTag = bug.source ? chalk.dim(` (via ${bug.source.type}${bug.source.author ? `: ${bug.source.author}` : ''})`) : '';
|
|
144
|
+
console.log(` ${severityIcon(bug.assessedSeverity)} ${chalk.bold(bug.originalTitle)}${sourceTag}`);
|
|
145
|
+
console.log(` ${chalk.dim('Severity:')} ${bug.assessedSeverity}${bug.likelihood ? `, ${bug.likelihood} occurrence` : ''}`);
|
|
146
|
+
console.log(` ${bug.rationale}`);
|
|
147
|
+
console.log('');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Human review reasons
|
|
151
|
+
if (synthesis.humanReviewReasons && synthesis.humanReviewReasons.length > 0) {
|
|
152
|
+
console.log(chalk.bold(' Human Review Required\n'));
|
|
153
|
+
for (const reason of synthesis.humanReviewReasons) {
|
|
154
|
+
const icon = reason.severity === 'high' || reason.severity === 'critical'
|
|
155
|
+
? chalk.red('!')
|
|
156
|
+
: chalk.yellow('·');
|
|
157
|
+
console.log(` ${icon} ${reason.reason}`);
|
|
158
|
+
if (reason.source) {
|
|
159
|
+
console.log(` ${chalk.dim(`Source: ${reason.source}`)}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
console.log('');
|
|
163
|
+
}
|
|
164
|
+
// Agent fix prompts — the key actionable output for coding agents
|
|
165
|
+
const fixableFindings = (synthesis.synthesisDisplay || []).filter(f => f.agentFixPrompt);
|
|
166
|
+
if (fixableFindings.length > 0) {
|
|
167
|
+
console.log(chalk.bold(' Agent Fix Prompts'));
|
|
168
|
+
console.log(chalk.dim(' (Copy these to your coding agent to fix the issues)\n'));
|
|
169
|
+
for (let i = 0; i < fixableFindings.length; i++) {
|
|
170
|
+
const finding = fixableFindings[i];
|
|
171
|
+
console.log(chalk.cyan(` --- Fix ${i + 1}: ${finding.category} ---`));
|
|
172
|
+
console.log(` ${finding.agentFixPrompt}`);
|
|
173
|
+
console.log('');
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Verification impact
|
|
177
|
+
if (synthesis.verificationImpact) {
|
|
178
|
+
console.log(` ${chalk.dim('Verification impact:')} ${synthesis.verificationImpact}`);
|
|
179
|
+
console.log('');
|
|
180
|
+
}
|
|
181
|
+
console.log(` ${chalk.dim('Full review:')} ${chalk.cyan(reviewUrl)}`);
|
|
182
|
+
console.log('');
|
|
183
|
+
}
|
|
184
|
+
function printJsonOutput(pr, synthesis) {
|
|
185
|
+
const reviewUrl = `https://haystackeditor.com/review/${pr.owner}/${pr.repo}/${pr.prNumber}`;
|
|
186
|
+
const output = {
|
|
187
|
+
owner: pr.owner,
|
|
188
|
+
repo: pr.repo,
|
|
189
|
+
prNumber: pr.prNumber,
|
|
190
|
+
haystackRating: synthesis.haystackRating,
|
|
191
|
+
analysisVerdict: synthesis.analysisVerdict,
|
|
192
|
+
phase: synthesis.phase,
|
|
193
|
+
canAutoMerge: synthesis.reviewRecommendation?.canAutoMerge ?? false,
|
|
194
|
+
needsHumanReview: synthesis.needsHumanReview,
|
|
195
|
+
findings: (synthesis.synthesisDisplay || []).map(f => ({
|
|
196
|
+
category: f.category,
|
|
197
|
+
summary: f.summary,
|
|
198
|
+
detail: f.detail,
|
|
199
|
+
agentFixPrompt: f.agentFixPrompt || null,
|
|
200
|
+
source: f.source || null,
|
|
201
|
+
})),
|
|
202
|
+
verifiedBugs: (synthesis.verifiedBugs || []).filter(b => b.verified).map(b => ({
|
|
203
|
+
title: b.originalTitle,
|
|
204
|
+
severity: b.assessedSeverity,
|
|
205
|
+
likelihood: b.likelihood,
|
|
206
|
+
rationale: b.rationale,
|
|
207
|
+
source: b.source || null,
|
|
208
|
+
})),
|
|
209
|
+
humanReviewReasons: synthesis.humanReviewReasons || [],
|
|
210
|
+
verification: synthesis.verification || null,
|
|
211
|
+
verificationConfidence: synthesis.verificationConfidence || null,
|
|
212
|
+
reviewUrl,
|
|
213
|
+
};
|
|
214
|
+
console.log(JSON.stringify(output, null, 2));
|
|
215
|
+
}
|
|
216
|
+
// ============================================================================
|
|
217
|
+
// Polling
|
|
218
|
+
// ============================================================================
|
|
219
|
+
const POLL_INTERVAL_MS = 5_000;
|
|
220
|
+
const POLL_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes
|
|
221
|
+
async function waitForAnalysis(owner, repo, prNumber, token, quiet) {
|
|
222
|
+
const deadline = Date.now() + POLL_TIMEOUT_MS;
|
|
223
|
+
let consecutiveErrors = 0;
|
|
224
|
+
while (Date.now() < deadline) {
|
|
225
|
+
const result = await checkAnalysisReady(owner, repo, prNumber, token);
|
|
226
|
+
if (result.status === 'ready')
|
|
227
|
+
return 'ready';
|
|
228
|
+
if (result.status === 'error') {
|
|
229
|
+
consecutiveErrors++;
|
|
230
|
+
if (consecutiveErrors >= 5)
|
|
231
|
+
return 'error';
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
consecutiveErrors = 0;
|
|
235
|
+
}
|
|
236
|
+
if (!quiet) {
|
|
237
|
+
process.stderr.write(`\r ${chalk.dim('Waiting for analysis to complete...')}${' '.repeat(5)}`);
|
|
238
|
+
}
|
|
239
|
+
await new Promise(r => setTimeout(r, POLL_INTERVAL_MS));
|
|
240
|
+
}
|
|
241
|
+
return 'pending';
|
|
242
|
+
}
|
|
243
|
+
// ============================================================================
|
|
244
|
+
// Main command
|
|
245
|
+
// ============================================================================
|
|
246
|
+
export async function triageCommand(identifier, options) {
|
|
247
|
+
// Parse PR identifier
|
|
248
|
+
let pr;
|
|
249
|
+
try {
|
|
250
|
+
pr = parsePRIdentifier(identifier);
|
|
251
|
+
}
|
|
252
|
+
catch (err) {
|
|
253
|
+
console.error(chalk.red(`\n${err.message}\n`));
|
|
254
|
+
process.exit(1);
|
|
255
|
+
}
|
|
256
|
+
// Load GitHub token for private repo auth
|
|
257
|
+
const token = await loadToken() || undefined;
|
|
258
|
+
if (!options.json) {
|
|
259
|
+
console.log(chalk.dim(`\nFetching analysis for ${pr.owner}/${pr.repo}#${pr.prNumber}...`));
|
|
260
|
+
}
|
|
261
|
+
// Check if analysis is ready — resolve to a terminal state before fetching
|
|
262
|
+
let analysisReady = false;
|
|
263
|
+
const readyResult = await checkAnalysisReady(pr.owner, pr.repo, pr.prNumber, token);
|
|
264
|
+
if (readyResult.status === 'ready') {
|
|
265
|
+
analysisReady = true;
|
|
266
|
+
}
|
|
267
|
+
else if (readyResult.status === 'pending') {
|
|
268
|
+
if (options.wait === false) {
|
|
269
|
+
if (options.json) {
|
|
270
|
+
console.log(JSON.stringify({
|
|
271
|
+
owner: pr.owner, repo: pr.repo, prNumber: pr.prNumber, status: 'pending',
|
|
272
|
+
}, null, 2));
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
console.log(chalk.yellow('\n Analysis is still in progress.'));
|
|
276
|
+
console.log(chalk.dim(' Re-run without --no-wait to poll until complete.\n'));
|
|
277
|
+
}
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
// Wait for analysis
|
|
281
|
+
if (!options.json) {
|
|
282
|
+
console.log(chalk.dim(' Analysis in progress, waiting...'));
|
|
283
|
+
}
|
|
284
|
+
const waitResult = await waitForAnalysis(pr.owner, pr.repo, pr.prNumber, token, options.json);
|
|
285
|
+
if (!options.json) {
|
|
286
|
+
process.stderr.write('\r' + ' '.repeat(60) + '\r');
|
|
287
|
+
}
|
|
288
|
+
if (waitResult === 'ready') {
|
|
289
|
+
analysisReady = true;
|
|
290
|
+
}
|
|
291
|
+
else if (waitResult === 'pending') {
|
|
292
|
+
if (options.json) {
|
|
293
|
+
console.log(JSON.stringify({
|
|
294
|
+
owner: pr.owner, repo: pr.repo, prNumber: pr.prNumber, status: 'pending',
|
|
295
|
+
}, null, 2));
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
console.log(chalk.yellow('\n Timed out waiting for analysis.'));
|
|
299
|
+
console.log(chalk.dim(' Try again later: ') + chalk.cyan(`haystack triage ${pr.owner}/${pr.repo}#${pr.prNumber}\n`));
|
|
300
|
+
}
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
// waitResult === 'error'
|
|
305
|
+
if (options.json) {
|
|
306
|
+
console.log(JSON.stringify({
|
|
307
|
+
owner: pr.owner, repo: pr.repo, prNumber: pr.prNumber, status: 'error',
|
|
308
|
+
}, null, 2));
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
console.error(chalk.red('\n Analysis check failed.\n'));
|
|
312
|
+
}
|
|
313
|
+
process.exit(1);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
// readyResult.status === 'error'
|
|
318
|
+
if (options.json) {
|
|
319
|
+
console.log(JSON.stringify({
|
|
320
|
+
owner: pr.owner, repo: pr.repo, prNumber: pr.prNumber,
|
|
321
|
+
status: 'error', message: readyResult.message,
|
|
322
|
+
}, null, 2));
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
console.error(chalk.red(`\n Analysis check failed: ${readyResult.message}\n`));
|
|
326
|
+
}
|
|
327
|
+
process.exit(1);
|
|
328
|
+
}
|
|
329
|
+
if (!analysisReady)
|
|
330
|
+
return;
|
|
331
|
+
// Fetch the full rating synthesis
|
|
332
|
+
const synthesis = await fetchRatingSynthesis(pr.owner, pr.repo, pr.prNumber, token);
|
|
333
|
+
if (!synthesis) {
|
|
334
|
+
if (options.json) {
|
|
335
|
+
console.log(JSON.stringify({
|
|
336
|
+
owner: pr.owner, repo: pr.repo, prNumber: pr.prNumber,
|
|
337
|
+
status: 'error', message: 'Rating synthesis not available',
|
|
338
|
+
}, null, 2));
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
console.error(chalk.yellow('\n Analysis completed but rating synthesis is not available yet.'));
|
|
342
|
+
console.log(chalk.dim(' Try again in a moment, or view the full review:'));
|
|
343
|
+
console.log(chalk.cyan(` https://haystackeditor.com/review/${pr.owner}/${pr.repo}/${pr.prNumber}\n`));
|
|
344
|
+
}
|
|
345
|
+
process.exit(1);
|
|
346
|
+
}
|
|
347
|
+
// Output
|
|
348
|
+
if (options.json) {
|
|
349
|
+
printJsonOutput(pr, synthesis);
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
printRichOutput(pr, synthesis);
|
|
353
|
+
}
|
|
354
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
* npx @haystackeditor/cli init # Set up .haystack.json
|
|
10
10
|
* npx @haystackeditor/cli status # Check configuration
|
|
11
11
|
* npx @haystackeditor/cli login # Authenticate with GitHub
|
|
12
|
+
* npx @haystackeditor/cli submit # Create a PR (auto-merge or review)
|
|
13
|
+
* npx @haystackeditor/cli check-pending # Check analysis status
|
|
14
|
+
* npx @haystackeditor/cli triage 123 # View analysis results for a PR
|
|
12
15
|
* npx @haystackeditor/cli secrets list # List stored secrets
|
|
13
16
|
*/
|
|
14
17
|
export {};
|