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/dist/cli/index.js CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  restoreBackup,
17
17
  saveConfig,
18
18
  syncHooks
19
- } from "../chunk-ZT55DPAC.js";
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-IVAFJ6DS.js";
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-3SI436SZ.js";
45
+ } from "../chunk-P5TQ5C3J.js";
46
46
 
47
47
  // src/cli/index.ts
48
48
  import { Command } from "commander";
49
- import chalk29 from "chalk";
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 (!confirm) {
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-EHEXMVSP.js");
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
- await this.checkHandoffTriggers(agentHealths);
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/project.ts
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(chalk26.red(`Path does not exist: ${fullPath}`));
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(chalk26.yellow(`Project already registered with key: ${key}`));
6429
- console.log(chalk26.dim(`Existing path: ${existing.path}`));
6430
- console.log(chalk26.dim(`To update, first run: pan project remove ${key}`));
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(chalk26.green(`\u2713 Added project: ${name}`));
6451
- console.log(chalk26.dim(` Key: ${key}`));
6452
- console.log(chalk26.dim(` Path: ${fullPath}`));
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(chalk26.dim(` Linear team: ${linearTeam}`));
6864
+ console.log(chalk30.dim(` Linear team: ${linearTeam}`));
6455
6865
  }
6456
6866
  console.log("");
6457
- console.log(chalk26.dim(`Edit ${PROJECTS_CONFIG_FILE} to add issue routing rules.`));
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(chalk26.dim("No projects registered."));
6463
- console.log(chalk26.dim("Add one with: pan project add <path> --linear-team <TEAM>"));
6464
- console.log(chalk26.dim(`Or edit: ${PROJECTS_CONFIG_FILE}`));
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(chalk26.bold("\nRegistered Projects:\n"));
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 ? chalk26.green("\u2713") : chalk26.red("\u2717");
6479
- console.log(`${statusIcon} ${chalk26.bold(config.name)} ${chalk26.dim(`(${key})`)}`);
6480
- console.log(` ${chalk26.dim(config.path)}`);
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(` ${chalk26.cyan(`Linear: ${config.linear_team}`)}`);
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(` ${chalk26.dim(`Routes: ${config.issue_routing.length} rules`)}`);
6895
+ console.log(` ${chalk30.dim(`Routes: ${config.issue_routing.length} rules`)}`);
6486
6896
  }
6487
6897
  console.log("");
6488
6898
  }
6489
- console.log(chalk26.dim(`Config: ${PROJECTS_CONFIG_FILE}`));
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(chalk26.green(`\u2713 Removed project: ${nameOrPath}`));
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(chalk26.green(`\u2713 Removed project: ${config.name}`));
6910
+ console.log(chalk30.green(`\u2713 Removed project: ${config.name}`));
6501
6911
  return;
6502
6912
  }
6503
6913
  }
6504
- console.log(chalk26.red(`Project not found: ${nameOrPath}`));
6505
- console.log(chalk26.dim(`Use 'pan project list' to see registered projects.`));
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(chalk26.yellow(`Config already exists: ${PROJECTS_CONFIG_FILE}`));
6919
+ console.log(chalk30.yellow(`Config already exists: ${PROJECTS_CONFIG_FILE}`));
6510
6920
  return;
6511
6921
  }
6512
6922
  initializeProjectsConfig();
6513
- console.log(chalk26.green("\u2713 Projects config initialized"));
6923
+ console.log(chalk30.green("\u2713 Projects config initialized"));
6514
6924
  console.log("");
6515
- console.log(chalk26.dim(`Edit ${PROJECTS_CONFIG_FILE} to add your projects.`));
6925
+ console.log(chalk30.dim(`Edit ${PROJECTS_CONFIG_FILE} to add your projects.`));
6516
6926
  console.log("");
