panopticon-cli 0.4.25 → 0.4.27

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.
@@ -65663,6 +65663,91 @@ var init_agents = __esm({
65663
65663
  }
65664
65664
  });
65665
65665
 
65666
+ // review-status.ts
65667
+ var review_status_exports = {};
65668
+ __export(review_status_exports, {
65669
+ clearReviewStatus: () => clearReviewStatus,
65670
+ getReviewStatus: () => getReviewStatus,
65671
+ loadReviewStatuses: () => loadReviewStatuses,
65672
+ saveReviewStatuses: () => saveReviewStatuses,
65673
+ setReviewStatus: () => setReviewStatus
65674
+ });
65675
+ import { existsSync as existsSync19, readFileSync as readFileSync16, writeFileSync as writeFileSync12, mkdirSync as mkdirSync14 } from "fs";
65676
+ import { join as join20, dirname as dirname2 } from "path";
65677
+ import { homedir as homedir10 } from "os";
65678
+ function loadReviewStatuses(filePath = DEFAULT_STATUS_FILE) {
65679
+ try {
65680
+ if (existsSync19(filePath)) {
65681
+ return JSON.parse(readFileSync16(filePath, "utf-8"));
65682
+ }
65683
+ } catch (err) {
65684
+ console.error("Failed to load review statuses:", err);
65685
+ }
65686
+ return {};
65687
+ }
65688
+ function saveReviewStatuses(statuses, filePath = DEFAULT_STATUS_FILE) {
65689
+ try {
65690
+ const dir = dirname2(filePath);
65691
+ if (!existsSync19(dir)) {
65692
+ mkdirSync14(dir, { recursive: true });
65693
+ }
65694
+ writeFileSync12(filePath, JSON.stringify(statuses, null, 2));
65695
+ } catch (err) {
65696
+ console.error("Failed to save review statuses:", err);
65697
+ }
65698
+ }
65699
+ function setReviewStatus(issueId, update, filePath = DEFAULT_STATUS_FILE) {
65700
+ const statuses = loadReviewStatuses(filePath);
65701
+ const existing = statuses[issueId] || {
65702
+ issueId,
65703
+ reviewStatus: "pending",
65704
+ testStatus: "pending",
65705
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
65706
+ readyForMerge: false
65707
+ };
65708
+ const merged = { ...existing, ...update };
65709
+ const history = [...existing.history || []];
65710
+ const now = (/* @__PURE__ */ new Date()).toISOString();
65711
+ if (update.reviewStatus && update.reviewStatus !== existing.reviewStatus) {
65712
+ history.push({ type: "review", status: update.reviewStatus, timestamp: now, notes: update.reviewNotes });
65713
+ }
65714
+ if (update.testStatus && update.testStatus !== existing.testStatus) {
65715
+ history.push({ type: "test", status: update.testStatus, timestamp: now, notes: update.testNotes });
65716
+ }
65717
+ if (update.mergeStatus && update.mergeStatus !== existing.mergeStatus) {
65718
+ history.push({ type: "merge", status: update.mergeStatus, timestamp: now });
65719
+ }
65720
+ while (history.length > 10)
65721
+ history.shift();
65722
+ const readyForMerge = update.readyForMerge !== void 0 ? update.readyForMerge : merged.reviewStatus === "passed" && merged.testStatus === "passed" && merged.mergeStatus !== "merged";
65723
+ const updated = {
65724
+ ...merged,
65725
+ issueId,
65726
+ updatedAt: now,
65727
+ readyForMerge,
65728
+ history
65729
+ };
65730
+ statuses[issueId] = updated;
65731
+ saveReviewStatuses(statuses, filePath);
65732
+ return updated;
65733
+ }
65734
+ function getReviewStatus(issueId, filePath = DEFAULT_STATUS_FILE) {
65735
+ const statuses = loadReviewStatuses(filePath);
65736
+ return statuses[issueId] || null;
65737
+ }
65738
+ function clearReviewStatus(issueId, filePath = DEFAULT_STATUS_FILE) {
65739
+ const statuses = loadReviewStatuses(filePath);
65740
+ delete statuses[issueId];
65741
+ saveReviewStatuses(statuses, filePath);
65742
+ }
65743
+ var DEFAULT_STATUS_FILE;
65744
+ var init_review_status = __esm({
65745
+ "review-status.ts"() {
65746
+ "use strict";
65747
+ DEFAULT_STATUS_FILE = join20(homedir10(), ".panopticon", "review-status.json");
65748
+ }
65749
+ });
65750
+
65666
65751
  // ../../lib/cloister/specialists.ts
65667
65752
  var specialists_exports = {};
