panopticon-cli 0.4.25 → 0.4.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-G4H6KZDC.js → chunk-4KNEZGKZ.js} +61 -10
- package/dist/chunk-4KNEZGKZ.js.map +1 -0
- package/dist/cli/index.js +7 -7
- package/dist/dashboard/server.js +421 -348
- package/dist/review-status-GWQYY77L.js +89 -0
- package/dist/review-status-GWQYY77L.js.map +1 -0
- package/dist/{specialist-context-VWST6O2N.js → specialist-context-WXO3FKIB.js} +2 -2
- package/dist/{specialist-logs-OU3KESAI.js → specialist-logs-SJWLETJT.js} +2 -2
- package/dist/{specialists-QVAZGJPU.js → specialists-5YJIDRW6.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-G4H6KZDC.js.map +0 -1
- /package/dist/{specialist-context-VWST6O2N.js.map → specialist-context-WXO3FKIB.js.map} +0 -0
- /package/dist/{specialist-logs-OU3KESAI.js.map → specialist-logs-SJWLETJT.js.map} +0 -0
- /package/dist/{specialists-QVAZGJPU.js.map → specialists-5YJIDRW6.js.map} +0 -0
package/dist/dashboard/server.js
CHANGED
|
@@ -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
|
|
65726
|
-
import { join as
|
|
65727
|
-
import { homedir as
|
|
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 (!
|
|
65732
|
-
|
|
65816
|
+
if (!existsSync20(SPECIALISTS_DIR)) {
|
|
65817
|
+
mkdirSync15(SPECIALISTS_DIR, { recursive: true });
|
|
65733
65818
|
}
|
|
65734
|
-
if (!
|
|
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 =
|
|
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 =
|
|
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 (!
|
|
65807
|
-
|
|
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
|
-
|
|
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
|
|
65904
|
+
return join21(SPECIALISTS_DIR, `${name}.session`);
|
|
65820
65905
|
}
|
|
65821
65906
|
function getSessionId2(name) {
|
|
65822
65907
|
const sessionFile = getSessionFilePath(name);
|
|
65823
|
-
if (!
|
|
65908
|
+
if (!existsSync20(sessionFile)) {
|
|
65824
65909
|
return null;
|
|
65825
65910
|
}
|
|
65826
65911
|
try {
|
|
65827
|
-
return
|
|
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
|
-
|
|
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 (!
|
|
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 =
|
|
66011
|
+
const agentDir = join21(homedir11(), ".panopticon", "agents", tmuxSession);
|
|
65927
66012
|
await execAsync4(`mkdir -p "${agentDir}"`, { encoding: "utf-8" });
|
|
65928
|
-
const promptFile =
|
|
65929
|
-
|
|
65930
|
-
const launcherScript =
|
|
65931
|
-
|
|
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
|
|
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 =
|
|
66174
|
-
const contextDir =
|
|
66175
|
-
if (!
|
|
66176
|
-
|
|
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 (!
|
|
66179
|
-
|
|
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 =
|
|
66496
|
+
const agentDir = join21(homedir11(), ".panopticon", "agents", tmuxSession);
|
|
66409
66497
|
await execAsync4(`mkdir -p "${agentDir}"`, { encoding: "utf-8" });
|
|
66410
|
-
const promptFile =
|
|
66411
|
-
const launcherScript =
|
|
66412
|
-
|
|
66413
|
-
|
|
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 (!
|
|
66517
|
-
|
|
66604
|
+
if (!existsSync20(TASKS_DIR)) {
|
|
66605
|
+
mkdirSync15(TASKS_DIR, { recursive: true });
|
|
66518
66606
|
}
|
|
66519
|
-
const taskFile =
|
|
66520
|
-
|
|
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: ${
|
|
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 ${
|
|
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 ${
|
|
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 (!
|
|
66831
|
-
|
|
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 (!
|
|
67040
|
+
if (!existsSync20(FEEDBACK_LOG)) {
|
|
66905
67041
|
return [];
|
|
66906
67042
|
}
|
|
66907
67043
|
try {
|
|
66908
|
-
const content =
|
|
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 (!
|
|
67063
|
+
if (!existsSync20(FEEDBACK_LOG)) {
|
|
66928
67064
|
return stats;
|
|
66929
67065
|
}
|
|
66930
67066
|
try {
|
|
66931
|
-
const content =
|
|
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 =
|
|
66956
|
-
REGISTRY_FILE =
|
|
66957
|
-
TASKS_DIR =
|
|
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 =
|
|
66983
|
-
FEEDBACK_LOG =
|
|
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
|
|
66991
|
-
import { existsSync as
|
|
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 ||
|
|
67079
|
-
if (!
|
|
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
|
|
67152
|
-
import { join as
|
|
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 =
|
|
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 =
|
|
67181
|
-
if (
|
|
67316
|
+
const indexLock = join33(repoPath, ".git", "index.lock");
|
|
67317
|
+
if (existsSync32(indexLock)) {
|
|
67182
67318
|
lockFiles.push(indexLock);
|
|
67183
67319
|
}
|
|
67184
|
-
const refsDir =
|
|
67185
|
-
if (
|
|
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 =
|
|
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
|
|
67249
|
-
import { join as
|
|
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 =
|
|
67255
|
-
if (!
|
|
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 =
|
|
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 =
|
|
67267
|
-
if (
|
|
67402
|
+
const packageJsonPath = join34(projectPath, "package.json");
|
|
67403
|
+
if (existsSync33(packageJsonPath)) {
|
|
67268
67404
|
try {
|
|
67269
|
-
const packageJson = JSON.parse(
|
|
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 (
|
|
67412
|
+
if (existsSync33(join34(projectPath, "pom.xml"))) {
|
|
67277
67413
|
return "mvn test";
|
|
67278
67414
|
}
|
|
67279
|
-
if (
|
|
67415
|
+
if (existsSync33(join34(projectPath, "Cargo.toml"))) {
|
|
67280
67416
|
return "cargo test";
|
|
67281
67417
|
}
|
|
67282
|
-
if (
|
|
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 =
|
|
67297
|
-
if (!
|
|
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 =
|
|
67335
|
-
const completedPrdPath =
|
|
67336
|
-
if (
|
|
67337
|
-
const completedDir =
|
|
67338
|
-
if (!
|
|
67339
|
-
|
|
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 (!
|
|
67525
|
-
|
|
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 =
|
|
67981
|
-
SPECIALISTS_DIR2 =
|
|
67982
|
-
MERGE_HISTORY_DIR =
|
|
67983
|
-
MERGE_HISTORY_FILE =
|
|
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
|
|
85991
|
-
import { join as
|
|
85992
|
-
import { homedir as
|
|
85993
|
-
var CLAUDE_PROJECTS_DIR2 =
|
|
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 (!
|
|
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 =
|
|
86009
|
-
if (
|
|
86144
|
+
const indexPath = join22(projectDir, "sessions-index.json");
|
|
86145
|
+
if (existsSync21(indexPath)) {
|
|
86010
86146
|
try {
|
|
86011
|
-
const indexContent =
|
|
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 =
|
|
86026
|
-
if (!
|
|
86161
|
+
const indexPath = join22(projectDir, "sessions-index.json");
|
|
86162
|
+
if (!existsSync21(indexPath)) {
|
|
86027
86163
|
return null;
|
|
86028
86164
|
}
|
|
86029
86165
|
try {
|
|
86030
|
-
const indexContent =
|
|
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 =
|
|
86067
|
-
if (
|
|
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 || !
|
|
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 =
|
|
86095
|
-
if (!
|
|
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 =
|
|
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 =
|
|
86204
|
-
|
|
86339
|
+
const mailDir = join22(getAgentDir(agentId), "mail");
|
|
86340
|
+
mkdirSync16(mailDir, { recursive: true });
|
|
86205
86341
|
const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
86206
|
-
|
|
86207
|
-
|
|
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
|
|
86379
|
-
import { join as
|
|
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 =
|
|
86472
|
-
if (
|
|
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
|
|
86620
|
-
import { join as
|
|
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
|
|
86624
|
-
import { join as
|
|
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 =
|
|
86649
|
-
if (
|
|
86650
|
-
context.stateFile =
|
|
86784
|
+
const stateFile = join24(workspace, ".planning/STATE.md");
|
|
86785
|
+
if (existsSync23(stateFile)) {
|
|
86786
|
+
context.stateFile = readFileSync19(stateFile, "utf-8");
|
|
86651
86787
|
}
|
|
86652
|
-
const claudeMd =
|
|
86653
|
-
if (
|
|
86654
|
-
context.claudeMd =
|
|
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 =
|
|
86846
|
-
|
|
86847
|
-
const handoffFile =
|
|
86848
|
-
|
|
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
|
|
86955
|
-
import { join as
|
|
86956
|
-
var HANDOFF_LOG_FILE =
|
|
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 =
|
|
86959
|
-
if (!
|
|
86960
|
-
|
|
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 (!
|
|
87137
|
+
if (!existsSync25(HANDOFF_LOG_FILE)) {
|
|
87002
87138
|
return [];
|
|
87003
87139
|
}
|
|
87004
|
-
const content =
|
|
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
|
|
87063
|
-
import { join as
|
|
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 =
|
|
87207
|
+
var VIOLATIONS_DATA_FILE = join27(PANOPTICON_HOME2, "fpp-violations.json");
|
|
87072
87208
|
function loadViolations() {
|
|
87073
|
-
if (!
|
|
87209
|
+
if (!existsSync26(VIOLATIONS_DATA_FILE)) {
|
|
87074
87210
|
return /* @__PURE__ */ new Map();
|
|
87075
87211
|
}
|
|
87076
87212
|
try {
|
|
87077
|
-
const fileContent =
|
|
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 =
|
|
87088
|
-
if (!
|
|
87089
|
-
|
|
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
|
-
|
|
87096
|
-
|
|
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
|
|
87190
|
-
import { join as
|
|
87191
|
-
var COST_DATA_FILE =
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
87223
|
-
if (!
|
|
87224
|
-
|
|
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
|
-
|
|
87234
|
-
|
|
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
|
|
87354
|
-
import { join as
|
|
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 =
|
|
87495
|
-
|
|
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
|
|
87547
|
-
import { join as
|
|
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
|
|
87686
|
+
import { homedir as homedir13 } from "os";
|
|
87551
87687
|
var execAsync7 = promisify7(exec7);
|
|
87552
|
-
var REVIEW_STATUS_FILE =
|
|
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 =
|
|
87568
|
-
var STATE_FILE =
|
|
87569
|
-
var CONFIG_FILE2 =
|
|
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 (
|
|
87575
|
-
const content =
|
|
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 (!
|
|
87586
|
-
|
|
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 (
|
|
87593
|
-
const content =
|
|
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
|
-
|
|
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 =
|
|
87778
|
+
const heartbeatFile = join30(PANOPTICON_HOME2, "heartbeats", `${tmuxSession}.json`);
|
|
87643
87779
|
try {
|
|
87644
|
-
if (!
|
|
87780
|
+
if (!existsSync29(heartbeatFile)) {
|
|
87645
87781
|
return { isResponsive: false };
|
|
87646
87782
|
}
|
|
87647
|
-
const content =
|
|
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 =
|
|
87811
|
-
if (
|
|
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 (!
|
|
88054
|
+
if (!existsSync29(REVIEW_STATUS_FILE)) {
|
|
87919
88055
|
return false;
|
|
87920
88056
|
}
|
|
87921
|
-
const content =
|
|
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 (!
|
|
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 =
|
|
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 =
|
|
88145
|
+
const stateFile = join30(agentDir, "state.json");
|
|
88010
88146
|
let mtime;
|
|
88011
|
-
if (
|
|
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 =
|
|
88021
|
-
if (
|
|
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 (!
|
|
88184
|
+
if (!existsSync29(REVIEW_STATUS_FILE)) {
|
|
88049
88185
|
return actions;
|
|
88050
88186
|
}
|
|
88051
|
-
const content =
|
|
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
|
-
|
|
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
|
|
88226
|
-
import { join as
|
|
88227
|
-
var CLOISTER_STATE_FILE =
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
88248
|
-
const data = JSON.parse(
|
|
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 (!
|
|
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 =
|
|
88472
|
-
const processedFile =
|
|
88473
|
-
if (!
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
|
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 (
|
|
89352
|
+
if (existsSync34(stateFile)) {
|
|
89217
89353
|
try {
|
|
89218
|
-
const state = JSON.parse(
|
|
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
|
|
89268
|
-
import { join as
|
|
89269
|
-
import { homedir as
|
|
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 =
|
|
89528
|
+
var CONVOY_DIR = join35(homedir14(), ".panopticon", "convoys");
|
|
89393
89529
|
function getConvoyStateFile(convoyId) {
|
|
89394
|
-
return
|
|
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
|
|
89534
|
+
return join35(process.cwd(), baseDir, convoyId);
|
|
89399
89535
|
}
|
|
89400
89536
|
function saveConvoyState(state) {
|
|
89401
|
-
|
|
89402
|
-
|
|
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 (!
|
|
89542
|
+
if (!existsSync35(stateFile)) {
|
|
89407
89543
|
return void 0;
|
|
89408
89544
|
}
|
|
89409
89545
|
try {
|
|
89410
|
-
const content =
|
|
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 (!
|
|
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 (!
|
|
89575
|
+
if (!existsSync35(templatePath)) {
|
|
89440
89576
|
throw new Error(`Agent template not found: ${templatePath}`);
|
|
89441
89577
|
}
|
|
89442
|
-
const content =
|
|
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 =
|
|
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
|
-
|
|
89507
|
-
const promptFile =
|
|
89508
|
-
|
|
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
|
-
|
|
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 =
|
|
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 &&
|
|
89581
|
-
agentContext[`${depRole}_output`] =
|
|
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 &&
|
|
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
|
|
89673
|
-
import { join as
|
|
89674
|
-
import { homedir as
|
|
89675
|
-
var ENV_FILE_PATH =
|
|
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 (!
|
|
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 =
|
|
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
|
|
89719
|
-
import { join as
|
|
89720
|
-
import { homedir as
|
|
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
|
|
89858
|
+
return join37(process.env.HOME || homedir16(), ".panopticon", "costs");
|
|
89723
89859
|
}
|
|
89724
89860
|
function getEventsFile() {
|
|
89725
|
-
return
|
|
89861
|
+
return join37(getCostsDir(), "events.jsonl");
|
|
89726
89862
|
}
|
|
89727
89863
|
function ensureEventsFile() {
|
|
89728
89864
|
const costsDir = getCostsDir();
|
|
89729
89865
|
const eventsFile = getEventsFile();
|
|
89730
|
-
|
|
89731
|
-
if (!
|
|
89732
|
-
|
|
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 (!
|
|
89880
|
+
if (!existsSync37(getEventsFile())) {
|
|
89745
89881
|
return [];
|
|
89746
89882
|
}
|
|
89747
|
-
const content =
|
|
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 (!
|
|
89918
|
+
if (!existsSync37(getEventsFile())) {
|
|
89783
89919
|
return [];
|
|
89784
89920
|
}
|
|
89785
|
-
const content =
|
|
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 (!
|
|
89934
|
+
if (!existsSync37(getEventsFile())) {
|
|
89799
89935
|
return { events: [], newLine: startLine };
|
|
89800
89936
|
}
|
|
89801
|
-
const content =
|
|
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 (!
|
|
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 =
|
|
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
|
|
89974
|
+
return existsSync37(getEventsFile());
|
|
89839
89975
|
}
|
|
89840
89976
|
|
|
89841
89977
|
// ../../lib/costs/aggregator.ts
|
|
89842
|
-
import { existsSync as
|
|
89843
|
-
import { join as
|
|
89844
|
-
import { homedir as
|
|
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
|
|
89984
|
+
return join38(process.env.HOME || homedir17(), ".panopticon", "costs");
|
|
89849
89985
|
}
|
|
89850
89986
|
function getCacheFile() {
|
|
89851
|
-
return
|
|
89987
|
+
return join38(getCostsDir2(), "by-issue.json");
|
|
89852
89988
|
}
|
|
89853
89989
|
function loadCache() {
|
|
89854
89990
|
const cacheFile = getCacheFile();
|
|
89855
|
-
if (!
|
|
89991
|
+
if (!existsSync38(cacheFile)) {
|
|
89856
89992
|
return createEmptyCache();
|
|
89857
89993
|
}
|
|
89858
89994
|
try {
|
|
89859
|
-
const content =
|
|
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
|
-
|
|
90020
|
+
mkdirSync25(costsDir, { recursive: true });
|
|
89885
90021
|
const tempFile = cacheFile + ".tmp";
|
|
89886
90022
|
const content = JSON.stringify(cache, null, 2);
|
|
89887
|
-
|
|
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
|
|
90023
|
-
import { join as
|
|
90024
|
-
import { homedir as
|
|
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
|
|
90163
|
+
return join39(process.env.HOME || homedir18(), ".panopticon", "agents");
|
|
90028
90164
|
}
|
|
90029
90165
|
function getClaudeProjectsDir() {
|
|
90030
|
-
return
|
|
90166
|
+
return join39(process.env.HOME || homedir18(), ".claude", "projects");
|
|
90031
90167
|
}
|
|
90032
90168
|
function getProjectsYamlPath() {
|
|
90033
|
-
return
|
|
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 (
|
|
90048
|
-
const content =
|
|
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 =
|
|
90068
|
-
if (
|
|
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 =
|
|
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 =
|
|
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 =
|
|
90162
|
-
if (
|
|
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 =
|
|
90169
|
-
if (!
|
|
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(
|
|
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 =
|
|
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 =
|
|
90241
|
-
if (
|
|
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 =
|
|
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 (!
|
|
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
|
-
//
|
|
90337
|
-
|
|
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();
|