@in-the-loop-labs/pair-review 3.8.0 → 3.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.
@@ -14,6 +14,7 @@ const execAsync = promisify(exec);
14
14
  const { STOPS, scopeIncludes, includesBranch, DEFAULT_SCOPE, scopeLabel, reviewScope } = require('./local-scope');
15
15
  const { initializeDatabase, ReviewRepository, RepoSettingsRepository } = require('./database');
16
16
  const { resolveCouncilHandle } = require('./councils/resolve-council');
17
+ const { prepareInteractiveAnalysisConfig } = require('./interactive-analysis-config');
17
18
  const { startServer } = require('./server');
18
19
  const { localReviewDiffs } = require('./routes/shared');
19
20
  const summaryGenerator = require('./ai/summary-generator');
@@ -703,9 +704,14 @@ async function generateLocalDiff(repoPath, options = {}) {
703
704
  * @param {Object} params.config - Loaded config
704
705
  * @param {string} params.repoPath - Path to the working tree (already validated)
705
706
  * @param {Object} [params.flags] - CLI flags / options
707
+ * @param {boolean} [params.startBackgroundJobs=true] - When false, skip the
708
+ * fire-and-forget summary/tour generation jobs. The interactive UI wants them
709
+ * (they populate the review while the human reads the diff), but a one-shot
710
+ * headless run does not: they would spend provider budget after the JSON
711
+ * result is already printed and keep the event loop alive (CLI hang).
706
712
  * @returns {Promise<{sessionId: number, repoPath: string, branch: string, repository: string, headSha: string, diff: string, stats: Object, digest: string|null, branchInfo: Object|null, scopeStart: string, scopeEnd: string, baseBranch: string|null}>}
707
713
  */
708
- async function setupLocalReviewSession({ db, config, repoPath, flags = {} }) {
714
+ async function setupLocalReviewSession({ db, config, repoPath, flags = {}, startBackgroundJobs = true }) {
709
715
  const headSha = await module.exports.getHeadSha(repoPath);
710
716
  console.log(`HEAD SHA: ${headSha}`);
711
717
 
@@ -837,25 +843,30 @@ async function setupLocalReviewSession({ db, config, repoPath, flags = {} }) {
837
843
  logger.warn(`Could not persist diff to database: ${persistError.message}`);
838
844
  }
839
845
 
840
- summaryGenerator.kickOffSummaryJob({
841
- db,
842
- config,
843
- reviewId: sessionId,
844
- diffText: diff,
845
- worktreePath: repoPath,
846
- reviewContext: { prTitle: branch },
847
- trigger: 'auto'
848
- })?.catch((err) => logger.warn(`Hunk summary job failed for review ${sessionId}: ${err.message}`));
849
-
850
- tourGenerator.kickOffTourJob({
851
- db,
852
- config,
853
- reviewId: sessionId,
854
- diffText: diff,
855
- worktreePath: repoPath,
856
- reviewContext: { prTitle: branch },
857
- trigger: 'auto'
858
- })?.catch((err) => logger.warn(`Tour job failed for review ${sessionId}: ${err.message}`));
846
+ // Fire-and-forget summary/tour generation. Skipped for headless one-shot runs
847
+ // (startBackgroundJobs:false) — they would spend provider budget after the
848
+ // result is reported and keep the event loop alive, hanging the CLI.
849
+ if (startBackgroundJobs) {
850
+ summaryGenerator.kickOffSummaryJob({
851
+ db,
852
+ config,
853
+ reviewId: sessionId,
854
+ diffText: diff,
855
+ worktreePath: repoPath,
856
+ reviewContext: { prTitle: branch },
857
+ trigger: 'auto'
858
+ })?.catch((err) => logger.warn(`Hunk summary job failed for review ${sessionId}: ${err.message}`));
859
+
860
+ tourGenerator.kickOffTourJob({
861
+ db,
862
+ config,
863
+ reviewId: sessionId,
864
+ diffText: diff,
865
+ worktreePath: repoPath,
866
+ reviewContext: { prTitle: branch },
867
+ trigger: 'auto'
868
+ })?.catch((err) => logger.warn(`Tour job failed for review ${sessionId}: ${err.message}`));
869
+ }
859
870
 
860
871
  return {
861
872
  sessionId,
@@ -928,7 +939,18 @@ async function handleLocalReview(targetPath, flags = {}) {
928
939
  let url = `http://localhost:${port}/local/${session.sessionId}`;
929
940
  const shouldAnalyze = flags.ai || flags.council;
930
941
  if (shouldAnalyze) url += '?analyze=true';
931
- if (councilSelection) url += `&council=${councilSelection.id}`;
942
+ // When the run carries --instructions, stash the resolved analysis config and
943
+ // thread its id so the browser-side auto-analyze honors the instructions
944
+ // instead of silently dropping them (mirrors handlePullRequest). The id
945
+ // encodes the council selection, so prefer it over the bare &council= param.
946
+ const analysisConfigId = shouldAnalyze
947
+ ? await prepareInteractiveAnalysisConfig({ db, config, flags, repository: session.repository })
948
+ : null;
949
+ if (analysisConfigId) {
950
+ url += `&analysisConfigId=${analysisConfigId}`;
951
+ } else if (councilSelection) {
952
+ url += `&council=${councilSelection.id}`;
953
+ }
932
954
  console.log(`\nOpening browser to: ${url}`);
933
955
  await open(url);
934
956