@haystackeditor/cli 0.10.3 → 0.12.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 (42) hide show
  1. package/README.md +15 -16
  2. package/dist/assets/hooks/agent-context/detect.ts +180 -0
  3. package/dist/assets/hooks/agent-context/format.ts +1 -0
  4. package/dist/assets/hooks/agent-context/index.ts +2 -0
  5. package/dist/assets/hooks/agent-context/parsers/claude.ts +14 -5
  6. package/dist/assets/hooks/agent-context/parsers/codex.ts +416 -0
  7. package/dist/assets/hooks/agent-context/tsconfig.json +1 -0
  8. package/dist/assets/hooks/agent-context/types.ts +1 -1
  9. package/dist/assets/hooks/package.json +2 -1
  10. package/dist/assets/hooks/scripts/pre-commit.sh +80 -2
  11. package/dist/assets/hooks/scripts/pre-push.sh +1 -1
  12. package/dist/assets/skills/submit.md +20 -0
  13. package/dist/commands/config.d.ts +4 -0
  14. package/dist/commands/config.js +94 -69
  15. package/dist/commands/dismiss.js +2 -1
  16. package/dist/commands/install-session-hooks.d.ts +3 -2
  17. package/dist/commands/install-session-hooks.js +27 -11
  18. package/dist/commands/pr-status.d.ts +5 -0
  19. package/dist/commands/pr-status.js +2 -2
  20. package/dist/commands/request-review.d.ts +15 -0
  21. package/dist/commands/request-review.js +95 -0
  22. package/dist/commands/setup.d.ts +1 -0
  23. package/dist/commands/setup.js +285 -2
  24. package/dist/commands/submit.d.ts +1 -0
  25. package/dist/commands/submit.js +12 -19
  26. package/dist/commands/triage.d.ts +16 -4
  27. package/dist/commands/triage.js +278 -179
  28. package/dist/index.d.ts +2 -1
  29. package/dist/index.js +73 -50
  30. package/dist/triage/prompts.js +13 -5
  31. package/dist/triage/runner.js +1 -1
  32. package/dist/triage/traces.js +9 -3
  33. package/dist/triage/types.d.ts +1 -1
  34. package/dist/types.d.ts +146 -26
  35. package/dist/types.js +44 -5
  36. package/dist/utils/analysis-api.d.ts +16 -0
  37. package/dist/utils/analysis-api.js +23 -5
  38. package/dist/utils/telemetry.d.ts +17 -0
  39. package/dist/utils/telemetry.js +109 -0
  40. package/package.json +1 -1
  41. package/dist/commands/check-pending.d.ts +0 -19
  42. package/dist/commands/check-pending.js +0 -217
@@ -9,10 +9,17 @@
9
9
  * 5. Scan for review policies
10
10
  * 6. Review discovered items (toggle on/off)
11
11
  * 7. Confirm & write .haystack.json to selected repos
12
+ * 8. Install session tracking (Entire CLI + .entire/settings.json + local hooks)
12
13
  */
13
14
  import chalk from 'chalk';
14
15
  import inquirer from 'inquirer';
16
+ import { execFileSync, execSync } from 'child_process';
17
+ import { mkdirSync } from 'fs';
18
+ import { basename, join } from 'path';
15
19
  import { loadToken } from './login.js';
20
+ import { hooksInstall } from './hooks.js';
21
+ import { findGitRoot } from '../utils/hooks.js';
22
+ import { trackError, trackSetupEvent } from '../utils/telemetry.js';
16
23
  // =============================================================================
17
24
  // Constants
18
25
  // =============================================================================
@@ -433,7 +440,7 @@ async function stepConfirm(selectedRepos, rules, signals, policies, token) {
433
440
  ]);
434
441
  if (!confirmed) {
435
442
  console.log(chalk.yellow('\n Setup cancelled.\n'));
436
- return;
443
+ return false;
437
444
  }
438
445
  // Write to each repo
439
446
  const failedRepos = [];
@@ -458,6 +465,7 @@ async function stepConfirm(selectedRepos, rules, signals, policies, token) {
458
465
  process.exit(1);
459
466
  }
460
467
  console.log(chalk.green('\n Setup complete!\n'));
468
+ return true;
461
469
  }
462
470
  // =============================================================================
463
471
  // Main command
