panopticon-cli 0.5.9 → 0.5.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/{agents-M2ZOZL3P.js → agents-RL2KUSP3.js} +3 -3
  2. package/dist/{archive-planning-U3AZAKWI.js → archive-planning-54J6EP6A.js} +3 -3
  3. package/dist/{chunk-WEQW3EAT.js → chunk-F4XS2FQN.js} +3 -2
  4. package/dist/chunk-F4XS2FQN.js.map +1 -0
  5. package/dist/{chunk-OJF4QS3S.js → chunk-GIW2TUWI.js} +2 -2
  6. package/dist/{chunk-GM22HPYS.js → chunk-H7T35QDO.js} +21 -3
  7. package/dist/chunk-H7T35QDO.js.map +1 -0
  8. package/dist/{chunk-MJXYTGK5.js → chunk-JZWCL5S5.js} +2 -2
  9. package/dist/{chunk-3WDSD2VK.js → chunk-NLN3ZLCN.js} +186 -88
  10. package/dist/chunk-NLN3ZLCN.js.map +1 -0
  11. package/dist/{chunk-QQ27EVBD.js → chunk-OMOEGJDB.js} +3 -3
  12. package/dist/{chunk-4R6ATXYI.js → chunk-PFA5XE2V.js} +1 -37
  13. package/dist/chunk-PFA5XE2V.js.map +1 -0
  14. package/dist/{chunk-6OYUJ4AJ.js → chunk-R47UJWF6.js} +2 -2
  15. package/dist/{chunk-KPGVCGST.js → chunk-S7EJ2OLR.js} +10 -4
  16. package/dist/chunk-S7EJ2OLR.js.map +1 -0
  17. package/dist/{chunk-R4KPLLRB.js → chunk-SFX3BG6N.js} +1 -1
  18. package/dist/chunk-SFX3BG6N.js.map +1 -0
  19. package/dist/clean-planning-V4SSVU26.js +9 -0
  20. package/dist/cli/index.js +1111 -901
  21. package/dist/cli/index.js.map +1 -1
  22. package/dist/close-issue-5OMOP2FU.js +9 -0
  23. package/dist/compact-beads-YQDVF6FQ.js +9 -0
  24. package/dist/dashboard/prompts/merge-agent.md +11 -0
  25. package/dist/dashboard/prompts/review-agent.md +9 -0
  26. package/dist/dashboard/prompts/test-agent.md +9 -0
  27. package/dist/dashboard/prompts/work-agent.md +10 -2
  28. package/dist/dashboard/public/assets/index-5hYjhhGn.js +826 -0
  29. package/dist/dashboard/public/assets/index-DIFh3T1V.css +32 -0
  30. package/dist/dashboard/public/index.html +2 -2
  31. package/dist/dashboard/server.js +2405 -1754
  32. package/dist/index.d.ts +8 -3
  33. package/dist/index.js +3 -3
  34. package/dist/{label-cleanup-4HJVX6NP.js → label-cleanup-4IVZIPGK.js} +2 -2
  35. package/dist/{merge-agent-756U4NPX.js → merge-agent-7L7MWJEC.js} +12 -12
  36. package/dist/{specialist-context-UBVUUFJV.js → specialist-context-L37RF6Z5.js} +3 -3
  37. package/dist/{specialist-logs-FQRI3AIS.js → specialist-logs-B7UC3UDO.js} +3 -3
  38. package/dist/{specialists-CXRGSJY3.js → specialists-X4OGA7WX.js} +3 -3
  39. package/dist/{workspace-manager-OWHLR5BL.js → workspace-manager-6RP5A5HF.js} +2 -2
  40. package/package.json +1 -1
  41. package/skills/pan-new-project/SKILL.md +1 -1
  42. package/skills/pan-oversee/SKILL.md +45 -10
  43. package/skills/plan/SKILL.md +336 -0
  44. package/dist/chunk-3WDSD2VK.js.map +0 -1
  45. package/dist/chunk-4R6ATXYI.js.map +0 -1
  46. package/dist/chunk-GM22HPYS.js.map +0 -1
  47. package/dist/chunk-KPGVCGST.js.map +0 -1
  48. package/dist/chunk-R4KPLLRB.js.map +0 -1
  49. package/dist/chunk-WEQW3EAT.js.map +0 -1
  50. package/dist/clean-planning-7Z5YY64X.js +0 -9
  51. package/dist/close-issue-CTZK777I.js +0 -9
  52. package/dist/compact-beads-72SHALOL.js +0 -9
  53. package/dist/dashboard/public/assets/index-Bx4NCn9A.css +0 -32
  54. package/dist/dashboard/public/assets/index-DqPey4Of.js +0 -756
  55. package/skills/opus-plan/SKILL.md +0 -400
  56. /package/dist/{agents-M2ZOZL3P.js.map → agents-RL2KUSP3.js.map} +0 -0
  57. /package/dist/{archive-planning-U3AZAKWI.js.map → archive-planning-54J6EP6A.js.map} +0 -0
  58. /package/dist/{chunk-OJF4QS3S.js.map → chunk-GIW2TUWI.js.map} +0 -0
  59. /package/dist/{chunk-MJXYTGK5.js.map → chunk-JZWCL5S5.js.map} +0 -0
  60. /package/dist/{chunk-QQ27EVBD.js.map → chunk-OMOEGJDB.js.map} +0 -0
  61. /package/dist/{chunk-6OYUJ4AJ.js.map → chunk-R47UJWF6.js.map} +0 -0
  62. /package/dist/{clean-planning-7Z5YY64X.js.map → clean-planning-V4SSVU26.js.map} +0 -0
  63. /package/dist/{close-issue-CTZK777I.js.map → close-issue-5OMOP2FU.js.map} +0 -0
  64. /package/dist/{compact-beads-72SHALOL.js.map → compact-beads-YQDVF6FQ.js.map} +0 -0
  65. /package/dist/{label-cleanup-4HJVX6NP.js.map → label-cleanup-4IVZIPGK.js.map} +0 -0
  66. /package/dist/{merge-agent-756U4NPX.js.map → merge-agent-7L7MWJEC.js.map} +0 -0
  67. /package/dist/{specialist-context-UBVUUFJV.js.map → specialist-context-L37RF6Z5.js.map} +0 -0
  68. /package/dist/{specialist-logs-FQRI3AIS.js.map → specialist-logs-B7UC3UDO.js.map} +0 -0
  69. /package/dist/{specialists-CXRGSJY3.js.map → specialists-X4OGA7WX.js.map} +0 -0
  70. /package/dist/{workspace-manager-OWHLR5BL.js.map → workspace-manager-6RP5A5HF.js.map} +0 -0
