panopticon-cli 0.4.28 → 0.4.30
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/{agents-ND4NKCK2.js → agents-ZG4JIPO2.js} +4 -4
- package/dist/{chunk-4KNEZGKZ.js → chunk-42O6XJWZ.js} +45 -24
- package/dist/chunk-42O6XJWZ.js.map +1 -0
- package/dist/{chunk-46DPNFMW.js → chunk-J3J32DIR.js} +9 -8
- package/dist/chunk-J3J32DIR.js.map +1 -0
- package/dist/{chunk-ZLB6G4NW.js → chunk-VH27COUW.js} +6 -6
- package/dist/chunk-VH27COUW.js.map +1 -0
- package/dist/{chunk-ON5NIBGW.js → chunk-VIWUCJ4V.js} +37 -8
- package/dist/chunk-VIWUCJ4V.js.map +1 -0
- package/dist/cli/index.js +117 -68
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/public/assets/{index--VPaQ2VU.css → index-C7X6LP5Z.css} +1 -1
- package/dist/dashboard/public/assets/{index-GYQaqwVS.js → index-Dhwz2I7n.js} +152 -152
- package/dist/dashboard/public/index.html +2 -2
- package/dist/dashboard/server.js +1741 -1381
- package/dist/feedback-writer-AAKF5BTK.js +111 -0
- package/dist/feedback-writer-AAKF5BTK.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/{specialist-context-WXO3FKIB.js → specialist-context-QVRSHPYN.js} +3 -3
- package/dist/{specialist-logs-SJWLETJT.js → specialist-logs-7TQMHCUN.js} +3 -3
- package/dist/{specialists-5YJIDRW6.js → specialists-UUXB5KFV.js} +3 -3
- package/package.json +1 -1
- package/templates/traefik/docker-compose.yml +7 -4
- package/templates/traefik/dynamic/panopticon.yml.template +3 -1
- package/dist/chunk-46DPNFMW.js.map +0 -1
- package/dist/chunk-4KNEZGKZ.js.map +0 -1
- package/dist/chunk-ON5NIBGW.js.map +0 -1
- package/dist/chunk-ZLB6G4NW.js.map +0 -1
- /package/dist/{agents-ND4NKCK2.js.map → agents-ZG4JIPO2.js.map} +0 -0
- /package/dist/{specialist-context-WXO3FKIB.js.map → specialist-context-QVRSHPYN.js.map} +0 -0
- /package/dist/{specialist-logs-SJWLETJT.js.map → specialist-logs-7TQMHCUN.js.map} +0 -0
- /package/dist/{specialists-5YJIDRW6.js.map → specialists-UUXB5KFV.js.map} +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
recordWake,
|
|
26
26
|
wakeSpecialistOrQueue,
|
|
27
27
|
wakeSpecialistWithTask
|
|
28
|
-
} from "../chunk-
|
|
28
|
+
} from "../chunk-42O6XJWZ.js";
|
|
29
29
|
import {
|
|
30
30
|
addAlias,
|
|
31
31
|
cleanOldBackups,
|
|
@@ -68,7 +68,7 @@ import {
|
|
|
68
68
|
saveSessionId,
|
|
69
69
|
spawnAgent,
|
|
70
70
|
stopAgent
|
|
71
|
-
} from "../chunk-
|
|
71
|
+
} from "../chunk-VH27COUW.js";
|
|
72
72
|
import {
|
|
73
73
|
checkHook,
|
|
74
74
|
clearHook,
|
|
@@ -82,13 +82,14 @@ import {
|
|
|
82
82
|
popFromHook,
|
|
83
83
|
pushToHook,
|
|
84
84
|
sendKeys,
|
|
85
|
+
sendKeysAsync,
|
|
85
86
|
sendMail,
|
|
86
87
|
sessionExists
|
|
87
|
-
} from "../chunk-
|
|
88
|
+
} from "../chunk-VIWUCJ4V.js";
|
|
88
89
|
import {
|
|
89
90
|
init_settings,
|
|
90
91
|
loadSettings
|
|
91
|
-
} from "../chunk-
|
|
92
|
+
} from "../chunk-J3J32DIR.js";
|
|
92
93
|
import {
|
|
93
94
|
init_config_yaml,
|
|
94
95
|
loadConfig as loadConfig2
|
|
@@ -894,7 +895,7 @@ async function isRemoteAvailable() {
|
|
|
894
895
|
|
|
895
896
|
// src/lib/cloister/work-agent-prompt.ts
|
|
896
897
|
init_esm_shims();
|
|
897
|
-
import { existsSync as existsSync6, readFileSync as readFileSync4 } from "fs";
|
|
898
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, readdirSync as readdirSync6 } from "fs";
|
|
898
899
|
import { join as join6, dirname as dirname3 } from "path";
|
|
899
900
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
900
901
|
|
|
@@ -1008,6 +1009,7 @@ function buildWorkAgentPrompt(ctx) {
|
|
|
1008
1009
|
let beadsTasksStr = "";
|
|
1009
1010
|
let stitchDesignsStr = "";
|
|
1010
1011
|
let polyrepoContextStr = "";
|
|
1012
|
+
let pendingFeedbackStr = "";
|
|
1011
1013
|
if (!ctx.skipDynamicContext && ctx.projectRoot) {
|
|
1012
1014
|
const planningContent = readPlanningContext(ctx.workspacePath);
|
|
1013
1015
|
const beadsTasks = readBeadsTasks(ctx.workspacePath, ctx.projectRoot, ctx.issueId);
|
|
@@ -1019,6 +1021,7 @@ function buildWorkAgentPrompt(ctx) {
|
|
|
1019
1021
|
stitchDesignsStr = stitchDesigns;
|
|
1020
1022
|
}
|
|
1021
1023
|
polyrepoContextStr = buildPolyrepoContext(ctx.issueId, ctx.workspacePath);
|
|
1024
|
+
pendingFeedbackStr = readPendingFeedback(ctx.workspacePath);
|
|
1022
1025
|
}
|
|
1023
1026
|
const apiUrl = process.env.DASHBOARD_URL || `http://localhost:${process.env.API_PORT || process.env.PORT || "3011"}`;
|
|
1024
1027
|
const vars = {
|
|
@@ -1029,13 +1032,36 @@ function buildWorkAgentPrompt(ctx) {
|
|
|
1029
1032
|
API_URL: apiUrl,
|
|
1030
1033
|
BEADS_TASKS: beadsTasksStr,
|
|
1031
1034
|
STITCH_DESIGNS: stitchDesignsStr,
|
|
1032
|
-
POLYREPO_CONTEXT: polyrepoContextStr
|
|
1035
|
+
POLYREPO_CONTEXT: polyrepoContextStr,
|
|
1036
|
+
PENDING_FEEDBACK: pendingFeedbackStr
|
|
1033
1037
|
};
|
|
1034
1038
|
template = processEnvBlocks(template, ctx.env);
|
|
1035
1039
|
template = processIfBlocks(template, vars);
|
|
1036
1040
|
template = substituteVariables(template, vars);
|
|
1037
1041
|
return template;
|
|
1038
1042
|
}
|
|
1043
|
+
function readPendingFeedback(workspacePath) {
|
|
1044
|
+
const feedbackDir = join6(workspacePath, ".planning", "feedback");
|
|
1045
|
+
if (!existsSync6(feedbackDir)) return "";
|
|
1046
|
+
try {
|
|
1047
|
+
const files = readdirSync6(feedbackDir).filter((f) => f.endsWith(".md")).sort();
|
|
1048
|
+
if (files.length === 0) return "";
|
|
1049
|
+
const latest = files[files.length - 1];
|
|
1050
|
+
const lines = [
|
|
1051
|
+
`**${files.length} feedback file(s) in \`.planning/feedback/\`:**`,
|
|
1052
|
+
""
|
|
1053
|
+
];
|
|
1054
|
+
for (const file of files) {
|
|
1055
|
+
const marker = file === latest ? " \u2190 **latest, read this first**" : "";
|
|
1056
|
+
lines.push(`- \`.planning/feedback/${file}\`${marker}`);
|
|
1057
|
+
}
|
|
1058
|
+
lines.push("");
|
|
1059
|
+
lines.push("Read the latest feedback file and address any issues before continuing other work.");
|
|
1060
|
+
return lines.join("\n");
|
|
1061
|
+
} catch {
|
|
1062
|
+
return "";
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1039
1065
|
function readPlanningContext(workspacePath) {
|
|
1040
1066
|
const statePath = join6(workspacePath, ".planning", "STATE.md");
|
|
1041
1067
|
if (existsSync6(statePath)) {
|
|
@@ -3338,7 +3364,7 @@ import chalk19 from "chalk";
|
|
|
3338
3364
|
// src/lib/context.ts
|
|
3339
3365
|
init_esm_shims();
|
|
3340
3366
|
init_paths();
|
|
3341
|
-
import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync6, appendFileSync, readdirSync as
|
|
3367
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync6, appendFileSync, readdirSync as readdirSync9 } from "fs";
|
|
3342
3368
|
import { join as join13 } from "path";
|
|
3343
3369
|
function getStateFile(agentId) {
|
|
3344
3370
|
return join13(AGENTS_DIR, agentId, "STATE.md");
|
|
@@ -3498,7 +3524,7 @@ function searchHistory(agentId, pattern) {
|
|
|
3498
3524
|
if (!existsSync13(historyDir)) return [];
|
|
3499
3525
|
const results = [];
|
|
3500
3526
|
const regex = new RegExp(pattern, "i");
|
|
3501
|
-
const files =
|
|
3527
|
+
const files = readdirSync9(historyDir).filter((f) => f.endsWith(".log"));
|
|
3502
3528
|
files.sort().reverse();
|
|
3503
3529
|
for (const file of files) {
|
|
3504
3530
|
const content = readFileSync11(join13(historyDir, file), "utf-8");
|
|
@@ -3515,7 +3541,7 @@ function getRecentHistory(agentId, limit = 20) {
|
|
|
3515
3541
|
const historyDir = getHistoryDir(agentId);
|
|
3516
3542
|
if (!existsSync13(historyDir)) return [];
|
|
3517
3543
|
const results = [];
|
|
3518
|
-
const files =
|
|
3544
|
+
const files = readdirSync9(historyDir).filter((f) => f.endsWith(".log"));
|
|
3519
3545
|
files.sort().reverse();
|
|
3520
3546
|
for (const file of files) {
|
|
3521
3547
|
if (results.length >= limit) break;
|
|
@@ -3537,7 +3563,7 @@ function getMaterializedDir(agentId) {
|
|
|
3537
3563
|
function listMaterialized(agentId) {
|
|
3538
3564
|
const dir = getMaterializedDir(agentId);
|
|
3539
3565
|
if (!existsSync13(dir)) return [];
|
|
3540
|
-
return
|
|
3566
|
+
return readdirSync9(dir).filter((f) => f.endsWith(".md")).map((f) => {
|
|
3541
3567
|
const match = f.match(/^(.+)-(\d+)\.md$/);
|
|
3542
3568
|
if (!match) return null;
|
|
3543
3569
|
return {
|
|
@@ -3882,8 +3908,8 @@ async function runHealthCheck(config2 = {
|
|
|
3882
3908
|
} catch {
|
|
3883
3909
|
}
|
|
3884
3910
|
if (existsSync15(AGENTS_DIR)) {
|
|
3885
|
-
const { readdirSync:
|
|
3886
|
-
const dirs =
|
|
3911
|
+
const { readdirSync: readdirSync20 } = await import("fs");
|
|
3912
|
+
const dirs = readdirSync20(AGENTS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.startsWith("agent-")).map((d) => d.name);
|
|
3887
3913
|
for (const dir of dirs) {
|
|
3888
3914
|
if (!sessions.includes(dir)) {
|
|
3889
3915
|
sessions.push(dir);
|
|
@@ -5069,7 +5095,7 @@ init_esm_shims();
|
|
|
5069
5095
|
init_paths();
|
|
5070
5096
|
import {
|
|
5071
5097
|
existsSync as existsSync20,
|
|
5072
|
-
readdirSync as
|
|
5098
|
+
readdirSync as readdirSync11,
|
|
5073
5099
|
lstatSync,
|
|
5074
5100
|
readlinkSync,
|
|
5075
5101
|
symlinkSync as symlinkSync2,
|
|
@@ -5108,12 +5134,12 @@ function mergeSkillsIntoWorkspace(workspacePath) {
|
|
|
5108
5134
|
mkdirSync11(skillsTarget, { recursive: true });
|
|
5109
5135
|
const existingSkills = /* @__PURE__ */ new Set();
|
|
5110
5136
|
if (existsSync20(skillsTarget)) {
|
|
5111
|
-
for (const item of
|
|
5137
|
+
for (const item of readdirSync11(skillsTarget)) {
|
|
5112
5138
|
existingSkills.add(item);
|
|
5113
5139
|
}
|
|
5114
5140
|
}
|
|
5115
5141
|
if (!existsSync20(SKILLS_DIR)) return { added, skipped };
|
|
5116
|
-
const panopticonSkills =
|
|
5142
|
+
const panopticonSkills = readdirSync11(SKILLS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
5117
5143
|
for (const skill of panopticonSkills) {
|
|
5118
5144
|
const targetPath = join19(skillsTarget, skill);
|
|
5119
5145
|
const sourcePath = join19(SKILLS_DIR, skill);
|
|
@@ -5216,7 +5242,7 @@ init_projects();
|
|
|
5216
5242
|
|
|
5217
5243
|
// src/lib/workspace-manager.ts
|
|
5218
5244
|
init_esm_shims();
|
|
5219
|
-
import { existsSync as existsSync21, mkdirSync as mkdirSync12, writeFileSync as writeFileSync10, readFileSync as readFileSync19, readdirSync as
|
|
5245
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync12, writeFileSync as writeFileSync10, readFileSync as readFileSync19, readdirSync as readdirSync12, copyFileSync, symlinkSync as symlinkSync3, chmodSync, realpathSync } from "fs";
|
|
5220
5246
|
import { join as join20, dirname as dirname8, basename as basename2 } from "path";
|
|
5221
5247
|
import { homedir as homedir11 } from "os";
|
|
5222
5248
|
import { exec as exec4 } from "child_process";
|
|
@@ -5355,7 +5381,7 @@ function processTemplates(templateDir, targetDir, placeholders, templates) {
|
|
|
5355
5381
|
}
|
|
5356
5382
|
}
|
|
5357
5383
|
} else {
|
|
5358
|
-
const files =
|
|
5384
|
+
const files = readdirSync12(templateDir);
|
|
5359
5385
|
for (const file of files) {
|
|
5360
5386
|
if (file.endsWith(".template")) {
|
|
5361
5387
|
const sourcePath = join20(templateDir, file);
|
|
@@ -5449,7 +5475,7 @@ async function createWorkspace(options) {
|
|
|
5449
5475
|
}
|
|
5450
5476
|
const devcontainerDir = join20(workspacePath, ".devcontainer");
|
|
5451
5477
|
if (existsSync21(devcontainerDir)) {
|
|
5452
|
-
const composeFiles =
|
|
5478
|
+
const composeFiles = readdirSync12(devcontainerDir).filter((f) => f.includes("compose") && (f.endsWith(".yml") || f.endsWith(".yaml")));
|
|
5453
5479
|
for (const composeFile of composeFiles) {
|
|
5454
5480
|
sanitizeComposeFile(join20(devcontainerDir, composeFile));
|
|
5455
5481
|
}
|
|
@@ -5510,7 +5536,7 @@ async function createWorkspace(options) {
|
|
|
5510
5536
|
const templateSteps = processTemplates(templateDir, devcontainerDir2, placeholders);
|
|
5511
5537
|
result.steps.push(...templateSteps);
|
|
5512
5538
|
if (existsSync21(templateDir)) {
|
|
5513
|
-
const files =
|
|
5539
|
+
const files = readdirSync12(templateDir);
|
|
5514
5540
|
for (const file of files) {
|
|
5515
5541
|
if (!file.endsWith(".template")) {
|
|
5516
5542
|
const sourcePath = join20(templateDir, file);
|
|
@@ -5519,7 +5545,7 @@ async function createWorkspace(options) {
|
|
|
5519
5545
|
}
|
|
5520
5546
|
}
|
|
5521
5547
|
}
|
|
5522
|
-
const composeFiles =
|
|
5548
|
+
const composeFiles = readdirSync12(devcontainerDir2).filter((f) => f.includes("compose") && (f.endsWith(".yml") || f.endsWith(".yaml")));
|
|
5523
5549
|
for (const composeFile of composeFiles) {
|
|
5524
5550
|
sanitizeComposeFile(join20(devcontainerDir2, composeFile));
|
|
5525
5551
|
}
|
|
@@ -6986,7 +7012,7 @@ import chalk30 from "chalk";
|
|
|
6986
7012
|
import ora17 from "ora";
|
|
6987
7013
|
import inquirer5 from "inquirer";
|
|
6988
7014
|
import { execSync as execSync5 } from "child_process";
|
|
6989
|
-
import { existsSync as existsSync24, mkdirSync as mkdirSync15, writeFileSync as writeFileSync13, readFileSync as readFileSync21, copyFileSync as copyFileSync2, readdirSync as
|
|
7015
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync15, writeFileSync as writeFileSync13, readFileSync as readFileSync21, copyFileSync as copyFileSync2, readdirSync as readdirSync13, statSync as statSync2 } from "fs";
|
|
6990
7016
|
import { join as join23 } from "path";
|
|
6991
7017
|
import { homedir as homedir14 } from "os";
|
|
6992
7018
|
function registerInstallCommand(program2) {
|
|
@@ -6997,7 +7023,7 @@ function copyDirectoryRecursive(source, dest) {
|
|
|
6997
7023
|
throw new Error(`Source directory not found: ${source}`);
|
|
6998
7024
|
}
|
|
6999
7025
|
mkdirSync15(dest, { recursive: true });
|
|
7000
|
-
const entries =
|
|
7026
|
+
const entries = readdirSync13(source);
|
|
7001
7027
|
for (const entry of entries) {
|
|
7002
7028
|
const sourcePath = join23(source, entry);
|
|
7003
7029
|
const destPath = join23(dest, entry);
|
|
@@ -7135,7 +7161,7 @@ async function installCommand(options) {
|
|
|
7135
7161
|
if (existsSync24(SOURCE_SKILLS_DIR)) {
|
|
7136
7162
|
spinner.start("Installing bundled skills...");
|
|
7137
7163
|
try {
|
|
7138
|
-
const skillDirs =
|
|
7164
|
+
const skillDirs = readdirSync13(SOURCE_SKILLS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
7139
7165
|
let installed = 0;
|
|
7140
7166
|
let skipped = 0;
|
|
7141
7167
|
for (const skillDir of skillDirs) {
|
|
@@ -7838,11 +7864,11 @@ var ClaudeCodeRuntime = class {
|
|
|
7838
7864
|
/**
|
|
7839
7865
|
* Send a message to a running agent
|
|
7840
7866
|
*/
|
|
7841
|
-
sendMessage(agentId, message) {
|
|
7867
|
+
async sendMessage(agentId, message) {
|
|
7842
7868
|
if (!sessionExists(agentId)) {
|
|
7843
7869
|
throw new Error(`Agent ${agentId} is not running`);
|
|
7844
7870
|
}
|
|
7845
|
-
|
|
7871
|
+
await sendKeysAsync(agentId, message);
|
|
7846
7872
|
const mailDir = join25(getAgentDir(agentId), "mail");
|
|
7847
7873
|
mkdirSync17(mailDir, { recursive: true });
|
|
7848
7874
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -9123,13 +9149,28 @@ init_config();
|
|
|
9123
9149
|
init_specialists();
|
|
9124
9150
|
init_agents();
|
|
9125
9151
|
init_tmux();
|
|
9126
|
-
import { readFileSync as readFileSync28, writeFileSync as writeFileSync20, existsSync as existsSync34, mkdirSync as mkdirSync22, readdirSync as
|
|
9152
|
+
import { readFileSync as readFileSync28, writeFileSync as writeFileSync20, existsSync as existsSync34, mkdirSync as mkdirSync22, readdirSync as readdirSync15, statSync as statSync4, rmSync as rmSync3 } from "fs";
|
|
9127
9153
|
import { join as join33 } from "path";
|
|
9128
9154
|
import { exec as exec9 } from "child_process";
|
|
9129
9155
|
import { promisify as promisify9 } from "util";
|
|
9130
9156
|
import { homedir as homedir16 } from "os";
|
|
9131
9157
|
var execAsync9 = promisify9(exec9);
|
|
9132
9158
|
var REVIEW_STATUS_FILE = join33(homedir16(), ".panopticon", "review-status.json");
|
|
9159
|
+
function updateTestStatusToTesting(issueId) {
|
|
9160
|
+
try {
|
|
9161
|
+
if (!existsSync34(REVIEW_STATUS_FILE)) return;
|
|
9162
|
+
const data = JSON.parse(readFileSync28(REVIEW_STATUS_FILE, "utf-8"));
|
|
9163
|
+
const upper = issueId.toUpperCase();
|
|
9164
|
+
if (data[upper]) {
|
|
9165
|
+
data[upper].testStatus = "testing";
|
|
9166
|
+
data[upper].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9167
|
+
writeFileSync20(REVIEW_STATUS_FILE, JSON.stringify(data, null, 2), "utf-8");
|
|
9168
|
+
console.log(`[deacon] Updated testStatus to 'testing' for ${upper}`);
|
|
9169
|
+
}
|
|
9170
|
+
} catch (error) {
|
|
9171
|
+
console.error(`[deacon] Failed to update testStatus for ${issueId}:`, error);
|
|
9172
|
+
}
|
|
9173
|
+
}
|
|
9133
9174
|
var DEFAULT_CONFIG = {
|
|
9134
9175
|
pingTimeoutMs: 3e4,
|
|
9135
9176
|
// How long to wait for response
|
|
@@ -9238,12 +9279,13 @@ function checkHeartbeat(name) {
|
|
|
9238
9279
|
return { isResponsive: false };
|
|
9239
9280
|
}
|
|
9240
9281
|
}
|
|
9241
|
-
function checkSpecialistHealth(name) {
|
|
9242
|
-
const state = loadState();
|
|
9282
|
+
async function checkSpecialistHealth(name, sharedState) {
|
|
9283
|
+
const state = sharedState ?? loadState();
|
|
9243
9284
|
const healthState = getSpecialistState(state, name);
|
|
9244
|
-
const wasRunning = isRunning(name);
|
|
9285
|
+
const wasRunning = await isRunning(name);
|
|
9245
9286
|
healthState.lastPingTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
9246
9287
|
if (!wasRunning) {
|
|
9288
|
+
if (!sharedState) saveState(state);
|
|
9247
9289
|
return {
|
|
9248
9290
|
specialistName: name,
|
|
9249
9291
|
isResponsive: false,
|
|
@@ -9260,7 +9302,7 @@ function checkSpecialistHealth(name) {
|
|
|
9260
9302
|
if (heartbeatResult.isResponsive) {
|
|
9261
9303
|
healthState.consecutiveFailures = 0;
|
|
9262
9304
|
healthState.lastResponseTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
9263
|
-
saveState(state);
|
|
9305
|
+
if (!sharedState) saveState(state);
|
|
9264
9306
|
return {
|
|
9265
9307
|
specialistName: name,
|
|
9266
9308
|
isResponsive: true,
|
|
@@ -9272,7 +9314,7 @@ function checkSpecialistHealth(name) {
|
|
|
9272
9314
|
};
|
|
9273
9315
|
}
|
|
9274
9316
|
healthState.consecutiveFailures++;
|
|
9275
|
-
saveState(state);
|
|
9317
|
+
if (!sharedState) saveState(state);
|
|
9276
9318
|
const shouldForceKill = healthState.consecutiveFailures >= config.consecutiveFailures && !isInCooldown(healthState);
|
|
9277
9319
|
return {
|
|
9278
9320
|
specialistName: name,
|
|
@@ -9284,9 +9326,9 @@ function checkSpecialistHealth(name) {
|
|
|
9284
9326
|
cooldownRemainingMs: getCooldownRemaining(healthState)
|
|
9285
9327
|
};
|
|
9286
9328
|
}
|
|
9287
|
-
async function forceKillSpecialist(name) {
|
|
9329
|
+
async function forceKillSpecialist(name, sharedState) {
|
|
9288
9330
|
const tmuxSession = getTmuxSessionName(name);
|
|
9289
|
-
const state = loadState();
|
|
9331
|
+
const state = sharedState ?? loadState();
|
|
9290
9332
|
const healthState = getSpecialistState(state, name);
|
|
9291
9333
|
if (isInCooldown(healthState)) {
|
|
9292
9334
|
const remaining = getCooldownRemaining(healthState);
|
|
@@ -9305,7 +9347,7 @@ async function forceKillSpecialist(name) {
|
|
|
9305
9347
|
state.recentDeaths = state.recentDeaths.filter(
|
|
9306
9348
|
(d) => new Date(d).getTime() > windowStart
|
|
9307
9349
|
);
|
|
9308
|
-
saveState(state);
|
|
9350
|
+
if (!sharedState) saveState(state);
|
|
9309
9351
|
console.log(`[deacon] Force-killed specialist ${name}`);
|
|
9310
9352
|
return {
|
|
9311
9353
|
success: true,
|
|
@@ -9319,19 +9361,19 @@ async function forceKillSpecialist(name) {
|
|
|
9319
9361
|
};
|
|
9320
9362
|
}
|
|
9321
9363
|
}
|
|
9322
|
-
function checkMassDeath() {
|
|
9323
|
-
const state = loadState();
|
|
9364
|
+
function checkMassDeath(sharedState) {
|
|
9365
|
+
const state = sharedState ?? loadState();
|
|
9324
9366
|
const windowStart = Date.now() - config.massDeathWindowMs;
|
|
9325
9367
|
state.recentDeaths = state.recentDeaths.filter(
|
|
9326
9368
|
(d) => new Date(d).getTime() > windowStart
|
|
9327
9369
|
);
|
|
9328
|
-
saveState(state);
|
|
9329
9370
|
const deathCount = state.recentDeaths.length;
|
|
9330
9371
|
if (deathCount >= config.massDeathThreshold) {
|
|
9331
9372
|
if (state.lastMassDeathAlert) {
|
|
9332
9373
|
const lastAlert = new Date(state.lastMassDeathAlert).getTime();
|
|
9333
9374
|
const alertCooldown = 5 * 6e4;
|
|
9334
9375
|
if (Date.now() - lastAlert < alertCooldown) {
|
|
9376
|
+
if (!sharedState) saveState(state);
|
|
9335
9377
|
return {
|
|
9336
9378
|
isMassDeath: true,
|
|
9337
9379
|
deathCount,
|
|
@@ -9340,13 +9382,14 @@ function checkMassDeath() {
|
|
|
9340
9382
|
}
|
|
9341
9383
|
}
|
|
9342
9384
|
state.lastMassDeathAlert = (/* @__PURE__ */ new Date()).toISOString();
|
|
9343
|
-
saveState(state);
|
|
9385
|
+
if (!sharedState) saveState(state);
|
|
9344
9386
|
return {
|
|
9345
9387
|
isMassDeath: true,
|
|
9346
9388
|
deathCount,
|
|
9347
9389
|
message: `ALERT: ${deathCount} specialist deaths in ${config.massDeathWindowMs / 1e3}s - possible infrastructure issue`
|
|
9348
9390
|
};
|
|
9349
9391
|
}
|
|
9392
|
+
if (!sharedState) saveState(state);
|
|
9350
9393
|
return {
|
|
9351
9394
|
isMassDeath: false,
|
|
9352
9395
|
deathCount
|
|
@@ -9384,14 +9427,11 @@ async function checkAndSuspendIdleAgents() {
|
|
|
9384
9427
|
const idleMs = Date.now() - lastActivity.getTime();
|
|
9385
9428
|
const idleMinutes = idleMs / (1e3 * 60);
|
|
9386
9429
|
const isSpecialist = specialistNames.has(agent.id);
|
|
9387
|
-
const timeoutMinutes = isSpecialist ? 5 : 10;
|
|
9388
9430
|
const isWorkAgent = agent.id.startsWith("agent-") && !isSpecialist;
|
|
9389
9431
|
if (isWorkAgent) {
|
|
9390
|
-
|
|
9391
|
-
if (existsSync34(completedFile)) {
|
|
9392
|
-
continue;
|
|
9393
|
-
}
|
|
9432
|
+
continue;
|
|
9394
9433
|
}
|
|
9434
|
+
const timeoutMinutes = 5;
|
|
9395
9435
|
if (idleMinutes > timeoutMinutes) {
|
|
9396
9436
|
console.log(`[deacon] Auto-suspending ${agent.id} (idle for ${Math.round(idleMinutes)} minutes)`);
|
|
9397
9437
|
try {
|
|
@@ -9574,7 +9614,7 @@ async function cleanupStaleAgentState() {
|
|
|
9574
9614
|
return actions;
|
|
9575
9615
|
}
|
|
9576
9616
|
try {
|
|
9577
|
-
const dirs =
|
|
9617
|
+
const dirs = readdirSync15(AGENTS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
9578
9618
|
for (const dir of dirs) {
|
|
9579
9619
|
const agentDir = join33(AGENTS_DIR, dir.name);
|
|
9580
9620
|
try {
|
|
@@ -9637,13 +9677,19 @@ async function checkOrphanedReviewStatuses() {
|
|
|
9637
9677
|
const testAgentActive = testAgentRunning && testAgentState?.state === "active";
|
|
9638
9678
|
let modified = false;
|
|
9639
9679
|
for (const [issueId, status] of Object.entries(statuses)) {
|
|
9640
|
-
|
|
9680
|
+
const hasPassedReview = status.history?.some(
|
|
9681
|
+
(h) => h.type === "review" && h.status === "passed"
|
|
9682
|
+
);
|
|
9683
|
+
const hasPassedTest = status.history?.some(
|
|
9684
|
+
(h) => h.type === "test" && (h.status === "passed" || h.status === "failed")
|
|
9685
|
+
);
|
|
9686
|
+
if (status.reviewStatus === "reviewing" && !reviewAgentActive && !hasPassedReview) {
|
|
9641
9687
|
console.log(`[deacon] Orphaned review detected: ${issueId} shows 'reviewing' but review-agent is not active`);
|
|
9642
9688
|
status.reviewStatus = "pending";
|
|
9643
9689
|
modified = true;
|
|
9644
9690
|
actions.push(`Reset orphaned review for ${issueId} (review-agent not active)`);
|
|
9645
9691
|
}
|
|
9646
|
-
if (status.testStatus === "testing" && !testAgentActive) {
|
|
9692
|
+
if (status.testStatus === "testing" && !testAgentActive && !hasPassedTest && !status.readyForMerge) {
|
|
9647
9693
|
console.log(`[deacon] Orphaned test detected: ${issueId} shows 'testing' but test-agent is not active`);
|
|
9648
9694
|
status.testStatus = "pending";
|
|
9649
9695
|
modified = true;
|
|
@@ -9668,11 +9714,11 @@ async function runPatrol() {
|
|
|
9668
9714
|
const actions = [];
|
|
9669
9715
|
console.log(`[deacon] Patrol cycle ${state.patrolCycle} - checking ${enabled.length} specialists`);
|
|
9670
9716
|
for (const specialist of enabled) {
|
|
9671
|
-
const result = checkSpecialistHealth(specialist.name);
|
|
9717
|
+
const result = await checkSpecialistHealth(specialist.name, state);
|
|
9672
9718
|
results.push(result);
|
|
9673
9719
|
if (result.shouldForceKill) {
|
|
9674
9720
|
console.log(`[deacon] ${specialist.name} stuck (${result.consecutiveFailures} failures), force-killing`);
|
|
9675
|
-
const killResult = await forceKillSpecialist(specialist.name);
|
|
9721
|
+
const killResult = await forceKillSpecialist(specialist.name, state);
|
|
9676
9722
|
actions.push(`Force-killed ${specialist.name}: ${killResult.message}`);
|
|
9677
9723
|
if (killResult.success) {
|
|
9678
9724
|
console.log(`[deacon] Auto-restarting ${specialist.name}...`);
|
|
@@ -9700,7 +9746,7 @@ async function runPatrol() {
|
|
|
9700
9746
|
if (nextTask) {
|
|
9701
9747
|
console.log(`[deacon] Auto-resuming suspended ${specialist.name} for queued task: ${nextTask.payload.issueId}`);
|
|
9702
9748
|
try {
|
|
9703
|
-
const { resumeAgent } = await import("../agents-
|
|
9749
|
+
const { resumeAgent } = await import("../agents-ZG4JIPO2.js");
|
|
9704
9750
|
const message = `# Queued Work
|
|
9705
9751
|
|
|
9706
9752
|
Processing queued task: ${nextTask.payload.issueId}`;
|
|
@@ -9731,6 +9777,9 @@ Processing queued task: ${nextTask.payload.issueId}`;
|
|
|
9731
9777
|
const wakeResult = await wakeSpecialistWithTask(specialist.name, taskDetails);
|
|
9732
9778
|
if (wakeResult.success) {
|
|
9733
9779
|
completeSpecialistTask(specialist.name, nextTask.id);
|
|
9780
|
+
if (specialist.name === "test-agent" && nextTask.payload.issueId) {
|
|
9781
|
+
updateTestStatusToTesting(nextTask.payload.issueId);
|
|
9782
|
+
}
|
|
9734
9783
|
actions.push(`Processed queued task for ${specialist.name}: ${nextTask.payload.issueId}`);
|
|
9735
9784
|
} else {
|
|
9736
9785
|
console.error(`[deacon] Failed to wake ${specialist.name} for queued task: ${wakeResult.error}`);
|
|
@@ -9752,12 +9801,12 @@ Processing queued task: ${nextTask.payload.issueId}`;
|
|
|
9752
9801
|
const cleanupActions = await cleanupStaleAgentState();
|
|
9753
9802
|
actions.push(...cleanupActions);
|
|
9754
9803
|
}
|
|
9755
|
-
|
|
9756
|
-
const massDeathCheck = checkMassDeath();
|
|
9804
|
+
const massDeathCheck = checkMassDeath(state);
|
|
9757
9805
|
if (massDeathCheck.isMassDeath && massDeathCheck.message) {
|
|
9758
9806
|
console.error(`[deacon] ${massDeathCheck.message}`);
|
|
9759
9807
|
actions.push(massDeathCheck.message);
|
|
9760
9808
|
}
|
|
9809
|
+
saveState(state);
|
|
9761
9810
|
return {
|
|
9762
9811
|
cycle: state.patrolCycle,
|
|
9763
9812
|
timestamp: state.lastPatrol,
|
|
@@ -9799,7 +9848,7 @@ function getDeaconStatus() {
|
|
|
9799
9848
|
// src/lib/cloister/service.ts
|
|
9800
9849
|
init_paths();
|
|
9801
9850
|
init_paths();
|
|
9802
|
-
import { existsSync as existsSync35, writeFileSync as writeFileSync21, unlinkSync as unlinkSync4, readFileSync as readFileSync29, readdirSync as
|
|
9851
|
+
import { existsSync as existsSync35, writeFileSync as writeFileSync21, unlinkSync as unlinkSync4, readFileSync as readFileSync29, readdirSync as readdirSync16, renameSync } from "fs";
|
|
9803
9852
|
import { join as join34 } from "path";
|
|
9804
9853
|
var CLOISTER_STATE_FILE = join34(PANOPTICON_HOME, "cloister.state");
|
|
9805
9854
|
function writeStateFile(running, pid) {
|
|
@@ -10041,7 +10090,7 @@ var CloisterService = class {
|
|
|
10041
10090
|
async checkCompletionMarkers() {
|
|
10042
10091
|
try {
|
|
10043
10092
|
if (!existsSync35(AGENTS_DIR)) return;
|
|
10044
|
-
const agentDirs =
|
|
10093
|
+
const agentDirs = readdirSync16(AGENTS_DIR, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.startsWith("agent-"));
|
|
10045
10094
|
for (const dir of agentDirs) {
|
|
10046
10095
|
const completedFile = join34(AGENTS_DIR, dir.name, "completed");
|
|
10047
10096
|
const processedFile = join34(AGENTS_DIR, dir.name, "completed.processed");
|
|
@@ -11522,7 +11571,7 @@ import { promisify as promisify12 } from "util";
|
|
|
11522
11571
|
var execAsync12 = promisify12(exec12);
|
|
11523
11572
|
async function listLogsCommand(project2, type, options) {
|
|
11524
11573
|
try {
|
|
11525
|
-
const { listRunLogs } = await import("../specialist-logs-
|
|
11574
|
+
const { listRunLogs } = await import("../specialist-logs-7TQMHCUN.js");
|
|
11526
11575
|
const limit = options.limit ? parseInt(options.limit) : 10;
|
|
11527
11576
|
const runs = listRunLogs(project2, type, { limit });
|
|
11528
11577
|
if (options.json) {
|
|
@@ -11567,7 +11616,7 @@ View a specific run: pan specialists logs ${project2} ${type} <runId>
|
|
|
11567
11616
|
}
|
|
11568
11617
|
async function viewLogCommand(project2, type, runId, options) {
|
|
11569
11618
|
try {
|
|
11570
|
-
const { getRunLog, parseLogMetadata, getRunLogPath } = await import("../specialist-logs-
|
|
11619
|
+
const { getRunLog, parseLogMetadata, getRunLogPath } = await import("../specialist-logs-7TQMHCUN.js");
|
|
11571
11620
|
const content = getRunLog(project2, type, runId);
|
|
11572
11621
|
if (!content) {
|
|
11573
11622
|
console.error(`\u274C Run log not found: ${runId}`);
|
|
@@ -11591,8 +11640,8 @@ async function viewLogCommand(project2, type, runId, options) {
|
|
|
11591
11640
|
}
|
|
11592
11641
|
async function tailLogCommand(project2, type) {
|
|
11593
11642
|
try {
|
|
11594
|
-
const { getRunLogPath } = await import("../specialist-logs-
|
|
11595
|
-
const { getProjectSpecialistMetadata } = await import("../specialists-
|
|
11643
|
+
const { getRunLogPath } = await import("../specialist-logs-7TQMHCUN.js");
|
|
11644
|
+
const { getProjectSpecialistMetadata } = await import("../specialists-UUXB5KFV.js");
|
|
11596
11645
|
const metadata = getProjectSpecialistMetadata(project2, type);
|
|
11597
11646
|
if (!metadata.currentRun) {
|
|
11598
11647
|
console.error(`\u274C No active run for ${project2}/${type}`);
|
|
@@ -11661,7 +11710,7 @@ async function cleanupLogsCommand(projectOrAll, type, options) {
|
|
|
11661
11710
|
console.log(" Use --force to confirm.");
|
|
11662
11711
|
process.exit(1);
|
|
11663
11712
|
}
|
|
11664
|
-
const { cleanupAllLogs } = await import("../specialist-logs-
|
|
11713
|
+
const { cleanupAllLogs } = await import("../specialist-logs-7TQMHCUN.js");
|
|
11665
11714
|
console.log("\u{1F9F9} Cleaning up old logs for all projects...\n");
|
|
11666
11715
|
const results = cleanupAllLogs();
|
|
11667
11716
|
console.log(`
|
|
@@ -11688,7 +11737,7 @@ async function cleanupLogsCommand(projectOrAll, type, options) {
|
|
|
11688
11737
|
console.log(" Use --force to confirm.");
|
|
11689
11738
|
process.exit(1);
|
|
11690
11739
|
}
|
|
11691
|
-
const { cleanupOldLogs } = await import("../specialist-logs-
|
|
11740
|
+
const { cleanupOldLogs } = await import("../specialist-logs-7TQMHCUN.js");
|
|
11692
11741
|
const { getSpecialistRetention } = await import("../projects-VXRUCMLM.js");
|
|
11693
11742
|
const retention = getSpecialistRetention(projectOrAll);
|
|
11694
11743
|
console.log(`\u{1F9F9} Cleaning up old logs for ${projectOrAll}/${type}...`);
|
|
@@ -11727,7 +11776,7 @@ import ora18 from "ora";
|
|
|
11727
11776
|
// src/lib/convoy.ts
|
|
11728
11777
|
init_esm_shims();
|
|
11729
11778
|
init_tmux();
|
|
11730
|
-
import { existsSync as existsSync41, mkdirSync as mkdirSync25, writeFileSync as writeFileSync26, readFileSync as readFileSync34, readdirSync as
|
|
11779
|
+
import { existsSync as existsSync41, mkdirSync as mkdirSync25, writeFileSync as writeFileSync26, readFileSync as readFileSync34, readdirSync as readdirSync17 } from "fs";
|
|
11731
11780
|
import { join as join39 } from "path";
|
|
11732
11781
|
import { homedir as homedir19 } from "os";
|
|
11733
11782
|
import { exec as exec13 } from "child_process";
|
|
@@ -11885,7 +11934,7 @@ function listConvoys(filter) {
|
|
|
11885
11934
|
if (!existsSync41(CONVOY_DIR)) {
|
|
11886
11935
|
return [];
|
|
11887
11936
|
}
|
|
11888
|
-
const files =
|
|
11937
|
+
const files = readdirSync17(CONVOY_DIR).filter((f) => f.endsWith(".json"));
|
|
11889
11938
|
const convoys = [];
|
|
11890
11939
|
for (const file of files) {
|
|
11891
11940
|
const convoyId = file.replace(".json", "");
|
|
@@ -12352,7 +12401,7 @@ function registerConvoyCommands(program2) {
|
|
|
12352
12401
|
init_esm_shims();
|
|
12353
12402
|
init_projects();
|
|
12354
12403
|
import chalk45 from "chalk";
|
|
12355
|
-
import { existsSync as existsSync42, readFileSync as readFileSync35, symlinkSync as symlinkSync4, mkdirSync as mkdirSync26, readdirSync as
|
|
12404
|
+
import { existsSync as existsSync42, readFileSync as readFileSync35, symlinkSync as symlinkSync4, mkdirSync as mkdirSync26, readdirSync as readdirSync18, statSync as statSync6 } from "fs";
|
|
12356
12405
|
import { join as join40, resolve, dirname as dirname11 } from "path";
|
|
12357
12406
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
12358
12407
|
var __filename5 = fileURLToPath4(import.meta.url);
|
|
@@ -12368,7 +12417,7 @@ function installGitHooks(gitDir) {
|
|
|
12368
12417
|
return 0;
|
|
12369
12418
|
}
|
|
12370
12419
|
try {
|
|
12371
|
-
const hooks =
|
|
12420
|
+
const hooks = readdirSync18(BUNDLED_HOOKS_DIR).filter((f) => {
|
|
12372
12421
|
const p = join40(BUNDLED_HOOKS_DIR, f);
|
|
12373
12422
|
return existsSync42(p) && statSync6(p).isFile();
|
|
12374
12423
|
});
|
|
@@ -12446,9 +12495,9 @@ async function projectAddCommand(projectPath, options = {}) {
|
|
|
12446
12495
|
const hasRootGit = existsSync42(join40(fullPath, ".git"));
|
|
12447
12496
|
const subRepos = [];
|
|
12448
12497
|
if (!hasRootGit) {
|
|
12449
|
-
const { readdirSync:
|
|
12498
|
+
const { readdirSync: readdirSync20, statSync: statSync8 } = await import("fs");
|
|
12450
12499
|
try {
|
|
12451
|
-
const entries =
|
|
12500
|
+
const entries = readdirSync20(fullPath);
|
|
12452
12501
|
for (const entry of entries) {
|
|
12453
12502
|
const entryPath = join40(fullPath, entry);
|
|
12454
12503
|
try {
|
|
@@ -12642,7 +12691,7 @@ Project: ${foundKey}
|
|
|
12642
12691
|
init_esm_shims();
|
|
12643
12692
|
init_paths();
|
|
12644
12693
|
import chalk46 from "chalk";
|
|
12645
|
-
import { existsSync as existsSync43, readdirSync as
|
|
12694
|
+
import { existsSync as existsSync43, readdirSync as readdirSync19, readFileSync as readFileSync36 } from "fs";
|
|
12646
12695
|
import { execSync as execSync8 } from "child_process";
|
|
12647
12696
|
import { homedir as homedir20 } from "os";
|
|
12648
12697
|
import { join as join41 } from "path";
|
|
@@ -12660,7 +12709,7 @@ function checkDirectory(path) {
|
|
|
12660
12709
|
function countItems(path) {
|
|
12661
12710
|
if (!existsSync43(path)) return 0;
|
|
12662
12711
|
try {
|
|
12663
|
-
return
|
|
12712
|
+
return readdirSync19(path).length;
|
|
12664
12713
|
} catch {
|
|
12665
12714
|
return 0;
|
|
12666
12715
|
}
|
|
@@ -14449,7 +14498,7 @@ if (existsSync48(PANOPTICON_ENV_FILE)) {
|
|
|
14449
14498
|
}
|
|
14450
14499
|
}
|
|
14451
14500
|
var program = new Command();
|
|
14452
|
-
program.name("pan").description("Multi-agent orchestration for AI coding assistants").version(
|
|
14501
|
+
program.name("pan").description("Multi-agent orchestration for AI coding assistants").version(JSON.parse(readFileSync42(join48(import.meta.dirname, "../../package.json"), "utf-8")).version);
|
|
14453
14502
|
program.command("init").description("Initialize Panopticon (~/.panopticon/)").action(initCommand);
|
|
14454
14503
|
program.command("sync").description("Sync skills/commands to AI tools").option("--dry-run", "Show what would be synced").option("--force", "Overwrite without prompts").option("--backup-only", "Only create backup").action(syncCommand);
|
|
14455
14504
|
program.command("restore [timestamp]").description("Restore from backup").action(restoreCommand);
|