@@ -492,5 +500,280 @@ export async function setupCommand() {
492
500
  // Step 5: Review
493
501
  const reviewed = await stepReview(rules, signals, policies);
494
502
  // Step 6: Confirm & write
495
- await stepConfirm(selectedRepos, reviewed.rules, reviewed.signals, reviewed.policies, token);
503
+ const confirmed = await stepConfirm(selectedRepos, reviewed.rules, reviewed.signals, reviewed.policies, token);
504
+ if (!confirmed)
505
+ return;
506
+ // Step 7: Install Entire CLI (session tracking)
507
+ await stepInstallEntire(selectedRepos, token);
508
+ }
509
+ // =============================================================================
510
+ // Step 7: Install Entire CLI for session tracking
511
+ // =============================================================================
512
+ const ENTIRE_VERSION = 'v0.5.3-haystack.2';
513
+ const ENTIRE_RELEASE_BASE = `https://github.com/haystackeditor/cli/releases/download/${ENTIRE_VERSION}`;
514
+ function getEntireBinaryName() {
515
+ const platform = process.platform === 'darwin' ? 'darwin'
516
+ : process.platform === 'linux' ? 'linux'
517
+ : null;
518
+ if (!platform)
519
+ return null; // unsupported (Windows, FreeBSD, etc.)
520
+ const arch = process.arch === 'arm64' ? 'arm64'
521
+ : process.arch === 'x64' ? 'amd64'
522
+ : null;
523
+ if (!arch)
524
+ return null; // unsupported (s390x, ppc64, etc.)
525
+ return `entire_${platform}_${arch}`;
526
+ }
527
+ function isEntireInstalled() {
528
+ // Check the canonical install location first, then fall back to PATH
529
+ const haystackBin = process.env.HOME
530
+ ? join(process.env.HOME, '.haystack', 'bin', 'entire')
531
+ : null;
532
+ const candidates = haystackBin ? [haystackBin, 'entire'] : ['entire'];
533
+ for (const bin of candidates) {
534
+ try {
535
+ const version = execSync(`"${bin}" --version 2>&1`, { encoding: 'utf-8' }).trim();
536
+ return {
537
+ installed: true,
538
+ isHaystackFork: version.includes('haystack'),
539
+ version,
540
+ };
541
+ }
542
+ catch {
543
+ // Try next candidate
544
+ }
545
+ }
546
+ return { installed: false, isHaystackFork: false, version: '' };
547
+ }
548
+ async function installEntireBinary() {
549
+ const binaryName = getEntireBinaryName();
550
+ if (!binaryName) {
551
+ console.log(chalk.yellow(` Entire CLI is not available for ${process.platform}/${process.arch}.`));
552
+ console.log(chalk.dim(' Supported: macOS (Intel/Apple Silicon), Linux (x86_64/ARM64)'));
553
+ return false;
554
+ }
555
+ const url = `${ENTIRE_RELEASE_BASE}/${binaryName}`;
556
+ const installDir = process.env.HOME
557
+ ? join(process.env.HOME, '.haystack', 'bin')
558
+ : '/usr/local/bin';
559
+ try {
560
+ mkdirSync(installDir, { recursive: true });
561
+ const targetPath = join(installDir, 'entire');
562
+ console.log(chalk.dim(` Downloading ${ENTIRE_VERSION}...`));
563
+ execFileSync('curl', ['-fsSL', url, '-o', targetPath], { stdio: 'pipe' });
564
+ execFileSync('chmod', ['+x', targetPath], { stdio: 'pipe' });
565
+ // Verify using absolute path (don't rely on PATH)
566
+ const version = execFileSync(targetPath, ['--version'], { encoding: 'utf-8' }).trim();
567
+ if (!version.includes('haystack')) {
568
+ console.log(chalk.red(` Unexpected version: ${version}`));
569
+ return false;
570
+ }
571
+ console.log(chalk.green(` ✓ Installed: ${version.split('\n')[0]}`));
572
+ // Check if install dir is on PATH
573
+ const pathDirs = (process.env.PATH || '').split(':');
574
+ if (!pathDirs.includes(installDir)) {
575
+ console.log(chalk.yellow(`\n Note: ${installDir} is not on your PATH.`));
576
+ console.log(chalk.dim(` Add to your shell profile: export PATH="${installDir}:$PATH"\n`));
577
+ }
578
+ return true;
579
+ }
580
+ catch (err) {
581
+ const errorMsg = err instanceof Error ? err.message : String(err);
582
+ console.log(chalk.red(` Failed to install Entire CLI: ${errorMsg}`));
583
+ console.log(chalk.dim(` Manual install: curl -fsSL ${url} -o ~/.haystack/bin/entire && chmod +x ~/.haystack/bin/entire`));
584
+ trackError('entire_install_failed', {
585
+ error_message: errorMsg,
586
+ error_stack: err instanceof Error ? err.stack : undefined,
587
+ entire_version: ENTIRE_VERSION,
588
+ install_dir: installDir,
589
+ });
590
+ return false;
591
+ }
592
+ }
593
+ /**
594
+ * Write .entire/settings.json via the GitHub API.
595
+ * selectedRepos are GitHub full names (owner/repo), not local paths.
596
+ */
597
+ async function configureEntireSettingsViaAPI(repoFullName, token) {
598
+ const [owner, repo] = repoFullName.split('/');
599
+ const filePath = '.entire/settings.json';
600
+ const headers = {
601
+ Authorization: `Bearer ${token}`,
602
+ Accept: 'application/vnd.github.v3+json',
603
+ 'User-Agent': 'Haystack-CLI',
604
+ 'Content-Type': 'application/json',
605
+ };
606
+ // Fetch existing .entire/settings.json (if any)
607
+ let existingSha = null;
608
+ let settings = { enabled: true };
609
+ let originalContent = '';
610
+ const getResp = await fetch(`${GITHUB_API}/repos/${owner}/${repo}/contents/${filePath}`, { headers });
611
+ if (getResp.ok) {
612
+ const data = (await getResp.json());
613
+ existingSha = data.sha;
614
+ originalContent = Buffer.from(data.content, 'base64').toString('utf-8');
615
+ try {
616
+ settings = JSON.parse(originalContent);
617
+ }
618
+ catch {
619
+ // Malformed JSON — keep sha for update but use fresh defaults
620
+ console.log(chalk.dim(` Existing .entire/settings.json in ${repoFullName} is malformed — will repair`));
621
+ }
622
+ }
623
+ else if (getResp.status !== 404) {
624
+ // Non-404 failure (auth, network, rate limit) — don't guess, surface it
625
+ const errText = await getResp.text().catch(() => '');
626
+ const error = new Error(`Failed to read .entire/settings.json: ${getResp.status} ${errText}`);
627
+ trackError('entire_settings_read_failed', {
628
+ error_message: error.message,
629
+ repo: repoFullName,
630
+ status: getResp.status,
631
+ });
632
+ throw error;
633
+ }
634
+ // 404 → file doesn't exist, existingSha stays null → PUT will create
635
+ // Remove legacy skip_raw_transcript if present (v0.5.3-haystack.2+ compacts automatically)
636
+ if (settings.strategy_options && typeof settings.strategy_options === 'object') {
637
+ const strategyOptions = settings.strategy_options;
638
+ delete strategyOptions.skip_raw_transcript;
639
+ if (Object.keys(strategyOptions).length === 0) {
640
+ delete settings.strategy_options;
641
+ }
642
+ }
643
+ const updatedContent = JSON.stringify(settings, null, 2) + '\n';
644
+ // Skip write if nothing changed (avoids no-op commits)
645
+ if (existingSha && updatedContent === originalContent) {
646
+ return;
647
+ }
648
+ const content = Buffer.from(updatedContent).toString('base64');
649
+ const body = {
650
+ message: 'chore: configure Entire CLI settings',
651
+ content,
652
+ };
653
+ if (existingSha)
654
+ body.sha = existingSha;
655
+ const putResp = await fetch(`${GITHUB_API}/repos/${owner}/${repo}/contents/${filePath}`, {
656
+ method: 'PUT',
657
+ headers,
658
+ body: JSON.stringify(body),
659
+ });
660
+ if (!putResp.ok) {
661
+ const errText = await putResp.text().catch(() => '');
662
+ const error = new Error(`GitHub API ${putResp.status}: ${errText}`);
663
+ trackError('entire_settings_write_failed', {
664
+ error_message: error.message,
665
+ repo: repoFullName,
666
+ status: putResp.status,
667
+ had_existing: !!existingSha,
668
+ });
669
+ throw error;
670
+ }
671
+ }
672
+ async function stepInstallEntire(selectedRepos, token) {
673
+ console.log(chalk.bold('\n Step 7: Session tracking (Entire CLI)\n'));
674
+ const status = isEntireInstalled();
675
+ let binaryReady = status.installed && status.isHaystackFork;
676
+ let userConsented = true; // true if user said yes or was already installed
677
+ if (binaryReady) {
678
+ console.log(chalk.dim(` Entire CLI already installed (${status.version.split('\n')[0]})`));
679
+ }
680
+ else if (status.installed) {
681
+ console.log(chalk.dim(` Stock Entire CLI detected: ${status.version.split('\n')[0]}`));
682
+ const { upgrade } = await inquirer.prompt([
683
+ {
684
+ type: 'confirm',
685
+ name: 'upgrade',
686
+ message: 'Upgrade to Haystack fork? (adds transcript optimization — saves GB of git storage)',
687
+ default: true,
688
+ },
689
+ ]);
690
+ if (upgrade) {
691
+ binaryReady = await installEntireBinary();
692
+ }
693
+ else {
694
+ userConsented = false;
695
+ }
696
+ }
697
+ else {
698
+ const { install } = await inquirer.prompt([
699
+ {
700
+ type: 'confirm',
701
+ name: 'install',
702
+ message: 'Install Entire CLI? (tracks AI coding sessions for analysis)',
703
+ default: true,
704
+ },
705
+ ]);
706
+ if (install) {
707
+ binaryReady = await installEntireBinary();
708
+ }
709
+ else {
710
+ userConsented = false;
711
+ }
712
+ }
713
+ if (!userConsented) {
714
+ console.log(chalk.dim(' Skipping session tracking.\n'));
715
+ }
716
+ // Configure .entire/settings.json on each repo via GitHub API
717
+ // (works even if binary install failed — settings are remote; skip if user declined)
718
+ if (userConsented) {
719
+ let configured = 0;
720
+ for (const repoFullName of selectedRepos) {
721
+ try {
722
+ process.stdout.write(chalk.dim(` Configuring ${repoFullName}...`));
723
+ await configureEntireSettingsViaAPI(repoFullName, token);
724
+ process.stdout.write('\r\x1b[K');
725
+ console.log(chalk.green(` ✓ ${repoFullName}`));
726
+ configured++;
727
+ }
728
+ catch (err) {
729
+ process.stdout.write('\r\x1b[K');
730
+ console.log(chalk.yellow(` ⚠ ${repoFullName}: ${err instanceof Error ? err.message : err}`));
731
+ }
732
+ }
733
+ if (configured > 0) {
734
+ console.log(chalk.green(`\n ✓ Session tracking configured on ${configured} repo(s) (transcripts auto-compacted)\n`));
735
+ trackSetupEvent('entire_configured', { configured, total: selectedRepos.length });
736
+ }
737
+ else {
738
+ console.log(chalk.yellow('\n ⚠ Could not configure session tracking on any repos.\n'));
739
+ trackError('entire_config_all_failed', { total: selectedRepos.length });
740
+ }
741
+ }
742
+ // Offer to install local git hooks if cwd is inside one of the selected repos
743
+ const gitRoot = findGitRoot();
744
+ if (gitRoot) {
745
+ const gitDirName = basename(gitRoot).toLowerCase();
746
+ const repoName = selectedRepos.find((r) => {
747
+ const name = r.split('/')[1]?.replace(/\.git$/, '');
748
+ return name && gitDirName === name.toLowerCase();
749
+ });
750
+ if (repoName) {
751
+ try {
752
+ const { installHooks } = await inquirer.prompt([
753
+ {
754
+ type: 'confirm',
755
+ name: 'installHooks',
756
+ message: `Install git hooks locally for ${repoName}? (agent detection, truncation checking, LLM rules)`,
757
+ default: true,
758
+ },
759
+ ]);
760
+ if (installHooks) {
761
+ console.log('');
762
+ await hooksInstall({ skipEntire: true, force: true });
763
+ }
764
+ }
765
+ catch (err) {
766
+ console.log(chalk.yellow(` ⚠ Hook installation failed: ${err instanceof Error ? err.message : err}`));
767
+ console.log(chalk.dim(' You can install hooks later with: haystack hooks install'));
768
+ }
769
+ }
770
+ else {
771
+ console.log(chalk.dim(' To install git hooks in a repo, cd into it and run:'));
772
+ console.log(chalk.dim(' haystack hooks install\n'));
773
+ }
774
+ }
775
+ else {
776
+ console.log(chalk.dim(' To install git hooks in a repo, cd into it and run:'));
777
+ console.log(chalk.dim(' haystack hooks install\n'));
778
+ }
496
779
  }