6517
- console.log(chalk26.bold("Quick start:"));
6927
+ console.log(chalk30.bold("Quick start:"));
6518
6928
  console.log(
6519
- chalk26.dim(
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(chalk26.red(`Project not found: ${keyOrName}`));
6539
- console.log(chalk26.dim(`Use 'pan project list' to see registered projects.`));
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 ? chalk26.green("\u2713") : chalk26.red("\u2717");
6544
- console.log(chalk26.bold(`
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 " + chalk26.bold("Routing Rules:"));
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 chalk27 from "chalk";
6978
+ import chalk31 from "chalk";
6569
6979
  import { existsSync as existsSync29, readdirSync as readdirSync15, readFileSync as readFileSync23 } from "fs";
6570
- import { execSync as execSync13 } from "child_process";
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
- execSync13(`which ${cmd}`, { encoding: "utf-8", stdio: "pipe" });
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(chalk27.bold("\nPanopticon Doctor\n"));
6594
- console.log(chalk27.dim("Checking system health...\n"));
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 = execSync13("tmux list-sessions 2>/dev/null || true", { encoding: "utf-8" });
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: chalk27.green("\u2713"),
6708
- warn: chalk27.yellow("\u26A0"),
6709
- error: chalk27.red("\u2717")
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" ? chalk27.red(check.message) : check.status === "warn" ? chalk27.yellow(check.message) : chalk27.dim(check.message);
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(chalk27.dim(` Fix: ${check.fix}`));
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(chalk27.red("Some required components are missing."));
6726
- console.log(chalk27.dim("Fix the errors above before using Panopticon."));
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(chalk27.yellow("System is functional with some optional features missing."));
7138
+ console.log(chalk31.yellow("System is functional with some optional features missing."));
6729
7139
  } else {
6730
- console.log(chalk27.green("All systems operational!"));
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 execSync14 } from "child_process";
6737
- import chalk28 from "chalk";
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 = execSync14("npm view panopticon-cli version", {
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(chalk28.bold("Panopticon Update\n"));
7189
+ console.log(chalk32.bold("Panopticon Update\n"));
6780
7190
  const currentVersion = getCurrentVersion();
6781
- console.log(`Current version: ${chalk28.cyan(currentVersion)}`);
7191
+ console.log(`Current version: ${chalk32.cyan(currentVersion)}`);
6782
7192
  let latestVersion;
6783
7193
  try {
6784
- console.log(chalk28.dim("Checking npm for latest version..."));
7194
+ console.log(chalk32.dim("Checking npm for latest version..."));
6785
7195
  latestVersion = await getLatestVersion();
6786
- console.log(`Latest version: ${chalk28.cyan(latestVersion)}`);
7196
+ console.log(`Latest version: ${chalk32.cyan(latestVersion)}`);
6787
7197
  } catch (error) {
6788
- console.error(chalk28.red("Failed to check for updates"));
6789
- console.error(chalk28.dim("Make sure you have internet connectivity"));
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(chalk28.green("\n\u2713 You are on the latest version"));
7204
+ console.log(chalk32.green("\n\u2713 You are on the latest version"));
6795
7205
  return;
6796
7206
  }
6797
7207
  console.log(
6798
- chalk28.yellow(`
7208
+ chalk32.yellow(`
6799
7209
  \u2191 Update available: ${currentVersion} \u2192 ${latestVersion}`)
6800
7210
  );
6801
7211
  if (options.check) {
6802
- console.log(chalk28.dim("\nRun `pan update` to install"));
7212
+ console.log(chalk32.dim("\nRun `pan update` to install"));
6803
7213
  return;
6804
7214
  }
6805
- console.log(chalk28.dim("\nUpdating Panopticon..."));
7215
+ console.log(chalk32.dim("\nUpdating Panopticon..."));
6806
7216
  try {
6807
- execSync14("npm install -g panopticon-cli@latest", {
7217
+ execSync16("npm install -g panopticon-cli@latest", {
6808
7218
  stdio: "inherit"
6809
7219
  });
6810
- console.log(chalk28.green(`
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(chalk28.dim("\nRunning auto-sync..."));
7224
+ console.log(chalk32.dim("\nRunning auto-sync..."));
6815
7225
  await syncCommand({});
6816
7226
  }
6817
- console.log(chalk28.dim("\nRestart any running agents to use the new version."));
7227
+ console.log(chalk32.dim("\nRestart any running agents to use the new version."));
6818
7228
  } catch (error) {
6819
- console.error(chalk28.red("\nUpdate failed"));
7229
+ console.error(chalk32.red("\nUpdate failed"));
6820
7230
  console.error(
6821
- chalk28.dim("Try running with sudo: sudo npm install -g panopticon-cli@latest")
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: execSync15 } = await import("child_process");
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(chalk29.yellow("Warning: Could not read config.toml"));
7272
+ console.log(chalk33.yellow("Warning: Could not read config.toml"));
6862
7273
  }
6863
7274
  }
6864
- console.log(chalk29.bold("Starting Panopticon...\n"));
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(chalk29.dim("Starting Traefik..."));
6870
- execSync15("docker-compose up -d", {
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(chalk29.green("\u2713 Traefik started"));
6875
- console.log(chalk29.dim(` Dashboard: https://traefik.${traefikDomain}:8080
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(chalk29.yellow("\u26A0 Failed to start Traefik (continuing anyway)"));
6879
- console.log(chalk29.dim(" Run with --skip-traefik to suppress this message\n"));
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
- console.log(chalk29.dim("Starting dashboard..."));
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
- child.unref();
6891
- console.log(chalk29.green("\u2713 Dashboard started in background"));
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: ${chalk29.cyan(`https://${traefikDomain}`)}`);
6894
- console.log(` API: ${chalk29.cyan(`https://${traefikDomain}/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: ${chalk29.cyan("http://localhost:3001")}`);
6897
- console.log(` API: ${chalk29.cyan("http://localhost:3002")}`);
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: ${chalk29.cyan(`https://${traefikDomain}`)}`);
6902
- console.log(` API: ${chalk29.cyan(`https://${traefikDomain}/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: ${chalk29.cyan("http://localhost:3001")}`);
6905
- console.log(` API: ${chalk29.cyan("http://localhost:3002")}`);
7333
+ console.log(` Frontend: ${chalk33.cyan("http://localhost:3001")}`);
7334
+ console.log(` API: ${chalk33.cyan("http://localhost:3002")}`);
6906
7335
  }
6907
- console.log(chalk29.dim("\nPress Ctrl+C to stop\n"));
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(chalk29.red("Failed to start dashboard:"), err.message);
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: execSync15 } = await import("child_process");
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(chalk29.bold("Stopping Panopticon...\n"));
6924
- console.log(chalk29.dim("Stopping dashboard..."));
7353
+ console.log(chalk33.bold("Stopping Panopticon...\n"));
7354
+ console.log(chalk33.dim("Stopping dashboard..."));
6925
7355
  try {
6926
- execSync15("lsof -ti:3001 | xargs kill -9 2>/dev/null || true", { stdio: "pipe" });
6927
- execSync15("lsof -ti:3002 | xargs kill -9 2>/dev/null || true", { stdio: "pipe" });
6928
- console.log(chalk29.green("\u2713 Dashboard stopped"));
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(chalk29.dim(" No dashboard processes found"));
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(chalk29.dim("Stopping Traefik..."));
7375
+ console.log(chalk33.dim("Stopping Traefik..."));
6946
7376
  try {
6947
- execSync15("docker-compose down", {
7377
+ execSync17("docker-compose down", {
6948
7378
  cwd: traefikDir,
6949
7379
  stdio: "pipe"
6950
7380
  });
6951
- console.log(chalk29.green("\u2713 Traefik stopped"));
7381
+ console.log(chalk33.green("\u2713 Traefik stopped"));
6952
7382
  } catch (error) {
6953
- console.log(chalk29.yellow("\u26A0 Failed to stop Traefik"));
7383
+ console.log(chalk33.yellow("\u26A0 Failed to stop Traefik"));
6954
7384
  }
6955
7385
  }
6956
7386
  }