65668
65753
  __export(specialists_exports, {
@@ -65722,16 +65807,16 @@ __export(specialists_exports, {
65722
65807
  wakeSpecialistOrQueue: () => wakeSpecialistOrQueue,
65723
65808
  wakeSpecialistWithTask: () => wakeSpecialistWithTask
65724
65809
  });
65725
- import { readFileSync as readFileSync16, writeFileSync as writeFileSync12, existsSync as existsSync19, mkdirSync as mkdirSync14, readdirSync as readdirSync8, unlinkSync as unlinkSync6, appendFileSync as appendFileSync5 } from "fs";
65726
- import { join as join20, basename as basename3 } from "path";
65727
- import { homedir as homedir10 } from "os";
65810
+ import { readFileSync as readFileSync17, writeFileSync as writeFileSync13, existsSync as existsSync20, mkdirSync as mkdirSync15, readdirSync as readdirSync8, unlinkSync as unlinkSync6, appendFileSync as appendFileSync5 } from "fs";
65811
+ import { join as join21, basename as basename3 } from "path";
65812
+ import { homedir as homedir11 } from "os";
65728
65813
  import { exec as exec4 } from "child_process";
65729
65814
  import { promisify as promisify4 } from "util";
65730
65815
  function initSpecialistsDirectory() {
65731
- if (!existsSync19(SPECIALISTS_DIR)) {
65732
- mkdirSync14(SPECIALISTS_DIR, { recursive: true });
65816
+ if (!existsSync20(SPECIALISTS_DIR)) {
65817
+ mkdirSync15(SPECIALISTS_DIR, { recursive: true });
65733
65818
  }
65734
- if (!existsSync19(REGISTRY_FILE)) {
65819
+ if (!existsSync20(REGISTRY_FILE)) {
65735
65820
  const registry = {
65736
65821
  version: "2.0",
65737
65822
  // Updated for per-project structure
@@ -65755,7 +65840,7 @@ function initSpecialistsDirectory() {
65755
65840
  }
65756
65841
  function migrateRegistryIfNeeded() {
65757
65842
  try {
65758
- const content = readFileSync16(REGISTRY_FILE, "utf-8");
65843
+ const content = readFileSync17(REGISTRY_FILE, "utf-8");
65759
65844
  const registry = JSON.parse(content);
65760
65845
  if (registry.version === "2.0" || registry.projects) {
65761
65846
  return;
@@ -65785,7 +65870,7 @@ function migrateRegistryIfNeeded() {
65785
65870
  function loadRegistry() {
65786
65871
  initSpecialistsDirectory();
65787
65872
  try {
65788
- const content = readFileSync16(REGISTRY_FILE, "utf-8");
65873
+ const content = readFileSync17(REGISTRY_FILE, "utf-8");
65789
65874
  return JSON.parse(content);
65790
65875
  } catch (error) {
65791
65876
  console.error("Failed to load specialist registry:", error);
@@ -65803,28 +65888,28 @@ function loadRegistry() {
65803
65888
  }
65804
65889
  }
65805
65890
  function saveRegistry(registry) {
65806
- if (!existsSync19(SPECIALISTS_DIR)) {
65807
- mkdirSync14(SPECIALISTS_DIR, { recursive: true });
65891
+ if (!existsSync20(SPECIALISTS_DIR)) {
65892
+ mkdirSync15(SPECIALISTS_DIR, { recursive: true });
65808
65893
  }
65809
65894
  registry.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
65810
65895
  try {
65811
65896
  const content = JSON.stringify(registry, null, 2);
65812
- writeFileSync12(REGISTRY_FILE, content, "utf-8");
65897
+ writeFileSync13(REGISTRY_FILE, content, "utf-8");
65813
65898
  } catch (error) {
65814
65899
  console.error("Failed to save specialist registry:", error);
65815
65900
  throw error;
65816
65901
  }
65817
65902
  }
65818
65903
  function getSessionFilePath(name) {
65819
- return join20(SPECIALISTS_DIR, `${name}.session`);
65904
+ return join21(SPECIALISTS_DIR, `${name}.session`);
65820
65905
  }
65821
65906
  function getSessionId2(name) {
65822
65907
  const sessionFile = getSessionFilePath(name);
65823
- if (!existsSync19(sessionFile)) {
65908
+ if (!existsSync20(sessionFile)) {
65824
65909
  return null;
65825
65910
  }
65826
65911
  try {
65827
- return readFileSync16(sessionFile, "utf-8").trim();
65912
+ return readFileSync17(sessionFile, "utf-8").trim();
65828
65913
  } catch (error) {
65829
65914
  console.error(`Failed to read session file for ${name}:`, error);
65830
65915
  return null;
@@ -65834,7 +65919,7 @@ function setSessionId(name, sessionId) {
65834
65919
  initSpecialistsDirectory();
65835
65920
  const sessionFile = getSessionFilePath(name);
65836
65921
  try {
65837
- writeFileSync12(sessionFile, sessionId.trim(), "utf-8");
65922
+ writeFileSync13(sessionFile, sessionId.trim(), "utf-8");
65838
65923
  } catch (error) {
65839
65924
  console.error(`Failed to write session file for ${name}:`, error);
65840
65925
  throw error;
@@ -65842,7 +65927,7 @@ function setSessionId(name, sessionId) {
65842
65927
  }
65843
65928
  function clearSessionId(name) {
65844
65929
  const sessionFile = getSessionFilePath(name);
65845
- if (!existsSync19(sessionFile)) {
65930
+ if (!existsSync20(sessionFile)) {
65846
65931
  return false;
65847
65932
  }
65848
65933
  try {
@@ -65923,12 +66008,12 @@ async function spawnEphemeralSpecialist(projectKey, specialistType, task) {
65923
66008
  console.warn(`Warning: Could not resolve model for ${specialistType}, using default`);
65924
66009
  }
65925
66010
  const permissionFlags = specialistType === "merge-agent" ? "--dangerously-skip-permissions --permission-mode bypassPermissions" : "--dangerously-skip-permissions";
65926
- const agentDir = join20(homedir10(), ".panopticon", "agents", tmuxSession);
66011
+ const agentDir = join21(homedir11(), ".panopticon", "agents", tmuxSession);
65927
66012
  await execAsync4(`mkdir -p "${agentDir}"`, { encoding: "utf-8" });
65928
- const promptFile = join20(agentDir, "task-prompt.md");
65929
- writeFileSync12(promptFile, taskPrompt);
65930
- const launcherScript = join20(agentDir, "launcher.sh");
65931
- writeFileSync12(launcherScript, `#!/bin/bash
66013
+ const promptFile = join21(agentDir, "task-prompt.md");
66014
+ writeFileSync13(promptFile, taskPrompt);
66015
+ const launcherScript = join21(agentDir, "launcher.sh");
66016
+ writeFileSync13(launcherScript, `#!/bin/bash
65932
66017
  cd "${cwd}"
65933
66018
  prompt=$(cat "${promptFile}")
65934
66019
 
@@ -66005,6 +66090,8 @@ ${customPrompt}
66005
66090
  switch (specialistType) {
66006
66091
  case "review-agent":
66007
66092
  prompt += `Your task:
66093
+ 0. FIRST: Check if branch has any changes vs main (git diff --name-only main...HEAD)
66094
+ - If 0 files changed: mark as passed with note "branch identical to main" and STOP
66008
66095
  1. Review all changes in the branch
66009
66096
  2. Check for code quality issues, security concerns, and best practices
66010
66097
  3. Verify test FILES exist for new code (DO NOT run tests)
@@ -66014,6 +66101,7 @@ ${customPrompt}
66014
66101
  IMPORTANT: DO NOT run tests. You are the REVIEW agent.
66015
66102
 
66016
66103
  Update status via API:
66104
+ - If no changes (stale branch): POST to /api/workspaces/${task.issueId}/review-status with {"reviewStatus":"passed","reviewNotes":"No changes \u2014 branch identical to main"}
66017
66105
  - If issues found: POST to /api/workspaces/${task.issueId}/review-status with {"reviewStatus":"blocked","reviewNotes":"..."}
66018
66106
  - If review passes: POST with {"reviewStatus":"passed"} then queue test-agent`;
66019
66107
  break;
@@ -66166,17 +66254,17 @@ function scheduleLogCleanup(projectKey, specialistType) {
66166
66254
  });
66167
66255
  }
66168
66256
  function getProjectSpecialistDir(projectKey, specialistType) {
66169
- return join20(SPECIALISTS_DIR, projectKey, specialistType);
66257
+ return join21(SPECIALISTS_DIR, projectKey, specialistType);
66170
66258
  }
66171
66259
  function ensureProjectSpecialistDir(projectKey, specialistType) {
66172
66260
  const specialistDir = getProjectSpecialistDir(projectKey, specialistType);
66173
- const runsDir = join20(specialistDir, "runs");
66174
- const contextDir = join20(specialistDir, "context");
66175
- if (!existsSync19(runsDir)) {
66176
- mkdirSync14(runsDir, { recursive: true });
66261
+ const runsDir = join21(specialistDir, "runs");
66262
+ const contextDir = join21(specialistDir, "context");
66263
+ if (!existsSync20(runsDir)) {
66264
+ mkdirSync15(runsDir, { recursive: true });
66177
66265
  }
66178
- if (!existsSync19(contextDir)) {
66179
- mkdirSync14(contextDir, { recursive: true });
66266
+ if (!existsSync20(contextDir)) {
66267
+ mkdirSync15(contextDir, { recursive: true });
66180
66268
  }
66181
66269
  }
66182
66270
  function getProjectSpecialistMetadata(projectKey, specialistType) {
@@ -66405,12 +66493,12 @@ Your role: ${name === "merge-agent" ? "Resolve merge conflicts and ensure clean
66405
66493
  You will be woken up when your services are needed. For now, acknowledge your initialization and wait.
66406
66494
  Say: "I am the ${name} specialist, ready and waiting for tasks."`;
66407
66495
  try {
66408
- const agentDir = join20(homedir10(), ".panopticon", "agents", tmuxSession);
66496
+ const agentDir = join21(homedir11(), ".panopticon", "agents", tmuxSession);
66409
66497
  await execAsync4(`mkdir -p "${agentDir}"`, { encoding: "utf-8" });
66410
- const promptFile = join20(agentDir, "identity-prompt.md");
66411
- const launcherScript = join20(agentDir, "launcher.sh");
66412
- writeFileSync12(promptFile, identityPrompt);
66413
- writeFileSync12(launcherScript, `#!/bin/bash
66498
+ const promptFile = join21(agentDir, "identity-prompt.md");
66499
+ const launcherScript = join21(agentDir, "launcher.sh");
66500
+ writeFileSync13(promptFile, identityPrompt);
66501
+ writeFileSync13(launcherScript, `#!/bin/bash
66414
66502
  cd "${cwd}"
66415
66503
  prompt=$(cat "${promptFile}")
66416
66504
  exec claude --dangerously-skip-permissions --model ${model} "$prompt"
@@ -66513,11 +66601,11 @@ async function wakeSpecialist(name, taskPrompt, options = {}) {
66513
66601
  try {
66514
66602
  const isLargePrompt = taskPrompt.length > 500 || taskPrompt.includes("\n");
66515
66603
  if (isLargePrompt) {
66516
- if (!existsSync19(TASKS_DIR)) {
66517
- mkdirSync14(TASKS_DIR, { recursive: true });
66604
+ if (!existsSync20(TASKS_DIR)) {
66605
+ mkdirSync15(TASKS_DIR, { recursive: true });
66518
66606
  }
66519
- const taskFile = join20(TASKS_DIR, `${name}-${Date.now()}.md`);
66520
- writeFileSync12(taskFile, taskPrompt, "utf-8");
66607
+ const taskFile = join21(TASKS_DIR, `${name}-${Date.now()}.md`);
66608
+ writeFileSync13(taskFile, taskPrompt, "utf-8");
66521
66609
  const shortMessage = `Read and execute the task in: ${taskFile}`;
66522
66610
  sendKeys(tmuxSession, shortMessage);
66523
66611
  } else {
@@ -66574,11 +66662,41 @@ When done, provide feedback on:
66574
66662
 
66575
66663
  Use the send-feedback-to-agent skill to report findings back to the issue agent.`;
66576
66664
  break;
66577
- case "review-agent":
66665
+ case "review-agent": {
66666
+ const workspace = task.workspace || "unknown";
66667
+ let staleBranch = false;
66668
+ if (workspace !== "unknown") {
66669
+ try {
66670
+ const { stdout: diffOutput } = await execAsync4(
66671
+ `cd "${workspace}" && git fetch origin main 2>/dev/null; git diff --name-only main...HEAD 2>/dev/null`,
66672
+ { encoding: "utf-8", timeout: 15e3 }
66673
+ );
66674
+ const changedFiles = diffOutput.trim().split("\n").filter((f) => f.length > 0);
66675
+ if (changedFiles.length === 0) {
66676
+ staleBranch = true;
66677
+ console.log(`[specialist] review-agent: stale branch detected for ${task.issueId} \u2014 0 files changed vs main`);
66678
+ const { setReviewStatus: setReviewStatus3 } = await Promise.resolve().then(() => (init_review_status(), review_status_exports));
66679
+ setReviewStatus3(task.issueId.toUpperCase(), {
66680
+ reviewStatus: "passed",
66681
+ reviewNotes: "No changes to review \u2014 branch identical to main (already merged or stale)"
66682
+ });
66683
+ console.log(`[specialist] review-agent: auto-passed ${task.issueId} (stale branch)`);
66684
+ const tmuxSession = getTmuxSessionName("review-agent");
66685
+ const { saveAgentRuntimeState: saveAgentRuntimeState2 } = await Promise.resolve().then(() => (init_agents(), agents_exports));
66686
+ saveAgentRuntimeState2(tmuxSession, {
66687
+ state: "idle",
66688
+ lastActivity: (/* @__PURE__ */ new Date()).toISOString()
66689
+ });
66690
+ return { success: true, message: `Stale branch auto-passed for ${task.issueId}`, wasAlreadyRunning: false, error: void 0 };
66691
+ }
66692
+ } catch (err) {
66693
+ console.warn(`[specialist] review-agent: stale branch pre-check failed for ${task.issueId}:`, err);
66694
+ }
66695
+ }
66578
66696
  prompt = `New review task for ${task.issueId}:
66579
66697
 
66580
66698
  Branch: ${task.branch || "unknown"}
66581
- Workspace: ${task.workspace || "unknown"}
66699
+ Workspace: ${workspace}
66582
66700
  ${task.prUrl ? `PR URL: ${task.prUrl}` : ""}
66583
66701
 
66584
66702
  Your task:
@@ -66592,9 +66710,26 @@ The TEST agent will run tests in the next step.
66592
66710
 
66593
66711
  ## How to Review Changes
66594
66712
 
66713
+ **Step 0 (CRITICAL):** First check if there are ANY changes to review:
66714
+ \`\`\`bash
66715
+ cd ${workspace} && git diff --name-only main...HEAD
66716
+ \`\`\`
66717
+
66718
+ **If the diff is EMPTY (0 files changed):** The branch is stale or already merged into main. In this case:
66719
+ 1. Do NOT attempt a full review
66720
+ 2. Update status as passed immediately:
66721
+ \`\`\`bash
66722
+ curl -s -X POST ${apiUrl}/api/workspaces/${task.issueId}/review-status -H "Content-Type: application/json" -d '{"reviewStatus":"passed","reviewNotes":"No changes to review \u2014 branch identical to main (already merged or stale)"}' | jq .
66723
+ \`\`\`
66724
+ 3. Tell the issue agent:
66725
+ \`\`\`bash
66726
+ pan work tell ${task.issueId} "Review complete: branch has 0 diff from main \u2014 already merged or stale. Marking as passed."
66727
+ \`\`\`
66728
+ 4. Stop here \u2014 you are done.
66729
+
66595
66730
  **Step 1:** Get the list of changed files:
66596
66731
  \`\`\`bash
66597
- cd ${task.workspace || "unknown"} && git diff --name-only main...HEAD
66732
+ cd ${workspace} && git diff --name-only main...HEAD
66598
66733
  \`\`\`
66599
66734
 
66600
66735
  **Step 2:** Read the CURRENT version of each changed file using the Read tool.
@@ -66602,7 +66737,7 @@ Review the actual file contents \u2014 do NOT rely solely on diff output.
66602
66737
 
66603
66738
  **Step 3:** If you need to see what specifically changed, use:
66604
66739
  \`\`\`bash
66605
- cd ${task.workspace || "unknown"} && git diff main...HEAD -- <file>
66740
+ cd ${workspace} && git diff main...HEAD -- <file>
66606
66741
  \`\`\`
66607
66742
 
66608
66743
  ## Avoiding False Positives
@@ -66642,6 +66777,7 @@ curl -s -X POST ${apiUrl}/api/specialists/test-agent/queue -H "Content-Type: app
66642
66777
 
66643
66778
  \u26A0\uFE0F VERIFICATION: After running each curl, confirm you see valid JSON output. If you get an error, report it.`;
66644
66779
  break;
66780
+ }
66645
66781
  case "test-agent":
66646
66782
  prompt = `New test task for ${task.issueId}:
66647
66783
 
@@ -66827,8 +66963,8 @@ function getNextSpecialistTask(specialistName) {
66827
66963
  }
66828
66964
  async function sendFeedbackToAgent(feedback) {
66829
66965
  const { fromSpecialist, toIssueId, summary, details } = feedback;
66830
- if (!existsSync19(FEEDBACK_DIR)) {
66831
- mkdirSync14(FEEDBACK_DIR, { recursive: true });
66966
+ if (!existsSync20(FEEDBACK_DIR)) {
66967
+ mkdirSync15(FEEDBACK_DIR, { recursive: true });
66832
66968
  }
66833
66969
  const fullFeedback = {
66834
66970
  ...feedback,
@@ -66901,11 +67037,11 @@ ${details}
66901
67037
  return message;
66902
67038
  }
66903
67039
  function getPendingFeedback(issueId) {
66904
- if (!existsSync19(FEEDBACK_LOG)) {
67040
+ if (!existsSync20(FEEDBACK_LOG)) {
66905
67041
  return [];
66906
67042
  }
66907
67043
  try {
66908
- const content = readFileSync16(FEEDBACK_LOG, "utf-8");
67044
+ const content = readFileSync17(FEEDBACK_LOG, "utf-8");
66909
67045
  const lines = content.trim().split("\n").filter((l) => l.length > 0);
66910
67046
  const allFeedback = lines.map((line) => JSON.parse(line));
66911
67047
  return allFeedback.filter((f) => f.toIssueId.toLowerCase() === issueId.toLowerCase());
@@ -66924,11 +67060,11 @@ function getFeedbackStats() {
66924
67060
  byType: {},
66925
67061
  total: 0
66926
67062
  };
66927
- if (!existsSync19(FEEDBACK_LOG)) {
67063
+ if (!existsSync20(FEEDBACK_LOG)) {
66928
67064
  return stats;
66929
67065
  }
66930
67066
  try {
66931
- const content = readFileSync16(FEEDBACK_LOG, "utf-8");
67067
+ const content = readFileSync17(FEEDBACK_LOG, "utf-8");
66932
67068
  const lines = content.trim().split("\n").filter((l) => l.length > 0);
66933
67069
  for (const line of lines) {
66934
67070
  const feedback = JSON.parse(line);
@@ -66952,9 +67088,9 @@ var init_specialists = __esm({
66952
67088
  init_tmux();
66953
67089
  init_hooks();
66954
67090
  execAsync4 = promisify4(exec4);
66955
- SPECIALISTS_DIR = join20(PANOPTICON_HOME2, "specialists");
66956
- REGISTRY_FILE = join20(SPECIALISTS_DIR, "registry.json");
66957
- TASKS_DIR = join20(SPECIALISTS_DIR, "tasks");
67091
+ SPECIALISTS_DIR = join21(PANOPTICON_HOME2, "specialists");
67092
+ REGISTRY_FILE = join21(SPECIALISTS_DIR, "registry.json");
67093
+ TASKS_DIR = join21(SPECIALISTS_DIR, "tasks");
66958
67094
  DEFAULT_SPECIALISTS = [
66959
67095
  {
66960
67096
  name: "merge-agent",
@@ -66979,16 +67115,16 @@ var init_specialists = __esm({
66979
67115
  }
66980
67116
  ];
66981
67117
  gracePeriodStates = /* @__PURE__ */ new Map();
66982
- FEEDBACK_DIR = join20(PANOPTICON_HOME2, "specialists", "feedback");
66983
- FEEDBACK_LOG = join20(FEEDBACK_DIR, "feedback.jsonl");
67118
+ FEEDBACK_DIR = join21(PANOPTICON_HOME2, "specialists", "feedback");
67119
+ FEEDBACK_LOG = join21(FEEDBACK_DIR, "feedback.jsonl");
66984
67120
  }
66985
67121
  });
66986
67122
 
66987
67123
  // ../../lib/cloister/validation.ts
66988
67124
  import { exec as exec8 } from "child_process";
66989
67125
  import { promisify as promisify8 } from "util";
66990
- import { join as join31 } from "path";
66991
- import { existsSync as existsSync30 } from "fs";
67126
+ import { join as join32 } from "path";
67127
+ import { existsSync as existsSync31 } from "fs";
66992
67128
  function parseValidationOutput(output, exitCode) {
66993
67129
  const lines = output.split("\n");
66994
67130
  const failures = [];
@@ -67075,8 +67211,8 @@ function parseValidationOutput(output, exitCode) {
67075
67211
  }
67076
67212
  async function runMergeValidation(context) {
67077
67213
  const { projectPath, validationScript } = context;
67078
- const scriptPath = validationScript || join31(projectPath, "scripts", "validate-merge.sh");
67079
- if (!existsSync30(scriptPath)) {
67214
+ const scriptPath = validationScript || join32(projectPath, "scripts", "validate-merge.sh");
67215
+ if (!existsSync31(scriptPath)) {
67080
67216
  return {
67081
67217
  success: false,
67082
67218
  valid: false,
@@ -67148,14 +67284,14 @@ var init_validation = __esm({
67148
67284
  });
67149
67285
 
67150
67286
  // ../../lib/git-utils.ts
67151
- import { existsSync as existsSync31, unlinkSync as unlinkSync10, readdirSync as readdirSync12 } from "fs";
67152
- import { join as join32 } from "path";
67287
+ import { existsSync as existsSync32, unlinkSync as unlinkSync10, readdirSync as readdirSync12 } from "fs";
67288
+ import { join as join33 } from "path";
67153
67289
  import { exec as exec9 } from "child_process";
67154
67290
  import { promisify as promisify9 } from "util";
67155
67291
  async function hasRunningGitProcesses(repoPath) {
67156
67292
  try {
67157
67293
  try {
67158
- const gitDir = join32(repoPath, ".git");
67294
+ const gitDir = join33(repoPath, ".git");
67159
67295
  const { stdout } = await execAsync9(`fuser "${gitDir}" 2>/dev/null`, {
67160
67296
  encoding: "utf-8"
67161
67297
  });
@@ -67177,16 +67313,16 @@ async function hasRunningGitProcesses(repoPath) {
67177
67313
  }
67178
67314
  function findGitLockFiles(repoPath) {
67179
67315
  const lockFiles = [];
67180
- const indexLock = join32(repoPath, ".git", "index.lock");
67181
- if (existsSync31(indexLock)) {
67316
+ const indexLock = join33(repoPath, ".git", "index.lock");
67317
+ if (existsSync32(indexLock)) {
67182
67318
  lockFiles.push(indexLock);
67183
67319
  }
67184
- const refsDir = join32(repoPath, ".git", "refs");
67185
- if (existsSync31(refsDir)) {
67320
+ const refsDir = join33(repoPath, ".git", "refs");
67321
+ if (existsSync32(refsDir)) {
67186
67322
  const findLocksRecursive = (dir) => {
67187
67323
  const entries = readdirSync12(dir, { withFileTypes: true });
67188
67324
  for (const entry of entries) {
67189
- const fullPath = join32(dir, entry.name);
67325
+ const fullPath = join33(dir, entry.name);
67190
67326
  if (entry.isDirectory()) {
67191
67327
  findLocksRecursive(fullPath);
67192
67328
  } else if (entry.name.endsWith(".lock")) {
@@ -67245,17 +67381,17 @@ __export(merge_agent_exports, {
67245
67381
  spawnMergeAgent: () => spawnMergeAgent,
67246
67382
  spawnMergeAgentForBranches: () => spawnMergeAgentForBranches
67247
67383
  });
67248
- import { readFileSync as readFileSync25, existsSync as existsSync32, mkdirSync as mkdirSync21, appendFileSync as appendFileSync7 } from "fs";
67249
- import { join as join33, dirname as dirname4 } from "path";
67384
+ import { readFileSync as readFileSync26, existsSync as existsSync33, mkdirSync as mkdirSync22, appendFileSync as appendFileSync7 } from "fs";
67385
+ import { join as join34, dirname as dirname5 } from "path";
67250
67386
  import { fileURLToPath as fileURLToPath2 } from "url";
67251
67387
  import { exec as exec10 } from "child_process";
67252
67388
  import { promisify as promisify10 } from "util";
67253
67389
  function buildMergePrompt(context) {
67254
- const templatePath = join33(__dirname2, "prompts", "merge-agent.md");
67255
- if (!existsSync32(templatePath)) {
67390
+ const templatePath = join34(__dirname2, "prompts", "merge-agent.md");
67391
+ if (!existsSync33(templatePath)) {
67256
67392
  throw new Error(`Merge agent prompt template not found at ${templatePath}`);
67257
67393
  }
67258
- const template = readFileSync25(templatePath, "utf-8");
67394
+ const template = readFileSync26(templatePath, "utf-8");
67259
67395
  const prompt = template.replace(/\{\{projectPath\}\}/g, context.projectPath).replace(/\{\{sourceBranch\}\}/g, context.sourceBranch).replace(/\{\{targetBranch\}\}/g, context.targetBranch).replace(/\{\{issueId\}\}/g, context.issueId).replace(
67260
67396
  /\{\{conflictFiles\}\}/g,
67261
67397
  context.conflictFiles.map((f) => ` - ${f}`).join("\n")
@@ -67263,23 +67399,23 @@ function buildMergePrompt(context) {
67263
67399
  return prompt;
67264
67400
  }
67265
67401
  function detectTestCommand(projectPath) {
67266
- const packageJsonPath = join33(projectPath, "package.json");
67267
- if (existsSync32(packageJsonPath)) {
67402
+ const packageJsonPath = join34(projectPath, "package.json");
67403
+ if (existsSync33(packageJsonPath)) {
67268
67404
  try {
67269
- const packageJson = JSON.parse(readFileSync25(packageJsonPath, "utf-8"));
67405
+ const packageJson = JSON.parse(readFileSync26(packageJsonPath, "utf-8"));
67270
67406
  if (packageJson.scripts?.test) {
67271
67407
  return "npm test";
67272
67408
  }
67273
67409
  } catch {
67274
67410
  }
67275
67411
  }
67276
- if (existsSync32(join33(projectPath, "pom.xml"))) {
67412
+ if (existsSync33(join34(projectPath, "pom.xml"))) {
67277
67413
  return "mvn test";
67278
67414
  }
67279
- if (existsSync32(join33(projectPath, "Cargo.toml"))) {
67415
+ if (existsSync33(join34(projectPath, "Cargo.toml"))) {
67280
67416
  return "cargo test";
67281
67417
  }
67282
- if (existsSync32(join33(projectPath, "pytest.ini")) || existsSync32(join33(projectPath, "setup.py"))) {
67418
+ if (existsSync33(join34(projectPath, "pytest.ini")) || existsSync33(join34(projectPath, "setup.py"))) {
67283
67419
  return "pytest";
67284
67420
  }
67285
67421
  return "skip";
@@ -67293,8 +67429,8 @@ async function conditionalBeadsCompaction(projectPath) {
67293
67429
  console.log(`[merge-agent] bd not available, skipping compaction`);
67294
67430
  return;
67295
67431
  }
67296
- const beadsDir = join33(projectPath, ".beads");
67297
- if (!existsSync32(beadsDir)) {
67432
+ const beadsDir = join34(projectPath, ".beads");
67433
+ if (!existsSync33(beadsDir)) {
67298
67434
  console.log(`[merge-agent] No .beads directory, skipping compaction`);
67299
67435
  return;
67300
67436
  }
@@ -67331,12 +67467,12 @@ async function conditionalBeadsCompaction(projectPath) {
67331
67467
  async function postMergeCleanup(issueId, projectPath) {
67332
67468
  console.log(`[merge-agent] Running post-merge cleanup for ${issueId}`);
67333
67469
  try {
67334
- const activePrdPath = join33(projectPath, "docs/prds/active", `${issueId.toLowerCase()}-plan.md`);
67335
- const completedPrdPath = join33(projectPath, "docs/prds/completed", `${issueId.toLowerCase()}-plan.md`);
67336
- if (existsSync32(activePrdPath)) {
67337
- const completedDir = dirname4(completedPrdPath);
67338
- if (!existsSync32(completedDir)) {
67339
- mkdirSync21(completedDir, { recursive: true });
67470
+ const activePrdPath = join34(projectPath, "docs/prds/active", `${issueId.toLowerCase()}-plan.md`);
67471
+ const completedPrdPath = join34(projectPath, "docs/prds/completed", `${issueId.toLowerCase()}-plan.md`);
67472
+ if (existsSync33(activePrdPath)) {
67473
+ const completedDir = dirname5(completedPrdPath);
67474
+ if (!existsSync33(completedDir)) {
67475
+ mkdirSync22(completedDir, { recursive: true });
67340
67476
  }
67341
67477
  await execAsync10(`git mv "${activePrdPath}" "${completedPrdPath}"`, { cwd: projectPath, encoding: "utf-8" });
67342
67478
  await execAsync10(`git commit -m "Move ${issueId} PRD to completed"`, { cwd: projectPath, encoding: "utf-8" });
@@ -67521,8 +67657,8 @@ function parseAgentOutput(output) {
67521
67657
  }
67522
67658
  }
67523
67659
  function logMergeHistory(context, result, sessionId) {
67524
- if (!existsSync32(MERGE_HISTORY_DIR)) {
67525
- mkdirSync21(MERGE_HISTORY_DIR, { recursive: true });
67660
+ if (!existsSync33(MERGE_HISTORY_DIR)) {
67661
+ mkdirSync22(MERGE_HISTORY_DIR, { recursive: true });
67526
67662
  }
67527
67663
  const entry = {
67528
67664
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -67977,10 +68113,10 @@ var init_merge_agent = __esm({
67977
68113
  init_git_utils();
67978
68114
  execAsync10 = promisify10(exec10);
67979
68115
  __filename = fileURLToPath2(import.meta.url);
67980
- __dirname2 = dirname4(__filename);
67981
- SPECIALISTS_DIR2 = join33(PANOPTICON_HOME2, "specialists");
67982
- MERGE_HISTORY_DIR = join33(SPECIALISTS_DIR2, "merge-agent");
67983
- MERGE_HISTORY_FILE = join33(MERGE_HISTORY_DIR, "history.jsonl");
68116
+ __dirname2 = dirname5(__filename);
68117
+ SPECIALISTS_DIR2 = join34(PANOPTICON_HOME2, "specialists");
68118
+ MERGE_HISTORY_DIR = join34(SPECIALISTS_DIR2, "merge-agent");
68119
+ MERGE_HISTORY_FILE = join34(MERGE_HISTORY_DIR, "history.jsonl");
67984
68120
  MERGE_TIMEOUT_MS = 15 * 60 * 1e3;
67985
68121
  }
67986
68122
  });
@@ -85987,10 +86123,10 @@ init_specialists();
85987
86123
  init_agents();
85988
86124
  init_tmux();
85989
86125
  init_jsonl_parser();
85990
- import { existsSync as existsSync20, readFileSync as readFileSync17, statSync as statSync4, mkdirSync as mkdirSync15, writeFileSync as writeFileSync13 } from "fs";
85991
- import { join as join21 } from "path";
85992
- import { homedir as homedir11 } from "os";
85993
- var CLAUDE_PROJECTS_DIR2 = join21(homedir11(), ".claude", "projects");
86126
+ import { existsSync as existsSync21, readFileSync as readFileSync18, statSync as statSync4, mkdirSync as mkdirSync16, writeFileSync as writeFileSync14 } from "fs";
86127
+ import { join as join22 } from "path";
86128
+ import { homedir as homedir12 } from "os";
86129
+ var CLAUDE_PROJECTS_DIR2 = join22(homedir12(), ".claude", "projects");
85994
86130
  var ClaudeCodeRuntime = class {
85995
86131
  name = "claude-code";
85996
86132
  /**
@@ -86000,15 +86136,15 @@ var ClaudeCodeRuntime = class {
86000
86136
  * We need to find the project directory that contains sessions for this workspace.
86001
86137
  */
86002
86138
  getProjectDirForWorkspace(workspace) {
86003
- if (!existsSync20(CLAUDE_PROJECTS_DIR2)) {
86139
+ if (!existsSync21(CLAUDE_PROJECTS_DIR2)) {
86004
86140
  return null;
86005
86141
  }
86006
86142
  const projectDirs = getProjectDirs();
86007
86143
  for (const projectDir of projectDirs) {
86008
- const indexPath = join21(projectDir, "sessions-index.json");
86009
- if (existsSync20(indexPath)) {
86144
+ const indexPath = join22(projectDir, "sessions-index.json");
86145
+ if (existsSync21(indexPath)) {
86010
86146
  try {
86011
- const indexContent = readFileSync17(indexPath, "utf-8");
86147
+ const indexContent = readFileSync18(indexPath, "utf-8");
86012
86148
  if (indexContent.includes(workspace)) {
86013
86149
  return projectDir;
86014
86150
  }
@@ -86022,12 +86158,12 @@ var ClaudeCodeRuntime = class {
86022
86158
  * Get the active session ID for an agent from the sessions index
86023
86159
  */
86024
86160
  getActiveSessionId(projectDir) {
86025
- const indexPath = join21(projectDir, "sessions-index.json");
86026
- if (!existsSync20(indexPath)) {
86161
+ const indexPath = join22(projectDir, "sessions-index.json");
86162
+ if (!existsSync21(indexPath)) {
86027
86163
  return null;
86028
86164
  }
86029
86165
  try {
86030
- const indexContent = readFileSync17(indexPath, "utf-8");
86166
+ const indexContent = readFileSync18(indexPath, "utf-8");
86031
86167
  const index = JSON.parse(indexContent);
86032
86168
  if (index.sessions && Array.isArray(index.sessions)) {
86033
86169
  const sessions = index.sessions;
@@ -86063,8 +86199,8 @@ var ClaudeCodeRuntime = class {
86063
86199
  }
86064
86200
  const sessionId = this.getActiveSessionId(projectDir);
86065
86201
  if (sessionId) {
86066
- const sessionPath = join21(projectDir, `${sessionId}.jsonl`);
86067
- if (existsSync20(sessionPath)) {
86202
+ const sessionPath = join22(projectDir, `${sessionId}.jsonl`);
86203
+ if (existsSync21(sessionPath)) {
86068
86204
  return sessionPath;
86069
86205
  }
86070
86206
  }
@@ -86077,7 +86213,7 @@ var ClaudeCodeRuntime = class {
86077
86213
  */
86078
86214
  getLastActivity(agentId) {
86079
86215
  const sessionPath = this.getSessionPath(agentId);
86080
- if (!sessionPath || !existsSync20(sessionPath)) {
86216
+ if (!sessionPath || !existsSync21(sessionPath)) {
86081
86217
  return null;
86082
86218
  }
86083
86219
  try {
@@ -86091,12 +86227,12 @@ var ClaudeCodeRuntime = class {
86091
86227
  * Read active heartbeat file if it exists
86092
86228
  */
86093
86229
  getActiveHeartbeat(agentId) {
86094
- const heartbeatPath = join21(homedir11(), ".panopticon", "heartbeats", `${agentId}.json`);
86095
- if (!existsSync20(heartbeatPath)) {
86230
+ const heartbeatPath = join22(homedir12(), ".panopticon", "heartbeats", `${agentId}.json`);
86231
+ if (!existsSync21(heartbeatPath)) {
86096
86232
  return null;
86097
86233
  }
86098
86234
  try {
86099
- const content = readFileSync17(heartbeatPath, "utf-8");
86235
+ const content = readFileSync18(heartbeatPath, "utf-8");
86100
86236
  const data = JSON.parse(content);
86101
86237
  const timestamp2 = new Date(data.timestamp);
86102
86238
  const now = /* @__PURE__ */ new Date();
@@ -86200,11 +86336,11 @@ var ClaudeCodeRuntime = class {
86200
86336
  throw new Error(`Agent ${agentId} is not running`);
86201
86337
  }
86202
86338
  sendKeys(agentId, message);
86203
- const mailDir = join21(getAgentDir(agentId), "mail");
86204
- mkdirSync15(mailDir, { recursive: true });
86339
+ const mailDir = join22(getAgentDir(agentId), "mail");
86340
+ mkdirSync16(mailDir, { recursive: true });
86205
86341
  const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
86206
- writeFileSync13(
86207
- join21(mailDir, `${timestamp2}.md`),
86342
+ writeFileSync14(
86343
+ join22(mailDir, `${timestamp2}.md`),
86208
86344
  `# Message
86209
86345
 
86210
86346
  ${message}
@@ -86375,8 +86511,8 @@ init_agents();
86375
86511
 
86376
86512
  // ../../lib/cloister/triggers.ts
86377
86513
  init_config();
86378
- import { existsSync as existsSync21 } from "fs";
86379
- import { join as join22 } from "path";
86514
+ import { existsSync as existsSync22 } from "fs";
86515
+ import { join as join23 } from "path";
86380
86516
  import { exec as exec5 } from "child_process";
86381
86517
  import { promisify as promisify5 } from "util";
86382
86518
  var execAsync5 = promisify5(exec5);
@@ -86468,8 +86604,8 @@ async function checkPlanningComplete(agentId, workspace, issueId, config2) {
86468
86604
  }
86469
86605
  } catch (error) {
86470
86606
  }
86471
- const prdPath = join22(workspace, `docs/prds/active/${issueId.toLowerCase()}-plan.md`);
86472
- if (existsSync21(prdPath)) {
86607
+ const prdPath = join23(workspace, `docs/prds/active/${issueId.toLowerCase()}-plan.md`);
86608
+ if (existsSync22(prdPath)) {
86473
86609
  signals.push("PRD file exists");
86474
86610
  signalCount++;
86475
86611
  }
@@ -86616,12 +86752,12 @@ async function checkAllTriggers(agentId, workspace, issueId, currentModel, healt
86616
86752
 
86617
86753
  // ../../lib/cloister/handoff.ts
86618
86754
  init_agents();
86619
- import { writeFileSync as writeFileSync14, mkdirSync as mkdirSync16 } from "fs";
86620
- import { join as join24 } from "path";
86755
+ import { writeFileSync as writeFileSync15, mkdirSync as mkdirSync17 } from "fs";
86756
+ import { join as join25 } from "path";
86621
86757
 
86622
86758
  // ../../lib/cloister/handoff-context.ts
86623
- import { existsSync as existsSync22, readFileSync as readFileSync18 } from "fs";
86624
- import { join as join23 } from "path";
86759
+ import { existsSync as existsSync23, readFileSync as readFileSync19 } from "fs";
86760
+ import { join as join24 } from "path";
86625
86761
  import { exec as exec6 } from "child_process";
86626
86762
  import { promisify as promisify6 } from "util";
86627
86763
  var execAsync6 = promisify6(exec6);
@@ -86645,13 +86781,13 @@ async function captureHandoffContext(agentState, targetModel, reason) {
86645
86781
  }
86646
86782
  async function captureFiles(context, workspace) {
86647
86783
  try {
86648
- const stateFile = join23(workspace, ".planning/STATE.md");
86649
- if (existsSync22(stateFile)) {
86650
- context.stateFile = readFileSync18(stateFile, "utf-8");
86784
+ const stateFile = join24(workspace, ".planning/STATE.md");
86785
+ if (existsSync23(stateFile)) {
86786
+ context.stateFile = readFileSync19(stateFile, "utf-8");
86651
86787
  }
86652
- const claudeMd = join23(workspace, "CLAUDE.md");
86653
- if (existsSync22(claudeMd)) {
86654
- context.claudeMd = readFileSync18(claudeMd, "utf-8");
86788
+ const claudeMd = join24(workspace, "CLAUDE.md");
86789
+ if (existsSync23(claudeMd)) {
86790
+ context.claudeMd = readFileSync19(claudeMd, "utf-8");
86655
86791
  }
86656
86792
  } catch (error) {
86657
86793
  console.error("Error capturing files:", error);
@@ -86842,10 +86978,10 @@ async function performKillAndSpawn(state, options) {
86842
86978
  const context = await captureHandoffContext(state, options.targetModel, options.reason);
86843
86979
  stopAgent(state.id);
86844
86980
  const prompt = buildHandoffPrompt(context, options.additionalInstructions);
86845
- const handoffDir = join24(getAgentDir(state.id), "handoffs");
86846
- mkdirSync16(handoffDir, { recursive: true });
86847
- const handoffFile = join24(handoffDir, `handoff-${Date.now()}.md`);
86848
- writeFileSync14(handoffFile, prompt);
86981
+ const handoffDir = join25(getAgentDir(state.id), "handoffs");
86982
+ mkdirSync17(handoffDir, { recursive: true });
86983
+ const handoffFile = join25(handoffDir, `handoff-${Date.now()}.md`);
86984
+ writeFileSync15(handoffFile, prompt);
86849
86985
  const newState = await spawnAgent({
86850
86986
  issueId: state.issueId,
86851
86987
  workspace: state.workspace,
@@ -86951,13 +87087,13 @@ function sleep(ms) {
86951
87087
 
86952
87088
  // ../../lib/cloister/handoff-logger.ts
86953
87089
  init_paths();
86954
- import { existsSync as existsSync24, mkdirSync as mkdirSync17, appendFileSync as appendFileSync6, readFileSync as readFileSync19, writeFileSync as writeFileSync15 } from "fs";
86955
- import { join as join25 } from "path";
86956
- var HANDOFF_LOG_FILE = join25(PANOPTICON_HOME2, "logs", "handoffs.jsonl");
87090
+ import { existsSync as existsSync25, mkdirSync as mkdirSync18, appendFileSync as appendFileSync6, readFileSync as readFileSync20, writeFileSync as writeFileSync16 } from "fs";
87091
+ import { join as join26 } from "path";
87092
+ var HANDOFF_LOG_FILE = join26(PANOPTICON_HOME2, "logs", "handoffs.jsonl");
86957
87093
  function ensureLogDir3() {
86958
- const logDir = join25(PANOPTICON_HOME2, "logs");
86959
- if (!existsSync24(logDir)) {
86960
- mkdirSync17(logDir, { recursive: true });
87094
+ const logDir = join26(PANOPTICON_HOME2, "logs");
87095
+ if (!existsSync25(logDir)) {
87096
+ mkdirSync18(logDir, { recursive: true });
86961
87097
  }
86962
87098
  }
86963
87099
  function logHandoffEvent(event) {
@@ -86998,10 +87134,10 @@ function createHandoffEvent(agentId, issueId, context, trigger, success, errorMe
86998
87134
  }
86999
87135
  function readHandoffEvents(limit) {
87000
87136
  ensureLogDir3();
87001
- if (!existsSync24(HANDOFF_LOG_FILE)) {
87137
+ if (!existsSync25(HANDOFF_LOG_FILE)) {
87002
87138
  return [];
87003
87139
  }
87004
- const content = readFileSync19(HANDOFF_LOG_FILE, "utf-8");
87140
+ const content = readFileSync20(HANDOFF_LOG_FILE, "utf-8");
87005
87141
  const lines = content.trim().split("\n").filter((line) => line.trim());
87006
87142
  const events = lines.map((line) => JSON.parse(line));
87007
87143
  events.reverse();
@@ -87059,8 +87195,8 @@ function getHandoffStats() {
87059
87195
 
87060
87196
  // ../../lib/cloister/fpp-violations.ts
87061
87197
  init_hooks();
87062
- import { readFileSync as readFileSync20, existsSync as existsSync25, writeFileSync as writeFileSync16, mkdirSync as mkdirSync18, unlinkSync as unlinkSync7 } from "fs";
87063
- import { join as join26, dirname as dirname2 } from "path";
87198
+ import { readFileSync as readFileSync21, existsSync as existsSync26, writeFileSync as writeFileSync17, mkdirSync as mkdirSync19, unlinkSync as unlinkSync7 } from "fs";
87199
+ import { join as join27, dirname as dirname3 } from "path";
87064
87200
  init_paths();
87065
87201
  var DEFAULT_FPP_CONFIG = {
87066
87202
  hook_idle_minutes: 5,
@@ -87068,13 +87204,13 @@ var DEFAULT_FPP_CONFIG = {
87068
87204
  review_pending_minutes: 15,
87069
87205
  max_nudges: 3
87070
87206
  };
87071
- var VIOLATIONS_DATA_FILE = join26(PANOPTICON_HOME2, "fpp-violations.json");
87207
+ var VIOLATIONS_DATA_FILE = join27(PANOPTICON_HOME2, "fpp-violations.json");
87072
87208
  function loadViolations() {
87073
- if (!existsSync25(VIOLATIONS_DATA_FILE)) {
87209
+ if (!existsSync26(VIOLATIONS_DATA_FILE)) {
87074
87210
  return /* @__PURE__ */ new Map();
87075
87211
  }
87076
87212
  try {
87077
- const fileContent = readFileSync20(VIOLATIONS_DATA_FILE, "utf-8");
87213
+ const fileContent = readFileSync21(VIOLATIONS_DATA_FILE, "utf-8");
87078
87214
  const persisted = JSON.parse(fileContent);
87079
87215
  return new Map(persisted.violations || []);
87080
87216
  } catch (error) {
@@ -87084,16 +87220,16 @@ function loadViolations() {
87084
87220
  }
87085
87221
  function saveViolations(violations) {
87086
87222
  try {
87087
- const dir = dirname2(VIOLATIONS_DATA_FILE);
87088
- if (!existsSync25(dir)) {
87089
- mkdirSync18(dir, { recursive: true });
87223
+ const dir = dirname3(VIOLATIONS_DATA_FILE);
87224
+ if (!existsSync26(dir)) {
87225
+ mkdirSync19(dir, { recursive: true });
87090
87226
  }
87091
87227
  const persisted = {
87092
87228
  violations: Array.from(violations.entries())
87093
87229
  };
87094
87230
  const tempFile = `${VIOLATIONS_DATA_FILE}.tmp`;
87095
- writeFileSync16(tempFile, JSON.stringify(persisted, null, 2));
87096
- writeFileSync16(VIOLATIONS_DATA_FILE, readFileSync20(tempFile));
87231
+ writeFileSync17(tempFile, JSON.stringify(persisted, null, 2));
87232
+ writeFileSync17(VIOLATIONS_DATA_FILE, readFileSync21(tempFile));
87097
87233
  try {
87098
87234
  unlinkSync7(tempFile);
87099
87235
  } catch (unlinkError) {
@@ -87186,11 +87322,11 @@ function clearOldViolations(hoursOld = 24) {
87186
87322
  // ../../lib/cloister/cost-monitor.ts
87187
87323
  init_paths();
87188
87324
  init_config();
87189
- import { readFileSync as readFileSync21, existsSync as existsSync26, writeFileSync as writeFileSync17, mkdirSync as mkdirSync19, unlinkSync as unlinkSync8 } from "fs";
87190
- import { join as join27, dirname as dirname3 } from "path";
87191
- var COST_DATA_FILE = join27(PANOPTICON_HOME2, "cost-data.json");
87325
+ import { readFileSync as readFileSync22, existsSync as existsSync27, writeFileSync as writeFileSync18, mkdirSync as mkdirSync20, unlinkSync as unlinkSync8 } from "fs";
87326
+ import { join as join28, dirname as dirname4 } from "path";
87327
+ var COST_DATA_FILE = join28(PANOPTICON_HOME2, "cost-data.json");
87192
87328
  function loadCostData() {
87193
- if (!existsSync26(COST_DATA_FILE)) {
87329
+ if (!existsSync27(COST_DATA_FILE)) {
87194
87330
  return {
87195
87331
  perAgent: /* @__PURE__ */ new Map(),
87196
87332
  perIssue: /* @__PURE__ */ new Map(),
@@ -87199,7 +87335,7 @@ function loadCostData() {
87199
87335
  };
87200
87336
  }
87201
87337
  try {
87202
- const fileContent = readFileSync21(COST_DATA_FILE, "utf-8");
87338
+ const fileContent = readFileSync22(COST_DATA_FILE, "utf-8");
87203
87339
  const persisted = JSON.parse(fileContent);
87204
87340
  return {
87205
87341
  perAgent: new Map(Object.entries(persisted.perAgent || {})),
@@ -87219,9 +87355,9 @@ function loadCostData() {
87219
87355
  }
87220
87356
  function saveCostData(data) {
87221
87357
  try {
87222
- const dir = dirname3(COST_DATA_FILE);
87223
- if (!existsSync26(dir)) {
87224
- mkdirSync19(dir, { recursive: true });
87358
+ const dir = dirname4(COST_DATA_FILE);
87359
+ if (!existsSync27(dir)) {
87360
+ mkdirSync20(dir, { recursive: true });
87225
87361
  }
87226
87362
  const persisted = {
87227
87363
  perAgent: Object.fromEntries(data.perAgent),
@@ -87230,8 +87366,8 @@ function saveCostData(data) {
87230
87366
  lastResetDate: data.lastResetDate
87231
87367
  };
87232
87368
  const tempFile = `${COST_DATA_FILE}.tmp`;
87233
- writeFileSync17(tempFile, JSON.stringify(persisted, null, 2));
87234
- writeFileSync17(COST_DATA_FILE, readFileSync21(tempFile));
87369
+ writeFileSync18(tempFile, JSON.stringify(persisted, null, 2));
87370
+ writeFileSync18(COST_DATA_FILE, readFileSync22(tempFile));
87235
87371
  try {
87236
87372
  unlinkSync8(tempFile);
87237
87373
  } catch (unlinkError) {
@@ -87350,8 +87486,8 @@ function getCostSummary() {
87350
87486
 
87351
87487
  // ../../lib/cloister/session-rotation.ts
87352
87488
  init_paths();
87353
- import { writeFileSync as writeFileSync18 } from "fs";
87354
- import { join as join28 } from "path";
87489
+ import { writeFileSync as writeFileSync19 } from "fs";
87490
+ import { join as join29 } from "path";
87355
87491
  import { execSync as execSync2 } from "child_process";
87356
87492
  init_agents();
87357
87493
  init_specialists();
@@ -87491,8 +87627,8 @@ async function rotateSpecialistSession(specialistName, workingDir) {
87491
87627
  let memoryFile;
87492
87628
  if (specialistName === "merge-agent" && workingDir) {
87493
87629
  memoryContent = buildMergeAgentMemory(workingDir);
87494
- memoryFile = join28(PANOPTICON_HOME2, `merge-agent-memory-${Date.now()}.md`);
87495
- writeFileSync18(memoryFile, memoryContent);
87630
+ memoryFile = join29(PANOPTICON_HOME2, `merge-agent-memory-${Date.now()}.md`);
87631
+ writeFileSync19(memoryFile, memoryContent);
87496
87632
  console.log(`Built memory file: ${memoryFile}`);
87497
87633
  }
87498
87634
  const tmuxSession = getTmuxSessionName(specialistName);
@@ -87543,13 +87679,13 @@ init_config();
87543
87679
  init_specialists();
87544
87680
  init_agents();
87545
87681
  init_tmux();
87546
- import { readFileSync as readFileSync23, writeFileSync as writeFileSync19, existsSync as existsSync28, mkdirSync as mkdirSync20, readdirSync as readdirSync10, statSync as statSync5, rmSync } from "fs";
87547
- import { join as join29 } from "path";
87682
+ import { readFileSync as readFileSync24, writeFileSync as writeFileSync20, existsSync as existsSync29, mkdirSync as mkdirSync21, readdirSync as readdirSync10, statSync as statSync5, rmSync } from "fs";
87683
+ import { join as join30 } from "path";
87548
87684
  import { exec as exec7 } from "child_process";
87549
87685
  import { promisify as promisify7 } from "util";
87550
- import { homedir as homedir12 } from "os";
87686
+ import { homedir as homedir13 } from "os";
87551
87687
  var execAsync7 = promisify7(exec7);
87552
- var REVIEW_STATUS_FILE = join29(homedir12(), ".panopticon", "review-status.json");
87688
+ var REVIEW_STATUS_FILE = join30(homedir13(), ".panopticon", "review-status.json");
87553
87689
  var DEFAULT_CONFIG2 = {
87554
87690
  pingTimeoutMs: 3e4,
87555
87691
  // How long to wait for response
@@ -87564,15 +87700,15 @@ var DEFAULT_CONFIG2 = {
87564
87700
  massDeathWindowMs: 6e4
87565
87701
  // 1 minute window for mass death detection
87566
87702
  };
87567
- var DEACON_DIR = join29(PANOPTICON_HOME2, "deacon");
87568
- var STATE_FILE = join29(DEACON_DIR, "health-state.json");
87569
- var CONFIG_FILE2 = join29(DEACON_DIR, "config.json");
87703
+ var DEACON_DIR = join30(PANOPTICON_HOME2, "deacon");
87704
+ var STATE_FILE = join30(DEACON_DIR, "health-state.json");
87705
+ var CONFIG_FILE2 = join30(DEACON_DIR, "config.json");
87570
87706
  var deaconInterval = null;
87571
87707
  var config = { ...DEFAULT_CONFIG2 };
87572
87708
  function loadConfig2() {
87573
87709
  try {
87574
- if (existsSync28(CONFIG_FILE2)) {
87575
- const content = readFileSync23(CONFIG_FILE2, "utf-8");
87710
+ if (existsSync29(CONFIG_FILE2)) {
87711
+ const content = readFileSync24(CONFIG_FILE2, "utf-8");
87576
87712
  const loaded = JSON.parse(content);
87577
87713
  config = { ...DEFAULT_CONFIG2, ...loaded };
87578
87714
  }
@@ -87582,15 +87718,15 @@ function loadConfig2() {
87582
87718
  return config;
87583
87719
  }
87584
87720
  function ensureDeaconDir() {
87585
- if (!existsSync28(DEACON_DIR)) {
87586
- mkdirSync20(DEACON_DIR, { recursive: true });
87721
+ if (!existsSync29(DEACON_DIR)) {
87722
+ mkdirSync21(DEACON_DIR, { recursive: true });
87587
87723
  }
87588
87724
  }
87589
87725
  function loadState() {
87590
87726
  ensureDeaconDir();
87591
87727
  try {
87592
- if (existsSync28(STATE_FILE)) {
87593
- const content = readFileSync23(STATE_FILE, "utf-8");
87728
+ if (existsSync29(STATE_FILE)) {
87729
+ const content = readFileSync24(STATE_FILE, "utf-8");
87594
87730
  return JSON.parse(content);
87595
87731
  }
87596
87732
  } catch (error) {
@@ -87605,7 +87741,7 @@ function loadState() {
87605
87741
  function saveState(state) {
87606
87742
  ensureDeaconDir();
87607
87743
  try {
87608
- writeFileSync19(STATE_FILE, JSON.stringify(state, null, 2), "utf-8");
87744
+ writeFileSync20(STATE_FILE, JSON.stringify(state, null, 2), "utf-8");
87609
87745
  } catch (error) {
87610
87746
  console.error("[deacon] Failed to save state:", error);
87611
87747
  }
@@ -87639,12 +87775,12 @@ function getCooldownRemaining(healthState) {
87639
87775
  }
87640
87776
  function checkHeartbeat(name) {
87641
87777
  const tmuxSession = getTmuxSessionName(name);
87642
- const heartbeatFile = join29(PANOPTICON_HOME2, "heartbeats", `${tmuxSession}.json`);
87778
+ const heartbeatFile = join30(PANOPTICON_HOME2, "heartbeats", `${tmuxSession}.json`);
87643
87779
  try {
87644
- if (!existsSync28(heartbeatFile)) {
87780
+ if (!existsSync29(heartbeatFile)) {
87645
87781
  return { isResponsive: false };
87646
87782
  }
87647
- const content = readFileSync23(heartbeatFile, "utf-8");
87783
+ const content = readFileSync24(heartbeatFile, "utf-8");
87648
87784
  const heartbeat = JSON.parse(content);
87649
87785
  const lastActivity = new Date(heartbeat.timestamp).getTime();
87650
87786
  const age = Date.now() - lastActivity;
@@ -87807,8 +87943,8 @@ async function checkAndSuspendIdleAgents() {
87807
87943
  const timeoutMinutes = isSpecialist ? 5 : 10;
87808
87944
  const isWorkAgent = agent.id.startsWith("agent-") && !isSpecialist;
87809
87945
  if (isWorkAgent) {
87810
- const completedFile = join29(getAgentDir(agent.id), "completed");
87811
- if (existsSync28(completedFile)) {
87946
+ const completedFile = join30(getAgentDir(agent.id), "completed");
87947
+ if (existsSync29(completedFile)) {
87812
87948
  continue;
87813
87949
  }
87814
87950
  }
@@ -87915,10 +88051,10 @@ function isIssueCompletedOrInReview(agentId) {
87915
88051
  if (!match)
87916
88052
  return false;
87917
88053
  const issueId = match[1].toUpperCase();
87918
- if (!existsSync28(REVIEW_STATUS_FILE)) {
88054
+ if (!existsSync29(REVIEW_STATUS_FILE)) {
87919
88055
  return false;
87920
88056
  }
87921
- const content = readFileSync23(REVIEW_STATUS_FILE, "utf-8");
88057
+ const content = readFileSync24(REVIEW_STATUS_FILE, "utf-8");
87922
88058
  const statuses = JSON.parse(content);
87923
88059
  const status = statuses[issueId];
87924
88060
  if (!status) {
@@ -87993,22 +88129,22 @@ async function cleanupStaleAgentState() {
87993
88129
  const retentionDays = cloisterConfig.retention?.agent_state_days ?? 30;
87994
88130
  const retentionMs = retentionDays * 24 * 60 * 60 * 1e3;
87995
88131
  const now = Date.now();
87996
- if (!existsSync28(AGENTS_DIR)) {
88132
+ if (!existsSync29(AGENTS_DIR)) {
87997
88133
  return actions;
87998
88134
  }
87999
88135
  try {
88000
88136
  const dirs = readdirSync10(AGENTS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());
88001
88137
  for (const dir of dirs) {
88002
- const agentDir = join29(AGENTS_DIR, dir.name);
88138
+ const agentDir = join30(AGENTS_DIR, dir.name);
88003
88139
  try {
88004
88140
  try {
88005
88141
  await execAsync7(`tmux has-session -t "${dir.name}" 2>/dev/null`);
88006
88142
  continue;
88007
88143
  } catch {
88008
88144
  }
88009
- const stateFile = join29(agentDir, "state.json");
88145
+ const stateFile = join30(agentDir, "state.json");
88010
88146
  let mtime;
88011
- if (existsSync28(stateFile)) {
88147
+ if (existsSync29(stateFile)) {
88012
88148
  mtime = statSync5(stateFile).mtimeMs;
88013
88149
  } else {
88014
88150
  mtime = statSync5(agentDir).mtimeMs;
@@ -88017,8 +88153,8 @@ async function cleanupStaleAgentState() {
88017
88153
  if (ageMs < retentionMs) {
88018
88154
  continue;
88019
88155
  }
88020
- const completedFile = join29(agentDir, "completed");
88021
- if (existsSync28(completedFile)) {
88156
+ const completedFile = join30(agentDir, "completed");
88157
+ if (existsSync29(completedFile)) {
88022
88158
  const completedAge = now - statSync5(completedFile).mtimeMs;
88023
88159
  if (completedAge < 7 * 24 * 60 * 60 * 1e3) {
88024
88160
  continue;
@@ -88045,10 +88181,10 @@ async function cleanupStaleAgentState() {
88045
88181
  async function checkOrphanedReviewStatuses() {
88046
88182
  const actions = [];
88047
88183
  try {
88048
- if (!existsSync28(REVIEW_STATUS_FILE)) {
88184
+ if (!existsSync29(REVIEW_STATUS_FILE)) {
88049
88185
  return actions;
88050
88186
  }
88051
- const content = readFileSync23(REVIEW_STATUS_FILE, "utf-8");
88187
+ const content = readFileSync24(REVIEW_STATUS_FILE, "utf-8");
88052
88188
  const statuses = JSON.parse(content);
88053
88189
  const reviewAgentSession = getTmuxSessionName("review-agent");
88054
88190
  const reviewAgentRunning = sessionExists(reviewAgentSession);
@@ -88074,7 +88210,7 @@ async function checkOrphanedReviewStatuses() {
88074
88210
  }
88075
88211
  }
88076
88212
  if (modified) {
88077
- writeFileSync19(REVIEW_STATUS_FILE, JSON.stringify(statuses, null, 2), "utf-8");
88213
+ writeFileSync20(REVIEW_STATUS_FILE, JSON.stringify(statuses, null, 2), "utf-8");
88078
88214
  }
88079
88215
  } catch (error) {
88080
88216
  const msg = error instanceof Error ? error.message : String(error);
@@ -88222,19 +88358,19 @@ function getDeaconStatus() {
88222
88358
  // ../../lib/cloister/service.ts
88223
88359
  init_paths();
88224
88360
  init_paths();
88225
- import { existsSync as existsSync29, writeFileSync as writeFileSync20, unlinkSync as unlinkSync9, readFileSync as readFileSync24, readdirSync as readdirSync11, renameSync } from "fs";
88226
- import { join as join30 } from "path";
88227
- var CLOISTER_STATE_FILE = join30(PANOPTICON_HOME2, "cloister.state");
88361
+ import { existsSync as existsSync30, writeFileSync as writeFileSync21, unlinkSync as unlinkSync9, readFileSync as readFileSync25, readdirSync as readdirSync11, renameSync } from "fs";
88362
+ import { join as join31 } from "path";
88363
+ var CLOISTER_STATE_FILE = join31(PANOPTICON_HOME2, "cloister.state");
88228
88364
  function writeStateFile(running, pid) {
88229
88365
  try {
88230
88366
  if (running) {
88231
- writeFileSync20(CLOISTER_STATE_FILE, JSON.stringify({
88367
+ writeFileSync21(CLOISTER_STATE_FILE, JSON.stringify({
88232
88368
  running: true,
88233
88369
  pid: pid || process.pid,
88234
88370
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
88235
88371
  }));
88236
88372
  } else {
88237
- if (existsSync29(CLOISTER_STATE_FILE)) {
88373
+ if (existsSync30(CLOISTER_STATE_FILE)) {
88238
88374
  unlinkSync9(CLOISTER_STATE_FILE);
88239
88375
  }
88240
88376
  }
@@ -88244,8 +88380,8 @@ function writeStateFile(running, pid) {
88244
88380
  }
88245
88381
  function readStateFile() {
88246
88382
  try {
88247
- if (existsSync29(CLOISTER_STATE_FILE)) {
88248
- const data = JSON.parse(readFileSync24(CLOISTER_STATE_FILE, "utf-8"));
88383
+ if (existsSync30(CLOISTER_STATE_FILE)) {
88384
+ const data = JSON.parse(readFileSync25(CLOISTER_STATE_FILE, "utf-8"));
88249
88385
  if (data.pid) {
88250
88386
  try {
88251
88387
  process.kill(data.pid, 0);
@@ -88464,16 +88600,16 @@ var CloisterService = class {
88464
88600
  */
88465
88601
  async checkCompletionMarkers() {
88466
88602
  try {
88467
- if (!existsSync29(AGENTS_DIR))
88603
+ if (!existsSync30(AGENTS_DIR))
88468
88604
  return;
88469
88605
  const agentDirs = readdirSync11(AGENTS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.startsWith("agent-"));
88470
88606
  for (const dir of agentDirs) {
88471
- const completedFile = join30(AGENTS_DIR, dir.name, "completed");
88472
- const processedFile = join30(AGENTS_DIR, dir.name, "completed.processed");
88473
- if (!existsSync29(completedFile) || existsSync29(processedFile))
88607
+ const completedFile = join31(AGENTS_DIR, dir.name, "completed");
88608
+ const processedFile = join31(AGENTS_DIR, dir.name, "completed.processed");
88609
+ if (!existsSync30(completedFile) || existsSync30(processedFile))
88474
88610
  continue;
88475
88611
  try {
88476
- const content = JSON.parse(readFileSync24(completedFile, "utf-8"));
88612
+ const content = JSON.parse(readFileSync25(completedFile, "utf-8"));
88477
88613
  const ageMs = Date.now() - new Date(content.timestamp).getTime();
88478
88614
  if (ageMs > 24 * 60 * 60 * 1e3) {
88479
88615
  console.log(`\u{1F514} Cloister: Skipping stale completion marker for ${dir.name} (${Math.floor(ageMs / 36e5)}h old)`);
@@ -89033,7 +89169,7 @@ init_settings();
89033
89169
  init_js_yaml();
89034
89170
  init_config_yaml();
89035
89171
  init_model_capabilities();
89036
- import { writeFileSync as writeFileSync21 } from "fs";
89172
+ import { writeFileSync as writeFileSync22 } from "fs";
89037
89173
  function getOptimalModelDefaults() {
89038
89174
  return {
89039
89175
  // High-complexity phases - Opus 4.6 for deep analysis
@@ -89109,7 +89245,7 @@ function saveSettingsApi(settings) {
89109
89245
  lineWidth: 120,
89110
89246
  noRefs: true
89111
89247
  });
89112
- writeFileSync21(getGlobalConfigPath(), yamlContent, "utf-8");
89248
+ writeFileSync22(getGlobalConfigPath(), yamlContent, "utf-8");
89113
89249
  }
89114
89250
  function validateSettingsApi(settings) {
89115
89251
  const errors = [];
@@ -89193,7 +89329,7 @@ init_merge_agent();
89193
89329
 
89194
89330
  // ../lib/health-filtering.ts
89195
89331
  init_config();
89196
- import { readFileSync as readFileSync26, existsSync as existsSync33 } from "fs";
89332
+ import { readFileSync as readFileSync27, existsSync as existsSync34 } from "fs";
89197
89333
  import { exec as exec11 } from "child_process";
89198
89334
  import { promisify as promisify11 } from "util";
89199
89335
  var execAsync11 = promisify11(exec11);
@@ -89213,9 +89349,9 @@ async function determineHealthStatusAsync(agentId, stateFile) {
89213
89349
  const health = await checkAgentHealthAsync(agentId);
89214
89350
  let agentStatus;
89215
89351
  let lastActivity = null;
89216
- if (existsSync33(stateFile)) {
89352
+ if (existsSync34(stateFile)) {
89217
89353
  try {
89218
- const state = JSON.parse(readFileSync26(stateFile, "utf-8"));
89354
+ const state = JSON.parse(readFileSync27(stateFile, "utf-8"));
89219
89355
  agentStatus = state.status;
89220
89356
  lastActivity = state.lastActivity ? new Date(state.lastActivity) : null;
89221
89357
  } catch {
@@ -89264,9 +89400,9 @@ init_jsonl_parser();
89264
89400
  // ../../lib/convoy.ts
89265
89401
  var import_yaml2 = __toESM(require_dist3(), 1);
89266
89402
  init_tmux();
89267
- import { existsSync as existsSync34, mkdirSync as mkdirSync22, writeFileSync as writeFileSync23, readFileSync as readFileSync27, readdirSync as readdirSync13 } from "fs";
89268
- import { join as join34 } from "path";
89269
- import { homedir as homedir13 } from "os";
89403
+ import { existsSync as existsSync35, mkdirSync as mkdirSync23, writeFileSync as writeFileSync24, readFileSync as readFileSync28, readdirSync as readdirSync13 } from "fs";
89404
+ import { join as join35 } from "path";
89405
+ import { homedir as homedir14 } from "os";
89270
89406
  import { exec as exec12 } from "child_process";
89271
89407
  import { promisify as promisify12 } from "util";
89272
89408
 
@@ -89389,25 +89525,25 @@ function getExecutionOrder(template) {
89389
89525
  init_paths();
89390
89526
  init_work_type_router();
89391
89527
  var execAsync12 = promisify12(exec12);
89392
- var CONVOY_DIR = join34(homedir13(), ".panopticon", "convoys");
89528
+ var CONVOY_DIR = join35(homedir14(), ".panopticon", "convoys");
89393
89529
  function getConvoyStateFile(convoyId) {
89394
- return join34(CONVOY_DIR, `${convoyId}.json`);
89530
+ return join35(CONVOY_DIR, `${convoyId}.json`);
89395
89531
  }
89396
89532
  function getConvoyOutputDir(convoyId, template) {
89397
89533
  const baseDir = template.config?.outputDir || ".panopticon/convoy-output";
89398
- return join34(process.cwd(), baseDir, convoyId);
89534
+ return join35(process.cwd(), baseDir, convoyId);
89399
89535
  }
89400
89536
  function saveConvoyState(state) {
89401
- mkdirSync22(CONVOY_DIR, { recursive: true });
89402
- writeFileSync23(getConvoyStateFile(state.id), JSON.stringify(state, null, 2));
89537
+ mkdirSync23(CONVOY_DIR, { recursive: true });
89538
+ writeFileSync24(getConvoyStateFile(state.id), JSON.stringify(state, null, 2));
89403
89539
  }
89404
89540
  function loadConvoyState(convoyId) {
89405
89541
  const stateFile = getConvoyStateFile(convoyId);
89406
- if (!existsSync34(stateFile)) {
89542
+ if (!existsSync35(stateFile)) {
89407
89543
  return void 0;
89408
89544
  }
89409
89545
  try {
89410
- const content = readFileSync27(stateFile, "utf-8");
89546
+ const content = readFileSync28(stateFile, "utf-8");
89411
89547
  return JSON.parse(content);
89412
89548
  } catch {
89413
89549
  return void 0;
@@ -89417,7 +89553,7 @@ function getConvoyStatus(convoyId) {
89417
89553
  return loadConvoyState(convoyId);
89418
89554
  }
89419
89555
  function listConvoys(filter) {
89420
- if (!existsSync34(CONVOY_DIR)) {
89556
+ if (!existsSync35(CONVOY_DIR)) {
89421
89557
  return [];
89422
89558
  }
89423
89559
  const files = readdirSync13(CONVOY_DIR).filter((f) => f.endsWith(".json"));
@@ -89436,10 +89572,10 @@ function listConvoys(filter) {
89436
89572
  );
89437
89573
  }
89438
89574
  function parseAgentTemplate(templatePath) {
89439
- if (!existsSync34(templatePath)) {
89575
+ if (!existsSync35(templatePath)) {
89440
89576
  throw new Error(`Agent template not found: ${templatePath}`);
89441
89577
  }
89442
- const content = readFileSync27(templatePath, "utf-8");
89578
+ const content = readFileSync28(templatePath, "utf-8");
89443
89579
  const frontmatterMatch = content.match(/^---\n([\s\S]+?)\n---\n([\s\S]*)$/);
89444
89580
  if (!frontmatterMatch) {
89445
89581
  throw new Error(`Invalid agent template format (missing frontmatter): ${templatePath}`);
@@ -89465,7 +89601,7 @@ function mapConvoyRoleToWorkType(role) {
89465
89601
  }
89466
89602
  async function spawnConvoyAgent(convoy, agent, agentState, context) {
89467
89603
  const { role, subagent } = agent;
89468
- const templatePath = join34(AGENTS_DIR, `${subagent}.md`);
89604
+ const templatePath = join35(AGENTS_DIR, `${subagent}.md`);
89469
89605
  const template = parseAgentTemplate(templatePath);
89470
89606
  let model = template.model;
89471
89607
  try {
@@ -89503,9 +89639,9 @@ ${context.issueId ? `**Issue ID**: ${context.issueId}` : ""}
89503
89639
 
89504
89640
  `;
89505
89641
  prompt = contextInstructions + prompt;
89506
- mkdirSync22(convoy.outputDir, { recursive: true });
89507
- const promptFile = join34(convoy.outputDir, `${role}-prompt.md`);
89508
- writeFileSync23(promptFile, prompt);
89642
+ mkdirSync23(convoy.outputDir, { recursive: true });
89643
+ const promptFile = join35(convoy.outputDir, `${role}-prompt.md`);
89644
+ writeFileSync24(promptFile, prompt);
89509
89645
  const claudeCmd = `claude --dangerously-skip-permissions --model ${model}`;
89510
89646
  createSession(agentState.tmuxSession, convoy.context.projectPath, claudeCmd, {
89511
89647
  env: {
@@ -89530,7 +89666,7 @@ async function startConvoy(templateName, context) {
89530
89666
  const timestamp2 = Date.now();
89531
89667
  const convoyId = `convoy-${templateName}-${timestamp2}`;
89532
89668
  const outputDir = getConvoyOutputDir(convoyId, template);
89533
- mkdirSync22(outputDir, { recursive: true });
89669
+ mkdirSync23(outputDir, { recursive: true });
89534
89670
  const state = {
89535
89671
  id: convoyId,
89536
89672
  template: templateName,
@@ -89542,7 +89678,7 @@ async function startConvoy(templateName, context) {
89542
89678
  };
89543
89679
  for (const agent of template.agents) {
89544
89680
  const tmuxSession = `${convoyId}-${agent.role}`;
89545
- const outputFile = join34(outputDir, `${agent.role}.md`);
89681
+ const outputFile = join35(outputDir, `${agent.role}.md`);
89546
89682
  state.agents.push({
89547
89683
  role: agent.role,
89548
89684
  subagent: agent.subagent,
@@ -89577,8 +89713,8 @@ async function executePhase(convoy, template, phaseAgents, context) {
89577
89713
  const agentContext = { ...context };
89578
89714
  for (const depRole of deps) {
89579
89715
  const depAgent = convoy.agents.find((a) => a.role === depRole);
89580
- if (depAgent?.outputFile && existsSync34(depAgent.outputFile)) {
89581
- agentContext[`${depRole}_output`] = readFileSync27(depAgent.outputFile, "utf-8");
89716
+ if (depAgent?.outputFile && existsSync35(depAgent.outputFile)) {
89717
+ agentContext[`${depRole}_output`] = readFileSync28(depAgent.outputFile, "utf-8");
89582
89718
  }
89583
89719
  }
89584
89720
  spawnPromises.push(spawnConvoyAgent(convoy, agent, agentState, agentContext));
@@ -89641,7 +89777,7 @@ function updateAgentStatuses(convoy) {
89641
89777
  agent.status = "completed";
89642
89778
  agent.completedAt = (/* @__PURE__ */ new Date()).toISOString();
89643
89779
  updated = true;
89644
- if (agent.outputFile && existsSync34(agent.outputFile)) {
89780
+ if (agent.outputFile && existsSync35(agent.outputFile)) {
89645
89781
  agent.exitCode = 0;
89646
89782
  } else {
89647
89783
  agent.exitCode = 1;
@@ -89669,10 +89805,10 @@ async function stopConvoy(convoyId) {
89669
89805
  }
89670
89806
 
89671
89807
  // ../../lib/env-loader.ts
89672
- import { readFileSync as readFileSync28, existsSync as existsSync35 } from "fs";
89673
- import { join as join35 } from "path";
89674
- import { homedir as homedir14 } from "os";
89675
- var ENV_FILE_PATH = join35(homedir14(), ".panopticon.env");
89808
+ import { readFileSync as readFileSync29, existsSync as existsSync36 } from "fs";
89809
+ import { join as join36 } from "path";
89810
+ import { homedir as homedir15 } from "os";
89811
+ var ENV_FILE_PATH = join36(homedir15(), ".panopticon.env");
89676
89812
  function parseEnvFile(content) {
89677
89813
  const result = {};
89678
89814
  for (const line of content.split("\n")) {
@@ -89694,11 +89830,11 @@ function loadPanopticonEnv() {
89694
89830
  loaded: [],
89695
89831
  skipped: []
89696
89832
  };
89697
- if (!existsSync35(ENV_FILE_PATH)) {
89833
+ if (!existsSync36(ENV_FILE_PATH)) {
89698
89834
  return { ...result, error: `Env file not found: ${ENV_FILE_PATH}` };
89699
89835
  }
89700
89836
  try {
89701
- const content = readFileSync28(ENV_FILE_PATH, "utf-8");
89837
+ const content = readFileSync29(ENV_FILE_PATH, "utf-8");
89702
89838
  const envVars = parseEnvFile(content);
89703
89839
  for (const [key, value] of Object.entries(envVars)) {
89704
89840
  if (process.env[key]) {
@@ -89715,21 +89851,21 @@ function loadPanopticonEnv() {
89715
89851
  }
89716
89852
 
89717
89853
  // ../../lib/costs/events.ts
89718
- import { existsSync as existsSync36, mkdirSync as mkdirSync23, readFileSync as readFileSync29, appendFileSync as appendFileSync8, writeFileSync as writeFileSync24, renameSync as renameSync2 } from "fs";
89719
- import { join as join36 } from "path";
89720
- import { homedir as homedir15 } from "os";
89854
+ import { existsSync as existsSync37, mkdirSync as mkdirSync24, readFileSync as readFileSync30, appendFileSync as appendFileSync8, writeFileSync as writeFileSync25, renameSync as renameSync2 } from "fs";
89855
+ import { join as join37 } from "path";
89856
+ import { homedir as homedir16 } from "os";
89721
89857
  function getCostsDir() {
89722
- return join36(process.env.HOME || homedir15(), ".panopticon", "costs");
89858
+ return join37(process.env.HOME || homedir16(), ".panopticon", "costs");
89723
89859
  }
89724
89860
  function getEventsFile() {
89725
- return join36(getCostsDir(), "events.jsonl");
89861
+ return join37(getCostsDir(), "events.jsonl");
89726
89862
  }
89727
89863
  function ensureEventsFile() {
89728
89864
  const costsDir = getCostsDir();
89729
89865
  const eventsFile = getEventsFile();
89730
- mkdirSync23(costsDir, { recursive: true });
89731
- if (!existsSync36(eventsFile)) {
89732
- writeFileSync24(eventsFile, "", "utf-8");
89866
+ mkdirSync24(costsDir, { recursive: true });
89867
+ if (!existsSync37(eventsFile)) {
89868
+ writeFileSync25(eventsFile, "", "utf-8");
89733
89869
  }
89734
89870
  }
89735
89871
  function appendCostEvent(event) {
@@ -89741,10 +89877,10 @@ function appendCostEvent(event) {
89741
89877
  appendFileSync8(getEventsFile(), line, "utf-8");
89742
89878
  }
89743
89879
  function readEvents(options = {}) {
89744
- if (!existsSync36(getEventsFile())) {
89880
+ if (!existsSync37(getEventsFile())) {
89745
89881
  return [];
89746
89882
  }
89747
- const content = readFileSync29(getEventsFile(), "utf-8");
89883
+ const content = readFileSync30(getEventsFile(), "utf-8");
89748
89884
  const lines = content.split("\n").filter((line) => line.trim());
89749
89885
  let events = [];
89750
89886
  for (const line of lines) {
@@ -89779,10 +89915,10 @@ function readEvents(options = {}) {
89779
89915
  return events;
89780
89916
  }
89781
89917
  function tailEvents(n) {
89782
- if (!existsSync36(getEventsFile())) {
89918
+ if (!existsSync37(getEventsFile())) {
89783
89919
  return [];
89784
89920
  }
89785
- const content = readFileSync29(getEventsFile(), "utf-8");
89921
+ const content = readFileSync30(getEventsFile(), "utf-8");
89786
89922
  const lines = content.split("\n").filter((line) => line.trim());
89787
89923
  const lastLines = lines.slice(-n);
89788
89924
  const events = [];
@@ -89795,10 +89931,10 @@ function tailEvents(n) {
89795
89931
  return events;
89796
89932
  }
89797
89933
  function readEventsFromLine(startLine) {
89798
- if (!existsSync36(getEventsFile())) {
89934
+ if (!existsSync37(getEventsFile())) {
89799
89935
  return { events: [], newLine: startLine };
89800
89936
  }
89801
- const content = readFileSync29(getEventsFile(), "utf-8");
89937
+ const content = readFileSync30(getEventsFile(), "utf-8");
89802
89938
  const lines = content.split("\n").filter((line) => line.trim());
89803
89939
  const events = [];
89804
89940
  for (let i = startLine; i < lines.length; i++) {
@@ -89811,14 +89947,14 @@ function readEventsFromLine(startLine) {
89811
89947
  return { events, newLine: lines.length };
89812
89948
  }
89813
89949
  function getLastEventMetadata() {
89814
- if (!existsSync36(getEventsFile())) {
89950
+ if (!existsSync37(getEventsFile())) {
89815
89951
  return {
89816
89952
  lastEventTs: null,
89817
89953
  lastEventLine: 0,
89818
89954
  totalEvents: 0
89819
89955
  };
89820
89956
  }
89821
- const content = readFileSync29(getEventsFile(), "utf-8");
89957
+ const content = readFileSync30(getEventsFile(), "utf-8");
89822
89958
  const lines = content.split("\n").filter((line) => line.trim());
89823
89959
  let lastEventTs = null;
89824
89960
  if (lines.length > 0) {
@@ -89835,28 +89971,28 @@ function getLastEventMetadata() {
89835
89971
  };
89836
89972
  }
89837
89973
  function eventsFileExists() {
89838
- return existsSync36(getEventsFile());
89974
+ return existsSync37(getEventsFile());
89839
89975
  }
89840
89976
 
89841
89977
  // ../../lib/costs/aggregator.ts
89842
- import { existsSync as existsSync37, mkdirSync as mkdirSync24, readFileSync as readFileSync30, writeFileSync as writeFileSync25, renameSync as renameSync3 } from "fs";
89843
- import { join as join37 } from "path";
89844
- import { homedir as homedir16 } from "os";
89978
+ import { existsSync as existsSync38, mkdirSync as mkdirSync25, readFileSync as readFileSync31, writeFileSync as writeFileSync26, renameSync as renameSync3 } from "fs";
89979
+ import { join as join38 } from "path";
89980
+ import { homedir as homedir17 } from "os";
89845
89981
  var CACHE_VERSION = 3;
89846
89982
  var DEFAULT_RETENTION_DAYS = 90;
89847
89983
  function getCostsDir2() {
89848
- return join37(process.env.HOME || homedir16(), ".panopticon", "costs");
89984
+ return join38(process.env.HOME || homedir17(), ".panopticon", "costs");
89849
89985
  }
89850
89986
  function getCacheFile() {
89851
- return join37(getCostsDir2(), "by-issue.json");
89987
+ return join38(getCostsDir2(), "by-issue.json");
89852
89988
  }
89853
89989
  function loadCache() {
89854
89990
  const cacheFile = getCacheFile();
89855
- if (!existsSync37(cacheFile)) {
89991
+ if (!existsSync38(cacheFile)) {
89856
89992
  return createEmptyCache();
89857
89993
  }
89858
89994
  try {
89859
- const content = readFileSync30(cacheFile, "utf-8");
89995
+ const content = readFileSync31(cacheFile, "utf-8");
89860
89996
  const cache = JSON.parse(content);
89861
89997
  if (cache.version !== CACHE_VERSION) {
89862
89998
  console.warn(`Cache version mismatch: expected ${CACHE_VERSION}, got ${cache.version}. Rebuilding cache.`);
@@ -89881,10 +90017,10 @@ function createEmptyCache() {
89881
90017
  function saveCache(cache) {
89882
90018
  const costsDir = getCostsDir2();
89883
90019
  const cacheFile = getCacheFile();
89884
- mkdirSync24(costsDir, { recursive: true });
90020
+ mkdirSync25(costsDir, { recursive: true });
89885
90021
  const tempFile = cacheFile + ".tmp";
89886
90022
  const content = JSON.stringify(cache, null, 2);
89887
- writeFileSync25(tempFile, content, "utf-8");
90023
+ writeFileSync26(tempFile, content, "utf-8");
89888
90024
  renameSync3(tempFile, cacheFile);
89889
90025
  }
89890
90026
  function updateCacheFromEvents(events, newLineNumber) {
@@ -90019,18 +90155,18 @@ function getCacheStatus() {
90019
90155
  }
90020
90156
 
90021
90157
  // ../../lib/costs/migration.ts
90022
- import { existsSync as existsSync38, readdirSync as readdirSync14, readFileSync as readFileSync31 } from "fs";
90023
- import { join as join38 } from "path";
90024
- import { homedir as homedir17 } from "os";
90158
+ import { existsSync as existsSync39, readdirSync as readdirSync14, readFileSync as readFileSync32 } from "fs";
90159
+ import { join as join39 } from "path";
90160
+ import { homedir as homedir18 } from "os";
90025
90161
  init_cost();
90026
90162
  function getAgentsDir() {
90027
- return join38(process.env.HOME || homedir17(), ".panopticon", "agents");
90163
+ return join39(process.env.HOME || homedir18(), ".panopticon", "agents");
90028
90164
  }
90029
90165
  function getClaudeProjectsDir() {
90030
- return join38(process.env.HOME || homedir17(), ".claude", "projects");
90166
+ return join39(process.env.HOME || homedir18(), ".claude", "projects");
90031
90167
  }
90032
90168
  function getProjectsYamlPath() {
90033
- return join38(process.env.HOME || homedir17(), ".panopticon", "projects.yaml");
90169
+ return join39(process.env.HOME || homedir18(), ".panopticon", "projects.yaml");
90034
90170
  }
90035
90171
  function inferIssueId(agentDir) {
90036
90172
  const stripped = agentDir.replace(/^(agent|planning)-/, "");
@@ -90044,8 +90180,8 @@ function getProjectPaths() {
90044
90180
  const paths = [];
90045
90181
  try {
90046
90182
  const yamlPath = getProjectsYamlPath();
90047
- if (existsSync38(yamlPath)) {
90048
- const content = readFileSync31(yamlPath, "utf-8");
90183
+ if (existsSync39(yamlPath)) {
90184
+ const content = readFileSync32(yamlPath, "utf-8");
90049
90185
  const pathMatches = content.match(/^\s+path:\s+(.+)$/gm);
90050
90186
  if (pathMatches) {
90051
90187
  for (const m of pathMatches) {
@@ -90064,8 +90200,8 @@ function resolveWorkspace(issueId) {
90064
90200
  const issueLower = issueId.toLowerCase();
90065
90201
  const projectPaths = getProjectPaths();
90066
90202
  for (const projectPath of projectPaths) {
90067
- const wsPath = join38(projectPath, "workspaces", `feature-${issueLower}`);
90068
- if (existsSync38(wsPath)) {
90203
+ const wsPath = join39(projectPath, "workspaces", `feature-${issueLower}`);
90204
+ if (existsSync39(wsPath)) {
90069
90205
  return wsPath;
90070
90206
  }
90071
90207
  }
@@ -90079,7 +90215,7 @@ function findSessionDirsByIssue(issueId) {
90079
90215
  const entries = readdirSync14(claudeDir);
90080
90216
  for (const entry of entries) {
90081
90217
  if (entry.includes(`feature-${issueLower}`)) {
90082
- const fullPath = join38(claudeDir, entry);
90218
+ const fullPath = join39(claudeDir, entry);
90083
90219
  dirs.push(fullPath);
90084
90220
  }
90085
90221
  }
@@ -90090,7 +90226,7 @@ function findSessionDirsByIssue(issueId) {
90090
90226
  function parseSessionFile(filePath) {
90091
90227
  const usages = [];
90092
90228
  try {
90093
- const content = readFileSync31(filePath, "utf-8");
90229
+ const content = readFileSync32(filePath, "utf-8");
90094
90230
  const lines = content.split("\n").filter((l) => l.trim());
90095
90231
  for (const line of lines) {
90096
90232
  try {
@@ -90158,15 +90294,15 @@ function usageToCostEvents(usages, context) {
90158
90294
  }
90159
90295
  function getSessionDir(workspacePath) {
90160
90296
  const sessionDirName = `-${workspacePath.replace(/^\//, "").replace(/\//g, "-")}`;
90161
- const sessionDir = join38(getClaudeProjectsDir(), sessionDirName);
90162
- if (existsSync38(sessionDir)) {
90297
+ const sessionDir = join39(getClaudeProjectsDir(), sessionDirName);
90298
+ if (existsSync39(sessionDir)) {
90163
90299
  return sessionDir;
90164
90300
  }
90165
90301
  return null;
90166
90302
  }
90167
90303
  function migrateAgent(agentDir, stats) {
90168
- const stateFile = join38(getAgentsDir(), agentDir, "state.json");
90169
- if (!existsSync38(stateFile)) {
90304
+ const stateFile = join39(getAgentsDir(), agentDir, "state.json");
90305
+ if (!existsSync39(stateFile)) {
90170
90306
  stats.warnings.push({
90171
90307
  file: stateFile,
90172
90308
  message: "No state.json found"
@@ -90175,7 +90311,7 @@ function migrateAgent(agentDir, stats) {
90175
90311
  }
90176
90312
  let state;
90177
90313
  try {
90178
- state = JSON.parse(readFileSync31(stateFile, "utf-8"));
90314
+ state = JSON.parse(readFileSync32(stateFile, "utf-8"));
90179
90315
  } catch (err) {
90180
90316
  stats.errors.push({
90181
90317
  file: stateFile,
@@ -90213,7 +90349,7 @@ function migrateAgent(agentDir, stats) {
90213
90349
  try {
90214
90350
  const files = readdirSync14(sessionDir).filter((f) => f.endsWith(".jsonl"));
90215
90351
  for (const file of files) {
90216
- const filePath = join38(sessionDir, file);
90352
+ const filePath = join39(sessionDir, file);
90217
90353
  try {
90218
90354
  const usages = parseSessionFile(filePath);
90219
90355
  const events = usageToCostEvents(usages, context);
@@ -90237,12 +90373,12 @@ function migrateAgent(agentDir, stats) {
90237
90373
  error: `Failed to read session directory: ${err}`
90238
90374
  });
90239
90375
  }
90240
- const subagentsDir = join38(sessionDir, "subagents");
90241
- if (existsSync38(subagentsDir)) {
90376
+ const subagentsDir = join39(sessionDir, "subagents");
90377
+ if (existsSync39(subagentsDir)) {
90242
90378
  try {
90243
90379
  const subagentFiles = readdirSync14(subagentsDir).filter((f) => f.endsWith(".jsonl"));
90244
90380
  for (const file of subagentFiles) {
90245
- const filePath = join38(subagentsDir, file);
90381
+ const filePath = join39(subagentsDir, file);
90246
90382
  try {
90247
90383
  const usages = parseSessionFile(filePath);
90248
90384
  const subagentContext = {
@@ -90286,7 +90422,7 @@ function migrateAllSessions() {
90286
90422
  };
90287
90423
  console.log("Starting migration of historical session data...");
90288
90424
  const agentsDir = getAgentsDir();
90289
- if (!existsSync38(agentsDir)) {
90425
+ if (!existsSync39(agentsDir)) {
90290
90426
  console.log("No agents directory found - nothing to migrate");
90291
90427
  return stats;
90292
90428
  }
@@ -90333,71 +90469,8 @@ function migrateIfNeeded() {
90333
90469
  return migrateAllSessions();
90334
90470
  }
90335
90471
 
90336
- // review-status.ts
90337
- import { existsSync as existsSync39, readFileSync as readFileSync32, writeFileSync as writeFileSync26, mkdirSync as mkdirSync25 } from "fs";
90338
- import { join as join39, dirname as dirname5 } from "path";
90339
- import { homedir as homedir18 } from "os";
90340
- var DEFAULT_STATUS_FILE = join39(homedir18(), ".panopticon", "review-status.json");
90341
- function loadReviewStatuses(filePath = DEFAULT_STATUS_FILE) {
90342
- try {
90343
- if (existsSync39(filePath)) {
90344
- return JSON.parse(readFileSync32(filePath, "utf-8"));
90345
- }
90346
- } catch (err) {
90347
- console.error("Failed to load review statuses:", err);
90348
- }
90349
- return {};
90350
- }
90351
- function saveReviewStatuses(statuses, filePath = DEFAULT_STATUS_FILE) {
90352
- try {
90353
- const dir = dirname5(filePath);
90354
- if (!existsSync39(dir)) {
90355
- mkdirSync25(dir, { recursive: true });
90356
- }
90357
- writeFileSync26(filePath, JSON.stringify(statuses, null, 2));
90358
- } catch (err) {
90359
- console.error("Failed to save review statuses:", err);
90360
- }
90361
- }
90362
- function setReviewStatus(issueId, update, filePath = DEFAULT_STATUS_FILE) {
90363
- const statuses = loadReviewStatuses(filePath);
90364
- const existing = statuses[issueId] || {
90365
- issueId,
90366
- reviewStatus: "pending",
90367
- testStatus: "pending",
90368
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
90369
- readyForMerge: false
90370
- };
90371
- const merged = { ...existing, ...update };
90372
- const history = [...existing.history || []];
90373
- const now = (/* @__PURE__ */ new Date()).toISOString();
90374
- if (update.reviewStatus && update.reviewStatus !== existing.reviewStatus) {
90375
- history.push({ type: "review", status: update.reviewStatus, timestamp: now, notes: update.reviewNotes });
90376
- }
90377
- if (update.testStatus && update.testStatus !== existing.testStatus) {
90378
- history.push({ type: "test", status: update.testStatus, timestamp: now, notes: update.testNotes });
90379
- }
90380
- if (update.mergeStatus && update.mergeStatus !== existing.mergeStatus) {
90381
- history.push({ type: "merge", status: update.mergeStatus, timestamp: now });
90382
- }
90383
- while (history.length > 10)
90384
- history.shift();
90385
- const readyForMerge = update.readyForMerge !== void 0 ? update.readyForMerge : merged.reviewStatus === "passed" && merged.testStatus === "passed" && merged.mergeStatus !== "merged";
90386
- const updated = {
90387
- ...merged,
90388
- issueId,
90389
- updatedAt: now,
90390
- readyForMerge,
90391
- history
90392
- };
90393
- statuses[issueId] = updated;
90394
- saveReviewStatuses(statuses, filePath);
90395
- return updated;
90396
- }
90397
- function getReviewStatus(issueId, filePath = DEFAULT_STATUS_FILE) {
90398
- const statuses = loadReviewStatuses(filePath);
90399
- return statuses[issueId] || null;
90400
- }
90472
+ // index.ts
90473
+ init_review_status();
90401
90474
 
90402
90475
  // ../../lib/remote/index.ts
90403
90476
  init_exe_provider();
@@ -99563,8 +99636,20 @@ app.post("/api/mission-control/planning/:issueId/status-review", async (req, res
99563
99636
  const statePath = join43(planningDir, "STATE.md");
99564
99637
  const prd = existsSync44(prdPath) ? readFileSync37(prdPath, "utf-8") : null;
99565
99638
  const state = existsSync44(statePath) ? readFileSync37(statePath, "utf-8") : null;
99566
- if (!prd && !state) {
99567
- return res.status(400).json({ error: "No PRD or STATE.md to review against" });
99639
+ const discussionsDir = join43(planningDir, "discussions");
99640
+ let discussionsContent = "";
99641
+ if (existsSync44(discussionsDir)) {
99642
+ const discussionFiles = readdirSync17(discussionsDir).filter((f) => f.endsWith(".md") || f.endsWith(".txt"));
99643
+ for (const file of discussionFiles.slice(0, 5)) {
99644
+ const content = readFileSync37(join43(discussionsDir, file), "utf-8");
99645
+ discussionsContent += `
99646
+ ### ${file}
99647
+ ${content.slice(0, 2e3)}
99648
+ `;
99649
+ }
99650
+ }
99651
+ if (!prd && !state && !discussionsContent) {
99652
+ return res.status(400).json({ error: "No PRD, STATE.md, or discussions to review against" });
99568
99653
  }
99569
99654
  let gitDiff = "";
99570
99655
  let gitLog = "";
@@ -99622,16 +99707,19 @@ ${gitDiff || "No diff available"}
99622
99707
  ## Recent Commits
99623
99708
  ${gitLog || "No commits yet"}
99624
99709
 
99710
+ ## Discussions & Comments
99711
+ ${discussionsContent || "(No discussions synced)"}
99712
+
99625
99713
  ---
99626
99714
 
99627
99715
  Based on the above, produce a concise status review in markdown format with these sections:
99628
99716
 
99629
99717
  1. **Summary** (2-3 sentences: overall progress, what's done, what's remaining)
99630
- 2. **PRD Coverage** (which requirements are met, partially met, or not yet started \u2014 use a table)
99718
+ 2. **PRD Coverage** (which requirements are met, partially met, or not yet started \u2014 use a table. If no PRD, summarize based on available context)
99631
99719
  3. **Risk Assessment** (any concerns about code quality, missing tests, incomplete features)
99632
99720
  4. **Recommendation** (next steps, whether it's ready for review/merge, or what needs attention)
99633
99721
 
99634
- Be specific and reference actual file names and requirements. Keep it under 500 words.`;
99722
+ Be specific and reference actual file names, requirements, and discussion points. Keep it under 500 words.`;
99635
99723
  const aiReview = await callLLM(analysisPrompt);
99636
99724
  review = `# Status Review - ${issueId}
99637
99725
 
@@ -99670,6 +99758,9 @@ ${filesChanged || "No changes detected"}
99670
99758
  ${gitLog || "No commits yet"}
99671
99759
  \`\`\`
99672
99760
 
99761
+ ## Discussions
99762
+ ${discussionsContent || "(No discussions synced)"}
99763
+
99673
99764
  ---
99674
99765
  *Review by Panopticon Mission Control (static fallback)*
99675
99766
  `;