@@ -19,5 +19,6 @@ export interface SubmitOptions {
19
19
  force?: boolean;
20
20
  wait?: boolean;
21
21
  autoFix?: boolean;
22
+ autoMerge?: boolean;
22
23
  }
23
24
  export declare function submitCommand(options: SubmitOptions): Promise<void>;
@@ -16,7 +16,6 @@ import { createPullRequest, findExistingPR, requireGitHubAuth, requestReviewers,
16
16
  import { runTriage } from '../triage/runner.js';
17
17
  import { checkAnalysisReady, fetchAnalysisResults } from '../utils/analysis-api.js';
18
18
  import { savePendingSubmit, updatePendingSubmit } from '../utils/pending-state.js';
19
- import { isAutoMergeEnabled } from './config.js';
20
19
  import { ensureHaystackLabels, addLabelsToIssue, } from '../utils/github-api.js';
21
20
  // ============================================================================
22
21
  // Helpers
@@ -51,7 +50,7 @@ function checkerDisplayName(checker) {
51
50
  switch (checker) {
52
51
  case 'code-review': return 'Code Review';
53
52
  case 'rules-validator': return 'Rules Validator';
54
- case 'intent-drift': return 'Intent Drift';
53
+ case 'intent-drift': return 'Instruction Drift';
55
54
  default: return checker;
56
55
  }
57
56
  }
@@ -269,14 +268,7 @@ export async function submitCommand(options) {
269
268
  // -------------------------------------------------------------------------
270
269
  // Step 6: Apply auto-merge label if enabled
271
270
  // -------------------------------------------------------------------------
272
- let autoMergeEnabled = false;
273
- try {
274
- autoMergeEnabled = await isAutoMergeEnabled();
275
- }
276
- catch {
277
- // Preference check failure should not block submission
278
- }
279
- if (autoMergeEnabled) {
271
+ if (options.autoMerge) {
280
272
  try {
281
273
  await ensureHaystackLabels(remoteInfo.owner, remoteInfo.repo, ['haystack:auto-merge']);
282
274
  await addLabelsToIssue(remoteInfo.owner, remoteInfo.repo, prNumber, ['haystack:auto-merge']);
@@ -293,7 +285,7 @@ export async function submitCommand(options) {
293
285
  try {
294
286
  await ensureHaystackLabels(remoteInfo.owner, remoteInfo.repo, ['haystack:auto-fix']);
295
287
  await addLabelsToIssue(remoteInfo.owner, remoteInfo.repo, prNumber, ['haystack:auto-fix']);
296
- console.log(chalk.green('✓ Auto-fix enabled for this PR'));
288
+ console.log(chalk.green('✓ Auto-fix (alpha) enabled for this PR'));
297
289
  }
298
290
  catch (err) {
299
291
  console.log(chalk.yellow(`⚠ Could not set auto-fix label: ${err.message}`));
@@ -328,11 +320,11 @@ export async function submitCommand(options) {
328
320
  console.log(` ${chalk.dim('URL:')} ${chalk.cyan(prUrl)}`);
329
321
  console.log(` ${chalk.dim('Title:')} ${options.title || getLastCommitMessage()}`);
330
322
  console.log(` ${chalk.dim('Branch:')} ${currentBranch} → ${baseBranch}`);
331
- if (autoMergeEnabled) {
323
+ if (options.autoMerge) {
332
324
  console.log(` ${chalk.dim('Merge:')} ${chalk.green('Auto-merge if safe')}`);
333
325
  }
334
326
  if (options.autoFix) {
335
- console.log(` ${chalk.dim('Fix:')} ${chalk.cyan('Auto-fix issues before Feed')}`);
327
+ console.log(` ${chalk.dim('Fix:')} ${chalk.cyan('Auto-fix alpha enabled')}`);
336
328
  }
337
329
  if (options.review) {
338
330
  const reviewer = typeof options.review === 'string' ? options.review : 'needs assignment';
@@ -343,7 +335,7 @@ export async function submitCommand(options) {
343
335
  // Step 9: Wait for Haystack analysis (triggered via GitHub App webhook)
344
336
  // -------------------------------------------------------------------------
345
337
  console.log(chalk.dim('Haystack analysis triggered via GitHub App webhook.'));
346
- // Save pending state (so check-pending works even if we disconnect)
338
+ // Save pending state (so `haystack triage` works even if we disconnect)
347
339
  const pendingState = {
348
340
  version: 1,
349
341
  prNumber,
@@ -359,7 +351,7 @@ export async function submitCommand(options) {
359
351
  // If --no-wait or non-interactive, exit here
360
352
  if (options.wait === false || !process.stdin.isTTY) {
361
353
  console.log(chalk.dim('\nAnalysis running in background.'));
362
- console.log(chalk.dim('Check results later with: ') + chalk.cyan('haystack check-pending\n'));
354
+ console.log(chalk.dim('Check results later with: ') + chalk.cyan('haystack triage\n'));
363
355
  return;
364
356
  }
365
357
  // Wait for analysis inline by polling the result endpoint
@@ -369,7 +361,7 @@ export async function submitCommand(options) {
369
361
  const sigintHandler = () => {
370
362
  interrupted = true;
371
363
  console.log(chalk.dim('\n\nAnalysis still running. Check later with:'));
372
- console.log(chalk.cyan(' haystack check-pending\n'));
364
+ console.log(chalk.cyan(' haystack triage\n'));
373
365
  process.exit(0);
374
366
  };
375
367
  process.on('SIGINT', sigintHandler);
@@ -407,9 +399,10 @@ export async function submitCommand(options) {
407
399
  else if (options.autoFix) {
408
400
  // Auto-fix flow: agent fixes straightforward issues in the background.
409
401
  // Issues appear in Feed immediately — user can review while agent works.
410
- console.log(chalk.cyan.bold(' ⚙ Auto-fix triggered\n'));
402
+ console.log(chalk.cyan.bold(' ⚙ Auto-fix triggered (alpha)\n'));
411
403
  console.log(chalk.dim(` ${verdict.summary}\n`));
412
- console.log(chalk.dim(' Coding agent is fixing straightforward issues in the background.'));
404
+ console.log(chalk.dim(' Coding agent is fixing straightforward mechanical issues in the background.'));
405
+ console.log(chalk.dim(' Auto-fix is currently alpha.'));
413
406
  console.log(chalk.dim(' Issues that need your judgment are in the Feed now.\n'));
414
407
  const reviewUrl = `https://haystackeditor.com/review/${remoteInfo.owner}/${remoteInfo.repo}/${prNumber}`;
415
408
  console.log(` ${chalk.dim('Feed:')} ${chalk.cyan(reviewUrl)}\n`);
@@ -451,6 +444,6 @@ export async function submitCommand(options) {
451
444
  process.removeListener('SIGINT', sigintHandler);
452
445
  if (Date.now() >= deadline && !interrupted) {
453
446
  console.log(chalk.yellow('\n\n Timed out waiting for analysis.'));
454
- console.log(chalk.dim(' Check results later with: ') + chalk.cyan('haystack check-pending\n'));
447
+ console.log(chalk.dim(' Check results later with: ') + chalk.cyan('haystack triage\n'));
455
448
  }
456
449
  }
@@ -1,16 +1,28 @@
1
1
  /**
2
- * triage command — View Haystack analysis results for any PR.
2
+ * triage command — Show Haystack analysis results for a PR.
3
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.
4
+ * Designed for coding agents to consume. Shows two things:
5
+ * 1. What the auto-fixer is already handling (so the agent doesn't duplicate work)
6
+ * 2. Findings with attribution and fix prompts (so the agent knows what to fix)
7
+ *
8
+ * When called without a PR identifier, reads the last submitted PR from
9
+ * `.haystack/pending-submit.json` (written by `haystack submit`).
6
10
  *
7
11
  * Accepts a PR identifier in several formats:
12
+ * haystack triage # Last submitted PR
8
13
  * haystack triage 123 # Uses current repo's owner/repo
9
14
  * haystack triage owner/repo#123 # Fully qualified
10
15
  * haystack triage https://github.com/owner/repo/pull/123
16
+ *
17
+ * Output modes:
18
+ * --hook Minimal single-line for session-start hooks
19
+ * --json Machine-readable JSON
20
+ * (default) Rich terminal output
11
21
  */
12
22
  export interface TriageOptions {
13
23
  json?: boolean;
14
24
  wait?: boolean;
25
+ hook?: boolean;
26
+ clear?: boolean;
15
27
  }
16
- export declare function triageCommand(identifier: string, options: TriageOptions): Promise<void>;
28
+ export declare function triageCommand(identifier: string | undefined, options: TriageOptions): Promise<void>;