panopticon-cli 0.4.24 → 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/{chunk-6CIBLKFZ.js → chunk-SUMIHS2B.js} +9 -2
- package/dist/chunk-SUMIHS2B.js.map +1 -0
- package/dist/cli/index.js +8 -8
- package/dist/dashboard/public/assets/{index-phv7_FXa.js → index-qJWm8JDC.js} +39 -39
- package/dist/dashboard/public/index.html +1 -1
- package/dist/dashboard/server.js +429 -349
- package/dist/index.js +1 -1
- 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-6CIBLKFZ.js.map +0 -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
|
@@ -51320,6 +51320,7 @@ var init_rally = __esm({
|
|
|
51320
51320
|
"CreationDate",
|
|
51321
51321
|
"LastUpdateDate",
|
|
51322
51322
|
"Parent",
|
|
51323
|
+
"PortfolioItem",
|
|
51323
51324
|
"_type"
|
|
51324
51325
|
];
|
|
51325
51326
|
PRIORITY_MAP = {
|
|
@@ -51665,7 +51666,13 @@ var init_rally = __esm({
|
|
|
51665
51666
|
const baseUrl = this.restApi.server.replace("/slm/webservice/", "");
|
|
51666
51667
|
const url = `${baseUrl}/#/detail/${artifactType.toLowerCase()}/${objectId}`;
|
|
51667
51668
|
let parentRef;
|
|
51668
|
-
if (rallyArtifact.
|
|
51669
|
+
if (rallyArtifact.PortfolioItem) {
|
|
51670
|
+
if (rallyArtifact.PortfolioItem.FormattedID) {
|
|
51671
|
+
parentRef = rallyArtifact.PortfolioItem.FormattedID;
|
|
51672
|
+
} else if (rallyArtifact.PortfolioItem._refObjectName) {
|
|
51673
|
+
parentRef = rallyArtifact.PortfolioItem._refObjectName;
|
|
51674
|
+
}
|
|
51675
|
+
} else if (rallyArtifact.Parent) {
|
|
51669
51676
|
if (rallyArtifact.Parent.FormattedID) {
|
|
51670
51677
|
parentRef = rallyArtifact.Parent.FormattedID;
|
|
51671
51678
|
} else if (rallyArtifact.Parent._refObjectName) {
|
|
@@ -65656,6 +65663,91 @@ var init_agents = __esm({
|
|
|
65656
65663
|
}
|
|
65657
65664
|
});
|
|
65658
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
|
+
|
|
65659
65751
|
// ../../lib/cloister/specialists.ts
|
|
65660
65752
|
var specialists_exports = {};
|
|
65661
65753
|
__export(specialists_exports, {
|
|
@@ -65715,16 +65807,16 @@ __export(specialists_exports, {
|
|
|
65715
65807
|
wakeSpecialistOrQueue: () => wakeSpecialistOrQueue,
|
|
65716
65808
|
wakeSpecialistWithTask: () => wakeSpecialistWithTask
|
|
65717
65809
|
});
|
|
65718
|
-
import { readFileSync as
|
|
65719
|
-
import { join as
|
|
65720
|
-
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";
|
|
65721
65813
|
import { exec as exec4 } from "child_process";
|
|
65722
65814
|
import { promisify as promisify4 } from "util";
|
|
65723
65815
|
function initSpecialistsDirectory() {
|
|
65724
|
-
if (!
|
|
65725
|
-
|
|
65816
|
+
if (!existsSync20(SPECIALISTS_DIR)) {
|
|
65817
|
+
mkdirSync15(SPECIALISTS_DIR, { recursive: true });
|
|
65726
65818
|
}
|
|
65727
|
-
if (!
|
|
65819
|
+
if (!existsSync20(REGISTRY_FILE)) {
|
|
65728
65820
|
const registry = {
|
|
65729
65821
|
version: "2.0",
|
|
65730
65822
|
// Updated for per-project structure
|
|
@@ -65748,7 +65840,7 @@ function initSpecialistsDirectory() {
|
|
|
65748
65840
|
}
|
|
65749
65841
|
function migrateRegistryIfNeeded() {
|
|
65750
65842
|
try {
|
|
65751
|
-
const content =
|
|
65843
|
+
const content = readFileSync17(REGISTRY_FILE, "utf-8");
|
|
65752
65844
|
const registry = JSON.parse(content);
|
|
65753
65845
|
if (registry.version === "2.0" || registry.projects) {
|
|
65754
65846
|
return;
|
|
@@ -65778,7 +65870,7 @@ function migrateRegistryIfNeeded() {
|
|
|
65778
65870
|
function loadRegistry() {
|
|
65779
65871
|
initSpecialistsDirectory();
|
|
65780
65872
|
try {
|
|
65781
|
-
const content =
|
|
65873
|
+
const content = readFileSync17(REGISTRY_FILE, "utf-8");
|
|
65782
65874
|
return JSON.parse(content);
|
|
65783
65875
|
} catch (error) {
|
|
65784
65876
|
console.error("Failed to load specialist registry:", error);
|
|
@@ -65796,28 +65888,28 @@ function loadRegistry() {
|
|
|
65796
65888
|
}
|
|
65797
65889
|
}
|
|
65798
65890
|
function saveRegistry(registry) {
|
|
65799
|
-
if (!
|
|
65800
|
-
|
|
65891
|
+
if (!existsSync20(SPECIALISTS_DIR)) {
|
|
65892
|
+
mkdirSync15(SPECIALISTS_DIR, { recursive: true });
|
|
65801
65893
|
}
|
|
65802
65894
|
registry.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
65803
65895
|
try {
|
|
65804
65896
|
const content = JSON.stringify(registry, null, 2);
|
|
65805
|
-
|
|
65897
|
+
writeFileSync13(REGISTRY_FILE, content, "utf-8");
|
|
65806
65898
|
} catch (error) {
|
|
65807
65899
|
console.error("Failed to save specialist registry:", error);
|
|
65808
65900
|
throw error;
|
|
65809
65901
|
}
|
|
65810
65902
|
}
|
|
65811
65903
|
function getSessionFilePath(name) {
|
|
65812
|
-
return
|
|
65904
|
+
return join21(SPECIALISTS_DIR, `${name}.session`);
|
|
65813
65905
|
}
|
|
65814
65906
|
function getSessionId2(name) {
|
|
65815
65907
|
const sessionFile = getSessionFilePath(name);
|
|
65816
|
-
if (!
|
|
65908
|
+
if (!existsSync20(sessionFile)) {
|
|
65817
65909
|
return null;
|
|
65818
65910
|
}
|
|
65819
65911
|
try {
|
|
65820
|
-
return
|
|
65912
|
+
return readFileSync17(sessionFile, "utf-8").trim();
|
|
65821
65913
|
} catch (error) {
|
|
65822
65914
|
console.error(`Failed to read session file for ${name}:`, error);
|
|
65823
65915
|
return null;
|
|
@@ -65827,7 +65919,7 @@ function setSessionId(name, sessionId) {
|
|
|
65827
65919
|
initSpecialistsDirectory();
|
|
65828
65920
|
const sessionFile = getSessionFilePath(name);
|
|
65829
65921
|
try {
|
|
65830
|
-
|
|
65922
|
+
writeFileSync13(sessionFile, sessionId.trim(), "utf-8");
|
|
65831
65923
|
} catch (error) {
|
|
65832
65924
|
console.error(`Failed to write session file for ${name}:`, error);
|
|
65833
65925
|
throw error;
|
|
@@ -65835,7 +65927,7 @@ function setSessionId(name, sessionId) {
|
|
|
65835
65927
|
}
|
|
65836
65928
|
function clearSessionId(name) {
|
|
65837
65929
|
const sessionFile = getSessionFilePath(name);
|
|
65838
|
-
if (!
|
|
65930
|
+
if (!existsSync20(sessionFile)) {
|
|
65839
65931
|
return false;
|
|
65840
65932
|
}
|
|
65841
65933
|
try {
|
|
@@ -65916,12 +66008,12 @@ async function spawnEphemeralSpecialist(projectKey, specialistType, task) {
|
|
|
65916
66008
|
console.warn(`Warning: Could not resolve model for ${specialistType}, using default`);
|
|
65917
66009
|
}
|
|
65918
66010
|
const permissionFlags = specialistType === "merge-agent" ? "--dangerously-skip-permissions --permission-mode bypassPermissions" : "--dangerously-skip-permissions";
|
|
65919
|
-
const agentDir =
|
|
66011
|
+
const agentDir = join21(homedir11(), ".panopticon", "agents", tmuxSession);
|
|
65920
66012
|
await execAsync4(`mkdir -p "${agentDir}"`, { encoding: "utf-8" });
|
|
65921
|
-
const promptFile =
|
|
65922
|
-
|
|
65923
|
-
const launcherScript =
|
|
65924
|
-
|
|
66013
|
+
const promptFile = join21(agentDir, "task-prompt.md");
|
|
66014
|
+
writeFileSync13(promptFile, taskPrompt);
|
|
66015
|
+
const launcherScript = join21(agentDir, "launcher.sh");
|
|
66016
|
+
writeFileSync13(launcherScript, `#!/bin/bash
|
|
65925
66017
|
cd "${cwd}"
|
|
65926
66018
|
prompt=$(cat "${promptFile}")
|
|
65927
66019
|
|
|
@@ -65998,6 +66090,8 @@ ${customPrompt}
|
|
|
65998
66090
|
switch (specialistType) {
|
|
65999
66091
|
case "review-agent":
|
|
66000
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
|
|
66001
66095
|
1. Review all changes in the branch
|
|
66002
66096
|
2. Check for code quality issues, security concerns, and best practices
|
|
66003
66097
|
3. Verify test FILES exist for new code (DO NOT run tests)
|
|
@@ -66007,6 +66101,7 @@ ${customPrompt}
|
|
|
66007
66101
|
IMPORTANT: DO NOT run tests. You are the REVIEW agent.
|
|
66008
66102
|
|
|
66009
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"}
|
|
66010
66105
|
- If issues found: POST to /api/workspaces/${task.issueId}/review-status with {"reviewStatus":"blocked","reviewNotes":"..."}
|
|
66011
66106
|
- If review passes: POST with {"reviewStatus":"passed"} then queue test-agent`;
|
|
66012
66107
|
break;
|
|
@@ -66159,17 +66254,17 @@ function scheduleLogCleanup(projectKey, specialistType) {
|
|
|
66159
66254
|
});
|
|
66160
66255
|
}
|
|
66161
66256
|
function getProjectSpecialistDir(projectKey, specialistType) {
|
|
66162
|
-
return
|
|
66257
|
+
return join21(SPECIALISTS_DIR, projectKey, specialistType);
|
|
66163
66258
|
}
|
|
66164
66259
|
function ensureProjectSpecialistDir(projectKey, specialistType) {
|
|
66165
66260
|
const specialistDir = getProjectSpecialistDir(projectKey, specialistType);
|
|
66166
|
-
const runsDir =
|
|
66167
|
-
const contextDir =
|
|
66168
|
-
if (!
|
|
66169
|
-
|
|
66261
|
+
const runsDir = join21(specialistDir, "runs");
|
|
66262
|
+
const contextDir = join21(specialistDir, "context");
|
|
66263
|
+
if (!existsSync20(runsDir)) {
|
|
66264
|
+
mkdirSync15(runsDir, { recursive: true });
|
|
66170
66265
|
}
|
|
66171
|
-
if (!
|
|
66172
|
-
|
|
66266
|
+
if (!existsSync20(contextDir)) {
|
|
66267
|
+
mkdirSync15(contextDir, { recursive: true });
|
|
66173
66268
|
}
|
|
66174
66269
|
}
|
|
66175
66270
|
function getProjectSpecialistMetadata(projectKey, specialistType) {
|
|
@@ -66398,12 +66493,12 @@ Your role: ${name === "merge-agent" ? "Resolve merge conflicts and ensure clean
|
|
|
66398
66493
|
You will be woken up when your services are needed. For now, acknowledge your initialization and wait.
|
|
66399
66494
|
Say: "I am the ${name} specialist, ready and waiting for tasks."`;
|
|
66400
66495
|
try {
|
|
66401
|
-
const agentDir =
|
|
66496
|
+
const agentDir = join21(homedir11(), ".panopticon", "agents", tmuxSession);
|
|
66402
66497
|
await execAsync4(`mkdir -p "${agentDir}"`, { encoding: "utf-8" });
|
|
66403
|
-
const promptFile =
|
|
66404
|
-
const launcherScript =
|
|
66405
|
-
|
|
66406
|
-
|
|
66498
|
+
const promptFile = join21(agentDir, "identity-prompt.md");
|
|
66499
|
+
const launcherScript = join21(agentDir, "launcher.sh");
|
|
66500
|
+
writeFileSync13(promptFile, identityPrompt);
|
|
66501
|
+
writeFileSync13(launcherScript, `#!/bin/bash
|
|
66407
66502
|
cd "${cwd}"
|
|
66408
66503
|
prompt=$(cat "${promptFile}")
|
|
66409
66504
|
exec claude --dangerously-skip-permissions --model ${model} "$prompt"
|
|
@@ -66506,11 +66601,11 @@ async function wakeSpecialist(name, taskPrompt, options = {}) {
|
|
|
66506
66601
|
try {
|
|
66507
66602
|
const isLargePrompt = taskPrompt.length > 500 || taskPrompt.includes("\n");
|
|
66508
66603
|
if (isLargePrompt) {
|
|
66509
|
-
if (!
|
|
66510
|
-
|
|
66604
|
+
if (!existsSync20(TASKS_DIR)) {
|
|
66605
|
+
mkdirSync15(TASKS_DIR, { recursive: true });
|
|
66511
66606
|
}
|
|
66512
|
-
const taskFile =
|
|
66513
|
-
|
|
66607
|
+
const taskFile = join21(TASKS_DIR, `${name}-${Date.now()}.md`);
|
|
66608
|
+
writeFileSync13(taskFile, taskPrompt, "utf-8");
|
|
66514
66609
|
const shortMessage = `Read and execute the task in: ${taskFile}`;
|
|
66515
66610
|
sendKeys(tmuxSession, shortMessage);
|
|
66516
66611
|
} else {
|
|
@@ -66567,11 +66662,41 @@ When done, provide feedback on:
|
|
|
66567
66662
|
|
|
66568
66663
|
Use the send-feedback-to-agent skill to report findings back to the issue agent.`;
|
|
66569
66664
|
break;
|
|
66570
|
-
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
|
+
}
|
|
66571
66696
|
prompt = `New review task for ${task.issueId}:
|
|
66572
66697
|
|
|
66573
66698
|
Branch: ${task.branch || "unknown"}
|
|
66574
|
-
Workspace: ${
|
|
66699
|
+
Workspace: ${workspace}
|
|
66575
66700
|
${task.prUrl ? `PR URL: ${task.prUrl}` : ""}
|
|
66576
66701
|
|
|
66577
66702
|
Your task:
|
|
@@ -66585,9 +66710,26 @@ The TEST agent will run tests in the next step.
|
|
|
66585
66710
|
|
|
66586
66711
|
## How to Review Changes
|
|
66587
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
|
+
|
|
66588
66730
|
**Step 1:** Get the list of changed files:
|
|
66589
66731
|
\`\`\`bash
|
|
66590
|
-
cd ${
|
|
66732
|
+
cd ${workspace} && git diff --name-only main...HEAD
|
|
66591
66733
|
\`\`\`
|
|
66592
66734
|
|
|
66593
66735
|
**Step 2:** Read the CURRENT version of each changed file using the Read tool.
|
|
@@ -66595,7 +66737,7 @@ Review the actual file contents \u2014 do NOT rely solely on diff output.
|
|
|
66595
66737
|
|
|
66596
66738
|
**Step 3:** If you need to see what specifically changed, use:
|
|
66597
66739
|
\`\`\`bash
|
|
66598
|
-
cd ${
|
|
66740
|
+
cd ${workspace} && git diff main...HEAD -- <file>
|
|
66599
66741
|
\`\`\`
|
|
66600
66742
|
|
|
66601
66743
|
## Avoiding False Positives
|
|
@@ -66635,6 +66777,7 @@ curl -s -X POST ${apiUrl}/api/specialists/test-agent/queue -H "Content-Type: app
|
|
|
66635
66777
|
|
|
66636
66778
|
\u26A0\uFE0F VERIFICATION: After running each curl, confirm you see valid JSON output. If you get an error, report it.`;
|
|
66637
66779
|
break;
|
|
66780
|
+
}
|
|
66638
66781
|
case "test-agent":
|
|
66639
66782
|
prompt = `New test task for ${task.issueId}:
|
|
66640
66783
|
|
|
@@ -66820,8 +66963,8 @@ function getNextSpecialistTask(specialistName) {
|
|
|
66820
66963
|
}
|
|
66821
66964
|
async function sendFeedbackToAgent(feedback) {
|
|
66822
66965
|
const { fromSpecialist, toIssueId, summary, details } = feedback;
|
|
66823
|
-
if (!
|
|
66824
|
-
|
|
66966
|
+
if (!existsSync20(FEEDBACK_DIR)) {
|
|
66967
|
+
mkdirSync15(FEEDBACK_DIR, { recursive: true });
|
|
66825
66968
|
}
|
|
66826
66969
|
const fullFeedback = {
|
|
66827
66970
|
...feedback,
|
|
@@ -66894,11 +67037,11 @@ ${details}
|
|
|
66894
67037
|
return message;
|
|
66895
67038
|
}
|
|
66896
67039
|
function getPendingFeedback(issueId) {
|
|
66897
|
-
if (!
|
|
67040
|
+
if (!existsSync20(FEEDBACK_LOG)) {
|
|
66898
67041
|
return [];
|
|
66899
67042
|
}
|
|
66900
67043
|
try {
|
|
66901
|
-
const content =
|
|
67044
|
+
const content = readFileSync17(FEEDBACK_LOG, "utf-8");
|
|
66902
67045
|
const lines = content.trim().split("\n").filter((l) => l.length > 0);
|
|
66903
67046
|
const allFeedback = lines.map((line) => JSON.parse(line));
|
|
66904
67047
|
return allFeedback.filter((f) => f.toIssueId.toLowerCase() === issueId.toLowerCase());
|
|
@@ -66917,11 +67060,11 @@ function getFeedbackStats() {
|
|
|
66917
67060
|
byType: {},
|
|
66918
67061
|
total: 0
|
|
66919
67062
|
};
|
|
66920
|
-
if (!
|
|
67063
|
+
if (!existsSync20(FEEDBACK_LOG)) {
|
|
66921
67064
|
return stats;
|
|
66922
67065
|
}
|
|
66923
67066
|
try {
|
|
66924
|
-
const content =
|
|
67067
|
+
const content = readFileSync17(FEEDBACK_LOG, "utf-8");
|
|
66925
67068
|
const lines = content.trim().split("\n").filter((l) => l.length > 0);
|
|
66926
67069
|
for (const line of lines) {
|
|
66927
67070
|
const feedback = JSON.parse(line);
|
|
@@ -66945,9 +67088,9 @@ var init_specialists = __esm({
|
|
|
66945
67088
|
init_tmux();
|
|
66946
67089
|
init_hooks();
|
|
66947
67090
|
execAsync4 = promisify4(exec4);
|
|
66948
|
-
SPECIALISTS_DIR =
|
|
66949
|
-
REGISTRY_FILE =
|
|
66950
|
-
TASKS_DIR =
|
|
67091
|
+
SPECIALISTS_DIR = join21(PANOPTICON_HOME2, "specialists");
|
|
67092
|
+
REGISTRY_FILE = join21(SPECIALISTS_DIR, "registry.json");
|
|
67093
|
+
TASKS_DIR = join21(SPECIALISTS_DIR, "tasks");
|
|
66951
67094
|
DEFAULT_SPECIALISTS = [
|
|
66952
67095
|
{
|
|
66953
67096
|
name: "merge-agent",
|
|
@@ -66972,16 +67115,16 @@ var init_specialists = __esm({
|
|
|
66972
67115
|
}
|
|
66973
67116
|
];
|
|
66974
67117
|
gracePeriodStates = /* @__PURE__ */ new Map();
|
|
66975
|
-
FEEDBACK_DIR =
|
|
66976
|
-
FEEDBACK_LOG =
|
|
67118
|
+
FEEDBACK_DIR = join21(PANOPTICON_HOME2, "specialists", "feedback");
|
|
67119
|
+
FEEDBACK_LOG = join21(FEEDBACK_DIR, "feedback.jsonl");
|
|
66977
67120
|
}
|
|
66978
67121
|
});
|
|
66979
67122
|
|
|
66980
67123
|
// ../../lib/cloister/validation.ts
|
|
66981
67124
|
import { exec as exec8 } from "child_process";
|
|
66982
67125
|
import { promisify as promisify8 } from "util";
|
|
66983
|
-
import { join as
|
|
66984
|
-
import { existsSync as
|
|
67126
|
+
import { join as join32 } from "path";
|
|
67127
|
+
import { existsSync as existsSync31 } from "fs";
|
|
66985
67128
|
function parseValidationOutput(output, exitCode) {
|
|
66986
67129
|
const lines = output.split("\n");
|
|
66987
67130
|
const failures = [];
|
|
@@ -67068,8 +67211,8 @@ function parseValidationOutput(output, exitCode) {
|
|
|
67068
67211
|
}
|
|
67069
67212
|
async function runMergeValidation(context) {
|
|
67070
67213
|
const { projectPath, validationScript } = context;
|
|
67071
|
-
const scriptPath = validationScript ||
|
|
67072
|
-
if (!
|
|
67214
|
+
const scriptPath = validationScript || join32(projectPath, "scripts", "validate-merge.sh");
|
|
67215
|
+
if (!existsSync31(scriptPath)) {
|
|
67073
67216
|
return {
|
|
67074
67217
|
success: false,
|
|
67075
67218
|
valid: false,
|
|
@@ -67141,14 +67284,14 @@ var init_validation = __esm({
|
|
|
67141
67284
|
});
|
|
67142
67285
|
|
|
67143
67286
|
// ../../lib/git-utils.ts
|
|
67144
|
-
import { existsSync as
|
|
67145
|
-
import { join as
|
|
67287
|
+
import { existsSync as existsSync32, unlinkSync as unlinkSync10, readdirSync as readdirSync12 } from "fs";
|
|
67288
|
+
import { join as join33 } from "path";
|
|
67146
67289
|
import { exec as exec9 } from "child_process";
|
|
67147
67290
|
import { promisify as promisify9 } from "util";
|
|
67148
67291
|
async function hasRunningGitProcesses(repoPath) {
|
|
67149
67292
|
try {
|
|
67150
67293
|
try {
|
|
67151
|
-
const gitDir =
|
|
67294
|
+
const gitDir = join33(repoPath, ".git");
|
|
67152
67295
|
const { stdout } = await execAsync9(`fuser "${gitDir}" 2>/dev/null`, {
|
|
67153
67296
|
encoding: "utf-8"
|
|
67154
67297
|
});
|
|
@@ -67170,16 +67313,16 @@ async function hasRunningGitProcesses(repoPath) {
|
|
|
67170
67313
|
}
|
|
67171
67314
|
function findGitLockFiles(repoPath) {
|
|
67172
67315
|
const lockFiles = [];
|
|
67173
|
-
const indexLock =
|
|
67174
|
-
if (
|
|
67316
|
+
const indexLock = join33(repoPath, ".git", "index.lock");
|
|
67317
|
+
if (existsSync32(indexLock)) {
|
|
67175
67318
|
lockFiles.push(indexLock);
|
|
67176
67319
|
}
|
|
67177
|
-
const refsDir =
|
|
67178
|
-
if (
|
|
67320
|
+
const refsDir = join33(repoPath, ".git", "refs");
|
|
67321
|
+
if (existsSync32(refsDir)) {
|
|
67179
67322
|
const findLocksRecursive = (dir) => {
|
|
67180
67323
|
const entries = readdirSync12(dir, { withFileTypes: true });
|
|
67181
67324
|
for (const entry of entries) {
|
|
67182
|
-
const fullPath =
|
|
67325
|
+
const fullPath = join33(dir, entry.name);
|
|
67183
67326
|
if (entry.isDirectory()) {
|
|
67184
67327
|
findLocksRecursive(fullPath);
|
|
67185
67328
|
} else if (entry.name.endsWith(".lock")) {
|
|
@@ -67238,17 +67381,17 @@ __export(merge_agent_exports, {
|
|
|
67238
67381
|
spawnMergeAgent: () => spawnMergeAgent,
|
|
67239
67382
|
spawnMergeAgentForBranches: () => spawnMergeAgentForBranches
|
|
67240
67383
|
});
|
|
67241
|
-
import { readFileSync as
|
|
67242
|
-
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";
|
|
67243
67386
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
67244
67387
|
import { exec as exec10 } from "child_process";
|
|
67245
67388
|
import { promisify as promisify10 } from "util";
|
|
67246
67389
|
function buildMergePrompt(context) {
|
|
67247
|
-
const templatePath =
|
|
67248
|
-
if (!
|
|
67390
|
+
const templatePath = join34(__dirname2, "prompts", "merge-agent.md");
|
|
67391
|
+
if (!existsSync33(templatePath)) {
|
|
67249
67392
|
throw new Error(`Merge agent prompt template not found at ${templatePath}`);
|
|
67250
67393
|
}
|
|
67251
|
-
const template =
|
|
67394
|
+
const template = readFileSync26(templatePath, "utf-8");
|
|
67252
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(
|
|
67253
67396
|
/\{\{conflictFiles\}\}/g,
|
|
67254
67397
|
context.conflictFiles.map((f) => ` - ${f}`).join("\n")
|
|
@@ -67256,23 +67399,23 @@ function buildMergePrompt(context) {
|
|
|
67256
67399
|
return prompt;
|
|
67257
67400
|
}
|
|
67258
67401
|
function detectTestCommand(projectPath) {
|
|
67259
|
-
const packageJsonPath =
|
|
67260
|
-
if (
|
|
67402
|
+
const packageJsonPath = join34(projectPath, "package.json");
|
|
67403
|
+
if (existsSync33(packageJsonPath)) {
|
|
67261
67404
|
try {
|
|
67262
|
-
const packageJson = JSON.parse(
|
|
67405
|
+
const packageJson = JSON.parse(readFileSync26(packageJsonPath, "utf-8"));
|
|
67263
67406
|
if (packageJson.scripts?.test) {
|
|
67264
67407
|
return "npm test";
|
|
67265
67408
|
}
|
|
67266
67409
|
} catch {
|
|
67267
67410
|
}
|
|
67268
67411
|
}
|
|
67269
|
-
if (
|
|
67412
|
+
if (existsSync33(join34(projectPath, "pom.xml"))) {
|
|
67270
67413
|
return "mvn test";
|
|
67271
67414
|
}
|
|
67272
|
-
if (
|
|
67415
|
+
if (existsSync33(join34(projectPath, "Cargo.toml"))) {
|
|
67273
67416
|
return "cargo test";
|
|
67274
67417
|
}
|
|
67275
|
-
if (
|
|
67418
|
+
if (existsSync33(join34(projectPath, "pytest.ini")) || existsSync33(join34(projectPath, "setup.py"))) {
|
|
67276
67419
|
return "pytest";
|
|
67277
67420
|
}
|
|
67278
67421
|
return "skip";
|
|
@@ -67286,8 +67429,8 @@ async function conditionalBeadsCompaction(projectPath) {
|
|
|
67286
67429
|
console.log(`[merge-agent] bd not available, skipping compaction`);
|
|
67287
67430
|
return;
|
|
67288
67431
|
}
|
|
67289
|
-
const beadsDir =
|
|
67290
|
-
if (!
|
|
67432
|
+
const beadsDir = join34(projectPath, ".beads");
|
|
67433
|
+
if (!existsSync33(beadsDir)) {
|
|
67291
67434
|
console.log(`[merge-agent] No .beads directory, skipping compaction`);
|
|
67292
67435
|
return;
|
|
67293
67436
|
}
|
|
@@ -67324,12 +67467,12 @@ async function conditionalBeadsCompaction(projectPath) {
|
|
|
67324
67467
|
async function postMergeCleanup(issueId, projectPath) {
|
|
67325
67468
|
console.log(`[merge-agent] Running post-merge cleanup for ${issueId}`);
|
|
67326
67469
|
try {
|
|
67327
|
-
const activePrdPath =
|
|
67328
|
-
const completedPrdPath =
|
|
67329
|
-
if (
|
|
67330
|
-
const completedDir =
|
|
67331
|
-
if (!
|
|
67332
|
-
|
|
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 });
|
|
67333
67476
|
}
|
|
67334
67477
|
await execAsync10(`git mv "${activePrdPath}" "${completedPrdPath}"`, { cwd: projectPath, encoding: "utf-8" });
|
|
67335
67478
|
await execAsync10(`git commit -m "Move ${issueId} PRD to completed"`, { cwd: projectPath, encoding: "utf-8" });
|
|
@@ -67514,8 +67657,8 @@ function parseAgentOutput(output) {
|
|
|
67514
67657
|
}
|
|
67515
67658
|
}
|
|
67516
67659
|
function logMergeHistory(context, result, sessionId) {
|
|
67517
|
-
if (!
|
|
67518
|
-
|
|
67660
|
+
if (!existsSync33(MERGE_HISTORY_DIR)) {
|
|
67661
|
+
mkdirSync22(MERGE_HISTORY_DIR, { recursive: true });
|
|
67519
67662
|
}
|
|
67520
67663
|
const entry = {
|
|
67521
67664
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -67970,10 +68113,10 @@ var init_merge_agent = __esm({
|
|
|
67970
68113
|
init_git_utils();
|
|
67971
68114
|
execAsync10 = promisify10(exec10);
|
|
67972
68115
|
__filename = fileURLToPath2(import.meta.url);
|
|
67973
|
-
__dirname2 =
|
|
67974
|
-
SPECIALISTS_DIR2 =
|
|
67975
|
-
MERGE_HISTORY_DIR =
|
|
67976
|
-
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");
|
|
67977
68120
|
MERGE_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
67978
68121
|
}
|
|
67979
68122
|
});
|
|
@@ -85980,10 +86123,10 @@ init_specialists();
|
|
|
85980
86123
|
init_agents();
|
|
85981
86124
|
init_tmux();
|
|
85982
86125
|
init_jsonl_parser();
|
|
85983
|
-
import { existsSync as
|
|
85984
|
-
import { join as
|
|
85985
|
-
import { homedir as
|
|
85986
|
-
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");
|
|
85987
86130
|
var ClaudeCodeRuntime = class {
|
|
85988
86131
|
name = "claude-code";
|
|
85989
86132
|
/**
|
|
@@ -85993,15 +86136,15 @@ var ClaudeCodeRuntime = class {
|
|
|
85993
86136
|
* We need to find the project directory that contains sessions for this workspace.
|
|
85994
86137
|
*/
|
|
85995
86138
|
getProjectDirForWorkspace(workspace) {
|
|
85996
|
-
if (!
|
|
86139
|
+
if (!existsSync21(CLAUDE_PROJECTS_DIR2)) {
|
|
85997
86140
|
return null;
|
|
85998
86141
|
}
|
|
85999
86142
|
const projectDirs = getProjectDirs();
|
|
86000
86143
|
for (const projectDir of projectDirs) {
|
|
86001
|
-
const indexPath =
|
|
86002
|
-
if (
|
|
86144
|
+
const indexPath = join22(projectDir, "sessions-index.json");
|
|
86145
|
+
if (existsSync21(indexPath)) {
|
|
86003
86146
|
try {
|
|
86004
|
-
const indexContent =
|
|
86147
|
+
const indexContent = readFileSync18(indexPath, "utf-8");
|
|
86005
86148
|
if (indexContent.includes(workspace)) {
|
|
86006
86149
|
return projectDir;
|
|
86007
86150
|
}
|
|
@@ -86015,12 +86158,12 @@ var ClaudeCodeRuntime = class {
|
|
|
86015
86158
|
* Get the active session ID for an agent from the sessions index
|
|
86016
86159
|
*/
|
|
86017
86160
|
getActiveSessionId(projectDir) {
|
|
86018
|
-
const indexPath =
|
|
86019
|
-
if (!
|
|
86161
|
+
const indexPath = join22(projectDir, "sessions-index.json");
|
|
86162
|
+
if (!existsSync21(indexPath)) {
|
|
86020
86163
|
return null;
|
|
86021
86164
|
}
|
|
86022
86165
|
try {
|
|
86023
|
-
const indexContent =
|
|
86166
|
+
const indexContent = readFileSync18(indexPath, "utf-8");
|
|
86024
86167
|
const index = JSON.parse(indexContent);
|
|
86025
86168
|
if (index.sessions && Array.isArray(index.sessions)) {
|
|
86026
86169
|
const sessions = index.sessions;
|
|
@@ -86056,8 +86199,8 @@ var ClaudeCodeRuntime = class {
|
|
|
86056
86199
|
}
|
|
86057
86200
|
const sessionId = this.getActiveSessionId(projectDir);
|
|
86058
86201
|
if (sessionId) {
|
|
86059
|
-
const sessionPath =
|
|
86060
|
-
if (
|
|
86202
|
+
const sessionPath = join22(projectDir, `${sessionId}.jsonl`);
|
|
86203
|
+
if (existsSync21(sessionPath)) {
|
|
86061
86204
|
return sessionPath;
|
|
86062
86205
|
}
|
|
86063
86206
|
}
|
|
@@ -86070,7 +86213,7 @@ var ClaudeCodeRuntime = class {
|
|
|
86070
86213
|
*/
|
|
86071
86214
|
getLastActivity(agentId) {
|
|
86072
86215
|
const sessionPath = this.getSessionPath(agentId);
|
|
86073
|
-
if (!sessionPath || !
|
|
86216
|
+
if (!sessionPath || !existsSync21(sessionPath)) {
|
|
86074
86217
|
return null;
|
|
86075
86218
|
}
|
|
86076
86219
|
try {
|
|
@@ -86084,12 +86227,12 @@ var ClaudeCodeRuntime = class {
|
|
|
86084
86227
|
* Read active heartbeat file if it exists
|
|
86085
86228
|
*/
|
|
86086
86229
|
getActiveHeartbeat(agentId) {
|
|
86087
|
-
const heartbeatPath =
|
|
86088
|
-
if (!
|
|
86230
|
+
const heartbeatPath = join22(homedir12(), ".panopticon", "heartbeats", `${agentId}.json`);
|
|
86231
|
+
if (!existsSync21(heartbeatPath)) {
|
|
86089
86232
|
return null;
|
|
86090
86233
|
}
|
|
86091
86234
|
try {
|
|
86092
|
-
const content =
|
|
86235
|
+
const content = readFileSync18(heartbeatPath, "utf-8");
|
|
86093
86236
|
const data = JSON.parse(content);
|
|
86094
86237
|
const timestamp2 = new Date(data.timestamp);
|
|
86095
86238
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -86193,11 +86336,11 @@ var ClaudeCodeRuntime = class {
|
|
|
86193
86336
|
throw new Error(`Agent ${agentId} is not running`);
|
|
86194
86337
|
}
|
|
86195
86338
|
sendKeys(agentId, message);
|
|
86196
|
-
const mailDir =
|
|
86197
|
-
|
|
86339
|
+
const mailDir = join22(getAgentDir(agentId), "mail");
|
|
86340
|
+
mkdirSync16(mailDir, { recursive: true });
|
|
86198
86341
|
const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
86199
|
-
|
|
86200
|
-
|
|
86342
|
+
writeFileSync14(
|
|
86343
|
+
join22(mailDir, `${timestamp2}.md`),
|
|
86201
86344
|
`# Message
|
|
86202
86345
|
|
|
86203
86346
|
${message}
|
|
@@ -86368,8 +86511,8 @@ init_agents();
|
|
|
86368
86511
|
|
|
86369
86512
|
// ../../lib/cloister/triggers.ts
|
|
86370
86513
|
init_config();
|
|
86371
|
-
import { existsSync as
|
|
86372
|
-
import { join as
|
|
86514
|
+
import { existsSync as existsSync22 } from "fs";
|
|
86515
|
+
import { join as join23 } from "path";
|
|
86373
86516
|
import { exec as exec5 } from "child_process";
|
|
86374
86517
|
import { promisify as promisify5 } from "util";
|
|
86375
86518
|
var execAsync5 = promisify5(exec5);
|
|
@@ -86461,8 +86604,8 @@ async function checkPlanningComplete(agentId, workspace, issueId, config2) {
|
|
|
86461
86604
|
}
|
|
86462
86605
|
} catch (error) {
|
|
86463
86606
|
}
|
|
86464
|
-
const prdPath =
|
|
86465
|
-
if (
|
|
86607
|
+
const prdPath = join23(workspace, `docs/prds/active/${issueId.toLowerCase()}-plan.md`);
|
|
86608
|
+
if (existsSync22(prdPath)) {
|
|
86466
86609
|
signals.push("PRD file exists");
|
|
86467
86610
|
signalCount++;
|
|
86468
86611
|
}
|
|
@@ -86609,12 +86752,12 @@ async function checkAllTriggers(agentId, workspace, issueId, currentModel, healt
|
|
|
86609
86752
|
|
|
86610
86753
|
// ../../lib/cloister/handoff.ts
|
|
86611
86754
|
init_agents();
|
|
86612
|
-
import { writeFileSync as
|
|
86613
|
-
import { join as
|
|
86755
|
+
import { writeFileSync as writeFileSync15, mkdirSync as mkdirSync17 } from "fs";
|
|
86756
|
+
import { join as join25 } from "path";
|
|
86614
86757
|
|
|
86615
86758
|
// ../../lib/cloister/handoff-context.ts
|
|
86616
|
-
import { existsSync as
|
|
86617
|
-
import { join as
|
|
86759
|
+
import { existsSync as existsSync23, readFileSync as readFileSync19 } from "fs";
|
|
86760
|
+
import { join as join24 } from "path";
|
|
86618
86761
|
import { exec as exec6 } from "child_process";
|
|
86619
86762
|
import { promisify as promisify6 } from "util";
|
|
86620
86763
|
var execAsync6 = promisify6(exec6);
|
|
@@ -86638,13 +86781,13 @@ async function captureHandoffContext(agentState, targetModel, reason) {
|
|
|
86638
86781
|
}
|
|
86639
86782
|
async function captureFiles(context, workspace) {
|
|
86640
86783
|
try {
|
|
86641
|
-
const stateFile =
|
|
86642
|
-
if (
|
|
86643
|
-
context.stateFile =
|
|
86784
|
+
const stateFile = join24(workspace, ".planning/STATE.md");
|
|
86785
|
+
if (existsSync23(stateFile)) {
|
|
86786
|
+
context.stateFile = readFileSync19(stateFile, "utf-8");
|
|
86644
86787
|
}
|
|
86645
|
-
const claudeMd =
|
|
86646
|
-
if (
|
|
86647
|
-
context.claudeMd =
|
|
86788
|
+
const claudeMd = join24(workspace, "CLAUDE.md");
|
|
86789
|
+
if (existsSync23(claudeMd)) {
|
|
86790
|
+
context.claudeMd = readFileSync19(claudeMd, "utf-8");
|
|
86648
86791
|
}
|
|
86649
86792
|
} catch (error) {
|
|
86650
86793
|
console.error("Error capturing files:", error);
|
|
@@ -86835,10 +86978,10 @@ async function performKillAndSpawn(state, options) {
|
|
|
86835
86978
|
const context = await captureHandoffContext(state, options.targetModel, options.reason);
|
|
86836
86979
|
stopAgent(state.id);
|
|
86837
86980
|
const prompt = buildHandoffPrompt(context, options.additionalInstructions);
|
|
86838
|
-
const handoffDir =
|
|
86839
|
-
|
|
86840
|
-
const handoffFile =
|
|
86841
|
-
|
|
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);
|
|
86842
86985
|
const newState = await spawnAgent({
|
|
86843
86986
|
issueId: state.issueId,
|
|
86844
86987
|
workspace: state.workspace,
|
|
@@ -86944,13 +87087,13 @@ function sleep(ms) {
|
|
|
86944
87087
|
|
|
86945
87088
|
// ../../lib/cloister/handoff-logger.ts
|
|
86946
87089
|
init_paths();
|
|
86947
|
-
import { existsSync as
|
|
86948
|
-
import { join as
|
|
86949
|
-
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");
|
|
86950
87093
|
function ensureLogDir3() {
|
|
86951
|
-
const logDir =
|
|
86952
|
-
if (!
|
|
86953
|
-
|
|
87094
|
+
const logDir = join26(PANOPTICON_HOME2, "logs");
|
|
87095
|
+
if (!existsSync25(logDir)) {
|
|
87096
|
+
mkdirSync18(logDir, { recursive: true });
|
|
86954
87097
|
}
|
|
86955
87098
|
}
|
|
86956
87099
|
function logHandoffEvent(event) {
|
|
@@ -86991,10 +87134,10 @@ function createHandoffEvent(agentId, issueId, context, trigger, success, errorMe
|
|
|
86991
87134
|
}
|
|
86992
87135
|
function readHandoffEvents(limit) {
|
|
86993
87136
|
ensureLogDir3();
|
|
86994
|
-
if (!
|
|
87137
|
+
if (!existsSync25(HANDOFF_LOG_FILE)) {
|
|
86995
87138
|
return [];
|
|
86996
87139
|
}
|
|
86997
|
-
const content =
|
|
87140
|
+
const content = readFileSync20(HANDOFF_LOG_FILE, "utf-8");
|
|
86998
87141
|
const lines = content.trim().split("\n").filter((line) => line.trim());
|
|
86999
87142
|
const events = lines.map((line) => JSON.parse(line));
|
|
87000
87143
|
events.reverse();
|
|
@@ -87052,8 +87195,8 @@ function getHandoffStats() {
|
|
|
87052
87195
|
|
|
87053
87196
|
// ../../lib/cloister/fpp-violations.ts
|
|
87054
87197
|
init_hooks();
|
|
87055
|
-
import { readFileSync as
|
|
87056
|
-
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";
|
|
87057
87200
|
init_paths();
|
|
87058
87201
|
var DEFAULT_FPP_CONFIG = {
|
|
87059
87202
|
hook_idle_minutes: 5,
|
|
@@ -87061,13 +87204,13 @@ var DEFAULT_FPP_CONFIG = {
|
|
|
87061
87204
|
review_pending_minutes: 15,
|
|
87062
87205
|
max_nudges: 3
|
|
87063
87206
|
};
|
|
87064
|
-
var VIOLATIONS_DATA_FILE =
|
|
87207
|
+
var VIOLATIONS_DATA_FILE = join27(PANOPTICON_HOME2, "fpp-violations.json");
|
|
87065
87208
|
function loadViolations() {
|
|
87066
|
-
if (!
|
|
87209
|
+
if (!existsSync26(VIOLATIONS_DATA_FILE)) {
|
|
87067
87210
|
return /* @__PURE__ */ new Map();
|
|
87068
87211
|
}
|
|
87069
87212
|
try {
|
|
87070
|
-
const fileContent =
|
|
87213
|
+
const fileContent = readFileSync21(VIOLATIONS_DATA_FILE, "utf-8");
|
|
87071
87214
|
const persisted = JSON.parse(fileContent);
|
|
87072
87215
|
return new Map(persisted.violations || []);
|
|
87073
87216
|
} catch (error) {
|
|
@@ -87077,16 +87220,16 @@ function loadViolations() {
|
|
|
87077
87220
|
}
|
|
87078
87221
|
function saveViolations(violations) {
|
|
87079
87222
|
try {
|
|
87080
|
-
const dir =
|
|
87081
|
-
if (!
|
|
87082
|
-
|
|
87223
|
+
const dir = dirname3(VIOLATIONS_DATA_FILE);
|
|
87224
|
+
if (!existsSync26(dir)) {
|
|
87225
|
+
mkdirSync19(dir, { recursive: true });
|
|
87083
87226
|
}
|
|
87084
87227
|
const persisted = {
|
|
87085
87228
|
violations: Array.from(violations.entries())
|
|
87086
87229
|
};
|
|
87087
87230
|
const tempFile = `${VIOLATIONS_DATA_FILE}.tmp`;
|
|
87088
|
-
|
|
87089
|
-
|
|
87231
|
+
writeFileSync17(tempFile, JSON.stringify(persisted, null, 2));
|
|
87232
|
+
writeFileSync17(VIOLATIONS_DATA_FILE, readFileSync21(tempFile));
|
|
87090
87233
|
try {
|
|
87091
87234
|
unlinkSync7(tempFile);
|
|
87092
87235
|
} catch (unlinkError) {
|
|
@@ -87179,11 +87322,11 @@ function clearOldViolations(hoursOld = 24) {
|
|
|
87179
87322
|
// ../../lib/cloister/cost-monitor.ts
|
|
87180
87323
|
init_paths();
|
|
87181
87324
|
init_config();
|
|
87182
|
-
import { readFileSync as
|
|
87183
|
-
import { join as
|
|
87184
|
-
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");
|
|
87185
87328
|
function loadCostData() {
|
|
87186
|
-
if (!
|
|
87329
|
+
if (!existsSync27(COST_DATA_FILE)) {
|
|
87187
87330
|
return {
|
|
87188
87331
|
perAgent: /* @__PURE__ */ new Map(),
|
|
87189
87332
|
perIssue: /* @__PURE__ */ new Map(),
|
|
@@ -87192,7 +87335,7 @@ function loadCostData() {
|
|
|
87192
87335
|
};
|
|
87193
87336
|
}
|
|
87194
87337
|
try {
|
|
87195
|
-
const fileContent =
|
|
87338
|
+
const fileContent = readFileSync22(COST_DATA_FILE, "utf-8");
|
|
87196
87339
|
const persisted = JSON.parse(fileContent);
|
|
87197
87340
|
return {
|
|
87198
87341
|
perAgent: new Map(Object.entries(persisted.perAgent || {})),
|
|
@@ -87212,9 +87355,9 @@ function loadCostData() {
|
|
|
87212
87355
|
}
|
|
87213
87356
|
function saveCostData(data) {
|
|
87214
87357
|
try {
|
|
87215
|
-
const dir =
|
|
87216
|
-
if (!
|
|
87217
|
-
|
|
87358
|
+
const dir = dirname4(COST_DATA_FILE);
|
|
87359
|
+
if (!existsSync27(dir)) {
|
|
87360
|
+
mkdirSync20(dir, { recursive: true });
|
|
87218
87361
|
}
|
|
87219
87362
|
const persisted = {
|
|
87220
87363
|
perAgent: Object.fromEntries(data.perAgent),
|
|
@@ -87223,8 +87366,8 @@ function saveCostData(data) {
|
|
|
87223
87366
|
lastResetDate: data.lastResetDate
|
|
87224
87367
|
};
|
|
87225
87368
|
const tempFile = `${COST_DATA_FILE}.tmp`;
|
|
87226
|
-
|
|
87227
|
-
|
|
87369
|
+
writeFileSync18(tempFile, JSON.stringify(persisted, null, 2));
|
|
87370
|
+
writeFileSync18(COST_DATA_FILE, readFileSync22(tempFile));
|
|
87228
87371
|
try {
|
|
87229
87372
|
unlinkSync8(tempFile);
|
|
87230
87373
|
} catch (unlinkError) {
|
|
@@ -87343,8 +87486,8 @@ function getCostSummary() {
|
|
|
87343
87486
|
|
|
87344
87487
|
// ../../lib/cloister/session-rotation.ts
|
|
87345
87488
|
init_paths();
|
|
87346
|
-
import { writeFileSync as
|
|
87347
|
-
import { join as
|
|
87489
|
+
import { writeFileSync as writeFileSync19 } from "fs";
|
|
87490
|
+
import { join as join29 } from "path";
|
|
87348
87491
|
import { execSync as execSync2 } from "child_process";
|
|
87349
87492
|
init_agents();
|
|
87350
87493
|
init_specialists();
|
|
@@ -87484,8 +87627,8 @@ async function rotateSpecialistSession(specialistName, workingDir) {
|
|
|
87484
87627
|
let memoryFile;
|
|
87485
87628
|
if (specialistName === "merge-agent" && workingDir) {
|
|
87486
87629
|
memoryContent = buildMergeAgentMemory(workingDir);
|
|
87487
|
-
memoryFile =
|
|
87488
|
-
|
|
87630
|
+
memoryFile = join29(PANOPTICON_HOME2, `merge-agent-memory-${Date.now()}.md`);
|
|
87631
|
+
writeFileSync19(memoryFile, memoryContent);
|
|
87489
87632
|
console.log(`Built memory file: ${memoryFile}`);
|
|
87490
87633
|
}
|
|
87491
87634
|
const tmuxSession = getTmuxSessionName(specialistName);
|
|
@@ -87536,13 +87679,13 @@ init_config();
|
|
|
87536
87679
|
init_specialists();
|
|
87537
87680
|
init_agents();
|
|
87538
87681
|
init_tmux();
|
|
87539
|
-
import { readFileSync as
|
|
87540
|
-
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";
|
|
87541
87684
|
import { exec as exec7 } from "child_process";
|
|
87542
87685
|
import { promisify as promisify7 } from "util";
|
|
87543
|
-
import { homedir as
|
|
87686
|
+
import { homedir as homedir13 } from "os";
|
|
87544
87687
|
var execAsync7 = promisify7(exec7);
|
|
87545
|
-
var REVIEW_STATUS_FILE =
|
|
87688
|
+
var REVIEW_STATUS_FILE = join30(homedir13(), ".panopticon", "review-status.json");
|
|
87546
87689
|
var DEFAULT_CONFIG2 = {
|
|
87547
87690
|
pingTimeoutMs: 3e4,
|
|
87548
87691
|
// How long to wait for response
|
|
@@ -87557,15 +87700,15 @@ var DEFAULT_CONFIG2 = {
|
|
|
87557
87700
|
massDeathWindowMs: 6e4
|
|
87558
87701
|
// 1 minute window for mass death detection
|
|
87559
87702
|
};
|
|
87560
|
-
var DEACON_DIR =
|
|
87561
|
-
var STATE_FILE =
|
|
87562
|
-
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");
|
|
87563
87706
|
var deaconInterval = null;
|
|
87564
87707
|
var config = { ...DEFAULT_CONFIG2 };
|
|
87565
87708
|
function loadConfig2() {
|
|
87566
87709
|
try {
|
|
87567
|
-
if (
|
|
87568
|
-
const content =
|
|
87710
|
+
if (existsSync29(CONFIG_FILE2)) {
|
|
87711
|
+
const content = readFileSync24(CONFIG_FILE2, "utf-8");
|
|
87569
87712
|
const loaded = JSON.parse(content);
|
|
87570
87713
|
config = { ...DEFAULT_CONFIG2, ...loaded };
|
|
87571
87714
|
}
|
|
@@ -87575,15 +87718,15 @@ function loadConfig2() {
|
|
|
87575
87718
|
return config;
|
|
87576
87719
|
}
|
|
87577
87720
|
function ensureDeaconDir() {
|
|
87578
|
-
if (!
|
|
87579
|
-
|
|
87721
|
+
if (!existsSync29(DEACON_DIR)) {
|
|
87722
|
+
mkdirSync21(DEACON_DIR, { recursive: true });
|
|
87580
87723
|
}
|
|
87581
87724
|
}
|
|
87582
87725
|
function loadState() {
|
|
87583
87726
|
ensureDeaconDir();
|
|
87584
87727
|
try {
|
|
87585
|
-
if (
|
|
87586
|
-
const content =
|
|
87728
|
+
if (existsSync29(STATE_FILE)) {
|
|
87729
|
+
const content = readFileSync24(STATE_FILE, "utf-8");
|
|
87587
87730
|
return JSON.parse(content);
|
|
87588
87731
|
}
|
|
87589
87732
|
} catch (error) {
|
|
@@ -87598,7 +87741,7 @@ function loadState() {
|
|
|
87598
87741
|
function saveState(state) {
|
|
87599
87742
|
ensureDeaconDir();
|
|
87600
87743
|
try {
|
|
87601
|
-
|
|
87744
|
+
writeFileSync20(STATE_FILE, JSON.stringify(state, null, 2), "utf-8");
|
|
87602
87745
|
} catch (error) {
|
|
87603
87746
|
console.error("[deacon] Failed to save state:", error);
|
|
87604
87747
|
}
|
|
@@ -87632,12 +87775,12 @@ function getCooldownRemaining(healthState) {
|
|
|
87632
87775
|
}
|
|
87633
87776
|
function checkHeartbeat(name) {
|
|
87634
87777
|
const tmuxSession = getTmuxSessionName(name);
|
|
87635
|
-
const heartbeatFile =
|
|
87778
|
+
const heartbeatFile = join30(PANOPTICON_HOME2, "heartbeats", `${tmuxSession}.json`);
|
|
87636
87779
|
try {
|
|
87637
|
-
if (!
|
|
87780
|
+
if (!existsSync29(heartbeatFile)) {
|
|
87638
87781
|
return { isResponsive: false };
|
|
87639
87782
|
}
|
|
87640
|
-
const content =
|
|
87783
|
+
const content = readFileSync24(heartbeatFile, "utf-8");
|
|
87641
87784
|
const heartbeat = JSON.parse(content);
|
|
87642
87785
|
const lastActivity = new Date(heartbeat.timestamp).getTime();
|
|
87643
87786
|
const age = Date.now() - lastActivity;
|
|
@@ -87800,8 +87943,8 @@ async function checkAndSuspendIdleAgents() {
|
|
|
87800
87943
|
const timeoutMinutes = isSpecialist ? 5 : 10;
|
|
87801
87944
|
const isWorkAgent = agent.id.startsWith("agent-") && !isSpecialist;
|
|
87802
87945
|
if (isWorkAgent) {
|
|
87803
|
-
const completedFile =
|
|
87804
|
-
if (
|
|
87946
|
+
const completedFile = join30(getAgentDir(agent.id), "completed");
|
|
87947
|
+
if (existsSync29(completedFile)) {
|
|
87805
87948
|
continue;
|
|
87806
87949
|
}
|
|
87807
87950
|
}
|
|
@@ -87908,10 +88051,10 @@ function isIssueCompletedOrInReview(agentId) {
|
|
|
87908
88051
|
if (!match)
|
|
87909
88052
|
return false;
|
|
87910
88053
|
const issueId = match[1].toUpperCase();
|
|
87911
|
-
if (!
|
|
88054
|
+
if (!existsSync29(REVIEW_STATUS_FILE)) {
|
|
87912
88055
|
return false;
|
|
87913
88056
|
}
|
|
87914
|
-
const content =
|
|
88057
|
+
const content = readFileSync24(REVIEW_STATUS_FILE, "utf-8");
|
|
87915
88058
|
const statuses = JSON.parse(content);
|
|
87916
88059
|
const status = statuses[issueId];
|
|
87917
88060
|
if (!status) {
|
|
@@ -87986,22 +88129,22 @@ async function cleanupStaleAgentState() {
|
|
|
87986
88129
|
const retentionDays = cloisterConfig.retention?.agent_state_days ?? 30;
|
|
87987
88130
|
const retentionMs = retentionDays * 24 * 60 * 60 * 1e3;
|
|
87988
88131
|
const now = Date.now();
|
|
87989
|
-
if (!
|
|
88132
|
+
if (!existsSync29(AGENTS_DIR)) {
|
|
87990
88133
|
return actions;
|
|
87991
88134
|
}
|
|
87992
88135
|
try {
|
|
87993
88136
|
const dirs = readdirSync10(AGENTS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
87994
88137
|
for (const dir of dirs) {
|
|
87995
|
-
const agentDir =
|
|
88138
|
+
const agentDir = join30(AGENTS_DIR, dir.name);
|
|
87996
88139
|
try {
|
|
87997
88140
|
try {
|
|
87998
88141
|
await execAsync7(`tmux has-session -t "${dir.name}" 2>/dev/null`);
|
|
87999
88142
|
continue;
|
|
88000
88143
|
} catch {
|
|
88001
88144
|
}
|
|
88002
|
-
const stateFile =
|
|
88145
|
+
const stateFile = join30(agentDir, "state.json");
|
|
88003
88146
|
let mtime;
|
|
88004
|
-
if (
|
|
88147
|
+
if (existsSync29(stateFile)) {
|
|
88005
88148
|
mtime = statSync5(stateFile).mtimeMs;
|
|
88006
88149
|
} else {
|
|
88007
88150
|
mtime = statSync5(agentDir).mtimeMs;
|
|
@@ -88010,8 +88153,8 @@ async function cleanupStaleAgentState() {
|
|
|
88010
88153
|
if (ageMs < retentionMs) {
|
|
88011
88154
|
continue;
|
|
88012
88155
|
}
|
|
88013
|
-
const completedFile =
|
|
88014
|
-
if (
|
|
88156
|
+
const completedFile = join30(agentDir, "completed");
|
|
88157
|
+
if (existsSync29(completedFile)) {
|
|
88015
88158
|
const completedAge = now - statSync5(completedFile).mtimeMs;
|
|
88016
88159
|
if (completedAge < 7 * 24 * 60 * 60 * 1e3) {
|
|
88017
88160
|
continue;
|
|
@@ -88038,10 +88181,10 @@ async function cleanupStaleAgentState() {
|
|
|
88038
88181
|
async function checkOrphanedReviewStatuses() {
|
|
88039
88182
|
const actions = [];
|
|
88040
88183
|
try {
|
|
88041
|
-
if (!
|
|
88184
|
+
if (!existsSync29(REVIEW_STATUS_FILE)) {
|
|
88042
88185
|
return actions;
|
|
88043
88186
|
}
|
|
88044
|
-
const content =
|
|
88187
|
+
const content = readFileSync24(REVIEW_STATUS_FILE, "utf-8");
|
|
88045
88188
|
const statuses = JSON.parse(content);
|
|
88046
88189
|
const reviewAgentSession = getTmuxSessionName("review-agent");
|
|
88047
88190
|
const reviewAgentRunning = sessionExists(reviewAgentSession);
|
|
@@ -88067,7 +88210,7 @@ async function checkOrphanedReviewStatuses() {
|
|
|
88067
88210
|
}
|
|
88068
88211
|
}
|
|
88069
88212
|
if (modified) {
|
|
88070
|
-
|
|
88213
|
+
writeFileSync20(REVIEW_STATUS_FILE, JSON.stringify(statuses, null, 2), "utf-8");
|
|
88071
88214
|
}
|
|
88072
88215
|
} catch (error) {
|
|
88073
88216
|
const msg = error instanceof Error ? error.message : String(error);
|
|
@@ -88215,19 +88358,19 @@ function getDeaconStatus() {
|
|
|
88215
88358
|
// ../../lib/cloister/service.ts
|
|
88216
88359
|
init_paths();
|
|
88217
88360
|
init_paths();
|
|
88218
|
-
import { existsSync as
|
|
88219
|
-
import { join as
|
|
88220
|
-
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");
|
|
88221
88364
|
function writeStateFile(running, pid) {
|
|
88222
88365
|
try {
|
|
88223
88366
|
if (running) {
|
|
88224
|
-
|
|
88367
|
+
writeFileSync21(CLOISTER_STATE_FILE, JSON.stringify({
|
|
88225
88368
|
running: true,
|
|
88226
88369
|
pid: pid || process.pid,
|
|
88227
88370
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
88228
88371
|
}));
|
|
88229
88372
|
} else {
|
|
88230
|
-
if (
|
|
88373
|
+
if (existsSync30(CLOISTER_STATE_FILE)) {
|
|
88231
88374
|
unlinkSync9(CLOISTER_STATE_FILE);
|
|
88232
88375
|
}
|
|
88233
88376
|
}
|
|
@@ -88237,8 +88380,8 @@ function writeStateFile(running, pid) {
|
|
|
88237
88380
|
}
|
|
88238
88381
|
function readStateFile() {
|
|
88239
88382
|
try {
|
|
88240
|
-
if (
|
|
88241
|
-
const data = JSON.parse(
|
|
88383
|
+
if (existsSync30(CLOISTER_STATE_FILE)) {
|
|
88384
|
+
const data = JSON.parse(readFileSync25(CLOISTER_STATE_FILE, "utf-8"));
|
|
88242
88385
|
if (data.pid) {
|
|
88243
88386
|
try {
|
|
88244
88387
|
process.kill(data.pid, 0);
|
|
@@ -88457,16 +88600,16 @@ var CloisterService = class {
|
|
|
88457
88600
|
*/
|
|
88458
88601
|
async checkCompletionMarkers() {
|
|
88459
88602
|
try {
|
|
88460
|
-
if (!
|
|
88603
|
+
if (!existsSync30(AGENTS_DIR))
|
|
88461
88604
|
return;
|
|
88462
88605
|
const agentDirs = readdirSync11(AGENTS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.startsWith("agent-"));
|
|
88463
88606
|
for (const dir of agentDirs) {
|
|
88464
|
-
const completedFile =
|
|
88465
|
-
const processedFile =
|
|
88466
|
-
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))
|
|
88467
88610
|
continue;
|
|
88468
88611
|
try {
|
|
88469
|
-
const content = JSON.parse(
|
|
88612
|
+
const content = JSON.parse(readFileSync25(completedFile, "utf-8"));
|
|
88470
88613
|
const ageMs = Date.now() - new Date(content.timestamp).getTime();
|
|
88471
88614
|
if (ageMs > 24 * 60 * 60 * 1e3) {
|
|
88472
88615
|
console.log(`\u{1F514} Cloister: Skipping stale completion marker for ${dir.name} (${Math.floor(ageMs / 36e5)}h old)`);
|
|
@@ -89026,7 +89169,7 @@ init_settings();
|
|
|
89026
89169
|
init_js_yaml();
|
|
89027
89170
|
init_config_yaml();
|
|
89028
89171
|
init_model_capabilities();
|
|
89029
|
-
import { writeFileSync as
|
|
89172
|
+
import { writeFileSync as writeFileSync22 } from "fs";
|
|
89030
89173
|
function getOptimalModelDefaults() {
|
|
89031
89174
|
return {
|
|
89032
89175
|
// High-complexity phases - Opus 4.6 for deep analysis
|
|
@@ -89102,7 +89245,7 @@ function saveSettingsApi(settings) {
|
|
|
89102
89245
|
lineWidth: 120,
|
|
89103
89246
|
noRefs: true
|
|
89104
89247
|
});
|
|
89105
|
-
|
|
89248
|
+
writeFileSync22(getGlobalConfigPath(), yamlContent, "utf-8");
|
|
89106
89249
|
}
|
|
89107
89250
|
function validateSettingsApi(settings) {
|
|
89108
89251
|
const errors = [];
|
|
@@ -89186,7 +89329,7 @@ init_merge_agent();
|
|
|
89186
89329
|
|
|
89187
89330
|
// ../lib/health-filtering.ts
|
|
89188
89331
|
init_config();
|
|
89189
|
-
import { readFileSync as
|
|
89332
|
+
import { readFileSync as readFileSync27, existsSync as existsSync34 } from "fs";
|
|
89190
89333
|
import { exec as exec11 } from "child_process";
|
|
89191
89334
|
import { promisify as promisify11 } from "util";
|
|
89192
89335
|
var execAsync11 = promisify11(exec11);
|
|
@@ -89206,9 +89349,9 @@ async function determineHealthStatusAsync(agentId, stateFile) {
|
|
|
89206
89349
|
const health = await checkAgentHealthAsync(agentId);
|
|
89207
89350
|
let agentStatus;
|
|
89208
89351
|
let lastActivity = null;
|
|
89209
|
-
if (
|
|
89352
|
+
if (existsSync34(stateFile)) {
|
|
89210
89353
|
try {
|
|
89211
|
-
const state = JSON.parse(
|
|
89354
|
+
const state = JSON.parse(readFileSync27(stateFile, "utf-8"));
|
|
89212
89355
|
agentStatus = state.status;
|
|
89213
89356
|
lastActivity = state.lastActivity ? new Date(state.lastActivity) : null;
|
|
89214
89357
|
} catch {
|
|
@@ -89257,9 +89400,9 @@ init_jsonl_parser();
|
|
|
89257
89400
|
// ../../lib/convoy.ts
|
|
89258
89401
|
var import_yaml2 = __toESM(require_dist3(), 1);
|
|
89259
89402
|
init_tmux();
|
|
89260
|
-
import { existsSync as
|
|
89261
|
-
import { join as
|
|
89262
|
-
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";
|
|
89263
89406
|
import { exec as exec12 } from "child_process";
|
|
89264
89407
|
import { promisify as promisify12 } from "util";
|
|
89265
89408
|
|
|
@@ -89382,25 +89525,25 @@ function getExecutionOrder(template) {
|
|
|
89382
89525
|
init_paths();
|
|
89383
89526
|
init_work_type_router();
|
|
89384
89527
|
var execAsync12 = promisify12(exec12);
|
|
89385
|
-
var CONVOY_DIR =
|
|
89528
|
+
var CONVOY_DIR = join35(homedir14(), ".panopticon", "convoys");
|
|
89386
89529
|
function getConvoyStateFile(convoyId) {
|
|
89387
|
-
return
|
|
89530
|
+
return join35(CONVOY_DIR, `${convoyId}.json`);
|
|
89388
89531
|
}
|
|
89389
89532
|
function getConvoyOutputDir(convoyId, template) {
|
|
89390
89533
|
const baseDir = template.config?.outputDir || ".panopticon/convoy-output";
|
|
89391
|
-
return
|
|
89534
|
+
return join35(process.cwd(), baseDir, convoyId);
|
|
89392
89535
|
}
|
|
89393
89536
|
function saveConvoyState(state) {
|
|
89394
|
-
|
|
89395
|
-
|
|
89537
|
+
mkdirSync23(CONVOY_DIR, { recursive: true });
|
|
89538
|
+
writeFileSync24(getConvoyStateFile(state.id), JSON.stringify(state, null, 2));
|
|
89396
89539
|
}
|
|
89397
89540
|
function loadConvoyState(convoyId) {
|
|
89398
89541
|
const stateFile = getConvoyStateFile(convoyId);
|
|
89399
|
-
if (!
|
|
89542
|
+
if (!existsSync35(stateFile)) {
|
|
89400
89543
|
return void 0;
|
|
89401
89544
|
}
|
|
89402
89545
|
try {
|
|
89403
|
-
const content =
|
|
89546
|
+
const content = readFileSync28(stateFile, "utf-8");
|
|
89404
89547
|
return JSON.parse(content);
|
|
89405
89548
|
} catch {
|
|
89406
89549
|
return void 0;
|
|
@@ -89410,7 +89553,7 @@ function getConvoyStatus(convoyId) {
|
|
|
89410
89553
|
return loadConvoyState(convoyId);
|
|
89411
89554
|
}
|
|
89412
89555
|
function listConvoys(filter) {
|
|
89413
|
-
if (!
|
|
89556
|
+
if (!existsSync35(CONVOY_DIR)) {
|
|
89414
89557
|
return [];
|
|
89415
89558
|
}
|
|
89416
89559
|
const files = readdirSync13(CONVOY_DIR).filter((f) => f.endsWith(".json"));
|
|
@@ -89429,10 +89572,10 @@ function listConvoys(filter) {
|
|
|
89429
89572
|
);
|
|
89430
89573
|
}
|
|
89431
89574
|
function parseAgentTemplate(templatePath) {
|
|
89432
|
-
if (!
|
|
89575
|
+
if (!existsSync35(templatePath)) {
|
|
89433
89576
|
throw new Error(`Agent template not found: ${templatePath}`);
|
|
89434
89577
|
}
|
|
89435
|
-
const content =
|
|
89578
|
+
const content = readFileSync28(templatePath, "utf-8");
|
|
89436
89579
|
const frontmatterMatch = content.match(/^---\n([\s\S]+?)\n---\n([\s\S]*)$/);
|
|
89437
89580
|
if (!frontmatterMatch) {
|
|
89438
89581
|
throw new Error(`Invalid agent template format (missing frontmatter): ${templatePath}`);
|
|
@@ -89458,7 +89601,7 @@ function mapConvoyRoleToWorkType(role) {
|
|
|
89458
89601
|
}
|
|
89459
89602
|
async function spawnConvoyAgent(convoy, agent, agentState, context) {
|
|
89460
89603
|
const { role, subagent } = agent;
|
|
89461
|
-
const templatePath =
|
|
89604
|
+
const templatePath = join35(AGENTS_DIR, `${subagent}.md`);
|
|
89462
89605
|
const template = parseAgentTemplate(templatePath);
|
|
89463
89606
|
let model = template.model;
|
|
89464
89607
|
try {
|
|
@@ -89496,9 +89639,9 @@ ${context.issueId ? `**Issue ID**: ${context.issueId}` : ""}
|
|
|
89496
89639
|
|
|
89497
89640
|
`;
|
|
89498
89641
|
prompt = contextInstructions + prompt;
|
|
89499
|
-
|
|
89500
|
-
const promptFile =
|
|
89501
|
-
|
|
89642
|
+
mkdirSync23(convoy.outputDir, { recursive: true });
|
|
89643
|
+
const promptFile = join35(convoy.outputDir, `${role}-prompt.md`);
|
|
89644
|
+
writeFileSync24(promptFile, prompt);
|
|
89502
89645
|
const claudeCmd = `claude --dangerously-skip-permissions --model ${model}`;
|
|
89503
89646
|
createSession(agentState.tmuxSession, convoy.context.projectPath, claudeCmd, {
|
|
89504
89647
|
env: {
|
|
@@ -89523,7 +89666,7 @@ async function startConvoy(templateName, context) {
|
|
|
89523
89666
|
const timestamp2 = Date.now();
|
|
89524
89667
|
const convoyId = `convoy-${templateName}-${timestamp2}`;
|
|
89525
89668
|
const outputDir = getConvoyOutputDir(convoyId, template);
|
|
89526
|
-
|
|
89669
|
+
mkdirSync23(outputDir, { recursive: true });
|
|
89527
89670
|
const state = {
|
|
89528
89671
|
id: convoyId,
|
|
89529
89672
|
template: templateName,
|
|
@@ -89535,7 +89678,7 @@ async function startConvoy(templateName, context) {
|
|
|
89535
89678
|
};
|
|
89536
89679
|
for (const agent of template.agents) {
|
|
89537
89680
|
const tmuxSession = `${convoyId}-${agent.role}`;
|
|
89538
|
-
const outputFile =
|
|
89681
|
+
const outputFile = join35(outputDir, `${agent.role}.md`);
|
|
89539
89682
|
state.agents.push({
|
|
89540
89683
|
role: agent.role,
|
|
89541
89684
|
subagent: agent.subagent,
|
|
@@ -89570,8 +89713,8 @@ async function executePhase(convoy, template, phaseAgents, context) {
|
|
|
89570
89713
|
const agentContext = { ...context };
|
|
89571
89714
|
for (const depRole of deps) {
|
|
89572
89715
|
const depAgent = convoy.agents.find((a) => a.role === depRole);
|
|
89573
|
-
if (depAgent?.outputFile &&
|
|
89574
|
-
agentContext[`${depRole}_output`] =
|
|
89716
|
+
if (depAgent?.outputFile && existsSync35(depAgent.outputFile)) {
|
|
89717
|
+
agentContext[`${depRole}_output`] = readFileSync28(depAgent.outputFile, "utf-8");
|
|
89575
89718
|
}
|
|
89576
89719
|
}
|
|
89577
89720
|
spawnPromises.push(spawnConvoyAgent(convoy, agent, agentState, agentContext));
|
|
@@ -89634,7 +89777,7 @@ function updateAgentStatuses(convoy) {
|
|
|
89634
89777
|
agent.status = "completed";
|
|
89635
89778
|
agent.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
89636
89779
|
updated = true;
|
|
89637
|
-
if (agent.outputFile &&
|
|
89780
|
+
if (agent.outputFile && existsSync35(agent.outputFile)) {
|
|
89638
89781
|
agent.exitCode = 0;
|
|
89639
89782
|
} else {
|
|
89640
89783
|
agent.exitCode = 1;
|
|
@@ -89662,10 +89805,10 @@ async function stopConvoy(convoyId) {
|
|
|
89662
89805
|
}
|
|
89663
89806
|
|
|
89664
89807
|
// ../../lib/env-loader.ts
|
|
89665
|
-
import { readFileSync as
|
|
89666
|
-
import { join as
|
|
89667
|
-
import { homedir as
|
|
89668
|
-
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");
|
|
89669
89812
|
function parseEnvFile(content) {
|
|
89670
89813
|
const result = {};
|
|
89671
89814
|
for (const line of content.split("\n")) {
|
|
@@ -89687,11 +89830,11 @@ function loadPanopticonEnv() {
|
|
|
89687
89830
|
loaded: [],
|
|
89688
89831
|
skipped: []
|
|
89689
89832
|
};
|
|
89690
|
-
if (!
|
|
89833
|
+
if (!existsSync36(ENV_FILE_PATH)) {
|
|
89691
89834
|
return { ...result, error: `Env file not found: ${ENV_FILE_PATH}` };
|
|
89692
89835
|
}
|
|
89693
89836
|
try {
|
|
89694
|
-
const content =
|
|
89837
|
+
const content = readFileSync29(ENV_FILE_PATH, "utf-8");
|
|
89695
89838
|
const envVars = parseEnvFile(content);
|
|
89696
89839
|
for (const [key, value] of Object.entries(envVars)) {
|
|
89697
89840
|
if (process.env[key]) {
|
|
@@ -89708,21 +89851,21 @@ function loadPanopticonEnv() {
|
|
|
89708
89851
|
}
|
|
89709
89852
|
|
|
89710
89853
|
// ../../lib/costs/events.ts
|
|
89711
|
-
import { existsSync as
|
|
89712
|
-
import { join as
|
|
89713
|
-
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";
|
|
89714
89857
|
function getCostsDir() {
|
|
89715
|
-
return
|
|
89858
|
+
return join37(process.env.HOME || homedir16(), ".panopticon", "costs");
|
|
89716
89859
|
}
|
|
89717
89860
|
function getEventsFile() {
|
|
89718
|
-
return
|
|
89861
|
+
return join37(getCostsDir(), "events.jsonl");
|
|
89719
89862
|
}
|
|
89720
89863
|
function ensureEventsFile() {
|
|
89721
89864
|
const costsDir = getCostsDir();
|
|
89722
89865
|
const eventsFile = getEventsFile();
|
|
89723
|
-
|
|
89724
|
-
if (!
|
|
89725
|
-
|
|
89866
|
+
mkdirSync24(costsDir, { recursive: true });
|
|
89867
|
+
if (!existsSync37(eventsFile)) {
|
|
89868
|
+
writeFileSync25(eventsFile, "", "utf-8");
|
|
89726
89869
|
}
|
|
89727
89870
|
}
|
|
89728
89871
|
function appendCostEvent(event) {
|
|
@@ -89734,10 +89877,10 @@ function appendCostEvent(event) {
|
|
|
89734
89877
|
appendFileSync8(getEventsFile(), line, "utf-8");
|
|
89735
89878
|
}
|
|
89736
89879
|
function readEvents(options = {}) {
|
|
89737
|
-
if (!
|
|
89880
|
+
if (!existsSync37(getEventsFile())) {
|
|
89738
89881
|
return [];
|
|
89739
89882
|
}
|
|
89740
|
-
const content =
|
|
89883
|
+
const content = readFileSync30(getEventsFile(), "utf-8");
|
|
89741
89884
|
const lines = content.split("\n").filter((line) => line.trim());
|
|
89742
89885
|
let events = [];
|
|
89743
89886
|
for (const line of lines) {
|
|
@@ -89772,10 +89915,10 @@ function readEvents(options = {}) {
|
|
|
89772
89915
|
return events;
|
|
89773
89916
|
}
|
|
89774
89917
|
function tailEvents(n) {
|
|
89775
|
-
if (!
|
|
89918
|
+
if (!existsSync37(getEventsFile())) {
|
|
89776
89919
|
return [];
|
|
89777
89920
|
}
|
|
89778
|
-
const content =
|
|
89921
|
+
const content = readFileSync30(getEventsFile(), "utf-8");
|
|
89779
89922
|
const lines = content.split("\n").filter((line) => line.trim());
|
|
89780
89923
|
const lastLines = lines.slice(-n);
|
|
89781
89924
|
const events = [];
|
|
@@ -89788,10 +89931,10 @@ function tailEvents(n) {
|
|
|
89788
89931
|
return events;
|
|
89789
89932
|
}
|
|
89790
89933
|
function readEventsFromLine(startLine) {
|
|
89791
|
-
if (!
|
|
89934
|
+
if (!existsSync37(getEventsFile())) {
|
|
89792
89935
|
return { events: [], newLine: startLine };
|
|
89793
89936
|
}
|
|
89794
|
-
const content =
|
|
89937
|
+
const content = readFileSync30(getEventsFile(), "utf-8");
|
|
89795
89938
|
const lines = content.split("\n").filter((line) => line.trim());
|
|
89796
89939
|
const events = [];
|
|
89797
89940
|
for (let i = startLine; i < lines.length; i++) {
|
|
@@ -89804,14 +89947,14 @@ function readEventsFromLine(startLine) {
|
|
|
89804
89947
|
return { events, newLine: lines.length };
|
|
89805
89948
|
}
|
|
89806
89949
|
function getLastEventMetadata() {
|
|
89807
|
-
if (!
|
|
89950
|
+
if (!existsSync37(getEventsFile())) {
|
|
89808
89951
|
return {
|
|
89809
89952
|
lastEventTs: null,
|
|
89810
89953
|
lastEventLine: 0,
|
|
89811
89954
|
totalEvents: 0
|
|
89812
89955
|
};
|
|
89813
89956
|
}
|
|
89814
|
-
const content =
|
|
89957
|
+
const content = readFileSync30(getEventsFile(), "utf-8");
|
|
89815
89958
|
const lines = content.split("\n").filter((line) => line.trim());
|
|
89816
89959
|
let lastEventTs = null;
|
|
89817
89960
|
if (lines.length > 0) {
|
|
@@ -89828,28 +89971,28 @@ function getLastEventMetadata() {
|
|
|
89828
89971
|
};
|
|
89829
89972
|
}
|
|
89830
89973
|
function eventsFileExists() {
|
|
89831
|
-
return
|
|
89974
|
+
return existsSync37(getEventsFile());
|
|
89832
89975
|
}
|
|
89833
89976
|
|
|
89834
89977
|
// ../../lib/costs/aggregator.ts
|
|
89835
|
-
import { existsSync as
|
|
89836
|
-
import { join as
|
|
89837
|
-
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";
|
|
89838
89981
|
var CACHE_VERSION = 3;
|
|
89839
89982
|
var DEFAULT_RETENTION_DAYS = 90;
|
|
89840
89983
|
function getCostsDir2() {
|
|
89841
|
-
return
|
|
89984
|
+
return join38(process.env.HOME || homedir17(), ".panopticon", "costs");
|
|
89842
89985
|
}
|
|
89843
89986
|
function getCacheFile() {
|
|
89844
|
-
return
|
|
89987
|
+
return join38(getCostsDir2(), "by-issue.json");
|
|
89845
89988
|
}
|
|
89846
89989
|
function loadCache() {
|
|
89847
89990
|
const cacheFile = getCacheFile();
|
|
89848
|
-
if (!
|
|
89991
|
+
if (!existsSync38(cacheFile)) {
|
|
89849
89992
|
return createEmptyCache();
|
|
89850
89993
|
}
|
|
89851
89994
|
try {
|
|
89852
|
-
const content =
|
|
89995
|
+
const content = readFileSync31(cacheFile, "utf-8");
|
|
89853
89996
|
const cache = JSON.parse(content);
|
|
89854
89997
|
if (cache.version !== CACHE_VERSION) {
|
|
89855
89998
|
console.warn(`Cache version mismatch: expected ${CACHE_VERSION}, got ${cache.version}. Rebuilding cache.`);
|
|
@@ -89874,10 +90017,10 @@ function createEmptyCache() {
|
|
|
89874
90017
|
function saveCache(cache) {
|
|
89875
90018
|
const costsDir = getCostsDir2();
|
|
89876
90019
|
const cacheFile = getCacheFile();
|
|
89877
|
-
|
|
90020
|
+
mkdirSync25(costsDir, { recursive: true });
|
|
89878
90021
|
const tempFile = cacheFile + ".tmp";
|
|
89879
90022
|
const content = JSON.stringify(cache, null, 2);
|
|
89880
|
-
|
|
90023
|
+
writeFileSync26(tempFile, content, "utf-8");
|
|
89881
90024
|
renameSync3(tempFile, cacheFile);
|
|
89882
90025
|
}
|
|
89883
90026
|
function updateCacheFromEvents(events, newLineNumber) {
|
|
@@ -90012,18 +90155,18 @@ function getCacheStatus() {
|
|
|
90012
90155
|
}
|
|
90013
90156
|
|
|
90014
90157
|
// ../../lib/costs/migration.ts
|
|
90015
|
-
import { existsSync as
|
|
90016
|
-
import { join as
|
|
90017
|
-
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";
|
|
90018
90161
|
init_cost();
|
|
90019
90162
|
function getAgentsDir() {
|
|
90020
|
-
return
|
|
90163
|
+
return join39(process.env.HOME || homedir18(), ".panopticon", "agents");
|
|
90021
90164
|
}
|
|
90022
90165
|
function getClaudeProjectsDir() {
|
|
90023
|
-
return
|
|
90166
|
+
return join39(process.env.HOME || homedir18(), ".claude", "projects");
|
|
90024
90167
|
}
|
|
90025
90168
|
function getProjectsYamlPath() {
|
|
90026
|
-
return
|
|
90169
|
+
return join39(process.env.HOME || homedir18(), ".panopticon", "projects.yaml");
|
|
90027
90170
|
}
|
|
90028
90171
|
function inferIssueId(agentDir) {
|
|
90029
90172
|
const stripped = agentDir.replace(/^(agent|planning)-/, "");
|
|
@@ -90037,8 +90180,8 @@ function getProjectPaths() {
|
|
|
90037
90180
|
const paths = [];
|
|
90038
90181
|
try {
|
|
90039
90182
|
const yamlPath = getProjectsYamlPath();
|
|
90040
|
-
if (
|
|
90041
|
-
const content =
|
|
90183
|
+
if (existsSync39(yamlPath)) {
|
|
90184
|
+
const content = readFileSync32(yamlPath, "utf-8");
|
|
90042
90185
|
const pathMatches = content.match(/^\s+path:\s+(.+)$/gm);
|
|
90043
90186
|
if (pathMatches) {
|
|
90044
90187
|
for (const m of pathMatches) {
|
|
@@ -90057,8 +90200,8 @@ function resolveWorkspace(issueId) {
|
|
|
90057
90200
|
const issueLower = issueId.toLowerCase();
|
|
90058
90201
|
const projectPaths = getProjectPaths();
|
|
90059
90202
|
for (const projectPath of projectPaths) {
|
|
90060
|
-
const wsPath =
|
|
90061
|
-
if (
|
|
90203
|
+
const wsPath = join39(projectPath, "workspaces", `feature-${issueLower}`);
|
|
90204
|
+
if (existsSync39(wsPath)) {
|
|
90062
90205
|
return wsPath;
|
|
90063
90206
|
}
|
|
90064
90207
|
}
|
|
@@ -90072,7 +90215,7 @@ function findSessionDirsByIssue(issueId) {
|
|
|
90072
90215
|
const entries = readdirSync14(claudeDir);
|
|
90073
90216
|
for (const entry of entries) {
|
|
90074
90217
|
if (entry.includes(`feature-${issueLower}`)) {
|
|
90075
|
-
const fullPath =
|
|
90218
|
+
const fullPath = join39(claudeDir, entry);
|
|
90076
90219
|
dirs.push(fullPath);
|
|
90077
90220
|
}
|
|
90078
90221
|
}
|
|
@@ -90083,7 +90226,7 @@ function findSessionDirsByIssue(issueId) {
|
|
|
90083
90226
|
function parseSessionFile(filePath) {
|
|
90084
90227
|
const usages = [];
|
|
90085
90228
|
try {
|
|
90086
|
-
const content =
|
|
90229
|
+
const content = readFileSync32(filePath, "utf-8");
|
|
90087
90230
|
const lines = content.split("\n").filter((l) => l.trim());
|
|
90088
90231
|
for (const line of lines) {
|
|
90089
90232
|
try {
|
|
@@ -90151,15 +90294,15 @@ function usageToCostEvents(usages, context) {
|
|
|
90151
90294
|
}
|
|
90152
90295
|
function getSessionDir(workspacePath) {
|
|
90153
90296
|
const sessionDirName = `-${workspacePath.replace(/^\//, "").replace(/\//g, "-")}`;
|
|
90154
|
-
const sessionDir =
|
|
90155
|
-
if (
|
|
90297
|
+
const sessionDir = join39(getClaudeProjectsDir(), sessionDirName);
|
|
90298
|
+
if (existsSync39(sessionDir)) {
|
|
90156
90299
|
return sessionDir;
|
|
90157
90300
|
}
|
|
90158
90301
|
return null;
|
|
90159
90302
|
}
|
|
90160
90303
|
function migrateAgent(agentDir, stats) {
|
|
90161
|
-
const stateFile =
|
|
90162
|
-
if (!
|
|
90304
|
+
const stateFile = join39(getAgentsDir(), agentDir, "state.json");
|
|
90305
|
+
if (!existsSync39(stateFile)) {
|
|
90163
90306
|
stats.warnings.push({
|
|
90164
90307
|
file: stateFile,
|
|
90165
90308
|
message: "No state.json found"
|
|
@@ -90168,7 +90311,7 @@ function migrateAgent(agentDir, stats) {
|
|
|
90168
90311
|
}
|
|
90169
90312
|
let state;
|
|
90170
90313
|
try {
|
|
90171
|
-
state = JSON.parse(
|
|
90314
|
+
state = JSON.parse(readFileSync32(stateFile, "utf-8"));
|
|
90172
90315
|
} catch (err) {
|
|
90173
90316
|
stats.errors.push({
|
|
90174
90317
|
file: stateFile,
|
|
@@ -90206,7 +90349,7 @@ function migrateAgent(agentDir, stats) {
|
|
|
90206
90349
|
try {
|
|
90207
90350
|
const files = readdirSync14(sessionDir).filter((f) => f.endsWith(".jsonl"));
|
|
90208
90351
|
for (const file of files) {
|
|
90209
|
-
const filePath =
|
|
90352
|
+
const filePath = join39(sessionDir, file);
|
|
90210
90353
|
try {
|
|
90211
90354
|
const usages = parseSessionFile(filePath);
|
|
90212
90355
|
const events = usageToCostEvents(usages, context);
|
|
@@ -90230,12 +90373,12 @@ function migrateAgent(agentDir, stats) {
|
|
|
90230
90373
|
error: `Failed to read session directory: ${err}`
|
|
90231
90374
|
});
|
|
90232
90375
|
}
|
|
90233
|
-
const subagentsDir =
|
|
90234
|
-
if (
|
|
90376
|
+
const subagentsDir = join39(sessionDir, "subagents");
|
|
90377
|
+
if (existsSync39(subagentsDir)) {
|
|
90235
90378
|
try {
|
|
90236
90379
|
const subagentFiles = readdirSync14(subagentsDir).filter((f) => f.endsWith(".jsonl"));
|
|
90237
90380
|
for (const file of subagentFiles) {
|
|
90238
|
-
const filePath =
|
|
90381
|
+
const filePath = join39(subagentsDir, file);
|
|
90239
90382
|
try {
|
|
90240
90383
|
const usages = parseSessionFile(filePath);
|
|
90241
90384
|
const subagentContext = {
|
|
@@ -90279,7 +90422,7 @@ function migrateAllSessions() {
|
|
|
90279
90422
|
};
|
|
90280
90423
|
console.log("Starting migration of historical session data...");
|
|
90281
90424
|
const agentsDir = getAgentsDir();
|
|
90282
|
-
if (!
|
|
90425
|
+
if (!existsSync39(agentsDir)) {
|
|
90283
90426
|
console.log("No agents directory found - nothing to migrate");
|
|
90284
90427
|
return stats;
|
|
90285
90428
|
}
|
|
@@ -90326,71 +90469,8 @@ function migrateIfNeeded() {
|
|
|
90326
90469
|
return migrateAllSessions();
|
|
90327
90470
|
}
|
|
90328
90471
|
|
|
90329
|
-
//
|
|
90330
|
-
|
|
90331
|
-
import { join as join39, dirname as dirname5 } from "path";
|
|
90332
|
-
import { homedir as homedir18 } from "os";
|
|
90333
|
-
var DEFAULT_STATUS_FILE = join39(homedir18(), ".panopticon", "review-status.json");
|
|
90334
|
-
function loadReviewStatuses(filePath = DEFAULT_STATUS_FILE) {
|
|
90335
|
-
try {
|
|
90336
|
-
if (existsSync39(filePath)) {
|
|
90337
|
-
return JSON.parse(readFileSync32(filePath, "utf-8"));
|
|
90338
|
-
}
|
|
90339
|
-
} catch (err) {
|
|
90340
|
-
console.error("Failed to load review statuses:", err);
|
|
90341
|
-
}
|
|
90342
|
-
return {};
|
|
90343
|
-
}
|
|
90344
|
-
function saveReviewStatuses(statuses, filePath = DEFAULT_STATUS_FILE) {
|
|
90345
|
-
try {
|
|
90346
|
-
const dir = dirname5(filePath);
|
|
90347
|
-
if (!existsSync39(dir)) {
|
|
90348
|
-
mkdirSync25(dir, { recursive: true });
|
|
90349
|
-
}
|
|
90350
|
-
writeFileSync26(filePath, JSON.stringify(statuses, null, 2));
|
|
90351
|
-
} catch (err) {
|
|
90352
|
-
console.error("Failed to save review statuses:", err);
|
|
90353
|
-
}
|
|
90354
|
-
}
|
|
90355
|
-
function setReviewStatus(issueId, update, filePath = DEFAULT_STATUS_FILE) {
|
|
90356
|
-
const statuses = loadReviewStatuses(filePath);
|
|
90357
|
-
const existing = statuses[issueId] || {
|
|
90358
|
-
issueId,
|
|
90359
|
-
reviewStatus: "pending",
|
|
90360
|
-
testStatus: "pending",
|
|
90361
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
90362
|
-
readyForMerge: false
|
|
90363
|
-
};
|
|
90364
|
-
const merged = { ...existing, ...update };
|
|
90365
|
-
const history = [...existing.history || []];
|
|
90366
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
90367
|
-
if (update.reviewStatus && update.reviewStatus !== existing.reviewStatus) {
|
|
90368
|
-
history.push({ type: "review", status: update.reviewStatus, timestamp: now, notes: update.reviewNotes });
|
|
90369
|
-
}
|
|
90370
|
-
if (update.testStatus && update.testStatus !== existing.testStatus) {
|
|
90371
|
-
history.push({ type: "test", status: update.testStatus, timestamp: now, notes: update.testNotes });
|
|
90372
|
-
}
|
|
90373
|
-
if (update.mergeStatus && update.mergeStatus !== existing.mergeStatus) {
|
|
90374
|
-
history.push({ type: "merge", status: update.mergeStatus, timestamp: now });
|
|
90375
|
-
}
|
|
90376
|
-
while (history.length > 10)
|
|
90377
|
-
history.shift();
|
|
90378
|
-
const readyForMerge = update.readyForMerge !== void 0 ? update.readyForMerge : merged.reviewStatus === "passed" && merged.testStatus === "passed" && merged.mergeStatus !== "merged";
|
|
90379
|
-
const updated = {
|
|
90380
|
-
...merged,
|
|
90381
|
-
issueId,
|
|
90382
|
-
updatedAt: now,
|
|
90383
|
-
readyForMerge,
|
|
90384
|
-
history
|
|
90385
|
-
};
|
|
90386
|
-
statuses[issueId] = updated;
|
|
90387
|
-
saveReviewStatuses(statuses, filePath);
|
|
90388
|
-
return updated;
|
|
90389
|
-
}
|
|
90390
|
-
function getReviewStatus(issueId, filePath = DEFAULT_STATUS_FILE) {
|
|
90391
|
-
const statuses = loadReviewStatuses(filePath);
|
|
90392
|
-
return statuses[issueId] || null;
|
|
90393
|
-
}
|
|
90472
|
+
// index.ts
|
|
90473
|
+
init_review_status();
|
|
90394
90474
|
|
|
90395
90475
|
// ../../lib/remote/index.ts
|
|
90396
90476
|
init_exe_provider();
|