panopticon-cli 0.3.3 → 0.3.4
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/README.md +16 -9
- package/dist/{chunk-IVAFJ6DS.js → chunk-BH6BR26M.js} +2 -2
- package/dist/{chunk-ZT55DPAC.js → chunk-C6A7S65K.js} +2 -2
- package/dist/{chunk-3SI436SZ.js → chunk-P5TQ5C3J.js} +2 -2
- package/dist/chunk-P5TQ5C3J.js.map +1 -0
- package/dist/cli/index.js +541 -111
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/{projects-EHEXMVSP.js → projects-54CV437J.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-3SI436SZ.js.map +0 -1
- /package/dist/{chunk-IVAFJ6DS.js.map → chunk-BH6BR26M.js.map} +0 -0
- /package/dist/{chunk-ZT55DPAC.js.map → chunk-C6A7S65K.js.map} +0 -0
- /package/dist/{projects-EHEXMVSP.js.map → projects-54CV437J.js.map} +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
restoreBackup,
|
|
17
17
|
saveConfig,
|
|
18
18
|
syncHooks
|
|
19
|
-
} from "../chunk-
|
|
19
|
+
} from "../chunk-C6A7S65K.js";
|
|
20
20
|
import {
|
|
21
21
|
PROJECTS_CONFIG_FILE,
|
|
22
22
|
getProject,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
registerProject,
|
|
27
27
|
resolveProjectFromIssue,
|
|
28
28
|
unregisterProject
|
|
29
|
-
} from "../chunk-
|
|
29
|
+
} from "../chunk-BH6BR26M.js";
|
|
30
30
|
import {
|
|
31
31
|
AGENTS_DIR,
|
|
32
32
|
CERTS_DIR,
|
|
@@ -42,11 +42,11 @@ import {
|
|
|
42
42
|
SYNC_TARGETS,
|
|
43
43
|
TRAEFIK_CERTS_DIR,
|
|
44
44
|
TRAEFIK_DIR
|
|
45
|
-
} from "../chunk-
|
|
45
|
+
} from "../chunk-P5TQ5C3J.js";
|
|
46
46
|
|
|
47
47
|
// src/cli/index.ts
|
|
48
48
|
import { Command } from "commander";
|
|
49
|
-
import
|
|
49
|
+
import chalk33 from "chalk";
|
|
50
50
|
|
|
51
51
|
// src/cli/commands/init.ts
|
|
52
52
|
import { existsSync, mkdirSync, readdirSync, cpSync } from "fs";
|
|
@@ -294,7 +294,7 @@ async function restoreCommand(timestamp) {
|
|
|
294
294
|
]);
|
|
295
295
|
timestamp = selected;
|
|
296
296
|
}
|
|
297
|
-
const { confirm } = await inquirer.prompt([
|
|
297
|
+
const { confirm: confirm2 } = await inquirer.prompt([
|
|
298
298
|
{
|
|
299
299
|
type: "confirm",
|
|
300
300
|
name: "confirm",
|
|
@@ -302,7 +302,7 @@ async function restoreCommand(timestamp) {
|
|
|
302
302
|
default: false
|
|
303
303
|
}
|
|
304
304
|
]);
|
|
305
|
-
if (!
|
|
305
|
+
if (!confirm2) {
|
|
306
306
|
console.log(chalk3.dim("Restore cancelled."));
|
|
307
307
|
return;
|
|
308
308
|
}
|
|
@@ -3759,7 +3759,7 @@ async function createCommand(issueId, options) {
|
|
|
3759
3759
|
}
|
|
3760
3760
|
}
|
|
3761
3761
|
async function listCommand2(options) {
|
|
3762
|
-
const { listProjects: listProjects3 } = await import("../projects-
|
|
3762
|
+
const { listProjects: listProjects3 } = await import("../projects-54CV437J.js");
|
|
3763
3763
|
const projects = listProjects3();
|
|
3764
3764
|
if (projects.length > 0 && options.all) {
|
|
3765
3765
|
const allWorkspaces = [];
|
|
@@ -4530,6 +4530,19 @@ function getSessionFiles(projectDir) {
|
|
|
4530
4530
|
}
|
|
4531
4531
|
});
|
|
4532
4532
|
}
|
|
4533
|
+
function getAllSessionFiles() {
|
|
4534
|
+
const files = [];
|
|
4535
|
+
for (const projectDir of getProjectDirs()) {
|
|
4536
|
+
files.push(...getSessionFiles(projectDir));
|
|
4537
|
+
}
|
|
4538
|
+
return files.sort((a, b) => {
|
|
4539
|
+
try {
|
|
4540
|
+
return statSync2(b).mtime.getTime() - statSync2(a).mtime.getTime();
|
|
4541
|
+
} catch {
|
|
4542
|
+
return 0;
|
|
4543
|
+
}
|
|
4544
|
+
});
|
|
4545
|
+
}
|
|
4533
4546
|
function normalizeModelName(model) {
|
|
4534
4547
|
if (model.includes("claude")) {
|
|
4535
4548
|
let normalizedModel = model;
|
|
@@ -4703,6 +4716,23 @@ function getSessionId(name) {
|
|
|
4703
4716
|
return null;
|
|
4704
4717
|
}
|
|
4705
4718
|
}
|
|
4719
|
+
function clearSessionId(name) {
|
|
4720
|
+
const sessionFile = getSessionFilePath(name);
|
|
4721
|
+
if (!existsSync21(sessionFile)) {
|
|
4722
|
+
return false;
|
|
4723
|
+
}
|
|
4724
|
+
try {
|
|
4725
|
+
unlinkSync2(sessionFile);
|
|
4726
|
+
return true;
|
|
4727
|
+
} catch (error) {
|
|
4728
|
+
console.error(`Failed to delete session file for ${name}:`, error);
|
|
4729
|
+
throw error;
|
|
4730
|
+
}
|
|
4731
|
+
}
|
|
4732
|
+
function getSpecialistMetadata(name) {
|
|
4733
|
+
const registry = loadRegistry();
|
|
4734
|
+
return registry.specialists.find((s) => s.name === name) || null;
|
|
4735
|
+
}
|
|
4706
4736
|
function updateSpecialistMetadata(name, updates) {
|
|
4707
4737
|
const registry = loadRegistry();
|
|
4708
4738
|
const index = registry.specialists.findIndex((s) => s.name === name);
|
|
@@ -4736,6 +4766,34 @@ function recordWake(name, sessionId) {
|
|
|
4736
4766
|
function getEnabledSpecialists() {
|
|
4737
4767
|
return getAllSpecialists().filter((s) => s.enabled);
|
|
4738
4768
|
}
|
|
4769
|
+
function findSessionFile(sessionId) {
|
|
4770
|
+
try {
|
|
4771
|
+
const allFiles = getAllSessionFiles();
|
|
4772
|
+
for (const file of allFiles) {
|
|
4773
|
+
const fileSessionId = basename4(file, ".jsonl");
|
|
4774
|
+
if (fileSessionId === sessionId) {
|
|
4775
|
+
return file;
|
|
4776
|
+
}
|
|
4777
|
+
}
|
|
4778
|
+
} catch {
|
|
4779
|
+
}
|
|
4780
|
+
return null;
|
|
4781
|
+
}
|
|
4782
|
+
function countContextTokens(name) {
|
|
4783
|
+
const sessionId = getSessionId(name);
|
|
4784
|
+
if (!sessionId) {
|
|
4785
|
+
return null;
|
|
4786
|
+
}
|
|
4787
|
+
const sessionFile = findSessionFile(sessionId);
|
|
4788
|
+
if (!sessionFile) {
|
|
4789
|
+
return null;
|
|
4790
|
+
}
|
|
4791
|
+
const sessionUsage = parseClaudeSession(sessionFile);
|
|
4792
|
+
if (!sessionUsage) {
|
|
4793
|
+
return null;
|
|
4794
|
+
}
|
|
4795
|
+
return sessionUsage.usage.inputTokens + sessionUsage.usage.outputTokens + (sessionUsage.usage.cacheReadTokens || 0) + (sessionUsage.usage.cacheWriteTokens || 0);
|
|
4796
|
+
}
|
|
4739
4797
|
function isRunning(name) {
|
|
4740
4798
|
const tmuxSession = getTmuxSessionName(name);
|
|
4741
4799
|
try {
|
|
@@ -4745,6 +4803,37 @@ function isRunning(name) {
|
|
|
4745
4803
|
return false;
|
|
4746
4804
|
}
|
|
4747
4805
|
}
|
|
4806
|
+
function getSpecialistStatus(name) {
|
|
4807
|
+
const metadata = getSpecialistMetadata(name) || {
|
|
4808
|
+
name,
|
|
4809
|
+
displayName: name,
|
|
4810
|
+
description: "",
|
|
4811
|
+
enabled: false,
|
|
4812
|
+
autoWake: false
|
|
4813
|
+
};
|
|
4814
|
+
const sessionId = getSessionId(name);
|
|
4815
|
+
const running = isRunning(name);
|
|
4816
|
+
const contextTokens = countContextTokens(name);
|
|
4817
|
+
let state;
|
|
4818
|
+
if (running) {
|
|
4819
|
+
state = "active";
|
|
4820
|
+
} else if (sessionId) {
|
|
4821
|
+
state = "sleeping";
|
|
4822
|
+
} else {
|
|
4823
|
+
state = "uninitialized";
|
|
4824
|
+
}
|
|
4825
|
+
return {
|
|
4826
|
+
...metadata,
|
|
4827
|
+
sessionId: sessionId || void 0,
|
|
4828
|
+
contextTokens: contextTokens || void 0,
|
|
4829
|
+
state,
|
|
4830
|
+
isRunning: running,
|
|
4831
|
+
tmuxSession: getTmuxSessionName(name)
|
|
4832
|
+
};
|
|
4833
|
+
}
|
|
4834
|
+
function getAllSpecialistStatus() {
|
|
4835
|
+
return getAllSpecialists().map((metadata) => getSpecialistStatus(metadata.name));
|
|
4836
|
+
}
|
|
4748
4837
|
async function initializeSpecialist(name) {
|
|
4749
4838
|
if (isRunning(name)) {
|
|
4750
4839
|
return {
|
|
@@ -4817,6 +4906,9 @@ async function initializeEnabledSpecialists() {
|
|
|
4817
4906
|
}
|
|
4818
4907
|
return results;
|
|
4819
4908
|
}
|
|
4909
|
+
function checkSpecialistQueue(specialistName) {
|
|
4910
|
+
return checkHook(specialistName);
|
|
4911
|
+
}
|
|
4820
4912
|
|
|
4821
4913
|
// src/lib/runtimes/claude-code.ts
|
|
4822
4914
|
import { existsSync as existsSync22, readFileSync as readFileSync18, statSync as statSync3, mkdirSync as mkdirSync16, writeFileSync as writeFileSync14 } from "fs";
|
|
@@ -5918,7 +6010,7 @@ var CloisterService = class {
|
|
|
5918
6010
|
}
|
|
5919
6011
|
}
|
|
5920
6012
|
}
|
|
5921
|
-
|
|
6013
|
+
void this.checkHandoffTriggers(agentHealths);
|
|
5922
6014
|
} catch (error) {
|
|
5923
6015
|
console.error("Cloister health check failed:", error);
|
|
5924
6016
|
this.emit({ type: "error", error });
|
|
@@ -6411,23 +6503,341 @@ function registerSetupCommands(program2) {
|
|
|
6411
6503
|
setup.command("hooks").description("Configure Claude Code hooks for heartbeat tracking").action(setupHooksCommand);
|
|
6412
6504
|
}
|
|
6413
6505
|
|
|
6414
|
-
// src/cli/commands/
|
|
6506
|
+
// src/cli/commands/specialists/list.ts
|
|
6415
6507
|
import chalk26 from "chalk";
|
|
6508
|
+
function listCommand3(options) {
|
|
6509
|
+
const specialists = getAllSpecialistStatus();
|
|
6510
|
+
if (options.json) {
|
|
6511
|
+
console.log(JSON.stringify(specialists, null, 2));
|
|
6512
|
+
return;
|
|
6513
|
+
}
|
|
6514
|
+
console.log(chalk26.bold("\nSpecialist Agents:\n"));
|
|
6515
|
+
if (specialists.length === 0) {
|
|
6516
|
+
console.log(chalk26.dim("No specialists configured."));
|
|
6517
|
+
console.log("");
|
|
6518
|
+
return;
|
|
6519
|
+
}
|
|
6520
|
+
for (const specialist of specialists) {
|
|
6521
|
+
displaySpecialist(specialist);
|
|
6522
|
+
}
|
|
6523
|
+
console.log("");
|
|
6524
|
+
}
|
|
6525
|
+
function displaySpecialist(specialist) {
|
|
6526
|
+
const statusIcon = getStatusIcon(specialist);
|
|
6527
|
+
const statusColor = getStatusColor(specialist);
|
|
6528
|
+
const enabledBadge = specialist.enabled ? chalk26.green("enabled") : chalk26.dim("disabled");
|
|
6529
|
+
console.log(`${statusIcon} ${chalk26.bold(specialist.displayName)} (${enabledBadge})`);
|
|
6530
|
+
console.log(` ${chalk26.dim(specialist.description)}`);
|
|
6531
|
+
console.log(` Status: ${statusColor(specialist.state)}`);
|
|
6532
|
+
if (specialist.isRunning) {
|
|
6533
|
+
console.log(` Running: ${chalk26.cyan(specialist.tmuxSession)}`);
|
|
6534
|
+
}
|
|
6535
|
+
if (specialist.sessionId) {
|
|
6536
|
+
const shortId = specialist.sessionId.substring(0, 8);
|
|
6537
|
+
console.log(` Session: ${chalk26.dim(shortId + "...")}`);
|
|
6538
|
+
}
|
|
6539
|
+
if (specialist.contextTokens) {
|
|
6540
|
+
const tokensFormatted = specialist.contextTokens.toLocaleString();
|
|
6541
|
+
console.log(` Context: ${chalk26.dim(tokensFormatted + " tokens")}`);
|
|
6542
|
+
}
|
|
6543
|
+
if (specialist.lastWake) {
|
|
6544
|
+
const lastWake = new Date(specialist.lastWake);
|
|
6545
|
+
const relative = getRelativeTime(lastWake);
|
|
6546
|
+
console.log(` Last wake: ${chalk26.dim(relative)}`);
|
|
6547
|
+
}
|
|
6548
|
+
console.log("");
|
|
6549
|
+
}
|
|
6550
|
+
function getStatusIcon(specialist) {
|
|
6551
|
+
if (!specialist.enabled) return chalk26.dim("\u25CB");
|
|
6552
|
+
if (specialist.isRunning) return chalk26.green("\u25CF");
|
|
6553
|
+
if (specialist.state === "sleeping") return chalk26.yellow("\u25CF");
|
|
6554
|
+
return chalk26.dim("\u25CB");
|
|
6555
|
+
}
|
|
6556
|
+
function getStatusColor(specialist) {
|
|
6557
|
+
if (specialist.isRunning) return chalk26.green;
|
|
6558
|
+
if (specialist.state === "sleeping") return chalk26.yellow;
|
|
6559
|
+
return chalk26.dim;
|
|
6560
|
+
}
|
|
6561
|
+
function getRelativeTime(date) {
|
|
6562
|
+
const now = /* @__PURE__ */ new Date();
|
|
6563
|
+
const diff = now.getTime() - date.getTime();
|
|
6564
|
+
const seconds = Math.floor(diff / 1e3);
|
|
6565
|
+
const minutes = Math.floor(seconds / 60);
|
|
6566
|
+
const hours = Math.floor(minutes / 60);
|
|
6567
|
+
const days = Math.floor(hours / 24);
|
|
6568
|
+
if (days > 0) return `${days}d ago`;
|
|
6569
|
+
if (hours > 0) return `${hours}h ago`;
|
|
6570
|
+
if (minutes > 0) return `${minutes}m ago`;
|
|
6571
|
+
return "just now";
|
|
6572
|
+
}
|
|
6573
|
+
|
|
6574
|
+
// src/cli/commands/specialists/wake.ts
|
|
6575
|
+
import chalk27 from "chalk";
|
|
6576
|
+
import { execSync as execSync13 } from "child_process";
|
|
6577
|
+
function wakeCommand(name, options) {
|
|
6578
|
+
const validNames = ["merge-agent", "review-agent", "test-agent"];
|
|
6579
|
+
if (!validNames.includes(name)) {
|
|
6580
|
+
console.log(chalk27.red(`
|
|
6581
|
+
Error: Unknown specialist '${name}'`));
|
|
6582
|
+
console.log(`Valid specialists: ${validNames.join(", ")}
|
|
6583
|
+
`);
|
|
6584
|
+
process.exit(1);
|
|
6585
|
+
}
|
|
6586
|
+
const specialistName = name;
|
|
6587
|
+
const status = getSpecialistStatus(specialistName);
|
|
6588
|
+
console.log(chalk27.bold(`
|
|
6589
|
+
Waking ${status.displayName}...
|
|
6590
|
+
`));
|
|
6591
|
+
if (status.isRunning) {
|
|
6592
|
+
console.log(chalk27.yellow(`Specialist is already running in tmux session: ${status.tmuxSession}`));
|
|
6593
|
+
if (options.task) {
|
|
6594
|
+
console.log(chalk27.dim("\nSending task message..."));
|
|
6595
|
+
try {
|
|
6596
|
+
const escapedTask = options.task.replace(/'/g, "'\\''");
|
|
6597
|
+
execSync13(`tmux send-keys -t "${status.tmuxSession}" '${escapedTask}' C-m`, { encoding: "utf-8" });
|
|
6598
|
+
console.log(chalk27.green("\u2713 Task message sent"));
|
|
6599
|
+
} catch (error) {
|
|
6600
|
+
console.log(chalk27.red(`Failed to send message: ${error.message}`));
|
|
6601
|
+
}
|
|
6602
|
+
} else {
|
|
6603
|
+
console.log(chalk27.dim("Use --task to send a message to the running specialist"));
|
|
6604
|
+
}
|
|
6605
|
+
console.log("");
|
|
6606
|
+
return;
|
|
6607
|
+
}
|
|
6608
|
+
if (!status.enabled) {
|
|
6609
|
+
console.log(chalk27.yellow(`Warning: Specialist '${specialistName}' is disabled in registry`));
|
|
6610
|
+
console.log(chalk27.dim("You can still wake it manually, but it won't auto-wake\n"));
|
|
6611
|
+
}
|
|
6612
|
+
const sessionId = getSessionId(specialistName);
|
|
6613
|
+
const tmuxSession = getTmuxSessionName(specialistName);
|
|
6614
|
+
const cwd = process.env.HOME || "/home/eltmon";
|
|
6615
|
+
try {
|
|
6616
|
+
let claudeCmd = "claude --dangerously-skip-permissions";
|
|
6617
|
+
if (sessionId) {
|
|
6618
|
+
claudeCmd += ` --resume ${sessionId}`;
|
|
6619
|
+
console.log(chalk27.dim(`Resuming session: ${sessionId.substring(0, 8)}...`));
|
|
6620
|
+
} else {
|
|
6621
|
+
console.log(chalk27.dim("Starting fresh session (no previous session found)"));
|
|
6622
|
+
}
|
|
6623
|
+
console.log(chalk27.dim(`Creating tmux session: ${tmuxSession}`));
|
|
6624
|
+
execSync13(
|
|
6625
|
+
`tmux new-session -d -s "${tmuxSession}" -c "${cwd}" "${claudeCmd}"`,
|
|
6626
|
+
{ encoding: "utf-8" }
|
|
6627
|
+
);
|
|
6628
|
+
execSync13("sleep 2", { encoding: "utf-8" });
|
|
6629
|
+
if (options.task) {
|
|
6630
|
+
console.log(chalk27.dim("Sending task message..."));
|
|
6631
|
+
const escapedTask = options.task.replace(/'/g, "'\\''");
|
|
6632
|
+
execSync13(`tmux send-keys -t "${tmuxSession}" '${escapedTask}' C-m`, { encoding: "utf-8" });
|
|
6633
|
+
}
|
|
6634
|
+
recordWake(specialistName);
|
|
6635
|
+
console.log(chalk27.green(`\u2713 Specialist ${specialistName} woken up successfully`));
|
|
6636
|
+
console.log(chalk27.dim(` Tmux session: ${tmuxSession}`));
|
|
6637
|
+
console.log(chalk27.dim(` Attach with: tmux attach -t ${tmuxSession}`));
|
|
6638
|
+
console.log("");
|
|
6639
|
+
} catch (error) {
|
|
6640
|
+
console.log(chalk27.red(`
|
|
6641
|
+
Failed to wake specialist: ${error.message}
|
|
6642
|
+
`));
|
|
6643
|
+
process.exit(1);
|
|
6644
|
+
}
|
|
6645
|
+
}
|
|
6646
|
+
|
|
6647
|
+
// src/cli/commands/specialists/queue.ts
|
|
6648
|
+
import chalk28 from "chalk";
|
|
6649
|
+
function queueCommand(name, options) {
|
|
6650
|
+
const validNames = ["merge-agent", "review-agent", "test-agent"];
|
|
6651
|
+
if (!validNames.includes(name)) {
|
|
6652
|
+
console.log(chalk28.red(`
|
|
6653
|
+
Error: Unknown specialist '${name}'`));
|
|
6654
|
+
console.log(`Valid specialists: ${validNames.join(", ")}
|
|
6655
|
+
`);
|
|
6656
|
+
process.exit(1);
|
|
6657
|
+
}
|
|
6658
|
+
const specialistName = name;
|
|
6659
|
+
const metadata = getSpecialistMetadata(specialistName);
|
|
6660
|
+
if (!metadata) {
|
|
6661
|
+
console.log(chalk28.red(`
|
|
6662
|
+
Error: Specialist '${specialistName}' not found in registry
|
|
6663
|
+
`));
|
|
6664
|
+
process.exit(1);
|
|
6665
|
+
}
|
|
6666
|
+
const queueStatus = checkSpecialistQueue(specialistName);
|
|
6667
|
+
if (options.json) {
|
|
6668
|
+
console.log(JSON.stringify(queueStatus, null, 2));
|
|
6669
|
+
return;
|
|
6670
|
+
}
|
|
6671
|
+
console.log(chalk28.bold(`
|
|
6672
|
+
${metadata.displayName} Queue:
|
|
6673
|
+
`));
|
|
6674
|
+
if (!queueStatus.hasWork) {
|
|
6675
|
+
console.log(chalk28.dim("Queue is empty - no pending work"));
|
|
6676
|
+
console.log("");
|
|
6677
|
+
return;
|
|
6678
|
+
}
|
|
6679
|
+
console.log(`Total items: ${chalk28.bold(queueStatus.items.length.toString())}`);
|
|
6680
|
+
if (queueStatus.urgentCount > 0) {
|
|
6681
|
+
console.log(`Urgent items: ${chalk28.red.bold(queueStatus.urgentCount.toString())}`);
|
|
6682
|
+
}
|
|
6683
|
+
console.log("");
|
|
6684
|
+
for (let i = 0; i < queueStatus.items.length; i++) {
|
|
6685
|
+
const item = queueStatus.items[i];
|
|
6686
|
+
const position = i + 1;
|
|
6687
|
+
const priorityColor = getPriorityColor(item.priority);
|
|
6688
|
+
console.log(`${position}. ${priorityColor(item.priority.toUpperCase())}`);
|
|
6689
|
+
console.log(` ID: ${chalk28.dim(item.id)}`);
|
|
6690
|
+
console.log(` Source: ${chalk28.dim(item.source)}`);
|
|
6691
|
+
if (item.payload) {
|
|
6692
|
+
const payload = item.payload;
|
|
6693
|
+
if (payload.issueId) {
|
|
6694
|
+
console.log(` Issue: ${chalk28.cyan(payload.issueId)}`);
|
|
6695
|
+
}
|
|
6696
|
+
if (payload.prUrl) {
|
|
6697
|
+
console.log(` PR: ${chalk28.dim(payload.prUrl)}`);
|
|
6698
|
+
}
|
|
6699
|
+
if (payload.workspace) {
|
|
6700
|
+
console.log(` Workspace: ${chalk28.dim(payload.workspace)}`);
|
|
6701
|
+
}
|
|
6702
|
+
if (payload.branch) {
|
|
6703
|
+
console.log(` Branch: ${chalk28.dim(payload.branch)}`);
|
|
6704
|
+
}
|
|
6705
|
+
if (payload.filesChanged && Array.isArray(payload.filesChanged)) {
|
|
6706
|
+
const fileCount = payload.filesChanged.length;
|
|
6707
|
+
console.log(` Files: ${chalk28.dim(fileCount + " changed")}`);
|
|
6708
|
+
}
|
|
6709
|
+
}
|
|
6710
|
+
const createdAt = new Date(item.createdAt);
|
|
6711
|
+
const age = getAge(createdAt);
|
|
6712
|
+
console.log(` Age: ${chalk28.dim(age)}`);
|
|
6713
|
+
console.log("");
|
|
6714
|
+
}
|
|
6715
|
+
}
|
|
6716
|
+
function getPriorityColor(priority) {
|
|
6717
|
+
switch (priority.toLowerCase()) {
|
|
6718
|
+
case "urgent":
|
|
6719
|
+
return chalk28.red.bold;
|
|
6720
|
+
case "high":
|
|
6721
|
+
return chalk28.yellow;
|
|
6722
|
+
case "normal":
|
|
6723
|
+
return chalk28.white;
|
|
6724
|
+
case "low":
|
|
6725
|
+
return chalk28.dim;
|
|
6726
|
+
default:
|
|
6727
|
+
return chalk28.white;
|
|
6728
|
+
}
|
|
6729
|
+
}
|
|
6730
|
+
function getAge(date) {
|
|
6731
|
+
const now = /* @__PURE__ */ new Date();
|
|
6732
|
+
const diff = now.getTime() - date.getTime();
|
|
6733
|
+
const seconds = Math.floor(diff / 1e3);
|
|
6734
|
+
const minutes = Math.floor(seconds / 60);
|
|
6735
|
+
const hours = Math.floor(minutes / 60);
|
|
6736
|
+
const days = Math.floor(hours / 24);
|
|
6737
|
+
if (days > 0) return `${days}d ago`;
|
|
6738
|
+
if (hours > 0) return `${hours}h ago`;
|
|
6739
|
+
if (minutes > 0) return `${minutes}m ago`;
|
|
6740
|
+
return "just now";
|
|
6741
|
+
}
|
|
6742
|
+
|
|
6743
|
+
// src/cli/commands/specialists/reset.ts
|
|
6744
|
+
import chalk29 from "chalk";
|
|
6745
|
+
import { execSync as execSync14 } from "child_process";
|
|
6746
|
+
import * as readline from "readline";
|
|
6747
|
+
async function resetCommand(name, options) {
|
|
6748
|
+
const validNames = ["merge-agent", "review-agent", "test-agent"];
|
|
6749
|
+
if (!validNames.includes(name)) {
|
|
6750
|
+
console.log(chalk29.red(`
|
|
6751
|
+
Error: Unknown specialist '${name}'`));
|
|
6752
|
+
console.log(`Valid specialists: ${validNames.join(", ")}
|
|
6753
|
+
`);
|
|
6754
|
+
process.exit(1);
|
|
6755
|
+
}
|
|
6756
|
+
const specialistName = name;
|
|
6757
|
+
const status = getSpecialistStatus(specialistName);
|
|
6758
|
+
console.log(chalk29.bold(`
|
|
6759
|
+
Resetting ${status.displayName}...
|
|
6760
|
+
`));
|
|
6761
|
+
console.log(chalk29.dim("Current state:"));
|
|
6762
|
+
console.log(` Status: ${status.state}`);
|
|
6763
|
+
if (status.isRunning) {
|
|
6764
|
+
console.log(` Running: ${chalk29.yellow("yes")} (tmux: ${status.tmuxSession})`);
|
|
6765
|
+
}
|
|
6766
|
+
if (status.sessionId) {
|
|
6767
|
+
console.log(` Session: ${status.sessionId.substring(0, 8)}...`);
|
|
6768
|
+
}
|
|
6769
|
+
if (status.contextTokens) {
|
|
6770
|
+
console.log(` Context: ${status.contextTokens.toLocaleString()} tokens`);
|
|
6771
|
+
}
|
|
6772
|
+
console.log("");
|
|
6773
|
+
if (!options.force) {
|
|
6774
|
+
const confirmed = await confirm(
|
|
6775
|
+
"This will clear the session and all context. Continue?"
|
|
6776
|
+
);
|
|
6777
|
+
if (!confirmed) {
|
|
6778
|
+
console.log(chalk29.dim("Reset cancelled\n"));
|
|
6779
|
+
return;
|
|
6780
|
+
}
|
|
6781
|
+
}
|
|
6782
|
+
if (status.isRunning) {
|
|
6783
|
+
console.log(chalk29.dim("Stopping tmux session..."));
|
|
6784
|
+
try {
|
|
6785
|
+
execSync14(`tmux kill-session -t "${status.tmuxSession}"`, { encoding: "utf-8", stdio: "ignore" });
|
|
6786
|
+
console.log(chalk29.green("\u2713 Tmux session stopped"));
|
|
6787
|
+
} catch (error) {
|
|
6788
|
+
console.log(chalk29.yellow("\u26A0 Failed to stop tmux session (may not be running)"));
|
|
6789
|
+
}
|
|
6790
|
+
}
|
|
6791
|
+
console.log(chalk29.dim("Clearing session file..."));
|
|
6792
|
+
const cleared = clearSessionId(specialistName);
|
|
6793
|
+
if (cleared) {
|
|
6794
|
+
console.log(chalk29.green("\u2713 Session file cleared"));
|
|
6795
|
+
} else {
|
|
6796
|
+
console.log(chalk29.dim("No session file to clear"));
|
|
6797
|
+
}
|
|
6798
|
+
console.log(chalk29.green(`
|
|
6799
|
+
\u2713 Specialist ${specialistName} has been reset`));
|
|
6800
|
+
console.log(chalk29.dim("Next wake will start a fresh session\n"));
|
|
6801
|
+
}
|
|
6802
|
+
function confirm(question) {
|
|
6803
|
+
const rl = readline.createInterface({
|
|
6804
|
+
input: process.stdin,
|
|
6805
|
+
output: process.stdout
|
|
6806
|
+
});
|
|
6807
|
+
return new Promise((resolve2) => {
|
|
6808
|
+
rl.question(chalk29.yellow(`${question} [y/N] `), (answer) => {
|
|
6809
|
+
rl.close();
|
|
6810
|
+
resolve2(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
6811
|
+
});
|
|
6812
|
+
});
|
|
6813
|
+
}
|
|
6814
|
+
|
|
6815
|
+
// src/cli/commands/specialists/index.ts
|
|
6816
|
+
function registerSpecialistsCommands(program2) {
|
|
6817
|
+
const specialists = program2.command("specialists").description("Manage specialist agents (review-agent, test-agent, merge-agent)");
|
|
6818
|
+
specialists.command("list").description("Show all specialists with their status").option("--json", "Output in JSON format").action(listCommand3);
|
|
6819
|
+
specialists.command("wake <name>").description("Wake up a specialist agent (for testing/debugging)").option("--task <description>", "Optional task description to wake with").action(wakeCommand);
|
|
6820
|
+
specialists.command("queue <name>").description("Show pending work in a specialist's queue").option("--json", "Output in JSON format").action(queueCommand);
|
|
6821
|
+
specialists.command("reset <name>").description("Reset a specialist (clear session, start fresh)").option("--force", "Skip confirmation prompt").action(resetCommand);
|
|
6822
|
+
}
|
|
6823
|
+
|
|
6824
|
+
// src/cli/commands/project.ts
|
|
6825
|
+
import chalk30 from "chalk";
|
|
6416
6826
|
import { existsSync as existsSync28, readFileSync as readFileSync22 } from "fs";
|
|
6417
6827
|
import { join as join28, resolve } from "path";
|
|
6418
6828
|
async function projectAddCommand(projectPath, options = {}) {
|
|
6419
6829
|
const fullPath = resolve(projectPath);
|
|
6420
6830
|
if (!existsSync28(fullPath)) {
|
|
6421
|
-
console.log(
|
|
6831
|
+
console.log(chalk30.red(`Path does not exist: ${fullPath}`));
|
|
6422
6832
|
return;
|
|
6423
6833
|
}
|
|
6424
6834
|
const name = options.name || fullPath.split("/").pop() || "unknown";
|
|
6425
6835
|
const key = name.toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
6426
6836
|
const existing = getProject(key);
|
|
6427
6837
|
if (existing) {
|
|
6428
|
-
console.log(
|
|
6429
|
-
console.log(
|
|
6430
|
-
console.log(
|
|
6838
|
+
console.log(chalk30.yellow(`Project already registered with key: ${key}`));
|
|
6839
|
+
console.log(chalk30.dim(`Existing path: ${existing.path}`));
|
|
6840
|
+
console.log(chalk30.dim(`To update, first run: pan project remove ${key}`));
|
|
6431
6841
|
return;
|
|
6432
6842
|
}
|
|
6433
6843
|
let linearTeam = options.linearTeam;
|
|
@@ -6447,21 +6857,21 @@ async function projectAddCommand(projectPath, options = {}) {
|
|
|
6447
6857
|
projectConfig.linear_team = linearTeam.toUpperCase();
|
|
6448
6858
|
}
|
|
6449
6859
|
registerProject(key, projectConfig);
|
|
6450
|
-
console.log(
|
|
6451
|
-
console.log(
|
|
6452
|
-
console.log(
|
|
6860
|
+
console.log(chalk30.green(`\u2713 Added project: ${name}`));
|
|
6861
|
+
console.log(chalk30.dim(` Key: ${key}`));
|
|
6862
|
+
console.log(chalk30.dim(` Path: ${fullPath}`));
|
|
6453
6863
|
if (linearTeam) {
|
|
6454
|
-
console.log(
|
|
6864
|
+
console.log(chalk30.dim(` Linear team: ${linearTeam}`));
|
|
6455
6865
|
}
|
|
6456
6866
|
console.log("");
|
|
6457
|
-
console.log(
|
|
6867
|
+
console.log(chalk30.dim(`Edit ${PROJECTS_CONFIG_FILE} to add issue routing rules.`));
|
|
6458
6868
|
}
|
|
6459
6869
|
async function projectListCommand(options = {}) {
|
|
6460
6870
|
const projects = listProjects();
|
|
6461
6871
|
if (projects.length === 0) {
|
|
6462
|
-
console.log(
|
|
6463
|
-
console.log(
|
|
6464
|
-
console.log(
|
|
6872
|
+
console.log(chalk30.dim("No projects registered."));
|
|
6873
|
+
console.log(chalk30.dim("Add one with: pan project add <path> --linear-team <TEAM>"));
|
|
6874
|
+
console.log(chalk30.dim(`Or edit: ${PROJECTS_CONFIG_FILE}`));
|
|
6465
6875
|
return;
|
|
6466
6876
|
}
|
|
6467
6877
|
if (options.json) {
|
|
@@ -6472,51 +6882,51 @@ async function projectListCommand(options = {}) {
|
|
|
6472
6882
|
console.log(JSON.stringify(output, null, 2));
|
|
6473
6883
|
return;
|
|
6474
6884
|
}
|
|
6475
|
-
console.log(
|
|
6885
|
+
console.log(chalk30.bold("\nRegistered Projects:\n"));
|
|
6476
6886
|
for (const { key, config } of projects) {
|
|
6477
6887
|
const exists = existsSync28(config.path);
|
|
6478
|
-
const statusIcon = exists ?
|
|
6479
|
-
console.log(`${statusIcon} ${
|
|
6480
|
-
console.log(` ${
|
|
6888
|
+
const statusIcon = exists ? chalk30.green("\u2713") : chalk30.red("\u2717");
|
|
6889
|
+
console.log(`${statusIcon} ${chalk30.bold(config.name)} ${chalk30.dim(`(${key})`)}`);
|
|
6890
|
+
console.log(` ${chalk30.dim(config.path)}`);
|
|
6481
6891
|
if (config.linear_team) {
|
|
6482
|
-
console.log(` ${
|
|
6892
|
+
console.log(` ${chalk30.cyan(`Linear: ${config.linear_team}`)}`);
|
|
6483
6893
|
}
|
|
6484
6894
|
if (config.issue_routing && config.issue_routing.length > 0) {
|
|
6485
|
-
console.log(` ${
|
|
6895
|
+
console.log(` ${chalk30.dim(`Routes: ${config.issue_routing.length} rules`)}`);
|
|
6486
6896
|
}
|
|
6487
6897
|
console.log("");
|
|
6488
6898
|
}
|
|
6489
|
-
console.log(
|
|
6899
|
+
console.log(chalk30.dim(`Config: ${PROJECTS_CONFIG_FILE}`));
|
|
6490
6900
|
}
|
|
6491
6901
|
async function projectRemoveCommand(nameOrPath) {
|
|
6492
6902
|
const projects = listProjects();
|
|
6493
6903
|
if (unregisterProject(nameOrPath)) {
|
|
6494
|
-
console.log(
|
|
6904
|
+
console.log(chalk30.green(`\u2713 Removed project: ${nameOrPath}`));
|
|
6495
6905
|
return;
|
|
6496
6906
|
}
|
|
6497
6907
|
for (const { key, config } of projects) {
|
|
6498
6908
|
if (config.name === nameOrPath || config.path === resolve(nameOrPath)) {
|
|
6499
6909
|
unregisterProject(key);
|
|
6500
|
-
console.log(
|
|
6910
|
+
console.log(chalk30.green(`\u2713 Removed project: ${config.name}`));
|
|
6501
6911
|
return;
|
|
6502
6912
|
}
|
|
6503
6913
|
}
|
|
6504
|
-
console.log(
|
|
6505
|
-
console.log(
|
|
6914
|
+
console.log(chalk30.red(`Project not found: ${nameOrPath}`));
|
|
6915
|
+
console.log(chalk30.dim(`Use 'pan project list' to see registered projects.`));
|
|
6506
6916
|
}
|
|
6507
6917
|
async function projectInitCommand() {
|
|
6508
6918
|
if (existsSync28(PROJECTS_CONFIG_FILE)) {
|
|
6509
|
-
console.log(
|
|
6919
|
+
console.log(chalk30.yellow(`Config already exists: ${PROJECTS_CONFIG_FILE}`));
|
|
6510
6920
|
return;
|
|
6511
6921
|
}
|
|
6512
6922
|
initializeProjectsConfig();
|
|
6513
|
-
console.log(
|
|
6923
|
+
console.log(chalk30.green("\u2713 Projects config initialized"));
|
|
6514
6924
|
console.log("");
|
|
6515
|
-
console.log(
|
|
6925
|
+
console.log(chalk30.dim(`Edit ${PROJECTS_CONFIG_FILE} to add your projects.`));
|
|
6516
6926
|
console.log("");
|
|
6517
|
-
console.log(
|
|
6927
|
+
console.log(chalk30.bold("Quick start:"));
|
|
6518
6928
|
console.log(
|
|
6519
|
-
|
|
6929
|
+
chalk30.dim(
|
|
6520
6930
|
' pan project add /path/to/project --name "My Project" --linear-team MIN'
|
|
6521
6931
|
)
|
|
6522
6932
|
);
|
|
@@ -6535,13 +6945,13 @@ async function projectShowCommand(keyOrName) {
|
|
|
6535
6945
|
}
|
|
6536
6946
|
}
|
|
6537
6947
|
if (!found) {
|
|
6538
|
-
console.error(
|
|
6539
|
-
console.log(
|
|
6948
|
+
console.error(chalk30.red(`Project not found: ${keyOrName}`));
|
|
6949
|
+
console.log(chalk30.dim(`Use 'pan project list' to see registered projects.`));
|
|
6540
6950
|
process.exit(1);
|
|
6541
6951
|
}
|
|
6542
6952
|
const pathExists = existsSync28(found.path);
|
|
6543
|
-
const pathStatus = pathExists ?
|
|
6544
|
-
console.log(
|
|
6953
|
+
const pathStatus = pathExists ? chalk30.green("\u2713") : chalk30.red("\u2717");
|
|
6954
|
+
console.log(chalk30.bold(`
|
|
6545
6955
|
Project: ${foundKey}
|
|
6546
6956
|
`));
|
|
6547
6957
|
console.log(` Name: ${found.name}`);
|
|
@@ -6550,7 +6960,7 @@ Project: ${foundKey}
|
|
|
6550
6960
|
console.log(` Team: ${found.linear_team}`);
|
|
6551
6961
|
}
|
|
6552
6962
|
if (found.issue_routing && found.issue_routing.length > 0) {
|
|
6553
|
-
console.log("\n " +
|
|
6963
|
+
console.log("\n " + chalk30.bold("Routing Rules:"));
|
|
6554
6964
|
for (const rule of found.issue_routing) {
|
|
6555
6965
|
if (rule.labels) {
|
|
6556
6966
|
console.log(` Labels: ${rule.labels.join(", ")}`);
|
|
@@ -6565,14 +6975,14 @@ Project: ${foundKey}
|
|
|
6565
6975
|
}
|
|
6566
6976
|
|
|
6567
6977
|
// src/cli/commands/doctor.ts
|
|
6568
|
-
import
|
|
6978
|
+
import chalk31 from "chalk";
|
|
6569
6979
|
import { existsSync as existsSync29, readdirSync as readdirSync15, readFileSync as readFileSync23 } from "fs";
|
|
6570
|
-
import { execSync as
|
|
6980
|
+
import { execSync as execSync15 } from "child_process";
|
|
6571
6981
|
import { homedir as homedir9 } from "os";
|
|
6572
6982
|
import { join as join29 } from "path";
|
|
6573
6983
|
function checkCommand2(cmd) {
|
|
6574
6984
|
try {
|
|
6575
|
-
|
|
6985
|
+
execSync15(`which ${cmd}`, { encoding: "utf-8", stdio: "pipe" });
|
|
6576
6986
|
return true;
|
|
6577
6987
|
} catch {
|
|
6578
6988
|
return false;
|
|
@@ -6590,8 +7000,8 @@ function countItems(path) {
|
|
|
6590
7000
|
}
|
|
6591
7001
|
}
|
|
6592
7002
|
async function doctorCommand() {
|
|
6593
|
-
console.log(
|
|
6594
|
-
console.log(
|
|
7003
|
+
console.log(chalk31.bold("\nPanopticon Doctor\n"));
|
|
7004
|
+
console.log(chalk31.dim("Checking system health...\n"));
|
|
6595
7005
|
const checks = [];
|
|
6596
7006
|
const requiredCommands = [
|
|
6597
7007
|
{ cmd: "git", name: "Git", fix: "Install git" },
|
|
@@ -6689,7 +7099,7 @@ async function doctorCommand() {
|
|
|
6689
7099
|
});
|
|
6690
7100
|
}
|
|
6691
7101
|
try {
|
|
6692
|
-
const sessions =
|
|
7102
|
+
const sessions = execSync15("tmux list-sessions 2>/dev/null || true", { encoding: "utf-8" });
|
|
6693
7103
|
const agentSessions = sessions.split("\n").filter((s) => s.includes("agent-")).length;
|
|
6694
7104
|
checks.push({
|
|
6695
7105
|
name: "Running Agents",
|
|
@@ -6704,37 +7114,37 @@ async function doctorCommand() {
|
|
|
6704
7114
|
});
|
|
6705
7115
|
}
|
|
6706
7116
|
const icons = {
|
|
6707
|
-
ok:
|
|
6708
|
-
warn:
|
|
6709
|
-
error:
|
|
7117
|
+
ok: chalk31.green("\u2713"),
|
|
7118
|
+
warn: chalk31.yellow("\u26A0"),
|
|
7119
|
+
error: chalk31.red("\u2717")
|
|
6710
7120
|
};
|
|
6711
7121
|
let hasErrors = false;
|
|
6712
7122
|
let hasWarnings = false;
|
|
6713
7123
|
for (const check of checks) {
|
|
6714
7124
|
const icon = icons[check.status];
|
|
6715
|
-
const message = check.status === "error" ?
|
|
7125
|
+
const message = check.status === "error" ? chalk31.red(check.message) : check.status === "warn" ? chalk31.yellow(check.message) : chalk31.dim(check.message);
|
|
6716
7126
|
console.log(`${icon} ${check.name}: ${message}`);
|
|
6717
7127
|
if (check.fix && check.status !== "ok") {
|
|
6718
|
-
console.log(
|
|
7128
|
+
console.log(chalk31.dim(` Fix: ${check.fix}`));
|
|
6719
7129
|
}
|
|
6720
7130
|
if (check.status === "error") hasErrors = true;
|
|
6721
7131
|
if (check.status === "warn") hasWarnings = true;
|
|
6722
7132
|
}
|
|
6723
7133
|
console.log("");
|
|
6724
7134
|
if (hasErrors) {
|
|
6725
|
-
console.log(
|
|
6726
|
-
console.log(
|
|
7135
|
+
console.log(chalk31.red("Some required components are missing."));
|
|
7136
|
+
console.log(chalk31.dim("Fix the errors above before using Panopticon."));
|
|
6727
7137
|
} else if (hasWarnings) {
|
|
6728
|
-
console.log(
|
|
7138
|
+
console.log(chalk31.yellow("System is functional with some optional features missing."));
|
|
6729
7139
|
} else {
|
|
6730
|
-
console.log(
|
|
7140
|
+
console.log(chalk31.green("All systems operational!"));
|
|
6731
7141
|
}
|
|
6732
7142
|
console.log("");
|
|
6733
7143
|
}
|
|
6734
7144
|
|
|
6735
7145
|
// src/cli/commands/update.ts
|
|
6736
|
-
import { execSync as
|
|
6737
|
-
import
|
|
7146
|
+
import { execSync as execSync16 } from "child_process";
|
|
7147
|
+
import chalk32 from "chalk";
|
|
6738
7148
|
import { readFileSync as readFileSync24 } from "fs";
|
|
6739
7149
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6740
7150
|
import { dirname as dirname5, join as join30 } from "path";
|
|
@@ -6751,7 +7161,7 @@ function getCurrentVersion() {
|
|
|
6751
7161
|
}
|
|
6752
7162
|
async function getLatestVersion() {
|
|
6753
7163
|
try {
|
|
6754
|
-
const result =
|
|
7164
|
+
const result = execSync16("npm view panopticon-cli version", {
|
|
6755
7165
|
encoding: "utf8",
|
|
6756
7166
|
stdio: ["pipe", "pipe", "pipe"]
|
|
6757
7167
|
});
|
|
@@ -6776,49 +7186,49 @@ function isNewer(latest, current) {
|
|
|
6776
7186
|
return l.patch > c.patch;
|
|
6777
7187
|
}
|
|
6778
7188
|
async function updateCommand(options) {
|
|
6779
|
-
console.log(
|
|
7189
|
+
console.log(chalk32.bold("Panopticon Update\n"));
|
|
6780
7190
|
const currentVersion = getCurrentVersion();
|
|
6781
|
-
console.log(`Current version: ${
|
|
7191
|
+
console.log(`Current version: ${chalk32.cyan(currentVersion)}`);
|
|
6782
7192
|
let latestVersion;
|
|
6783
7193
|
try {
|
|
6784
|
-
console.log(
|
|
7194
|
+
console.log(chalk32.dim("Checking npm for latest version..."));
|
|
6785
7195
|
latestVersion = await getLatestVersion();
|
|
6786
|
-
console.log(`Latest version: ${
|
|
7196
|
+
console.log(`Latest version: ${chalk32.cyan(latestVersion)}`);
|
|
6787
7197
|
} catch (error) {
|
|
6788
|
-
console.error(
|
|
6789
|
-
console.error(
|
|
7198
|
+
console.error(chalk32.red("Failed to check for updates"));
|
|
7199
|
+
console.error(chalk32.dim("Make sure you have internet connectivity"));
|
|
6790
7200
|
process.exit(1);
|
|
6791
7201
|
}
|
|
6792
7202
|
const needsUpdate = isNewer(latestVersion, currentVersion);
|
|
6793
7203
|
if (!needsUpdate) {
|
|
6794
|
-
console.log(
|
|
7204
|
+
console.log(chalk32.green("\n\u2713 You are on the latest version"));
|
|
6795
7205
|
return;
|
|
6796
7206
|
}
|
|
6797
7207
|
console.log(
|
|
6798
|
-
|
|
7208
|
+
chalk32.yellow(`
|
|
6799
7209
|
\u2191 Update available: ${currentVersion} \u2192 ${latestVersion}`)
|
|
6800
7210
|
);
|
|
6801
7211
|
if (options.check) {
|
|
6802
|
-
console.log(
|
|
7212
|
+
console.log(chalk32.dim("\nRun `pan update` to install"));
|
|
6803
7213
|
return;
|
|
6804
7214
|
}
|
|
6805
|
-
console.log(
|
|
7215
|
+
console.log(chalk32.dim("\nUpdating Panopticon..."));
|
|
6806
7216
|
try {
|
|
6807
|
-
|
|
7217
|
+
execSync16("npm install -g panopticon-cli@latest", {
|
|
6808
7218
|
stdio: "inherit"
|
|
6809
7219
|
});
|
|
6810
|
-
console.log(
|
|
7220
|
+
console.log(chalk32.green(`
|
|
6811
7221
|
\u2713 Updated to ${latestVersion}`));
|
|
6812
7222
|
const config = loadConfig();
|
|
6813
7223
|
if (config.sync.auto_sync) {
|
|
6814
|
-
console.log(
|
|
7224
|
+
console.log(chalk32.dim("\nRunning auto-sync..."));
|
|
6815
7225
|
await syncCommand({});
|
|
6816
7226
|
}
|
|
6817
|
-
console.log(
|
|
7227
|
+
console.log(chalk32.dim("\nRestart any running agents to use the new version."));
|
|
6818
7228
|
} catch (error) {
|
|
6819
|
-
console.error(
|
|
7229
|
+
console.error(chalk32.red("\nUpdate failed"));
|
|
6820
7230
|
console.error(
|
|
6821
|
-
|
|
7231
|
+
chalk32.dim("Try running with sudo: sudo npm install -g panopticon-cli@latest")
|
|
6822
7232
|
);
|
|
6823
7233
|
process.exit(1);
|
|
6824
7234
|
}
|
|
@@ -6837,11 +7247,12 @@ program.command("skills").description("List and manage skills").option("--json",
|
|
|
6837
7247
|
registerWorkCommands(program);
|
|
6838
7248
|
registerWorkspaceCommands(program);
|
|
6839
7249
|
registerCloisterCommands(program);
|
|
7250
|
+
registerSpecialistsCommands(program);
|
|
6840
7251
|
registerSetupCommands(program);
|
|
6841
7252
|
registerInstallCommand(program);
|
|
6842
7253
|
program.command("status").description("Show running agents (shorthand for work status)").option("--json", "Output as JSON").action(statusCommand);
|
|
6843
7254
|
program.command("up").description("Start dashboard (and Traefik if enabled)").option("--detach", "Run in background").option("--skip-traefik", "Skip Traefik startup").action(async (options) => {
|
|
6844
|
-
const { spawn, execSync:
|
|
7255
|
+
const { spawn, execSync: execSync17 } = await import("child_process");
|
|
6845
7256
|
const { join: join31, dirname: dirname6 } = await import("path");
|
|
6846
7257
|
const { fileURLToPath: fileURLToPath3 } = await import("url");
|
|
6847
7258
|
const { readFileSync: readFileSync25, existsSync: existsSync30 } = await import("fs");
|
|
@@ -6858,76 +7269,95 @@ program.command("up").description("Start dashboard (and Traefik if enabled)").op
|
|
|
6858
7269
|
traefikEnabled = config.traefik?.enabled === true;
|
|
6859
7270
|
traefikDomain = config.traefik?.domain || "pan.localhost";
|
|
6860
7271
|
} catch (error) {
|
|
6861
|
-
console.log(
|
|
7272
|
+
console.log(chalk33.yellow("Warning: Could not read config.toml"));
|
|
6862
7273
|
}
|
|
6863
7274
|
}
|
|
6864
|
-
console.log(
|
|
7275
|
+
console.log(chalk33.bold("Starting Panopticon...\n"));
|
|
6865
7276
|
if (traefikEnabled && !options.skipTraefik) {
|
|
6866
7277
|
const traefikDir = join31(process.env.HOME || "", ".panopticon", "traefik");
|
|
6867
7278
|
if (existsSync30(traefikDir)) {
|
|
6868
7279
|
try {
|
|
6869
|
-
console.log(
|
|
6870
|
-
|
|
7280
|
+
console.log(chalk33.dim("Starting Traefik..."));
|
|
7281
|
+
execSync17("docker-compose up -d", {
|
|
6871
7282
|
cwd: traefikDir,
|
|
6872
7283
|
stdio: "pipe"
|
|
6873
7284
|
});
|
|
6874
|
-
console.log(
|
|
6875
|
-
console.log(
|
|
7285
|
+
console.log(chalk33.green("\u2713 Traefik started"));
|
|
7286
|
+
console.log(chalk33.dim(` Dashboard: https://traefik.${traefikDomain}:8080
|
|
6876
7287
|
`));
|
|
6877
7288
|
} catch (error) {
|
|
6878
|
-
console.log(
|
|
6879
|
-
console.log(
|
|
7289
|
+
console.log(chalk33.yellow("\u26A0 Failed to start Traefik (continuing anyway)"));
|
|
7290
|
+
console.log(chalk33.dim(" Run with --skip-traefik to suppress this message\n"));
|
|
6880
7291
|
}
|
|
6881
7292
|
}
|
|
6882
7293
|
}
|
|
6883
|
-
|
|
7294
|
+
try {
|
|
7295
|
+
execSync17("npm --version", { stdio: "pipe" });
|
|
7296
|
+
} catch {
|
|
7297
|
+
console.error(chalk33.red("Error: npm not found in PATH"));
|
|
7298
|
+
console.error(chalk33.dim("Make sure Node.js and npm are installed and in your PATH"));
|
|
7299
|
+
process.exit(1);
|
|
7300
|
+
}
|
|
7301
|
+
console.log(chalk33.dim("Starting dashboard..."));
|
|
6884
7302
|
if (options.detach) {
|
|
6885
7303
|
const child = spawn("npm", ["run", "dev"], {
|
|
6886
7304
|
cwd: dashboardDir,
|
|
6887
7305
|
detached: true,
|
|
6888
|
-
stdio: "ignore"
|
|
7306
|
+
stdio: "ignore",
|
|
7307
|
+
shell: true
|
|
6889
7308
|
});
|
|
6890
|
-
|
|
6891
|
-
|
|
7309
|
+
let hasError = false;
|
|
7310
|
+
child.on("error", (err) => {
|
|
7311
|
+
hasError = true;
|
|
7312
|
+
console.error(chalk33.red("Failed to start dashboard in background:"), err.message);
|
|
7313
|
+
process.exit(1);
|
|
7314
|
+
});
|
|
7315
|
+
setTimeout(() => {
|
|
7316
|
+
if (!hasError) {
|
|
7317
|
+
child.unref();
|
|
7318
|
+
}
|
|
7319
|
+
}, 100);
|
|
7320
|
+
console.log(chalk33.green("\u2713 Dashboard started in background"));
|
|
6892
7321
|
if (traefikEnabled) {
|
|
6893
|
-
console.log(` Frontend: ${
|
|
6894
|
-
console.log(` API: ${
|
|
7322
|
+
console.log(` Frontend: ${chalk33.cyan(`https://${traefikDomain}`)}`);
|
|
7323
|
+
console.log(` API: ${chalk33.cyan(`https://${traefikDomain}/api`)}`);
|
|
6895
7324
|
} else {
|
|
6896
|
-
console.log(` Frontend: ${
|
|
6897
|
-
console.log(` API: ${
|
|
7325
|
+
console.log(` Frontend: ${chalk33.cyan("http://localhost:3001")}`);
|
|
7326
|
+
console.log(` API: ${chalk33.cyan("http://localhost:3002")}`);
|
|
6898
7327
|
}
|
|
6899
7328
|
} else {
|
|
6900
7329
|
if (traefikEnabled) {
|
|
6901
|
-
console.log(` Frontend: ${
|
|
6902
|
-
console.log(` API: ${
|
|
7330
|
+
console.log(` Frontend: ${chalk33.cyan(`https://${traefikDomain}`)}`);
|
|
7331
|
+
console.log(` API: ${chalk33.cyan(`https://${traefikDomain}/api`)}`);
|
|
6903
7332
|
} else {
|
|
6904
|
-
console.log(` Frontend: ${
|
|
6905
|
-
console.log(` API: ${
|
|
7333
|
+
console.log(` Frontend: ${chalk33.cyan("http://localhost:3001")}`);
|
|
7334
|
+
console.log(` API: ${chalk33.cyan("http://localhost:3002")}`);
|
|
6906
7335
|
}
|
|
6907
|
-
console.log(
|
|
7336
|
+
console.log(chalk33.dim("\nPress Ctrl+C to stop\n"));
|
|
6908
7337
|
const child = spawn("npm", ["run", "dev"], {
|
|
6909
7338
|
cwd: dashboardDir,
|
|
6910
|
-
stdio: "inherit"
|
|
7339
|
+
stdio: "inherit",
|
|
7340
|
+
shell: true
|
|
6911
7341
|
});
|
|
6912
7342
|
child.on("error", (err) => {
|
|
6913
|
-
console.error(
|
|
7343
|
+
console.error(chalk33.red("Failed to start dashboard:"), err.message);
|
|
6914
7344
|
process.exit(1);
|
|
6915
7345
|
});
|
|
6916
7346
|
}
|
|
6917
7347
|
});
|
|
6918
7348
|
program.command("down").description("Stop dashboard (and Traefik if enabled)").option("--skip-traefik", "Skip Traefik shutdown").action(async (options) => {
|
|
6919
|
-
const { execSync:
|
|
7349
|
+
const { execSync: execSync17 } = await import("child_process");
|
|
6920
7350
|
const { join: join31 } = await import("path");
|
|
6921
7351
|
const { readFileSync: readFileSync25, existsSync: existsSync30 } = await import("fs");
|
|
6922
7352
|
const { parse: parse2 } = await import("@iarna/toml");
|
|
6923
|
-
console.log(
|
|
6924
|
-
console.log(
|
|
7353
|
+
console.log(chalk33.bold("Stopping Panopticon...\n"));
|
|
7354
|
+
console.log(chalk33.dim("Stopping dashboard..."));
|
|
6925
7355
|
try {
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
console.log(
|
|
7356
|
+
execSync17("lsof -ti:3001 | xargs kill -9 2>/dev/null || true", { stdio: "pipe" });
|
|
7357
|
+
execSync17("lsof -ti:3002 | xargs kill -9 2>/dev/null || true", { stdio: "pipe" });
|
|
7358
|
+
console.log(chalk33.green("\u2713 Dashboard stopped"));
|
|
6929
7359
|
} catch {
|
|
6930
|
-
console.log(
|
|
7360
|
+
console.log(chalk33.dim(" No dashboard processes found"));
|
|
6931
7361
|
}
|
|
6932
7362
|
const configFile = join31(process.env.HOME || "", ".panopticon", "config.toml");
|
|
6933
7363
|
let traefikEnabled = false;
|
|
@@ -6942,15 +7372,15 @@ program.command("down").description("Stop dashboard (and Traefik if enabled)").o
|
|
|
6942
7372
|
if (traefikEnabled && !options.skipTraefik) {
|
|
6943
7373
|
const traefikDir = join31(process.env.HOME || "", ".panopticon", "traefik");
|
|
6944
7374
|
if (existsSync30(traefikDir)) {
|
|
6945
|
-
console.log(
|
|
7375
|
+
console.log(chalk33.dim("Stopping Traefik..."));
|
|
6946
7376
|
try {
|
|
6947
|
-
|
|
7377
|
+
execSync17("docker-compose down", {
|
|
6948
7378
|
cwd: traefikDir,
|
|
6949
7379
|
stdio: "pipe"
|
|
6950
7380
|
});
|
|
6951
|
-
console.log(
|
|
7381
|
+
console.log(chalk33.green("\u2713 Traefik stopped"));
|
|
6952
7382
|
} catch (error) {
|
|
6953
|
-
console.log(
|
|
7383
|
+
console.log(chalk33.yellow("\u26A0 Failed to stop Traefik"));
|
|
6954
7384
|
}
|
|
6955
7385
|
}
|
|
6956
7386
|
}
|