@@ -9,7 +9,7 @@ import {
9
9
  init_work_type_router,
10
10
  popFromHook,
11
11
  pushToHook
12
- } from "./chunk-4R6ATXYI.js";
12
+ } from "./chunk-PFA5XE2V.js";
13
13
  import {
14
14
  clearCredentialFileAuth,
15
15
  getProviderEnv,
@@ -552,6 +552,80 @@ var init_specialist_handoff_logger = __esm({
552
552
  }
553
553
  });
554
554
 
555
+ // src/lib/vbrief/io.ts
556
+ import { existsSync as existsSync4, readFileSync as readFileSync4, renameSync, writeFileSync as writeFileSync2 } from "fs";
557
+ import { join as join4 } from "path";
558
+ function findPlan(workspacePath) {
559
+ const planPath = join4(workspacePath, ".planning", PLAN_FILENAME);
560
+ return existsSync4(planPath) ? planPath : null;
561
+ }
562
+ function readPlan(planPath) {
563
+ const raw = readFileSync4(planPath, "utf-8");
564
+ return JSON.parse(raw);
565
+ }
566
+ function readWorkspacePlan(workspacePath) {
567
+ const planPath = findPlan(workspacePath);
568
+ if (!planPath) return null;
569
+ return readPlan(planPath);
570
+ }
571
+ function updateItemStatus(workspacePath, itemId, status) {
572
+ const planPath = findPlan(workspacePath);
573
+ if (!planPath) return;
574
+ const doc = readPlan(planPath);
575
+ const item = doc.plan.items.find((i) => i.id === itemId);
576
+ if (!item) return;
577
+ item.status = status;
578
+ const tempPath = planPath + ".tmp";
579
+ writeFileSync2(tempPath, JSON.stringify(doc, null, 2), "utf-8");
580
+ renameSync(tempPath, planPath);
581
+ }
582
+ function updateSubItemStatus(workspacePath, itemId, subItemId, status) {
583
+ const planPath = findPlan(workspacePath);
584
+ if (!planPath) return;
585
+ const doc = readPlan(planPath);
586
+ const item = doc.plan.items.find((i) => i.id === itemId);
587
+ if (!item?.subItems) return;
588
+ const subItem = item.subItems.find((s) => s.id === subItemId);
589
+ if (!subItem) return;
590
+ subItem.status = status;
591
+ const tempPath = planPath + ".tmp";
592
+ writeFileSync2(tempPath, JSON.stringify(doc, null, 2), "utf-8");
593
+ renameSync(tempPath, planPath);
594
+ }
595
+ var PLAN_FILENAME;
596
+ var init_io = __esm({
597
+ "src/lib/vbrief/io.ts"() {
598
+ "use strict";
599
+ init_esm_shims();
600
+ PLAN_FILENAME = "plan.vbrief.json";
601
+ }
602
+ });
603
+
604
+ // src/lib/cloister/task-readiness.ts
605
+ function isTaskReady(itemId, workspacePath) {
606
+ const doc = readWorkspacePlan(workspacePath);
607
+ if (!doc) return true;
608
+ const itemExists = doc.plan.items.some((i) => i.id === itemId);
609
+ if (!itemExists) return true;
610
+ const blockerIds = doc.plan.edges.filter((e) => e.type === "blocks" && e.to === itemId).map((e) => e.from);
611
+ if (blockerIds.length === 0) return true;
612
+ const itemById = new Map(doc.plan.items.map((i) => [i.id, i]));
613
+ return blockerIds.every((blockerId) => {
614
+ const blocker = itemById.get(blockerId);
615
+ if (!blocker) return true;
616
+ return TERMINAL_STATUSES.includes(blocker.status);
617
+ });
618
+ }
619
+ var TERMINAL_STATUSES;
620
+ var init_task_readiness = __esm({
621
+ "src/lib/cloister/task-readiness.ts"() {
622
+ "use strict";
623
+ init_esm_shims();
624
+ init_io();
625
+ TERMINAL_STATUSES = ["completed", "cancelled"];
626
+ }
627
+ });
628
+
555
629
  // src/lib/cloister/specialists.ts
556
630
  var specialists_exports = {};
557
631
  __export(specialists_exports, {
@@ -611,8 +685,8 @@ __export(specialists_exports, {
611
685
  wakeSpecialistOrQueue: () => wakeSpecialistOrQueue,
612
686
  wakeSpecialistWithTask: () => wakeSpecialistWithTask
613
687
  });
614
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync3, unlinkSync, appendFileSync as appendFileSync3 } from "fs";
615
- import { join as join4, basename as basename2 } from "path";
688
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync3, readdirSync as readdirSync3, unlinkSync, appendFileSync as appendFileSync3 } from "fs";
689
+ import { join as join5, basename as basename2 } from "path";
616
690
  import { homedir as homedir2 } from "os";
617
691
  import { exec } from "child_process";
618
692
  import { promisify } from "util";
@@ -623,14 +697,14 @@ async function resolveWorkspaceGitInfo(workspace, taskBranch) {
623
697
  if (!workspace || workspace === "unknown") {
624
698
  return { gitDirs, branch, isPolyrepo: false };
625
699
  }
626
- if (existsSync4(join4(workspace, ".git"))) {
700
+ if (existsSync5(join5(workspace, ".git"))) {
627
701
  gitDirs.push(workspace);
628
702
  } else {
629
703
  try {
630
704
  const entries = readdirSync3(workspace, { withFileTypes: true });
631
705
  for (const entry of entries) {
632
- if (entry.isDirectory() && existsSync4(join4(workspace, entry.name, ".git"))) {
633
- gitDirs.push(join4(workspace, entry.name));
706
+ if (entry.isDirectory() && existsSync5(join5(workspace, entry.name, ".git"))) {
707
+ gitDirs.push(join5(workspace, entry.name));
634
708
  }
635
709
  }
636
710
  } catch {
@@ -670,10 +744,10 @@ function buildTmuxEnvFlags(env) {
670
744
  return flags;
671
745
  }
672
746
  function initSpecialistsDirectory() {
673
- if (!existsSync4(SPECIALISTS_DIR)) {
747
+ if (!existsSync5(SPECIALISTS_DIR)) {
674
748
  mkdirSync3(SPECIALISTS_DIR, { recursive: true });
675
749
  }
676
- if (!existsSync4(REGISTRY_FILE)) {
750
+ if (!existsSync5(REGISTRY_FILE)) {
677
751
  const registry = {
678
752
  version: "2.0",
679
753
  // Updated for per-project structure
@@ -697,7 +771,7 @@ function initSpecialistsDirectory() {
697
771
  }
698
772
  function migrateRegistryIfNeeded() {
699
773
  try {
700
- const content = readFileSync4(REGISTRY_FILE, "utf-8");
774
+ const content = readFileSync5(REGISTRY_FILE, "utf-8");
701
775
  const registry = JSON.parse(content);
702
776
  if (registry.version === "2.0" || registry.projects) {
703
777
  return;
@@ -727,7 +801,7 @@ function migrateRegistryIfNeeded() {
727
801
  function loadRegistry() {
728
802
  initSpecialistsDirectory();
729
803
  try {
730
- const content = readFileSync4(REGISTRY_FILE, "utf-8");
804
+ const content = readFileSync5(REGISTRY_FILE, "utf-8");
731
805
  return JSON.parse(content);
732
806
  } catch (error) {
733
807
  console.error("Failed to load specialist registry:", error);
@@ -745,13 +819,13 @@ function loadRegistry() {
745
819
  }
746
820
  }
747
821
  function saveRegistry(registry) {
748
- if (!existsSync4(SPECIALISTS_DIR)) {
822
+ if (!existsSync5(SPECIALISTS_DIR)) {
749
823
  mkdirSync3(SPECIALISTS_DIR, { recursive: true });
750
824
  }
751
825
  registry.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
752
826
  try {
753
827
  const content = JSON.stringify(registry, null, 2);
754
- writeFileSync2(REGISTRY_FILE, content, "utf-8");
828
+ writeFileSync3(REGISTRY_FILE, content, "utf-8");
755
829
  } catch (error) {
756
830
  console.error("Failed to save specialist registry:", error);
757
831
  throw error;
@@ -763,17 +837,17 @@ function deterministicUUID(input) {
763
837
  }
764
838
  function getSessionFilePath(name, projectKey) {
765
839
  if (projectKey) {
766
- return join4(SPECIALISTS_DIR, "projects", projectKey, `${name}.session`);
840
+ return join5(SPECIALISTS_DIR, "projects", projectKey, `${name}.session`);
767
841
  }
768
- return join4(SPECIALISTS_DIR, `${name}.session`);
842
+ return join5(SPECIALISTS_DIR, `${name}.session`);
769
843
  }
770
844
  function getSessionId(name, projectKey) {
771
845
  const sessionFile = getSessionFilePath(name, projectKey);
772
- if (!existsSync4(sessionFile)) {
846
+ if (!existsSync5(sessionFile)) {
773
847
  return null;
774
848
  }
775
849
  try {
776
- const sessionId = readFileSync4(sessionFile, "utf-8").trim();
850
+ const sessionId = readFileSync5(sessionFile, "utf-8").trim();
777
851
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
778
852
  if (!uuidRegex.test(sessionId)) {
779
853
  console.warn(`[specialist] Invalid session ID format for ${name} (${projectKey ?? "global"}): ${sessionId} \u2014 discarding`);
@@ -788,12 +862,12 @@ function getSessionId(name, projectKey) {
788
862
  }
789
863
  function setSessionId(name, sessionId, projectKey) {
790
864
  const sessionFile = getSessionFilePath(name, projectKey);
791
- const dir = projectKey ? join4(SPECIALISTS_DIR, "projects", projectKey) : SPECIALISTS_DIR;
792
- if (!existsSync4(dir)) {
865
+ const dir = projectKey ? join5(SPECIALISTS_DIR, "projects", projectKey) : SPECIALISTS_DIR;
866
+ if (!existsSync5(dir)) {
793
867
  mkdirSync3(dir, { recursive: true });
794
868
  }
795
869
  try {
796
- writeFileSync2(sessionFile, sessionId.trim(), "utf-8");
870
+ writeFileSync3(sessionFile, sessionId.trim(), "utf-8");
797
871
  } catch (error) {
798
872
  console.error(`Failed to write session file for ${name} (${projectKey ?? "global"}):`, error);
799
873
  throw error;
@@ -801,7 +875,7 @@ function setSessionId(name, sessionId, projectKey) {
801
875
  }
802
876
  function clearSessionId(name, projectKey) {
803
877
  const sessionFile = getSessionFilePath(name, projectKey);
804
- if (!existsSync4(sessionFile)) {
878
+ if (!existsSync5(sessionFile)) {
805
879
  return false;
806
880
  }
807
881
  try {
@@ -859,9 +933,9 @@ function recordWake(name, sessionId) {
859
933
  }
860
934
  async function spawnEphemeralSpecialist(projectKey, specialistType, task) {
861
935
  ensureProjectSpecialistDir(projectKey, specialistType);
862
- const { loadContextDigest } = await import("./specialist-context-UBVUUFJV.js");
936
+ const { loadContextDigest } = await import("./specialist-context-L37RF6Z5.js");
863
937
  const contextDigest = loadContextDigest(projectKey, specialistType);
864
- const { createRunLog: createRunLog2 } = await import("./specialist-logs-FQRI3AIS.js");
938
+ const { createRunLog: createRunLog2 } = await import("./specialist-logs-B7UC3UDO.js");
865
939
  const { runId, filePath: logFilePath } = createRunLog2(
866
940
  projectKey,
867
941
  specialistType,
@@ -881,7 +955,7 @@ ${basePrompt}`;
881
955
  const project = getProject(projectKey);
882
956
  const cwd = project?.path || getDevrootPath() || homedir2();
883
957
  try {
884
- const { preTrustDirectory } = await import("./workspace-manager-OWHLR5BL.js");
958
+ const { preTrustDirectory } = await import("./workspace-manager-6RP5A5HF.js");
885
959
  preTrustDirectory(cwd);
886
960
  } catch {
887
961
  }
@@ -889,7 +963,7 @@ ${basePrompt}`;
889
963
  try {
890
964
  const { stdout: sessions } = await execAsync('tmux list-sessions -F "#{session_name}" 2>/dev/null || echo ""', { encoding: "utf-8" });
891
965
  if (sessions.split("\n").map((s) => s.trim()).includes(tmuxSession)) {
892
- const { getAgentRuntimeState } = await import("./agents-M2ZOZL3P.js");
966
+ const { getAgentRuntimeState } = await import("./agents-RL2KUSP3.js");
893
967
  const existingState = getAgentRuntimeState(tmuxSession);
894
968
  if (existingState?.state === "active") {
895
969
  return {
@@ -926,17 +1000,17 @@ ${basePrompt}`;
926
1000
  clearCredentialFileAuth(cwd);
927
1001
  }
928
1002
  const permissionFlags = specialistType === "merge-agent" ? "--dangerously-skip-permissions --permission-mode bypassPermissions" : "--dangerously-skip-permissions";
929
- const agentDir = join4(homedir2(), ".panopticon", "agents", tmuxSession);
1003
+ const agentDir = join5(homedir2(), ".panopticon", "agents", tmuxSession);
930
1004
  await execAsync(`mkdir -p "${agentDir}"`, { encoding: "utf-8" });
931
- const promptFile = join4(agentDir, "task-prompt.md");
932
- writeFileSync2(promptFile, taskPrompt);
1005
+ const promptFile = join5(agentDir, "task-prompt.md");
1006
+ writeFileSync3(promptFile, taskPrompt);
933
1007
  const sessionName = `specialist-${projectKey}-${specialistType}`;
934
1008
  const sessionId = deterministicUUID(sessionName);
935
1009
  setSessionId(specialistType, sessionId, projectKey);
936
1010
  console.log(`[specialist] Dispatching ${specialistType} for ${projectKey}/${task.issueId} (session: ${sessionId.slice(0, 8)}...)`);
937
- const launcherScript = join4(agentDir, "launcher.sh");
938
- const innerScript = join4(agentDir, "run-claude.sh");
939
- writeFileSync2(innerScript, `#!/bin/bash
1011
+ const launcherScript = join5(agentDir, "launcher.sh");
1012
+ const innerScript = join5(agentDir, "run-claude.sh");
1013
+ writeFileSync3(innerScript, `#!/bin/bash
940
1014
  set -o pipefail
941
1015
  cd "${cwd}"
942
1016
  export PANOPTICON_AGENT_ID="${tmuxSession}"
@@ -958,14 +1032,14 @@ fi
958
1032
  echo ""
959
1033
  echo "## Specialist completed task"
960
1034
  `, { mode: 493 });
961
- writeFileSync2(launcherScript, `#!/bin/bash
1035
+ writeFileSync3(launcherScript, `#!/bin/bash
962
1036
  script -qfec "bash '${innerScript}'" /dev/null 2>&1 | tee -a "${logFilePath}"
963
1037
  `, { mode: 493 });
964
1038
  await execAsync(
965
1039
  `tmux new-session -d -s "${tmuxSession}" -c "${cwd}"${envFlags} "bash '${launcherScript}'"`,
966
1040
  { encoding: "utf-8" }
967
1041
  );
968
- const { saveAgentRuntimeState } = await import("./agents-M2ZOZL3P.js");
1042
+ const { saveAgentRuntimeState } = await import("./agents-RL2KUSP3.js");
969
1043
  saveAgentRuntimeState(tmuxSession, {
970
1044
  state: "active",
971
1045
  lastActivity: (/* @__PURE__ */ new Date()).toISOString(),
@@ -1150,7 +1224,7 @@ async function terminateSpecialist(projectKey, specialistType) {
1150
1224
  console.error(`[specialist] Failed to kill tmux session ${tmuxSession}:`, error);
1151
1225
  }
1152
1226
  if (metadata.currentRun) {
1153
- const { finalizeRunLog: finalizeRunLog2 } = await import("./specialist-logs-FQRI3AIS.js");
1227
+ const { finalizeRunLog: finalizeRunLog2 } = await import("./specialist-logs-B7UC3UDO.js");
1154
1228
  try {
1155
1229
  finalizeRunLog2(projectKey, specialistType, metadata.currentRun, {
1156
1230
  status: metadata.lastRunStatus || "incomplete",
@@ -1163,19 +1237,19 @@ async function terminateSpecialist(projectKey, specialistType) {
1163
1237
  }
1164
1238
  const key = `${projectKey}-${specialistType}`;
1165
1239
  gracePeriodStates.delete(key);
1166
- const { saveAgentRuntimeState } = await import("./agents-M2ZOZL3P.js");
1240
+ const { saveAgentRuntimeState } = await import("./agents-RL2KUSP3.js");
1167
1241
  saveAgentRuntimeState(tmuxSession, {
1168
1242
  state: "suspended",
1169
1243
  lastActivity: (/* @__PURE__ */ new Date()).toISOString()
1170
1244
  });
1171
- const { scheduleDigestGeneration } = await import("./specialist-context-UBVUUFJV.js");
1245
+ const { scheduleDigestGeneration } = await import("./specialist-context-L37RF6Z5.js");
1172
1246
  scheduleDigestGeneration(projectKey, specialistType);
1173
1247
  scheduleLogCleanup(projectKey, specialistType);
1174
1248
  }
1175
1249
  function scheduleLogCleanup(projectKey, specialistType) {
1176
1250
  Promise.resolve().then(async () => {
1177
1251
  try {
1178
- const { cleanupOldLogs: cleanupOldLogs2 } = await import("./specialist-logs-FQRI3AIS.js");
1252
+ const { cleanupOldLogs: cleanupOldLogs2 } = await import("./specialist-logs-B7UC3UDO.js");
1179
1253
  const { getSpecialistRetention } = await import("./projects-BPGM6IFB.js");
1180
1254
  const retention = getSpecialistRetention(projectKey);
1181
1255
  const deleted = cleanupOldLogs2(projectKey, specialistType, { maxDays: retention.max_days, maxRuns: retention.max_runs });
@@ -1188,16 +1262,16 @@ function scheduleLogCleanup(projectKey, specialistType) {
1188
1262
  });
1189
1263
  }
1190
1264
  function getProjectSpecialistDir(projectKey, specialistType) {
1191
- return join4(SPECIALISTS_DIR, projectKey, specialistType);
1265
+ return join5(SPECIALISTS_DIR, projectKey, specialistType);
1192
1266
  }
1193
1267
  function ensureProjectSpecialistDir(projectKey, specialistType) {
1194
1268
  const specialistDir = getProjectSpecialistDir(projectKey, specialistType);
1195
- const runsDir = join4(specialistDir, "runs");
1196
- const contextDir = join4(specialistDir, "context");
1197
- if (!existsSync4(runsDir)) {
1269
+ const runsDir = join5(specialistDir, "runs");
1270
+ const contextDir = join5(specialistDir, "context");
1271
+ if (!existsSync5(runsDir)) {
1198
1272
  mkdirSync3(runsDir, { recursive: true });
1199
1273
  }
1200
- if (!existsSync4(contextDir)) {
1274
+ if (!existsSync5(contextDir)) {
1201
1275
  mkdirSync3(contextDir, { recursive: true });
1202
1276
  }
1203
1277
  }
@@ -1364,7 +1438,7 @@ async function getSpecialistStatus(name, projectKey) {
1364
1438
  const sessionId = getSessionId(name, projectKey);
1365
1439
  const running = await isRunning(name, projectKey);
1366
1440
  const contextTokens = countContextTokens(name);
1367
- const { getAgentRuntimeState } = await import("./agents-M2ZOZL3P.js");
1441
+ const { getAgentRuntimeState } = await import("./agents-RL2KUSP3.js");
1368
1442
  const tmuxSession = getTmuxSessionName(name, projectKey);
1369
1443
  const runtimeState = getAgentRuntimeState(tmuxSession);
1370
1444
  let state;
@@ -1445,13 +1519,13 @@ Say: "I am the ${name} specialist, ready and waiting for tasks."`;
1445
1519
  } else {
1446
1520
  clearCredentialFileAuth(cwd);
1447
1521
  }
1448
- const agentDir = join4(homedir2(), ".panopticon", "agents", tmuxSession);
1522
+ const agentDir = join5(homedir2(), ".panopticon", "agents", tmuxSession);
1449
1523
  await execAsync(`mkdir -p "${agentDir}"`, { encoding: "utf-8" });
1450
- const promptFile = join4(agentDir, "identity-prompt.md");
1451
- const launcherScript = join4(agentDir, "launcher.sh");
1452
- writeFileSync2(promptFile, identityPrompt);
1524
+ const promptFile = join5(agentDir, "identity-prompt.md");
1525
+ const launcherScript = join5(agentDir, "launcher.sh");
1526
+ writeFileSync3(promptFile, identityPrompt);
1453
1527
  const newSessionId = randomUUID();
1454
- writeFileSync2(launcherScript, `#!/bin/bash
1528
+ writeFileSync3(launcherScript, `#!/bin/bash
1455
1529
  cd "${cwd}"
1456
1530
  prompt=$(cat "${promptFile}")
1457
1531
  exec claude --dangerously-skip-permissions --session-id "${newSessionId}" --model ${model} "$prompt"
@@ -1519,7 +1593,7 @@ async function wakeSpecialist(name, taskPrompt, options = {}) {
1519
1593
  const sessionId = getSessionId(name);
1520
1594
  const wasAlreadyRunning = await isRunning(name);
1521
1595
  if (wasAlreadyRunning && !options.skipBusyGuard) {
1522
- const { getAgentRuntimeState } = await import("./agents-M2ZOZL3P.js");
1596
+ const { getAgentRuntimeState } = await import("./agents-RL2KUSP3.js");
1523
1597
  const runtimeState = getAgentRuntimeState(tmuxSession);
1524
1598
  if (runtimeState?.state === "active") {
1525
1599
  console.warn(`[specialist] ${name} is busy (working on ${runtimeState.currentIssue}), refusing to interrupt`);
@@ -1541,9 +1615,9 @@ async function wakeSpecialist(name, taskPrompt, options = {}) {
1541
1615
  error: "not_running"
1542
1616
  };
1543
1617
  }
1544
- const cwd = getDevrootPath() || join4(process.env.HOME || "/home/eltmon", "Projects");
1618
+ const cwd = getDevrootPath() || join5(process.env.HOME || "/home/eltmon", "Projects");
1545
1619
  try {
1546
- const { preTrustDirectory } = await import("./workspace-manager-OWHLR5BL.js");
1620
+ const { preTrustDirectory } = await import("./workspace-manager-6RP5A5HF.js");
1547
1621
  preTrustDirectory(cwd);
1548
1622
  } catch {
1549
1623
  }
@@ -1610,11 +1684,11 @@ async function wakeSpecialist(name, taskPrompt, options = {}) {
1610
1684
  const isLargePrompt = taskPrompt.length > 500 || taskPrompt.includes("\n");
1611
1685
  let messageToSend;
1612
1686
  if (isLargePrompt) {
1613
- if (!existsSync4(TASKS_DIR)) {
1687
+ if (!existsSync5(TASKS_DIR)) {
1614
1688
  mkdirSync3(TASKS_DIR, { recursive: true });
1615
1689
  }
1616
- const taskFile = join4(TASKS_DIR, `${name}-${Date.now()}.md`);
1617
- writeFileSync2(taskFile, taskPrompt, "utf-8");
1690
+ const taskFile = join5(TASKS_DIR, `${name}-${Date.now()}.md`);
1691
+ writeFileSync3(taskFile, taskPrompt, "utf-8");
1618
1692
  messageToSend = `Read and execute the task in: ${taskFile}`;
1619
1693
  } else {
1620
1694
  messageToSend = taskPrompt;
@@ -1638,7 +1712,7 @@ async function wakeSpecialist(name, taskPrompt, options = {}) {
1638
1712
  }
1639
1713
  }
1640
1714
  recordWake(name, sessionId || void 0);
1641
- const { saveAgentRuntimeState } = await import("./agents-M2ZOZL3P.js");
1715
+ const { saveAgentRuntimeState } = await import("./agents-RL2KUSP3.js");
1642
1716
  saveAgentRuntimeState(tmuxSession, {
1643
1717
  state: "active",
1644
1718
  lastActivity: (/* @__PURE__ */ new Date()).toISOString(),
@@ -1696,7 +1770,7 @@ PHASE 1 \u2014 SYNC & BASELINE (before merge):
1696
1770
  If rebase conflicts: abort and report failure.
1697
1771
  5. Run tests on main to establish a baseline. Record BASELINE_PASS and BASELINE_FAIL.
1698
1772
 
1699
- PHASE 2 \u2014 MERGE:
1773
+ PHASE 2 \u2014 MERGE (dry run):
1700
1774
  6. git merge ${mergeBranch} --no-edit
1701
1775
  7. If conflicts: resolve them intelligently, then git add and git commit
1702
1776
  8. If clean merge: the merge commit is auto-created (or fast-forward)
@@ -1706,10 +1780,13 @@ PHASE 3 \u2014 VERIFY:
1706
1780
 
1707
1781
  PHASE 4 \u2014 DECIDE:
1708
1782
  10. Compare results:
1709
- - If MERGE_FAIL > BASELINE_FAIL (NEW test failures): ROLLBACK with git reset --hard ORIG_HEAD
1710
- - If MERGE_FAIL <= BASELINE_FAIL (no new failures): PUSH with git push origin main
1783
+ - If MERGE_FAIL > BASELINE_FAIL (NEW test failures): ROLLBACK with git reset --hard ORIG_HEAD and report FAILED
1784
+ - If MERGE_FAIL <= BASELINE_FAIL (no new failures): Report PASSED (merge is validated)
1711
1785
  - Pre-existing failures on main are NOT a reason to rollback
1712
1786
 
1787
+ CRITICAL: Do NOT push to main. Do NOT run git push origin main.
1788
+ The merge validation stays LOCAL. A human will click Merge in the dashboard to push.
1789
+
1713
1790
  PHASE 5 \u2014 REPORT:
1714
1791
  11. Call the Panopticon API to report results:
1715
1792
  curl -s -X POST ${apiUrl}/api/specialists/done \\
@@ -1717,6 +1794,7 @@ PHASE 5 \u2014 REPORT:
1717
1794
  -d '{"specialist":"merge","issueId":"${task.issueId}","status":"passed|failed","notes":"<summary>"}'
1718
1795
 
1719
1796
  CRITICAL: You MUST call the /api/specialists/done endpoint whether you succeed or fail.
1797
+ CRITICAL: NEVER push to main \u2014 only humans merge. Your job is to VALIDATE the merge, not execute it.
1720
1798
  CRITICAL: NEVER use git push --force.
1721
1799
  CRITICAL: Do NOT delete the feature branch.`;
1722
1800
  break;
@@ -1747,7 +1825,7 @@ CRITICAL: Do NOT delete the feature branch.`;
1747
1825
  });
1748
1826
  console.log(`[specialist] review-agent: auto-passed ${task.issueId} (stale branch)`);
1749
1827
  const tmuxSession = getTmuxSessionName("review-agent");
1750
- const { saveAgentRuntimeState } = await import("./agents-M2ZOZL3P.js");
1828
+ const { saveAgentRuntimeState } = await import("./agents-RL2KUSP3.js");
1751
1829
  saveAgentRuntimeState(tmuxSession, {
1752
1830
  state: "idle",
1753
1831
  lastActivity: (/* @__PURE__ */ new Date()).toISOString()
@@ -2013,8 +2091,23 @@ IMPORTANT: Do NOT hand off to merge-agent. Human clicks Merge button when ready.
2013
2091
  }
2014
2092
  async function wakeSpecialistOrQueue(name, task, options = {}) {
2015
2093
  const { priority = "normal", source = "handoff" } = options;
2094
+ const vbriefItemId = task.context?.vbriefItemId;
2095
+ const workspacePath = task.workspace || task.context?.workspace;
2096
+ if (vbriefItemId && workspacePath) {
2097
+ try {
2098
+ if (!isTaskReady(vbriefItemId, workspacePath)) {
2099
+ return {
2100
+ success: false,
2101
+ queued: false,
2102
+ message: `Task "${vbriefItemId}" has incomplete blocking dependencies \u2014 not ready to schedule`
2103
+ };
2104
+ }
2105
+ } catch (readinessErr) {
2106
+ console.warn(`[specialist] Task readiness check failed for ${vbriefItemId}: ${readinessErr.message}`);
2107
+ }
2108
+ }
2016
2109
  const running = await isRunning(name);
2017
- const { getAgentRuntimeState } = await import("./agents-M2ZOZL3P.js");
2110
+ const { getAgentRuntimeState } = await import("./agents-RL2KUSP3.js");
2018
2111
  const tmuxSession = getTmuxSessionName(name);
2019
2112
  const runtimeState = getAgentRuntimeState(tmuxSession);
2020
2113
  const idle = runtimeState?.state === "idle" || runtimeState?.state === "suspended";
@@ -2045,7 +2138,7 @@ async function wakeSpecialistOrQueue(name, task, options = {}) {
2045
2138
  };
2046
2139
  }
2047
2140
  }
2048
- const { saveAgentRuntimeState } = await import("./agents-M2ZOZL3P.js");
2141
+ const { saveAgentRuntimeState } = await import("./agents-RL2KUSP3.js");
2049
2142
  saveAgentRuntimeState(tmuxSession, {
2050
2143
  state: "active",
2051
2144
  lastActivity: (/* @__PURE__ */ new Date()).toISOString(),
@@ -2129,7 +2222,7 @@ function getNextSpecialistTask(specialistName) {
2129
2222
  }
2130
2223
  async function sendFeedbackToAgent(feedback) {
2131
2224
  const { fromSpecialist, toIssueId, summary, details } = feedback;
2132
- if (!existsSync4(FEEDBACK_DIR)) {
2225
+ if (!existsSync5(FEEDBACK_DIR)) {
2133
2226
  mkdirSync3(FEEDBACK_DIR, { recursive: true });
2134
2227
  }
2135
2228
  const fullFeedback = {
@@ -2165,7 +2258,7 @@ async function sendFeedbackToAgent(feedback) {
2165
2258
  return false;
2166
2259
  }
2167
2260
  try {
2168
- const { messageAgent } = await import("./agents-M2ZOZL3P.js");
2261
+ const { messageAgent } = await import("./agents-RL2KUSP3.js");
2169
2262
  const msg = `SPECIALIST FEEDBACK: ${fromSpecialist} reported ${feedback.feedbackType.toUpperCase()} for ${toIssueId}.
2170
2263
  Read and address: ${fileResult.relativePath}`;
2171
2264
  await messageAgent(agentSession, msg);
@@ -2224,11 +2317,11 @@ ${details}
2224
2317
  return message;
2225
2318
  }
2226
2319
  function getPendingFeedback(issueId) {
2227
- if (!existsSync4(FEEDBACK_LOG)) {
2320
+ if (!existsSync5(FEEDBACK_LOG)) {
2228
2321
  return [];
2229
2322
  }
2230
2323
  try {
2231
- const content = readFileSync4(FEEDBACK_LOG, "utf-8");
2324
+ const content = readFileSync5(FEEDBACK_LOG, "utf-8");
2232
2325
  const lines = content.trim().split("\n").filter((l) => l.length > 0);
2233
2326
  const allFeedback = lines.map((line) => JSON.parse(line));
2234
2327
  return allFeedback.filter((f) => f.toIssueId.toLowerCase() === issueId.toLowerCase());
@@ -2247,11 +2340,11 @@ function getFeedbackStats() {
2247
2340
  byType: {},
2248
2341
  total: 0
2249
2342
  };
2250
- if (!existsSync4(FEEDBACK_LOG)) {
2343
+ if (!existsSync5(FEEDBACK_LOG)) {
2251
2344
  return stats;
2252
2345
  }
2253
2346
  try {
2254
- const content = readFileSync4(FEEDBACK_LOG, "utf-8");
2347
+ const content = readFileSync5(FEEDBACK_LOG, "utf-8");
2255
2348
  const lines = content.trim().split("\n").filter((l) => l.length > 0);
2256
2349
  for (const line of lines) {
2257
2350
  const feedback = JSON.parse(line);
@@ -2278,11 +2371,12 @@ var init_specialists = __esm({
2278
2371
  init_providers();
2279
2372
  init_tmux();
2280
2373
  init_pipeline_notifier();
2374
+ init_task_readiness();
2281
2375
  init_hooks();
2282
2376
  execAsync = promisify(exec);
2283
- SPECIALISTS_DIR = join4(PANOPTICON_HOME, "specialists");
2284
- REGISTRY_FILE = join4(SPECIALISTS_DIR, "registry.json");
2285
- TASKS_DIR = join4(SPECIALISTS_DIR, "tasks");
2377
+ SPECIALISTS_DIR = join5(PANOPTICON_HOME, "specialists");
2378
+ REGISTRY_FILE = join5(SPECIALISTS_DIR, "registry.json");
2379
+ TASKS_DIR = join5(SPECIALISTS_DIR, "tasks");
2286
2380
  DEFAULT_SPECIALISTS = [
2287
2381
  {
2288
2382
  name: "merge-agent",
@@ -2307,8 +2401,8 @@ var init_specialists = __esm({
2307
2401
  }
2308
2402
  ];
2309
2403
  gracePeriodStates = /* @__PURE__ */ new Map();
2310
- FEEDBACK_DIR = join4(PANOPTICON_HOME, "specialists", "feedback");
2311
- FEEDBACK_LOG = join4(FEEDBACK_DIR, "feedback.jsonl");
2404
+ FEEDBACK_DIR = join5(PANOPTICON_HOME, "specialists", "feedback");
2405
+ FEEDBACK_LOG = join5(FEEDBACK_DIR, "feedback.jsonl");
2312
2406
  }
2313
2407
  });
2314
2408
 
@@ -2332,13 +2426,13 @@ __export(specialist_logs_exports, {
2332
2426
  listRunLogs: () => listRunLogs,
2333
2427
  parseLogMetadata: () => parseLogMetadata
2334
2428
  });
2335
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, writeFileSync as writeFileSync3, appendFileSync as appendFileSync4, readFileSync as readFileSync5, readdirSync as readdirSync4, statSync as statSync2, unlinkSync as unlinkSync2 } from "fs";
2336
- import { join as join5, basename as basename3 } from "path";
2429
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, appendFileSync as appendFileSync4, readFileSync as readFileSync6, readdirSync as readdirSync4, statSync as statSync2, unlinkSync as unlinkSync2 } from "fs";
2430
+ import { join as join6, basename as basename3 } from "path";
2337
2431
  function getSpecialistsDir() {
2338
- return join5(getPanopticonHome(), "specialists");
2432
+ return join6(getPanopticonHome(), "specialists");
2339
2433
  }
2340
2434
  function getRunsDirectory(projectKey, specialistType) {
2341
- return join5(getSpecialistsDir(), projectKey, specialistType, "runs");
2435
+ return join6(getSpecialistsDir(), projectKey, specialistType, "runs");
2342
2436
  }
2343
2437
  function generateRunId(issueId) {
2344
2438
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").substring(0, 19);
@@ -2346,11 +2440,11 @@ function generateRunId(issueId) {
2346
2440
  }
2347
2441
  function getRunLogPath(projectKey, specialistType, runId) {
2348
2442
  const runsDir = getRunsDirectory(projectKey, specialistType);
2349
- return join5(runsDir, `${runId}.log`);
2443
+ return join6(runsDir, `${runId}.log`);
2350
2444
  }
2351
2445
  function ensureRunsDirectory(projectKey, specialistType) {
2352
2446
  const runsDir = getRunsDirectory(projectKey, specialistType);
2353
- if (!existsSync5(runsDir)) {
2447
+ if (!existsSync6(runsDir)) {
2354
2448
  mkdirSync4(runsDir, { recursive: true });
2355
2449
  }
2356
2450
  }
@@ -2370,22 +2464,22 @@ ${contextSeed ? contextSeed : "[No context digest available]"}
2370
2464
 
2371
2465
  ## Session Transcript
2372
2466
  `;
2373
- writeFileSync3(filePath, header, "utf-8");
2467
+ writeFileSync4(filePath, header, "utf-8");
2374
2468
  return { runId, filePath };
2375
2469
  }
2376
2470
  function appendToRunLog(projectKey, specialistType, runId, content) {
2377
2471
  const filePath = getRunLogPath(projectKey, specialistType, runId);
2378
- if (!existsSync5(filePath)) {
2472
+ if (!existsSync6(filePath)) {
2379
2473
  throw new Error(`Run log not found: ${filePath}`);
2380
2474
  }
2381
2475
  appendFileSync4(filePath, content, "utf-8");
2382
2476
  }
2383
2477
  function finalizeRunLog(projectKey, specialistType, runId, result) {
2384
2478
  const filePath = getRunLogPath(projectKey, specialistType, runId);
2385
- if (!existsSync5(filePath)) {
2479
+ if (!existsSync6(filePath)) {
2386
2480
  throw new Error(`Run log not found: ${filePath}`);
2387
2481
  }
2388
- const content = readFileSync5(filePath, "utf-8");
2482
+ const content = readFileSync6(filePath, "utf-8");
2389
2483
  const startMatch = content.match(/^Started: (.+)$/m);
2390
2484
  const startedAt = startMatch ? new Date(startMatch[1]) : /* @__PURE__ */ new Date();
2391
2485
  const finishedAt = /* @__PURE__ */ new Date();
@@ -2406,11 +2500,11 @@ Finished: ${finishedAt.toISOString()}
2406
2500
  }
2407
2501
  function getRunLog(projectKey, specialistType, runId) {
2408
2502
  const filePath = getRunLogPath(projectKey, specialistType, runId);
2409
- if (!existsSync5(filePath)) {
2503
+ if (!existsSync6(filePath)) {
2410
2504
  return null;
2411
2505
  }
2412
2506
  try {
2413
- return readFileSync5(filePath, "utf-8");
2507
+ return readFileSync6(filePath, "utf-8");
2414
2508
  } catch (error) {
2415
2509
  console.error(`Failed to read run log ${runId}:`, error);
2416
2510
  return null;
@@ -2445,15 +2539,15 @@ function parseLogMetadata(logContent) {
2445
2539
  }
2446
2540
  function listRunLogs(projectKey, specialistType, options = {}) {
2447
2541
  const runsDir = getRunsDirectory(projectKey, specialistType);
2448
- if (!existsSync5(runsDir)) {
2542
+ if (!existsSync6(runsDir)) {
2449
2543
  return [];
2450
2544
  }
2451
2545
  try {
2452
2546
  const files = readdirSync4(runsDir).filter((f) => f.endsWith(".log")).map((f) => {
2453
- const filePath = join5(runsDir, f);
2547
+ const filePath = join6(runsDir, f);
2454
2548
  const stats = statSync2(filePath);
2455
2549
  const runId = basename3(f, ".log");
2456
- const content = readFileSync5(filePath, "utf-8");
2550
+ const content = readFileSync6(filePath, "utf-8");
2457
2551
  const metadata = parseLogMetadata(content);
2458
2552
  return {
2459
2553
  runId,
@@ -2526,7 +2620,7 @@ function isRunLogActive(projectKey, specialistType, runId) {
2526
2620
  }
2527
2621
  function getRunLogSize(projectKey, specialistType, runId) {
2528
2622
  const filePath = getRunLogPath(projectKey, specialistType, runId);
2529
- if (!existsSync5(filePath)) {
2623
+ if (!existsSync6(filePath)) {
2530
2624
  return null;
2531
2625
  }
2532
2626
  try {
@@ -2583,6 +2677,10 @@ var init_specialist_logs = __esm({
2583
2677
  });
2584
2678
 
2585
2679
  export {
2680
+ readWorkspacePlan,
2681
+ updateItemStatus,
2682
+ updateSubItemStatus,
2683
+ init_io,
2586
2684
  readTodayCosts,
2587
2685
  readIssueCosts,
2588
2686
  summarizeCosts,
@@ -2674,4 +2772,4 @@ export {
2674
2772
  getFeedbackStats,
2675
2773
  init_specialists
2676
2774
  };
2677
- //# sourceMappingURL=chunk-3WDSD2VK.js.map
2775
+ //# sourceMappingURL=chunk-NLN3ZLCN.js.map