@askexenow/exe-os 0.8.41 → 0.8.42

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.
Files changed (74) hide show
  1. package/dist/bin/backfill-conversations.js +805 -642
  2. package/dist/bin/backfill-responses.js +804 -641
  3. package/dist/bin/backfill-vectors.js +791 -634
  4. package/dist/bin/cleanup-stale-review-tasks.js +788 -631
  5. package/dist/bin/cli.js +1326 -655
  6. package/dist/bin/exe-agent.js +20 -1
  7. package/dist/bin/exe-assign.js +1503 -1343
  8. package/dist/bin/exe-boot.js +2508 -1802
  9. package/dist/bin/exe-call.js +39 -1
  10. package/dist/bin/exe-dispatch.js +39 -2
  11. package/dist/bin/exe-doctor.js +790 -633
  12. package/dist/bin/exe-export-behaviors.js +792 -637
  13. package/dist/bin/exe-forget.js +145 -0
  14. package/dist/bin/exe-gateway.js +2487 -1878
  15. package/dist/bin/exe-heartbeat.js +147 -1
  16. package/dist/bin/exe-kill.js +795 -640
  17. package/dist/bin/exe-launch-agent.js +2168 -2008
  18. package/dist/bin/exe-link.js +9 -1
  19. package/dist/bin/exe-new-employee.js +6 -2
  20. package/dist/bin/exe-pending-messages.js +146 -1
  21. package/dist/bin/exe-pending-notifications.js +788 -631
  22. package/dist/bin/exe-pending-reviews.js +147 -1
  23. package/dist/bin/exe-rename.js +23 -0
  24. package/dist/bin/exe-review.js +490 -327
  25. package/dist/bin/exe-search.js +154 -3
  26. package/dist/bin/exe-session-cleanup.js +2466 -413
  27. package/dist/bin/exe-status.js +474 -317
  28. package/dist/bin/exe-team.js +474 -317
  29. package/dist/bin/git-sweep.js +2690 -150
  30. package/dist/bin/graph-backfill.js +794 -637
  31. package/dist/bin/graph-export.js +798 -641
  32. package/dist/bin/scan-tasks.js +2951 -44
  33. package/dist/bin/setup.js +47 -25
  34. package/dist/bin/shard-migrate.js +792 -637
  35. package/dist/bin/wiki-sync.js +794 -637
  36. package/dist/gateway/index.js +2504 -1895
  37. package/dist/hooks/bug-report-worker.js +2118 -576
  38. package/dist/hooks/commit-complete.js +2689 -149
  39. package/dist/hooks/error-recall.js +154 -3
  40. package/dist/hooks/ingest-worker.js +1420 -814
  41. package/dist/hooks/instructions-loaded.js +151 -0
  42. package/dist/hooks/notification.js +153 -2
  43. package/dist/hooks/post-compact.js +164 -0
  44. package/dist/hooks/pre-compact.js +3073 -101
  45. package/dist/hooks/pre-tool-use.js +151 -0
  46. package/dist/hooks/prompt-ingest-worker.js +1700 -1541
  47. package/dist/hooks/prompt-submit.js +2658 -1113
  48. package/dist/hooks/response-ingest-worker.js +151 -5
  49. package/dist/hooks/session-end.js +153 -2
  50. package/dist/hooks/session-start.js +154 -3
  51. package/dist/hooks/stop.js +151 -0
  52. package/dist/hooks/subagent-stop.js +151 -0
  53. package/dist/hooks/summary-worker.js +160 -6
  54. package/dist/index.js +278 -100
  55. package/dist/lib/cloud-sync.js +9 -1
  56. package/dist/lib/consolidation.js +69 -2
  57. package/dist/lib/database.js +19 -0
  58. package/dist/lib/device-registry.js +19 -0
  59. package/dist/lib/employee-templates.js +20 -1
  60. package/dist/lib/exe-daemon.js +236 -16
  61. package/dist/lib/hybrid-search.js +154 -3
  62. package/dist/lib/messaging.js +39 -2
  63. package/dist/lib/schedules.js +792 -637
  64. package/dist/lib/store.js +796 -636
  65. package/dist/lib/tasks.js +1614 -1091
  66. package/dist/lib/tmux-routing.js +149 -9
  67. package/dist/mcp/server.js +1810 -1137
  68. package/dist/mcp/tools/create-task.js +2280 -828
  69. package/dist/mcp/tools/list-tasks.js +2788 -159
  70. package/dist/mcp/tools/send-message.js +39 -2
  71. package/dist/mcp/tools/update-task.js +64 -0
  72. package/dist/runtime/index.js +235 -67
  73. package/dist/tui/App.js +1440 -646
  74. package/package.json +3 -2
package/dist/bin/cli.js CHANGED
@@ -1311,6 +1311,13 @@ async function ensureSchema() {
1311
1311
  });
1312
1312
  } catch {
1313
1313
  }
1314
+ try {
1315
+ await client.execute({
1316
+ sql: `ALTER TABLE tasks ADD COLUMN session_scope TEXT`,
1317
+ args: []
1318
+ });
1319
+ } catch {
1320
+ }
1314
1321
  try {
1315
1322
  await client.execute({
1316
1323
  sql: `ALTER TABLE memories ADD COLUMN task_id TEXT`,
@@ -1757,6 +1764,18 @@ async function ensureSchema() {
1757
1764
  CREATE INDEX IF NOT EXISTS idx_session_kills_agent
1758
1765
  ON session_kills(agent_id);
1759
1766
  `);
1767
+ await client.execute(`
1768
+ CREATE TABLE IF NOT EXISTS global_procedures (
1769
+ id TEXT PRIMARY KEY,
1770
+ title TEXT NOT NULL,
1771
+ content TEXT NOT NULL,
1772
+ priority TEXT NOT NULL DEFAULT 'p0',
1773
+ domain TEXT,
1774
+ active INTEGER NOT NULL DEFAULT 1,
1775
+ created_at TEXT NOT NULL,
1776
+ updated_at TEXT NOT NULL
1777
+ )
1778
+ `);
1760
1779
  await client.executeMultiple(`
1761
1780
  CREATE TABLE IF NOT EXISTS conversations (
1762
1781
  id TEXT PRIMARY KEY,
@@ -2061,6 +2080,61 @@ var init_keychain = __esm({
2061
2080
  }
2062
2081
  });
2063
2082
 
2083
+ // src/lib/state-bus.ts
2084
+ var StateBus, orgBus;
2085
+ var init_state_bus = __esm({
2086
+ "src/lib/state-bus.ts"() {
2087
+ "use strict";
2088
+ StateBus = class {
2089
+ handlers = /* @__PURE__ */ new Map();
2090
+ globalHandlers = /* @__PURE__ */ new Set();
2091
+ /** Emit an event to all subscribers */
2092
+ emit(event) {
2093
+ const typeHandlers = this.handlers.get(event.type);
2094
+ if (typeHandlers) {
2095
+ for (const handler of typeHandlers) {
2096
+ try {
2097
+ handler(event);
2098
+ } catch {
2099
+ }
2100
+ }
2101
+ }
2102
+ for (const handler of this.globalHandlers) {
2103
+ try {
2104
+ handler(event);
2105
+ } catch {
2106
+ }
2107
+ }
2108
+ }
2109
+ /** Subscribe to a specific event type */
2110
+ on(type, handler) {
2111
+ if (!this.handlers.has(type)) {
2112
+ this.handlers.set(type, /* @__PURE__ */ new Set());
2113
+ }
2114
+ this.handlers.get(type).add(handler);
2115
+ }
2116
+ /** Subscribe to ALL events */
2117
+ onAny(handler) {
2118
+ this.globalHandlers.add(handler);
2119
+ }
2120
+ /** Unsubscribe from a specific event type */
2121
+ off(type, handler) {
2122
+ this.handlers.get(type)?.delete(handler);
2123
+ }
2124
+ /** Unsubscribe from ALL events */
2125
+ offAny(handler) {
2126
+ this.globalHandlers.delete(handler);
2127
+ }
2128
+ /** Remove all listeners */
2129
+ clear() {
2130
+ this.handlers.clear();
2131
+ this.globalHandlers.clear();
2132
+ }
2133
+ };
2134
+ orgBus = new StateBus();
2135
+ }
2136
+ });
2137
+
2064
2138
  // src/lib/shard-manager.ts
2065
2139
  var shard_manager_exports = {};
2066
2140
  __export(shard_manager_exports, {
@@ -2302,6 +2376,71 @@ var init_shard_manager = __esm({
2302
2376
  }
2303
2377
  });
2304
2378
 
2379
+ // src/lib/global-procedures.ts
2380
+ var global_procedures_exports = {};
2381
+ __export(global_procedures_exports, {
2382
+ deactivateGlobalProcedure: () => deactivateGlobalProcedure,
2383
+ getGlobalProceduresBlock: () => getGlobalProceduresBlock,
2384
+ loadGlobalProcedures: () => loadGlobalProcedures,
2385
+ storeGlobalProcedure: () => storeGlobalProcedure
2386
+ });
2387
+ import { randomUUID } from "crypto";
2388
+ async function loadGlobalProcedures() {
2389
+ const client = getClient();
2390
+ const result = await client.execute({
2391
+ sql: "SELECT * FROM global_procedures WHERE active = 1 ORDER BY priority ASC, created_at ASC",
2392
+ args: []
2393
+ });
2394
+ const procedures = result.rows;
2395
+ if (procedures.length > 0) {
2396
+ _cache = procedures.map((p) => `### ${p.title}
2397
+ ${p.content}`).join("\n\n");
2398
+ } else {
2399
+ _cache = "";
2400
+ }
2401
+ _cacheLoaded = true;
2402
+ return procedures;
2403
+ }
2404
+ function getGlobalProceduresBlock() {
2405
+ if (!_cacheLoaded) return "";
2406
+ if (!_cache) return "";
2407
+ return `## Organization-Wide Procedures (MANDATORY \u2014 supersedes all other rules)
2408
+
2409
+ ${_cache}
2410
+ `;
2411
+ }
2412
+ async function storeGlobalProcedure(input) {
2413
+ const id = randomUUID();
2414
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2415
+ const client = getClient();
2416
+ await client.execute({
2417
+ sql: `INSERT INTO global_procedures (id, title, content, priority, domain, active, created_at, updated_at)
2418
+ VALUES (?, ?, ?, ?, ?, 1, ?, ?)`,
2419
+ args: [id, input.title, input.content, input.priority ?? "p0", input.domain ?? null, now, now]
2420
+ });
2421
+ await loadGlobalProcedures();
2422
+ return id;
2423
+ }
2424
+ async function deactivateGlobalProcedure(id) {
2425
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2426
+ const client = getClient();
2427
+ const result = await client.execute({
2428
+ sql: "UPDATE global_procedures SET active = 0, updated_at = ? WHERE id = ?",
2429
+ args: [now, id]
2430
+ });
2431
+ await loadGlobalProcedures();
2432
+ return result.rowsAffected > 0;
2433
+ }
2434
+ var _cache, _cacheLoaded;
2435
+ var init_global_procedures = __esm({
2436
+ "src/lib/global-procedures.ts"() {
2437
+ "use strict";
2438
+ init_database();
2439
+ _cache = "";
2440
+ _cacheLoaded = false;
2441
+ }
2442
+ });
2443
+
2305
2444
  // src/lib/store.ts
2306
2445
  var store_exports = {};
2307
2446
  __export(store_exports, {
@@ -2381,6 +2520,11 @@ async function initStore(options) {
2381
2520
  "version-query"
2382
2521
  );
2383
2522
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
2523
+ try {
2524
+ const { loadGlobalProcedures: loadGlobalProcedures2 } = await Promise.resolve().then(() => (init_global_procedures(), global_procedures_exports));
2525
+ await loadGlobalProcedures2();
2526
+ } catch {
2527
+ }
2384
2528
  }
2385
2529
  function classifyTier(record) {
2386
2530
  if (record.tool_name === "commit_to_long_term_memory" && (record.importance ?? 0) >= 8) return 1;
@@ -2422,6 +2566,12 @@ async function writeMemory(record) {
2422
2566
  supersedes_id: record.supersedes_id ?? null
2423
2567
  };
2424
2568
  _pendingRecords.push(dbRow);
2569
+ orgBus.emit({
2570
+ type: "memory_stored",
2571
+ agentId: record.agent_id,
2572
+ project: record.project_name,
2573
+ timestamp: record.timestamp
2574
+ });
2425
2575
  const MAX_PENDING = 1e3;
2426
2576
  if (_pendingRecords.length > MAX_PENDING) {
2427
2577
  const dropped = _pendingRecords.length - MAX_PENDING;
@@ -2767,6 +2917,7 @@ var init_store = __esm({
2767
2917
  init_database();
2768
2918
  init_keychain();
2769
2919
  init_config();
2920
+ init_state_bus();
2770
2921
  INIT_MAX_RETRIES = 3;
2771
2922
  INIT_RETRY_DELAY_MS = 1e3;
2772
2923
  _pendingRecords = [];
@@ -2781,7 +2932,7 @@ var init_store = __esm({
2781
2932
  // src/lib/exe-daemon-client.ts
2782
2933
  import net from "net";
2783
2934
  import { spawn } from "child_process";
2784
- import { randomUUID } from "crypto";
2935
+ import { randomUUID as randomUUID2 } from "crypto";
2785
2936
  import { existsSync as existsSync7, unlinkSync, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
2786
2937
  import path7 from "path";
2787
2938
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -2973,7 +3124,7 @@ function sendRequest(texts, priority) {
2973
3124
  resolve({ error: "Not connected" });
2974
3125
  return;
2975
3126
  }
2976
- const id = randomUUID();
3127
+ const id = randomUUID2();
2977
3128
  const timer = setTimeout(() => {
2978
3129
  _pending.delete(id);
2979
3130
  resolve({ error: "Request timeout" });
@@ -2991,7 +3142,7 @@ function sendRequest(texts, priority) {
2991
3142
  async function pingDaemon() {
2992
3143
  if (!_socket || !_connected) return null;
2993
3144
  return new Promise((resolve) => {
2994
- const id = randomUUID();
3145
+ const id = randomUUID2();
2995
3146
  const timer = setTimeout(() => {
2996
3147
  _pending.delete(id);
2997
3148
  resolve(null);
@@ -3537,7 +3688,8 @@ __export(employee_templates_exports, {
3537
3688
  function getSessionPrompt(storedPrompt) {
3538
3689
  const markerIndex = storedPrompt.indexOf(PROCEDURES_MARKER);
3539
3690
  const rolePrompt = markerIndex >= 0 ? storedPrompt.slice(0, markerIndex).trimEnd() : storedPrompt;
3540
- return `${rolePrompt}
3691
+ const globalBlock = getGlobalProceduresBlock();
3692
+ return `${globalBlock}${rolePrompt}
3541
3693
  ${BASE_OPERATING_PROCEDURES}`;
3542
3694
  }
3543
3695
  function buildCustomEmployeePrompt(name, role) {
@@ -3577,6 +3729,7 @@ var BASE_OPERATING_PROCEDURES, DEFAULT_EXE, TEMPLATE_VERSION, PROCEDURES_MARKER,
3577
3729
  var init_employee_templates = __esm({
3578
3730
  "src/lib/employee-templates.ts"() {
3579
3731
  "use strict";
3732
+ init_global_procedures();
3580
3733
  BASE_OPERATING_PROCEDURES = `
3581
3734
  EXE OS \u2014 VISION AND NON-NEGOTIABLE PRINCIPLES (above all work):
3582
3735
 
@@ -4510,7 +4663,7 @@ __export(license_exports, {
4510
4663
  validateLicense: () => validateLicense
4511
4664
  });
4512
4665
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync3 } from "fs";
4513
- import { randomUUID as randomUUID2 } from "crypto";
4666
+ import { randomUUID as randomUUID3 } from "crypto";
4514
4667
  import path11 from "path";
4515
4668
  import { jwtVerify, importSPKI } from "jose";
4516
4669
  async function fetchRetry(url, init) {
@@ -4537,7 +4690,7 @@ function loadDeviceId() {
4537
4690
  }
4538
4691
  } catch {
4539
4692
  }
4540
- const id = randomUUID2();
4693
+ const id = randomUUID3();
4541
4694
  mkdirSync3(EXE_AI_DIR, { recursive: true });
4542
4695
  writeFileSync2(DEVICE_ID_PATH, id, "utf8");
4543
4696
  return id;
@@ -12309,6 +12462,73 @@ var init_Sidebar = __esm({
12309
12462
  }
12310
12463
  });
12311
12464
 
12465
+ // src/lib/telemetry.ts
12466
+ var telemetry_exports = {};
12467
+ __export(telemetry_exports, {
12468
+ instrumentServer: () => instrumentServer,
12469
+ withTrace: () => withTrace
12470
+ });
12471
+ async function ensureInit() {
12472
+ if (initialized || !ENABLED) return;
12473
+ initialized = true;
12474
+ try {
12475
+ const { NodeSDK } = await import("@opentelemetry/sdk-node");
12476
+ const { ConsoleSpanExporter } = await import("@opentelemetry/sdk-trace-base");
12477
+ const sdk = new NodeSDK({
12478
+ serviceName: "exe-os",
12479
+ traceExporter: new ConsoleSpanExporter()
12480
+ });
12481
+ sdk.start();
12482
+ process.stderr.write("[exe-os] OpenTelemetry tracing enabled\n");
12483
+ } catch (err) {
12484
+ process.stderr.write(
12485
+ `[exe-os] OpenTelemetry init failed: ${err instanceof Error ? err.message : String(err)}
12486
+ `
12487
+ );
12488
+ }
12489
+ }
12490
+ async function withTrace(toolName, fn) {
12491
+ if (!ENABLED) return fn();
12492
+ await ensureInit();
12493
+ const { trace, SpanStatusCode } = await import("@opentelemetry/api");
12494
+ const tracer = trace.getTracer("exe-os", "1.0.0");
12495
+ return tracer.startActiveSpan(`mcp.tool.${toolName}`, async (span) => {
12496
+ span.setAttribute("mcp.tool.name", toolName);
12497
+ try {
12498
+ const result = await fn();
12499
+ span.setStatus({ code: SpanStatusCode.OK });
12500
+ return result;
12501
+ } catch (err) {
12502
+ span.setStatus({
12503
+ code: SpanStatusCode.ERROR,
12504
+ message: err instanceof Error ? err.message : String(err)
12505
+ });
12506
+ span.recordException(
12507
+ err instanceof Error ? err : new Error(String(err))
12508
+ );
12509
+ throw err;
12510
+ } finally {
12511
+ span.end();
12512
+ }
12513
+ });
12514
+ }
12515
+ function instrumentServer(server) {
12516
+ if (!ENABLED) return;
12517
+ const original = server.registerTool.bind(server);
12518
+ server.registerTool = (name, config, handler) => {
12519
+ const traced = async (...args2) => withTrace(name, () => handler(...args2));
12520
+ return original(name, config, traced);
12521
+ };
12522
+ }
12523
+ var ENABLED, initialized;
12524
+ var init_telemetry = __esm({
12525
+ "src/lib/telemetry.ts"() {
12526
+ "use strict";
12527
+ ENABLED = process.env.EXE_TELEMETRY === "1";
12528
+ initialized = false;
12529
+ }
12530
+ });
12531
+
12312
12532
  // src/lib/tmux-status.ts
12313
12533
  var tmux_status_exports = {};
12314
12534
  __export(tmux_status_exports, {
@@ -12603,61 +12823,64 @@ function Footer() {
12603
12823
  return () => clearInterval(timer);
12604
12824
  }, []);
12605
12825
  async function loadFooterData() {
12606
- try {
12607
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
12608
- const client = getClient2();
12609
- if (client) {
12610
- const result = await client.execute("SELECT COUNT(*) as cnt FROM memories");
12611
- setMemoryCount(Number(result.rows[0]?.cnt ?? 0));
12826
+ const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
12827
+ return withTrace2("tui.footer.loadData", async () => {
12828
+ try {
12829
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
12830
+ const client = getClient2();
12831
+ if (client) {
12832
+ const result = await client.execute("SELECT COUNT(*) as cnt FROM memories");
12833
+ setMemoryCount(Number(result.rows[0]?.cnt ?? 0));
12834
+ }
12835
+ } catch {
12612
12836
  }
12613
- } catch {
12614
- }
12615
- try {
12616
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
12617
- const client = getClient2();
12618
- if (client) {
12619
- const result = await client.execute(
12620
- "SELECT COUNT(*) as cnt FROM tasks WHERE status IN ('open','in_progress')"
12621
- );
12622
- setTaskCount(Number(result.rows[0]?.cnt ?? 0));
12837
+ try {
12838
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
12839
+ const client = getClient2();
12840
+ if (client) {
12841
+ const result = await client.execute(
12842
+ "SELECT COUNT(*) as cnt FROM tasks WHERE status IN ('open','in_progress')"
12843
+ );
12844
+ setTaskCount(Number(result.rows[0]?.cnt ?? 0));
12845
+ }
12846
+ } catch {
12623
12847
  }
12624
- } catch {
12625
- }
12626
- try {
12627
- const { existsSync: existsSync22 } = await import("fs");
12628
- const { join } = await import("path");
12629
- const home = process.env.HOME ?? "";
12630
- const pidPath = join(home, ".exe-os", "exed.pid");
12631
- setDaemon(existsSync22(pidPath) ? "running" : "stopped");
12632
- } catch {
12633
- setDaemon("unknown");
12634
- }
12635
- try {
12636
- const { checkLicense: checkLicense2 } = await Promise.resolve().then(() => (init_license(), license_exports));
12637
- const license = await checkLicense2();
12638
- setPlan(license.plan);
12639
- } catch {
12640
- setPlan("free");
12641
- }
12642
- try {
12643
- const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
12644
- if (inTmux2()) {
12645
- const allSessions = listTmuxSessions2();
12646
- setSessions(allSessions.length);
12647
- if (!currentSession) {
12648
- try {
12649
- const { execSync: execSync13 } = await import("child_process");
12650
- const name = execSync13("tmux display-message -p '#{session_name}' 2>/dev/null", {
12651
- encoding: "utf8",
12652
- timeout: 2e3
12653
- }).trim();
12654
- if (name) setCurrentSession(name);
12655
- } catch {
12848
+ try {
12849
+ const { existsSync: existsSync22 } = await import("fs");
12850
+ const { join } = await import("path");
12851
+ const home = process.env.HOME ?? "";
12852
+ const pidPath = join(home, ".exe-os", "exed.pid");
12853
+ setDaemon(existsSync22(pidPath) ? "running" : "stopped");
12854
+ } catch {
12855
+ setDaemon("unknown");
12856
+ }
12857
+ try {
12858
+ const { checkLicense: checkLicense2 } = await Promise.resolve().then(() => (init_license(), license_exports));
12859
+ const license = await checkLicense2();
12860
+ setPlan(license.plan);
12861
+ } catch {
12862
+ setPlan("free");
12863
+ }
12864
+ try {
12865
+ const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
12866
+ if (inTmux2()) {
12867
+ const allSessions = listTmuxSessions2();
12868
+ setSessions(allSessions.length);
12869
+ if (!currentSession) {
12870
+ try {
12871
+ const { execSync: execSync13 } = await import("child_process");
12872
+ const name = execSync13("tmux display-message -p '#{session_name}' 2>/dev/null", {
12873
+ encoding: "utf8",
12874
+ timeout: 2e3
12875
+ }).trim();
12876
+ if (name) setCurrentSession(name);
12877
+ } catch {
12878
+ }
12656
12879
  }
12657
12880
  }
12881
+ } catch {
12658
12882
  }
12659
- } catch {
12660
- }
12883
+ });
12661
12884
  }
12662
12885
  return /* @__PURE__ */ jsxs3(Box_default, { flexDirection: "column", children: [
12663
12886
  /* @__PURE__ */ jsx5(Text, { color: "#3D3660", children: "\u2500".repeat(process.stdout.columns || 120) }),
@@ -12698,6 +12921,8 @@ function Footer() {
12698
12921
  ] }),
12699
12922
  /* @__PURE__ */ jsx5(Text, { color: "#6B4C9A", children: "1-7 navigate" }),
12700
12923
  /* @__PURE__ */ jsx5(Text, { color: "#3D3660", children: "\u2502" }),
12924
+ /* @__PURE__ */ jsx5(Text, { color: "#6B4C9A", children: "d debug" }),
12925
+ /* @__PURE__ */ jsx5(Text, { color: "#3D3660", children: "\u2502" }),
12701
12926
  /* @__PURE__ */ jsx5(Text, { color: "#6B4C9A", children: "q quit" })
12702
12927
  ] })
12703
12928
  ] })
@@ -14346,7 +14571,7 @@ var init_anthropic = __esm({
14346
14571
 
14347
14572
  // src/gateway/providers/openai-compat.ts
14348
14573
  import OpenAI from "openai";
14349
- import { randomUUID as randomUUID3 } from "crypto";
14574
+ import { randomUUID as randomUUID4 } from "crypto";
14350
14575
  var OpenAICompatProvider;
14351
14576
  var init_openai_compat = __esm({
14352
14577
  "src/gateway/providers/openai-compat.ts"() {
@@ -14463,7 +14688,7 @@ var init_openai_compat = __esm({
14463
14688
  }
14464
14689
  content.push({
14465
14690
  type: "tool_use",
14466
- id: call.id ?? randomUUID3(),
14691
+ id: call.id ?? randomUUID4(),
14467
14692
  name: fn.name,
14468
14693
  input
14469
14694
  });
@@ -15537,7 +15762,7 @@ function CommandCenterView({
15537
15762
  setAgentConfig({
15538
15763
  provider,
15539
15764
  model,
15540
- systemPrompt: "You are an AI assistant in the exe-os TUI. Help the user with their questions. Be concise.",
15765
+ systemPrompt: getSessionPrompt("You are an AI assistant in the exe-os TUI. Help the user with their questions. Be concise."),
15541
15766
  tools: registry,
15542
15767
  hooks: createDefaultHooks2(),
15543
15768
  permissions,
@@ -15658,6 +15883,7 @@ function CommandCenterView({
15658
15883
  employeeCount: p.employees.length,
15659
15884
  activeCount: p.employees.filter((e) => e.status === "active").length,
15660
15885
  memoryCount: p.employees.length * 4e3,
15886
+ openTaskCount: Math.floor(p.employees.length * 1.5),
15661
15887
  status: p.employees.some((e) => e.status === "active") ? "active" : "idle",
15662
15888
  type: p.projectName.startsWith("exe-") ? "code" : "automation",
15663
15889
  recentTasks: DEMO_RECENT_TASKS[p.projectName] ?? []
@@ -15669,6 +15895,7 @@ function CommandCenterView({
15669
15895
  employeeCount: 0,
15670
15896
  activeCount: 0,
15671
15897
  memoryCount: 0,
15898
+ openTaskCount: 0,
15672
15899
  status: "offline",
15673
15900
  type: "reference",
15674
15901
  recentTasks: []
@@ -15683,182 +15910,122 @@ function CommandCenterView({
15683
15910
  return () => clearInterval(timer);
15684
15911
  }, []);
15685
15912
  async function loadData() {
15686
- try {
15687
- const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
15688
- const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
15689
- const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
15690
- const { existsSync: existsSync22 } = await import("fs");
15691
- const { join } = await import("path");
15692
- const registry = listSessions2();
15693
- const tmuxSessions = inTmux2() ? new Set(listTmuxSessions2()) : /* @__PURE__ */ new Set();
15694
- const roster = await loadEmployees2();
15695
- const exeSessions = /* @__PURE__ */ new Map();
15696
- for (const entry of registry) {
15697
- if (entry.agentId === "exe" && tmuxSessions.has(entry.windowName)) {
15698
- exeSessions.set(entry.windowName, entry.projectDir);
15699
- }
15700
- }
15701
- for (const s of tmuxSessions) {
15702
- if (/^exe\d+$/.test(s) && !exeSessions.has(s)) exeSessions.set(s, "");
15703
- }
15704
- let projectMemoryCounts = /* @__PURE__ */ new Map();
15705
- let projectRecentTasks = /* @__PURE__ */ new Map();
15913
+ const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
15914
+ return withTrace2("tui.commandCenter.loadData", async () => {
15706
15915
  try {
15707
15916
  const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
15917
+ const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
15918
+ const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
15919
+ const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
15920
+ const { existsSync: existsSync22 } = await import("fs");
15921
+ const { join } = await import("path");
15708
15922
  const client = getClient2();
15709
- if (client) {
15710
- const memResult = await client.execute(
15711
- "SELECT project_name, COUNT(*) as cnt FROM memories GROUP BY project_name"
15712
- );
15713
- for (const row of memResult.rows) {
15714
- projectMemoryCounts.set(String(row.project_name), Number(row.cnt));
15715
- }
15716
- for (const [, projectDir] of exeSessions) {
15717
- const projectName = projectDir.split("/").filter(Boolean).pop() ?? "";
15718
- if (!projectName) continue;
15719
- const taskResult = await client.execute({
15720
- sql: "SELECT title FROM tasks WHERE project_name = ? AND status = 'done' ORDER BY updated_at DESC LIMIT 3",
15721
- args: [projectName]
15722
- });
15723
- const tasks = taskResult.rows.map((r) => String(r.title));
15724
- if (tasks.length > 0) projectRecentTasks.set(projectName, tasks);
15725
- }
15923
+ if (!client) {
15924
+ setDbError(true);
15925
+ return;
15726
15926
  }
15727
- } catch {
15728
- }
15729
- const projectList = [];
15730
- for (const [exeSession, projectDir] of exeSessions) {
15731
- const projectName = projectDir.split("/").filter(Boolean).pop() ?? exeSession;
15927
+ const dbProjects = await client.execute(
15928
+ `SELECT DISTINCT project_name FROM memories WHERE project_name IS NOT NULL AND project_name != ''
15929
+ UNION
15930
+ SELECT DISTINCT project_name FROM tasks WHERE project_name IS NOT NULL AND project_name != ''
15931
+ ORDER BY project_name`
15932
+ );
15933
+ const projectNames = dbProjects.rows.map((r) => String(r.project_name));
15934
+ const memResult = await client.execute(
15935
+ "SELECT project_name, COUNT(*) as cnt FROM memories WHERE project_name IS NOT NULL GROUP BY project_name"
15936
+ );
15937
+ const memoryCounts = /* @__PURE__ */ new Map();
15938
+ for (const row of memResult.rows) {
15939
+ memoryCounts.set(String(row.project_name), Number(row.cnt));
15940
+ }
15941
+ const taskCountResult = await client.execute(
15942
+ "SELECT project_name, COUNT(*) as cnt FROM tasks WHERE status IN ('open', 'in_progress') AND project_name IS NOT NULL GROUP BY project_name"
15943
+ );
15944
+ const openTaskCounts = /* @__PURE__ */ new Map();
15945
+ for (const row of taskCountResult.rows) {
15946
+ openTaskCounts.set(String(row.project_name), Number(row.cnt));
15947
+ }
15948
+ const recentResult = await client.execute(
15949
+ "SELECT project_name, title FROM tasks WHERE status = 'done' AND project_name IS NOT NULL ORDER BY updated_at DESC LIMIT 30"
15950
+ );
15951
+ const recentTasksByProject = /* @__PURE__ */ new Map();
15952
+ for (const row of recentResult.rows) {
15953
+ const name = String(row.project_name);
15954
+ const tasks = recentTasksByProject.get(name) ?? [];
15955
+ if (tasks.length < 3) tasks.push(String(row.title));
15956
+ recentTasksByProject.set(name, tasks);
15957
+ }
15958
+ const registry = listSessions2();
15959
+ const tmuxSessions = inTmux2() ? new Set(listTmuxSessions2()) : /* @__PURE__ */ new Set();
15960
+ const roster = await loadEmployees2();
15732
15961
  const employeeNames = roster.map((e) => e.name).filter((n) => n !== "exe");
15733
- let activeCount = tmuxSessions.has(exeSession) ? 1 : 0;
15734
- for (const empName of employeeNames) {
15735
- if (tmuxSessions.has(`${empName}-${exeSession}`)) activeCount++;
15736
- }
15737
- const totalCount = 1 + employeeNames.length;
15738
- const memoryCount = projectMemoryCounts.get(projectName) ?? 0;
15739
- const hasGit = projectDir ? existsSync22(join(projectDir, ".git")) : false;
15740
- const hasActivity = memoryCount > 0;
15741
- let type = "automation";
15742
- if (hasGit && hasActivity) type = "code";
15743
- else if (hasGit && !hasActivity) type = "reference";
15744
- projectList.push({
15745
- projectName,
15746
- exeSession,
15747
- projectDir,
15748
- employeeCount: totalCount,
15749
- activeCount,
15750
- memoryCount,
15751
- status: activeCount > 0 ? "active" : "idle",
15752
- type,
15753
- recentTasks: projectRecentTasks.get(projectName) ?? []
15754
- });
15755
- }
15756
- const knownDirs = [
15757
- process.env.HOME ? join(process.env.HOME, "openclaw") : null,
15758
- process.env.HOME ? join(process.env.HOME, "agno") : null
15759
- ].filter(Boolean);
15760
- const activeProjectNames = new Set(projectList.map((p) => p.projectName));
15761
- for (const dir of knownDirs) {
15762
- const name = dir.split("/").filter(Boolean).pop() ?? "";
15763
- if (activeProjectNames.has(name) || !existsSync22(dir) || !existsSync22(join(dir, ".git"))) continue;
15764
- if ((projectMemoryCounts.get(name) ?? 0) > 0) continue;
15765
- projectList.push({
15766
- projectName: name,
15767
- exeSession: "",
15768
- projectDir: dir,
15769
- employeeCount: 0,
15770
- activeCount: 0,
15771
- memoryCount: 0,
15772
- status: "offline",
15773
- type: "reference",
15774
- recentTasks: []
15775
- });
15776
- }
15777
- const dbActiveProjectNames = new Set(projectList.map((p) => p.projectName));
15778
- try {
15779
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
15780
- const client = getClient2();
15781
- if (client) {
15782
- const dbProjects = await client.execute(
15783
- `SELECT DISTINCT project_name FROM memories WHERE project_name IS NOT NULL AND project_name != ''
15784
- UNION
15785
- SELECT DISTINCT project_name FROM tasks WHERE project_name IS NOT NULL AND project_name != ''
15786
- ORDER BY project_name`
15787
- );
15788
- for (const row of dbProjects.rows) {
15789
- const name = String(row.project_name);
15790
- if (dbActiveProjectNames.has(name)) continue;
15791
- const memCount = projectMemoryCounts.get(name) ?? 0;
15792
- const agentResult = await client.execute({
15793
- sql: "SELECT COUNT(DISTINCT agent_id) as cnt FROM memories WHERE project_name = ?",
15794
- args: [name]
15795
- });
15796
- const agentCount = Number(agentResult.rows[0]?.cnt ?? 0);
15797
- projectList.push({
15798
- projectName: name,
15799
- exeSession: "",
15800
- projectDir: "",
15801
- employeeCount: agentCount,
15802
- activeCount: 0,
15803
- memoryCount: memCount,
15804
- status: "offline",
15805
- type: "code",
15806
- recentTasks: projectRecentTasks.get(name) ?? []
15807
- });
15962
+ const projectSessions = /* @__PURE__ */ new Map();
15963
+ for (const entry of registry) {
15964
+ if (entry.agentId === "exe" && tmuxSessions.has(entry.windowName)) {
15965
+ const projName = entry.projectDir.split("/").filter(Boolean).pop() ?? "";
15966
+ if (projName) {
15967
+ projectSessions.set(projName, { exeSession: entry.windowName, projectDir: entry.projectDir });
15968
+ }
15808
15969
  }
15809
15970
  }
15810
- } catch {
15811
- }
15812
- if (projectList.filter((p) => p.type !== "reference").length === 0) {
15813
- projectList.unshift({
15814
- projectName: "(no active sessions)",
15815
- exeSession: "",
15816
- projectDir: "",
15817
- employeeCount: 0,
15818
- activeCount: 0,
15819
- memoryCount: 0,
15820
- status: "offline",
15821
- type: "code",
15822
- recentTasks: []
15971
+ const projectList = [];
15972
+ for (const name of projectNames) {
15973
+ const session = projectSessions.get(name);
15974
+ const exeSession = session?.exeSession ?? "";
15975
+ const projectDir = session?.projectDir ?? "";
15976
+ let activeCount = 0;
15977
+ if (exeSession && tmuxSessions.has(exeSession)) {
15978
+ activeCount = 1;
15979
+ for (const empName of employeeNames) {
15980
+ if (tmuxSessions.has(`${empName}-${exeSession}`)) activeCount++;
15981
+ }
15982
+ }
15983
+ const memoryCount = memoryCounts.get(name) ?? 0;
15984
+ const openTaskCount = openTaskCounts.get(name) ?? 0;
15985
+ const hasGit = projectDir ? existsSync22(join(projectDir, ".git")) : false;
15986
+ const type = hasGit ? "code" : memoryCount > 0 ? "code" : "automation";
15987
+ projectList.push({
15988
+ projectName: name,
15989
+ exeSession,
15990
+ projectDir,
15991
+ employeeCount: activeCount,
15992
+ activeCount,
15993
+ memoryCount,
15994
+ openTaskCount,
15995
+ status: activeCount > 0 ? "active" : "idle",
15996
+ type,
15997
+ recentTasks: recentTasksByProject.get(name) ?? []
15998
+ });
15999
+ }
16000
+ projectList.sort((a, b) => {
16001
+ if (a.activeCount > 0 && b.activeCount === 0) return -1;
16002
+ if (a.activeCount === 0 && b.activeCount > 0) return 1;
16003
+ return b.memoryCount - a.memoryCount;
15823
16004
  });
15824
- }
15825
- setProjects(projectList);
15826
- try {
15827
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
15828
- const client = getClient2();
15829
- if (client) {
15830
- const result = await client.execute("SELECT COUNT(*) as cnt FROM memories");
15831
- setHealth((h) => ({ ...h, memories: Number(result.rows[0]?.cnt ?? 0) }));
16005
+ setProjects(projectList);
16006
+ const totalResult = await client.execute("SELECT COUNT(*) as cnt FROM memories");
16007
+ setHealth((h) => ({ ...h, memories: Number(totalResult.rows[0]?.cnt ?? 0) }));
16008
+ try {
16009
+ const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
16010
+ setHealth((h) => ({ ...h, daemon: existsSync22(pidPath) ? "running" : "stopped" }));
16011
+ } catch {
15832
16012
  }
15833
- } catch {
15834
- }
15835
- try {
15836
- const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
15837
- setHealth((h) => ({ ...h, daemon: existsSync22(pidPath) ? "running" : "stopped" }));
15838
- } catch {
15839
- }
15840
- try {
15841
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
15842
- const client = getClient2();
15843
- if (client) {
15844
- const activityResult = await client.execute(
15845
- "SELECT agent_id, tool_name, project_name, created_at, text FROM memories ORDER BY created_at DESC LIMIT 20"
15846
- );
15847
- if (activityResult.rows.length > 0) {
15848
- setActivity(activityResult.rows.slice(0, 8).map((r) => ({
15849
- time: new Date(String(r.created_at)).toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", hour12: false }),
15850
- agent: String(r.agent_id ?? "system"),
15851
- action: String(r.text ?? "").slice(0, 60)
15852
- })));
15853
- }
16013
+ const activityResult = await client.execute(
16014
+ "SELECT agent_id, tool_name, project_name, created_at, text FROM memories ORDER BY created_at DESC LIMIT 20"
16015
+ );
16016
+ if (activityResult.rows.length > 0) {
16017
+ setActivity(activityResult.rows.slice(0, 8).map((r) => ({
16018
+ time: new Date(String(r.created_at)).toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", hour12: false }),
16019
+ agent: String(r.agent_id ?? "system"),
16020
+ action: String(r.text ?? "").slice(0, 60)
16021
+ })));
15854
16022
  }
15855
16023
  } catch {
16024
+ setDbError(true);
16025
+ } finally {
16026
+ setLoading(false);
15856
16027
  }
15857
- } catch {
15858
- setDbError(true);
15859
- } finally {
15860
- setLoading(false);
15861
- }
16028
+ });
15862
16029
  }
15863
16030
  const daemonColor = health.daemon === "running" ? "green" : health.daemon === "stopped" ? "red" : "gray";
15864
16031
  const handleChatSubmit = useCallback4((value) => {
@@ -15950,7 +16117,7 @@ function CommandCenterView({
15950
16117
  ] }),
15951
16118
  /* @__PURE__ */ jsx7(Text, { color: "#6B4C9A", children: "\u2191\u2193 navigate | c to chat | Enter to open | Escape to detach" }),
15952
16119
  /* @__PURE__ */ jsx7(Text, { children: " " }),
15953
- loading ? /* @__PURE__ */ jsx7(Text, { color: "#6B4C9A", children: "Loading projects..." }) : dbError ? /* @__PURE__ */ jsx7(Text, { color: "#EF4444", children: "Database unavailable. Run exe-os setup to initialize." }) : rows.length === 0 ? /* @__PURE__ */ jsx7(Text, { color: "#6B4C9A", children: " No projects detected." }) : (() => {
16120
+ loading ? /* @__PURE__ */ jsx7(Text, { color: "#6B4C9A", children: "Loading projects..." }) : dbError ? /* @__PURE__ */ jsx7(Text, { color: "#EF4444", children: "Database unavailable. Run exe-os setup to initialize." }) : rows.length === 0 ? /* @__PURE__ */ jsx7(Text, { color: "#6B4C9A", children: "No projects yet. Run exe-os in a project directory to get started." }) : (() => {
15954
16121
  const sections = [];
15955
16122
  let currentProjects = [];
15956
16123
  let sectionKey = 0;
@@ -15980,9 +16147,9 @@ function CommandCenterView({
15980
16147
  ] })
15981
16148
  ] }),
15982
16149
  /* @__PURE__ */ jsxs5(Text, { color: isSelected ? "#F0EDE8" : "#6B4C9A", children: [
15983
- entry.employeeCount,
16150
+ entry.openTaskCount,
15984
16151
  " ",
15985
- entry.employeeCount === 1 ? "employee" : "employees",
16152
+ entry.openTaskCount === 1 ? "task" : "tasks",
15986
16153
  " \xB7 ",
15987
16154
  entry.memoryCount.toLocaleString(),
15988
16155
  " memories"
@@ -16060,6 +16227,7 @@ var init_CommandCenter = __esm({
16060
16227
  init_DemoContext();
16061
16228
  init_demo_data();
16062
16229
  init_useAgentLoop();
16230
+ init_employee_templates();
16063
16231
  }
16064
16232
  });
16065
16233
 
@@ -16184,7 +16352,7 @@ var init_TmuxPane = __esm({
16184
16352
  });
16185
16353
 
16186
16354
  // src/lib/task-router.ts
16187
- import { randomUUID as randomUUID4 } from "crypto";
16355
+ import { randomUUID as randomUUID5 } from "crypto";
16188
16356
  function resolveBloomRouting(complexity, config = DEFAULT_BLOOM_CONFIG) {
16189
16357
  const tier = config.complexityToTier[complexity];
16190
16358
  const rule = config.tierRules[tier];
@@ -16836,9 +17004,15 @@ async function createTaskCore(input) {
16836
17004
  }
16837
17005
  }
16838
17006
  const complexity = input.complexity ?? "standard";
17007
+ let sessionScope = null;
17008
+ try {
17009
+ const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
17010
+ sessionScope = resolveExeSession2();
17011
+ } catch {
17012
+ }
16839
17013
  await client.execute({
16840
- sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, complexity, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at, created_at, updated_at)
16841
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
17014
+ sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, complexity, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at, session_scope, created_at, updated_at)
17015
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
16842
17016
  args: [
16843
17017
  id,
16844
17018
  input.title,
@@ -16857,6 +17031,7 @@ async function createTaskCore(input) {
16857
17031
  input.budgetFallbackModel ?? null,
16858
17032
  0,
16859
17033
  null,
17034
+ sessionScope,
16860
17035
  now,
16861
17036
  now
16862
17037
  ]
@@ -16901,6 +17076,15 @@ async function listTasks(input) {
16901
17076
  conditions.push("priority = ?");
16902
17077
  args2.push(input.priority);
16903
17078
  }
17079
+ try {
17080
+ const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
17081
+ const session = resolveExeSession2();
17082
+ if (session) {
17083
+ conditions.push("(session_scope IS NULL OR session_scope = ?)");
17084
+ args2.push(session);
17085
+ }
17086
+ } catch {
17087
+ }
16904
17088
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
16905
17089
  const result = await client.execute({
16906
17090
  sql: `SELECT * FROM tasks ${where} ORDER BY CASE status WHEN 'blocked' THEN 0 WHEN 'in_progress' THEN 1 WHEN 'open' THEN 2 ELSE 3 END, priority ASC, created_at DESC LIMIT 1000`,
@@ -17265,6 +17449,7 @@ var init_tasks_review = __esm({
17265
17449
  init_tasks_crud();
17266
17450
  init_tmux_routing();
17267
17451
  init_session_key();
17452
+ init_state_bus();
17268
17453
  }
17269
17454
  });
17270
17455
 
@@ -17429,13 +17614,12 @@ function assertSessionScope(actionType, targetProject) {
17429
17614
  };
17430
17615
  }
17431
17616
  process.stderr.write(
17432
- `[session-scope] Cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
17617
+ `[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
17433
17618
  `
17434
17619
  );
17435
17620
  return {
17436
- allowed: true,
17437
- // v1: warn-only, don't block
17438
- reason: "cross_session_granted",
17621
+ allowed: false,
17622
+ reason: "cross_session_denied",
17439
17623
  currentProject,
17440
17624
  targetProject,
17441
17625
  targetSession: findSessionForProject(targetProject)?.windowName
@@ -17461,8 +17645,9 @@ async function dispatchTaskToEmployee(input) {
17461
17645
  try {
17462
17646
  const { assertSessionScope: assertSessionScope2 } = (init_session_scope(), __toCommonJS(session_scope_exports));
17463
17647
  const check = assertSessionScope2("dispatch_task", input.projectName);
17464
- if (check.reason === "cross_session_granted") {
17648
+ if (check.reason === "cross_session_denied") {
17465
17649
  crossProject = true;
17650
+ return { dispatched: "skipped", crossProject: true };
17466
17651
  }
17467
17652
  } catch {
17468
17653
  }
@@ -17925,6 +18110,13 @@ async function updateTask(input) {
17925
18110
  await cascadeUnblock(taskId, input.baseDir, now);
17926
18111
  } catch {
17927
18112
  }
18113
+ orgBus.emit({
18114
+ type: "task_completed",
18115
+ taskId,
18116
+ employee: String(row.assigned_to),
18117
+ result: input.result ?? "",
18118
+ timestamp: now
18119
+ });
17928
18120
  if (row.parent_task_id) {
17929
18121
  try {
17930
18122
  await checkSubtaskCompletion(String(row.parent_task_id), String(row.project_name));
@@ -17992,6 +18184,7 @@ var init_tasks = __esm({
17992
18184
  init_database();
17993
18185
  init_config();
17994
18186
  init_notifications();
18187
+ init_state_bus();
17995
18188
  init_tasks_crud();
17996
18189
  init_tasks_review();
17997
18190
  init_tasks_crud();
@@ -18382,8 +18575,28 @@ function getMySession() {
18382
18575
  return getTransport().getMySession();
18383
18576
  }
18384
18577
  function employeeSessionName(employee, exeSession, instance) {
18578
+ if (!/^exe\d+$/.test(exeSession)) {
18579
+ const root = extractRootExe(exeSession);
18580
+ if (root) {
18581
+ process.stderr.write(
18582
+ `[tmux-routing] WARN: exeSession="${exeSession}" is not a root exe session, using "${root}" instead
18583
+ `
18584
+ );
18585
+ exeSession = root;
18586
+ } else {
18587
+ throw new Error(
18588
+ `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1"), not an agent session`
18589
+ );
18590
+ }
18591
+ }
18385
18592
  const suffix = instance != null && instance > 0 ? String(instance) : "";
18386
- return `${employee}${suffix}-${exeSession}`;
18593
+ const name = `${employee}${suffix}-${exeSession}`;
18594
+ if (!VALID_SESSION_NAME.test(name)) {
18595
+ throw new Error(
18596
+ `Invalid session name "${name}" \u2014 must match {agent}-exe{N} or {agent}{instance}-exe{N}`
18597
+ );
18598
+ }
18599
+ return name;
18387
18600
  }
18388
18601
  function parseParentExe(sessionName, agentId) {
18389
18602
  const escaped = agentId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -18623,6 +18836,22 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
18623
18836
  error: `Error: pass employee name ('${bare}'), not session name ('${employeeName}')`
18624
18837
  };
18625
18838
  }
18839
+ if (!/^exe\d+$/.test(exeSession)) {
18840
+ const root = extractRootExe(exeSession);
18841
+ if (root) {
18842
+ process.stderr.write(
18843
+ `[ensureEmployee] WARN: caller passed exeSession="${exeSession}" (not a root exe). Auto-correcting to "${root}".
18844
+ `
18845
+ );
18846
+ exeSession = root;
18847
+ } else {
18848
+ return {
18849
+ status: "failed",
18850
+ sessionName: "",
18851
+ error: `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1")`
18852
+ };
18853
+ }
18854
+ }
18626
18855
  let effectiveInstance = opts?.instance;
18627
18856
  if (effectiveInstance === void 0 && opts?.autoInstance) {
18628
18857
  const free = findFreeInstance(
@@ -18869,7 +19098,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
18869
19098
  releaseSpawnLock2(sessionName);
18870
19099
  return { sessionName };
18871
19100
  }
18872
- var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
19101
+ var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VALID_SESSION_NAME, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
18873
19102
  var init_tmux_routing = __esm({
18874
19103
  "src/lib/tmux-routing.ts"() {
18875
19104
  "use strict";
@@ -18884,6 +19113,7 @@ var init_tmux_routing = __esm({
18884
19113
  SPAWN_LOCK_DIR = path30.join(os10.homedir(), ".exe-os", "spawn-locks");
18885
19114
  SESSION_CACHE = path30.join(os10.homedir(), ".exe-os", "session-cache");
18886
19115
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
19116
+ VALID_SESSION_NAME = /^[a-z]+-exe\d+$|^[a-z]+\d+-exe\d+$/;
18887
19117
  VERIFY_PANE_LINES = 200;
18888
19118
  INTERCOM_DEBOUNCE_MS = 3e4;
18889
19119
  INTERCOM_LOG2 = path30.join(os10.homedir(), ".exe-os", "intercom.log");
@@ -19290,6 +19520,7 @@ function SessionsView({
19290
19520
  const [viewingEmployee, setViewingEmployee] = useState9(null);
19291
19521
  const [viewingProject, setViewingProject] = useState9(null);
19292
19522
  const [loading, setLoading] = useState9(!demo);
19523
+ const [sessionError, setSessionError] = useState9(null);
19293
19524
  const [tmuxAvailable, setTmuxAvailable] = useState9(true);
19294
19525
  const orch = useOrchestrator(!demo);
19295
19526
  const [carouselEmployees, setCarouselEmployees] = useState9([]);
@@ -19465,111 +19696,116 @@ function SessionsView({
19465
19696
  return ACTIVE_PANE_PATTERN.test(lines.join("\n")) ? "active" : "idle";
19466
19697
  }
19467
19698
  async function loadSessions() {
19468
- try {
19469
- const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
19470
- const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2, capturePaneLines: capturePaneLines2, parseActivity: parseActivity2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
19471
- const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
19472
- const { execSync: execSync13 } = await import("child_process");
19473
- if (!inTmux2()) {
19474
- setTmuxAvailable(false);
19475
- setProjects([]);
19476
- return;
19477
- }
19478
- setTmuxAvailable(true);
19479
- const attachedMap = /* @__PURE__ */ new Map();
19699
+ const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
19700
+ return withTrace2("tui.sessions.loadSessions", async () => {
19480
19701
  try {
19481
- const out = execSync13("tmux list-sessions -F '#{session_name}:#{session_attached}' 2>/dev/null", {
19482
- encoding: "utf8",
19483
- timeout: 3e3
19484
- });
19485
- for (const line of out.trim().split("\n").filter(Boolean)) {
19486
- const sepIdx = line.lastIndexOf(":");
19487
- if (sepIdx > 0) {
19488
- attachedMap.set(line.slice(0, sepIdx), line.slice(sepIdx + 1) === "1");
19702
+ const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
19703
+ const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2, capturePaneLines: capturePaneLines2, parseActivity: parseActivity2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
19704
+ const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
19705
+ const { execSync: execSync13 } = await import("child_process");
19706
+ if (!inTmux2()) {
19707
+ setTmuxAvailable(false);
19708
+ setProjects([]);
19709
+ return;
19710
+ }
19711
+ setTmuxAvailable(true);
19712
+ const attachedMap = /* @__PURE__ */ new Map();
19713
+ try {
19714
+ const out = execSync13("tmux list-sessions -F '#{session_name}:#{session_attached}' 2>/dev/null", {
19715
+ encoding: "utf8",
19716
+ timeout: 3e3
19717
+ });
19718
+ for (const line of out.trim().split("\n").filter(Boolean)) {
19719
+ const sepIdx = line.lastIndexOf(":");
19720
+ if (sepIdx > 0) {
19721
+ attachedMap.set(line.slice(0, sepIdx), line.slice(sepIdx + 1) === "1");
19722
+ }
19723
+ }
19724
+ } catch {
19725
+ }
19726
+ const registry = listSessions2();
19727
+ const tmuxSessions = new Set(listTmuxSessions2());
19728
+ const roster = await loadEmployees2();
19729
+ const exeSessions = /* @__PURE__ */ new Map();
19730
+ for (const entry of registry) {
19731
+ if (entry.agentId === "exe" && tmuxSessions.has(entry.windowName)) {
19732
+ exeSessions.set(entry.windowName, entry.projectDir);
19489
19733
  }
19490
19734
  }
19491
- } catch {
19492
- }
19493
- const registry = listSessions2();
19494
- const tmuxSessions = new Set(listTmuxSessions2());
19495
- const roster = await loadEmployees2();
19496
- const exeSessions = /* @__PURE__ */ new Map();
19497
- for (const entry of registry) {
19498
- if (entry.agentId === "exe" && tmuxSessions.has(entry.windowName)) {
19499
- exeSessions.set(entry.windowName, entry.projectDir);
19500
- }
19501
- }
19502
- for (const s of tmuxSessions) {
19503
- if (/^exe\d+$/.test(s) && !exeSessions.has(s)) {
19504
- exeSessions.set(s, "");
19505
- }
19506
- }
19507
- const projectList = [];
19508
- for (const [exeSession, projectDir] of exeSessions) {
19509
- const projectName = projectDir.split("/").filter(Boolean).pop() ?? exeSession;
19510
- const exeHasSession = tmuxSessions.has(exeSession);
19511
- let exeStatus = "offline";
19512
- let exeActivity = "";
19513
- if (exeHasSession) {
19514
- const exeLines = capturePaneLines2(exeSession, 10);
19515
- exeStatus = statusFromPaneLines(exeLines);
19516
- exeActivity = exeLines.length > 0 ? parseActivity2(exeLines) : "";
19517
- }
19518
- const employeeEntries = roster.filter((e) => e.name !== "exe").map((emp) => {
19519
- const sessionName = `${emp.name}-${exeSession}`;
19520
- const hasSession = tmuxSessions.has(sessionName);
19521
- const isCrashed = !hasSession && orch.crashedSessions.includes(emp.name);
19522
- if (isCrashed) {
19523
- return {
19524
- name: emp.name,
19525
- role: emp.role,
19526
- status: "crashed",
19527
- sessionName,
19528
- activity: "Dead session \u2014 has open tasks",
19529
- attached: false
19530
- };
19735
+ for (const s of tmuxSessions) {
19736
+ if (/^exe\d+$/.test(s) && !exeSessions.has(s)) {
19737
+ exeSessions.set(s, "");
19738
+ }
19739
+ }
19740
+ const projectList = [];
19741
+ for (const [exeSession, projectDir] of exeSessions) {
19742
+ const projectName = projectDir.split("/").filter(Boolean).pop() ?? exeSession;
19743
+ const exeHasSession = tmuxSessions.has(exeSession);
19744
+ let exeStatus = "offline";
19745
+ let exeActivity = "";
19746
+ if (exeHasSession) {
19747
+ const exeLines = capturePaneLines2(exeSession, 10);
19748
+ exeStatus = statusFromPaneLines(exeLines);
19749
+ exeActivity = exeLines.length > 0 ? parseActivity2(exeLines) : "";
19531
19750
  }
19532
- if (!hasSession) {
19751
+ const employeeEntries = roster.filter((e) => e.name !== "exe").map((emp) => {
19752
+ const sessionName = `${emp.name}-${exeSession}`;
19753
+ const hasSession = tmuxSessions.has(sessionName);
19754
+ const isCrashed = !hasSession && orch.crashedSessions.includes(emp.name);
19755
+ if (isCrashed) {
19756
+ return {
19757
+ name: emp.name,
19758
+ role: emp.role,
19759
+ status: "crashed",
19760
+ sessionName,
19761
+ activity: "Dead session \u2014 has open tasks",
19762
+ attached: false
19763
+ };
19764
+ }
19765
+ if (!hasSession) {
19766
+ return {
19767
+ name: emp.name,
19768
+ role: emp.role,
19769
+ status: "offline",
19770
+ sessionName,
19771
+ activity: "",
19772
+ attached: false
19773
+ };
19774
+ }
19775
+ const lines = capturePaneLines2(sessionName, 10);
19533
19776
  return {
19534
19777
  name: emp.name,
19535
19778
  role: emp.role,
19536
- status: "offline",
19779
+ status: statusFromPaneLines(lines),
19537
19780
  sessionName,
19538
- activity: "",
19539
- attached: false
19781
+ activity: lines.length > 0 ? parseActivity2(lines) : "",
19782
+ attached: attachedMap.get(sessionName) ?? false
19540
19783
  };
19541
- }
19542
- const lines = capturePaneLines2(sessionName, 10);
19543
- return {
19544
- name: emp.name,
19545
- role: emp.role,
19546
- status: statusFromPaneLines(lines),
19547
- sessionName,
19548
- activity: lines.length > 0 ? parseActivity2(lines) : "",
19549
- attached: attachedMap.get(sessionName) ?? false
19550
- };
19551
- });
19552
- const employees = [
19553
- {
19554
- name: "exe",
19555
- role: "COO",
19556
- status: exeStatus,
19557
- sessionName: exeSession,
19558
- activity: exeActivity,
19559
- attached: attachedMap.get(exeSession) ?? false
19560
- },
19561
- ...employeeEntries
19562
- ];
19563
- projectList.push({ projectName, exeSession, projectDir, employees });
19564
- }
19565
- if (projectList.length === 0) {
19566
- projectList.push({ projectName: "(no active sessions)", exeSession: "", projectDir: "", employees: [] });
19784
+ });
19785
+ const employees = [
19786
+ {
19787
+ name: "exe",
19788
+ role: "COO",
19789
+ status: exeStatus,
19790
+ sessionName: exeSession,
19791
+ activity: exeActivity,
19792
+ attached: attachedMap.get(exeSession) ?? false
19793
+ },
19794
+ ...employeeEntries
19795
+ ];
19796
+ projectList.push({ projectName, exeSession, projectDir, employees });
19797
+ }
19798
+ if (projectList.length === 0) {
19799
+ projectList.push({ projectName: "(no active sessions)", exeSession: "", projectDir: "", employees: [] });
19800
+ }
19801
+ setProjects(projectList);
19802
+ setSessionError(null);
19803
+ } catch (err) {
19804
+ setSessionError(err instanceof Error ? err.message : "Failed to query sessions.");
19805
+ } finally {
19806
+ setLoading(false);
19567
19807
  }
19568
- setProjects(projectList);
19569
- } catch {
19570
- } finally {
19571
- setLoading(false);
19572
- }
19808
+ });
19573
19809
  }
19574
19810
  if (viewingEmployee) {
19575
19811
  const inCarousel = carouselEmployees.length > 0;
@@ -19651,7 +19887,10 @@ function SessionsView({
19651
19887
  ] })
19652
19888
  ] }),
19653
19889
  /* @__PURE__ */ jsx9(Text, { children: " " }),
19654
- loading ? /* @__PURE__ */ jsx9(Text, { color: "#6B4C9A", children: "Loading sessions..." }) : !tmuxAvailable ? /* @__PURE__ */ jsx9(Text, { color: "#6B4C9A", children: "tmux not available. Start a tmux session first to manage employees." }) : flatItems.length === 0 ? /* @__PURE__ */ jsx9(Text, { color: "#6B4C9A", children: "No active tmux sessions. Press Enter on an employee to launch one, or start exe in a tmux session first." }) : flatItems.map((item, i) => {
19890
+ loading ? /* @__PURE__ */ jsx9(Text, { color: "#6B4C9A", children: "Loading sessions..." }) : sessionError ? /* @__PURE__ */ jsxs7(Text, { color: "#C91B00", children: [
19891
+ "Failed to query sessions: ",
19892
+ sessionError
19893
+ ] }) : !tmuxAvailable ? /* @__PURE__ */ jsx9(Text, { color: "#3D3660", children: "tmux not available. Start a tmux session first to manage employees." }) : flatItems.length === 0 ? /* @__PURE__ */ jsx9(Text, { color: "#3D3660", children: "No active sessions. Create a task to spawn an employee." }) : flatItems.map((item, i) => {
19655
19894
  const isSelected = i === selectedIdx;
19656
19895
  const marker = isSelected ? "\u25B8 " : " ";
19657
19896
  if (item.type === "project") {
@@ -19752,7 +19991,7 @@ function TasksView({ onBack }) {
19752
19991
  const demo = useDemo();
19753
19992
  const [allTasks, setAllTasks] = useState10([]);
19754
19993
  const [loading, setLoading] = useState10(!demo);
19755
- const [dbError, setDbError] = useState10(false);
19994
+ const [dbError, setDbError] = useState10(null);
19756
19995
  const [selectedTask, setSelectedTask] = useState10(0);
19757
19996
  const [showDetail, setShowDetail] = useState10(false);
19758
19997
  const [statusFilter, setStatusFilter] = useState10("all");
@@ -19824,40 +20063,43 @@ function TasksView({ onBack }) {
19824
20063
  }
19825
20064
  });
19826
20065
  async function loadTasks() {
19827
- try {
19828
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
19829
- const client = getClient2();
19830
- if (client) {
19831
- const result = await client.execute(
19832
- `SELECT id, title, priority, assigned_to, assigned_by, status, project_name, created_at, result
20066
+ const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
20067
+ return withTrace2("tui.tasks.loadTasks", async () => {
20068
+ try {
20069
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
20070
+ const client = getClient2();
20071
+ if (client) {
20072
+ const result = await client.execute(
20073
+ `SELECT id, title, priority, assigned_to, assigned_by, status, project_name, created_at, result
19833
20074
  FROM tasks
19834
20075
  ORDER BY
19835
20076
  CASE status WHEN 'in_progress' THEN 0 WHEN 'open' THEN 1 WHEN 'blocked' THEN 2 WHEN 'needs_review' THEN 3 WHEN 'done' THEN 4 ELSE 5 END,
19836
20077
  CASE priority WHEN 'p0' THEN 0 WHEN 'p1' THEN 1 WHEN 'p2' THEN 2 ELSE 3 END,
19837
20078
  created_at DESC`
19838
- );
19839
- setAllTasks(
19840
- result.rows.map((r) => ({
19841
- id: String(r.id ?? ""),
19842
- priority: String(r.priority ?? "p2").toUpperCase(),
19843
- title: String(r.title ?? ""),
19844
- assignee: String(r.assigned_to ?? ""),
19845
- assignedBy: String(r.assigned_by ?? ""),
19846
- status: String(r.status ?? "open"),
19847
- project: String(r.project_name ?? ""),
19848
- createdAt: String(r.created_at ?? ""),
19849
- result: String(r.result ?? "")
19850
- }))
19851
- );
19852
- setDbError(false);
19853
- } else {
19854
- setDbError(true);
20079
+ );
20080
+ setAllTasks(
20081
+ result.rows.map((r) => ({
20082
+ id: String(r.id ?? ""),
20083
+ priority: String(r.priority ?? "p2").toUpperCase(),
20084
+ title: String(r.title ?? ""),
20085
+ assignee: String(r.assigned_to ?? ""),
20086
+ assignedBy: String(r.assigned_by ?? ""),
20087
+ status: String(r.status ?? "open"),
20088
+ project: String(r.project_name ?? ""),
20089
+ createdAt: String(r.created_at ?? ""),
20090
+ result: String(r.result ?? "")
20091
+ }))
20092
+ );
20093
+ setDbError(null);
20094
+ } else {
20095
+ setDbError("Database client not initialized. Run exe-os setup.");
20096
+ }
20097
+ } catch (err) {
20098
+ setDbError(err instanceof Error ? err.message : "Unknown error");
20099
+ } finally {
20100
+ setLoading(false);
19855
20101
  }
19856
- } catch {
19857
- setDbError(true);
19858
- } finally {
19859
- setLoading(false);
19860
- }
20102
+ });
19861
20103
  }
19862
20104
  const selected = taskRows[selectedTask]?.task;
19863
20105
  if (showDetail && selected) {
@@ -19924,7 +20166,10 @@ function TasksView({ onBack }) {
19924
20166
  filterLabel
19925
20167
  ] }),
19926
20168
  /* @__PURE__ */ jsx10(Text, { children: " " }),
19927
- loading ? /* @__PURE__ */ jsx10(Text, { color: "#6B4C9A", children: "Loading tasks..." }) : dbError ? /* @__PURE__ */ jsx10(Text, { color: "#EF4444", children: "Database unavailable. Run exe-os setup to initialize." }) : filteredTasks.length === 0 ? /* @__PURE__ */ jsx10(Text, { color: "#6B4C9A", children: "No tasks match current filters." }) : displayRows.map((row, i) => {
20169
+ loading ? /* @__PURE__ */ jsx10(Text, { color: "#6B4C9A", children: "Loading tasks..." }) : dbError ? /* @__PURE__ */ jsxs8(Text, { color: "#C91B00", children: [
20170
+ "Failed to load tasks: ",
20171
+ dbError
20172
+ ] }) : allTasks.length === 0 ? /* @__PURE__ */ jsx10(Text, { color: "#3D3660", children: "No tasks. Create one with create_task." }) : filteredTasks.length === 0 ? /* @__PURE__ */ jsx10(Text, { color: "#3D3660", children: "No tasks match current filter." }) : displayRows.map((row, i) => {
19928
20173
  if (row.type === "header") {
19929
20174
  return /* @__PURE__ */ jsxs8(Box_default, { marginTop: i > 0 ? 1 : 0, children: [
19930
20175
  /* @__PURE__ */ jsx10(Text, { bold: true, color: "#F0EDE8", children: row.project }),
@@ -20611,6 +20856,31 @@ function GatewayView({ onBack }) {
20611
20856
  /* @__PURE__ */ jsx11(Text, { color: "#6B4C9A", children: "Loading gateway status..." })
20612
20857
  ] });
20613
20858
  }
20859
+ if (!gateway.running && gateway.gatewayUrl && connectionStatus === "disconnected") {
20860
+ return /* @__PURE__ */ jsxs9(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
20861
+ /* @__PURE__ */ jsx11(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx11(Text, { bold: true, children: "Gateway Monitor" }) }),
20862
+ /* @__PURE__ */ jsx11(Text, { children: " " }),
20863
+ /* @__PURE__ */ jsx11(Text, { color: "#C91B00", children: "Gateway connection failed." }),
20864
+ /* @__PURE__ */ jsx11(Text, { children: " " }),
20865
+ /* @__PURE__ */ jsxs9(Text, { color: "#6B4C9A", children: [
20866
+ "Gateway URL: ",
20867
+ /* @__PURE__ */ jsx11(Text, { color: "#F0EDE8", children: gateway.gatewayUrl })
20868
+ ] }),
20869
+ /* @__PURE__ */ jsxs9(Text, { color: "#6B4C9A", children: [
20870
+ "Process: ",
20871
+ /* @__PURE__ */ jsx11(Text, { color: "#C91B00", children: "not running" })
20872
+ ] }),
20873
+ /* @__PURE__ */ jsx11(Text, { children: " " }),
20874
+ /* @__PURE__ */ jsxs9(Text, { color: "#6B4C9A", children: [
20875
+ "Start the gateway: ",
20876
+ /* @__PURE__ */ jsx11(Text, { bold: true, children: "exe-os gateway" })
20877
+ ] }),
20878
+ /* @__PURE__ */ jsxs9(Text, { color: "#6B4C9A", children: [
20879
+ "Or check your config: ",
20880
+ /* @__PURE__ */ jsx11(Text, { bold: true, children: "~/.exe-os/gateway.json" })
20881
+ ] })
20882
+ ] });
20883
+ }
20614
20884
  if (!gateway.running && gateway.adapters.length === 0 && !gateway.gatewayUrl) {
20615
20885
  return /* @__PURE__ */ jsxs9(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
20616
20886
  /* @__PURE__ */ jsx11(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx11(Text, { bold: true, children: "Gateway Monitor" }) }),
@@ -20897,14 +21167,30 @@ var init_agent_status = __esm({
20897
21167
  // src/tui/views/Team.tsx
20898
21168
  import React22, { useState as useState12, useEffect as useEffect14 } from "react";
20899
21169
  import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
20900
- function TeamView({ onBack }) {
21170
+ function roleBadgeColor(role) {
21171
+ switch (role.toLowerCase()) {
21172
+ case "coo":
21173
+ return "#F5D76E";
21174
+ // gold
21175
+ case "cto":
21176
+ return "#3B82F6";
21177
+ // blue
21178
+ case "cmo":
21179
+ return "#6B4C9A";
21180
+ // purple
21181
+ default:
21182
+ return "#F0EDE8";
21183
+ }
21184
+ }
21185
+ function TeamView({ onBack, onViewSessions }) {
20901
21186
  const demo = useDemo();
20902
21187
  const [members, setMembers] = useState12([]);
20903
21188
  const [externals, setExternals] = useState12([]);
20904
21189
  const [loading, setLoading] = useState12(!demo);
20905
- const [dbError, setDbError] = useState12(false);
21190
+ const [dbError, setDbError] = useState12(null);
20906
21191
  const [selectedIdx, setSelectedIdx] = useState12(0);
20907
21192
  const [showDetail, setShowDetail] = useState12(false);
21193
+ const [showAddHint, setShowAddHint] = useState12(false);
20908
21194
  const orch = useOrchestrator(!demo);
20909
21195
  const orchEmployeeMap = new Map(orch.employees.map((e) => [e.name, e]));
20910
21196
  const allItems = [...members, ...externals.map((e) => ({ ...e, activity: "", memoryCount: 0 }))];
@@ -20919,7 +21205,7 @@ function TeamView({ onBack }) {
20919
21205
  const timer = setInterval(loadTeam, 5e3);
20920
21206
  return () => clearInterval(timer);
20921
21207
  }, []);
20922
- use_input_default((_input, key) => {
21208
+ use_input_default((input, key) => {
20923
21209
  if (showDetail) {
20924
21210
  if (key.escape) setShowDetail(false);
20925
21211
  return;
@@ -20928,91 +21214,103 @@ function TeamView({ onBack }) {
20928
21214
  onBack?.();
20929
21215
  return;
20930
21216
  }
21217
+ if (input === "a") {
21218
+ setShowAddHint(true);
21219
+ setTimeout(() => setShowAddHint(false), 3e3);
21220
+ return;
21221
+ }
21222
+ if (input === "s" && selected) {
21223
+ onViewSessions?.(selected.name);
21224
+ return;
21225
+ }
20931
21226
  if (key.upArrow && selectedIdx > 0) setSelectedIdx(selectedIdx - 1);
20932
21227
  if (key.downArrow && selectedIdx < allItems.length - 1) setSelectedIdx(selectedIdx + 1);
20933
21228
  if (key.return && allItems.length > 0) setShowDetail(true);
20934
21229
  });
20935
21230
  async function loadTeam() {
20936
- try {
20937
- const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
20938
- const roster = await loadEmployees2();
20939
- let memoryCounts = /* @__PURE__ */ new Map();
20940
- let projectsByEmployee = /* @__PURE__ */ new Map();
20941
- let currentTaskByEmployee = /* @__PURE__ */ new Map();
21231
+ const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
21232
+ return withTrace2("tui.team.loadTeam", async () => {
20942
21233
  try {
20943
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
20944
- const client = getClient2();
20945
- if (client) {
20946
- const memResult = await client.execute("SELECT agent_id, COUNT(*) as cnt FROM memories GROUP BY agent_id");
20947
- for (const row of memResult.rows) {
20948
- memoryCounts.set(String(row.agent_id), Number(row.cnt));
20949
- }
20950
- for (const emp of roster) {
20951
- const projResult = await client.execute({
20952
- sql: `SELECT DISTINCT project_name,
21234
+ const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
21235
+ const roster = await loadEmployees2();
21236
+ let memoryCounts = /* @__PURE__ */ new Map();
21237
+ let projectsByEmployee = /* @__PURE__ */ new Map();
21238
+ let currentTaskByEmployee = /* @__PURE__ */ new Map();
21239
+ try {
21240
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
21241
+ const client = getClient2();
21242
+ if (client) {
21243
+ const memResult = await client.execute("SELECT agent_id, COUNT(*) as cnt FROM memories GROUP BY agent_id");
21244
+ for (const row of memResult.rows) {
21245
+ memoryCounts.set(String(row.agent_id), Number(row.cnt));
21246
+ }
21247
+ for (const emp of roster) {
21248
+ const projResult = await client.execute({
21249
+ sql: `SELECT DISTINCT project_name,
20953
21250
  MAX(CASE WHEN status = 'in_progress' THEN 1 WHEN status = 'open' THEN 2 ELSE 3 END) as urgency
20954
21251
  FROM tasks WHERE assigned_to = ? AND status IN ('open','in_progress','done')
20955
21252
  GROUP BY project_name ORDER BY urgency ASC LIMIT 5`,
20956
- args: [emp.name]
20957
- });
20958
- const projects = projResult.rows.filter((r) => !DEPRECATED_PROJECTS.has(String(r.project_name))).map((r) => {
20959
- const urgency = Number(r.urgency);
20960
- let pStatus = "idle";
20961
- if (urgency === 1) pStatus = "active";
20962
- else if (urgency === 2) pStatus = "has_tasks";
20963
- return { name: String(r.project_name), status: pStatus };
20964
- });
20965
- if (projects.length > 0) projectsByEmployee.set(emp.name, projects);
20966
- }
20967
- for (const emp of roster) {
20968
- const taskResult = await client.execute({
20969
- sql: "SELECT title FROM tasks WHERE assigned_to = ? AND status = 'in_progress' ORDER BY updated_at DESC LIMIT 1",
20970
- args: [emp.name]
20971
- });
20972
- if (taskResult.rows.length > 0) {
20973
- currentTaskByEmployee.set(emp.name, String(taskResult.rows[0].title));
21253
+ args: [emp.name]
21254
+ });
21255
+ const projects = projResult.rows.filter((r) => !DEPRECATED_PROJECTS.has(String(r.project_name))).map((r) => {
21256
+ const urgency = Number(r.urgency);
21257
+ let pStatus = "idle";
21258
+ if (urgency === 1) pStatus = "active";
21259
+ else if (urgency === 2) pStatus = "has_tasks";
21260
+ return { name: String(r.project_name), status: pStatus };
21261
+ });
21262
+ if (projects.length > 0) projectsByEmployee.set(emp.name, projects);
21263
+ }
21264
+ for (const emp of roster) {
21265
+ const taskResult = await client.execute({
21266
+ sql: "SELECT title FROM tasks WHERE assigned_to = ? AND status = 'in_progress' ORDER BY updated_at DESC LIMIT 1",
21267
+ args: [emp.name]
21268
+ });
21269
+ if (taskResult.rows.length > 0) {
21270
+ currentTaskByEmployee.set(emp.name, String(taskResult.rows[0].title));
21271
+ }
20974
21272
  }
20975
21273
  }
21274
+ } catch {
20976
21275
  }
20977
- } catch {
20978
- }
20979
- const teamData = roster.map((emp) => {
20980
- const agentSt = getAgentStatus(emp.name);
20981
- return {
20982
- name: emp.name,
20983
- role: emp.role,
20984
- status: agentSt.label,
20985
- activity: agentSt.label === "active" ? "Processing..." : "",
20986
- memoryCount: memoryCounts.get(emp.name) ?? 0,
20987
- projects: projectsByEmployee.get(emp.name),
20988
- currentTask: currentTaskByEmployee.get(emp.name),
20989
- sessionName: agentSt.session
20990
- };
20991
- });
20992
- setMembers(teamData);
20993
- setDbError(false);
20994
- try {
20995
- const { existsSync: existsSync22, readFileSync: readFileSync18 } = await import("fs");
20996
- const { join } = await import("path");
20997
- const home = process.env.HOME ?? "";
20998
- const gatewayConfig = join(home, ".exe-os", "gateway.json");
20999
- if (existsSync22(gatewayConfig)) {
21000
- const raw = JSON.parse(readFileSync18(gatewayConfig, "utf8"));
21001
- if (raw.agents && raw.agents.length > 0) {
21002
- setExternals(raw.agents.map((a) => ({
21003
- name: a.name,
21004
- role: a.role ?? "external agent",
21005
- status: "offline"
21006
- })));
21276
+ const teamData = roster.map((emp) => {
21277
+ const agentSt = getAgentStatus(emp.name);
21278
+ return {
21279
+ name: emp.name,
21280
+ role: emp.role,
21281
+ status: agentSt.label,
21282
+ activity: agentSt.label === "active" ? "Processing..." : "",
21283
+ memoryCount: memoryCounts.get(emp.name) ?? 0,
21284
+ projects: projectsByEmployee.get(emp.name),
21285
+ currentTask: currentTaskByEmployee.get(emp.name),
21286
+ sessionName: agentSt.session
21287
+ };
21288
+ });
21289
+ setMembers(teamData);
21290
+ setDbError(null);
21291
+ try {
21292
+ const { existsSync: existsSync22, readFileSync: readFileSync18 } = await import("fs");
21293
+ const { join } = await import("path");
21294
+ const home = process.env.HOME ?? "";
21295
+ const gatewayConfig = join(home, ".exe-os", "gateway.json");
21296
+ if (existsSync22(gatewayConfig)) {
21297
+ const raw = JSON.parse(readFileSync18(gatewayConfig, "utf8"));
21298
+ if (raw.agents && raw.agents.length > 0) {
21299
+ setExternals(raw.agents.map((a) => ({
21300
+ name: a.name,
21301
+ role: a.role ?? "external agent",
21302
+ status: "offline"
21303
+ })));
21304
+ }
21007
21305
  }
21306
+ } catch {
21008
21307
  }
21009
- } catch {
21308
+ } catch (err) {
21309
+ setDbError(err instanceof Error ? err.message : "Unknown error");
21310
+ } finally {
21311
+ setLoading(false);
21010
21312
  }
21011
- } catch {
21012
- setDbError(true);
21013
- } finally {
21014
- setLoading(false);
21015
- }
21313
+ });
21016
21314
  }
21017
21315
  const totalCount = members.length + externals.length;
21018
21316
  const selected = allItems[selectedIdx];
@@ -21029,7 +21327,7 @@ function TeamView({ onBack }) {
21029
21327
  /* @__PURE__ */ jsx12(Text, { children: " " }),
21030
21328
  /* @__PURE__ */ jsxs10(Text, { children: [
21031
21329
  "Role: ",
21032
- /* @__PURE__ */ jsx12(Text, { bold: true, children: selected.role })
21330
+ /* @__PURE__ */ jsx12(Text, { bold: true, color: roleBadgeColor(selected.role), children: selected.role })
21033
21331
  ] }),
21034
21332
  /* @__PURE__ */ jsxs10(Text, { children: [
21035
21333
  "Status: ",
@@ -21058,8 +21356,9 @@ function TeamView({ onBack }) {
21058
21356
  /* @__PURE__ */ jsx12(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx12(Text, { bold: true, children: "Team Roster" }) }),
21059
21357
  /* @__PURE__ */ jsxs10(Text, { color: "#6B4C9A", children: [
21060
21358
  totalCount,
21061
- " agents | up/down navigate | Enter for details"
21359
+ " agents | \u2191\u2193 navigate | Enter details | a add | s sessions"
21062
21360
  ] }),
21361
+ showAddHint && /* @__PURE__ */ jsx12(Text, { color: "#F5D76E", children: "Run /exe-new-employee <name> from CLI to add an employee." }),
21063
21362
  !demo && orch.pendingReviews > 0 && /* @__PURE__ */ jsxs10(Text, { color: "#6B4C9A", children: [
21064
21363
  orch.pendingReviews,
21065
21364
  " review(s) pending exe attention"
@@ -21067,7 +21366,10 @@ function TeamView({ onBack }) {
21067
21366
  /* @__PURE__ */ jsx12(Text, { children: " " }),
21068
21367
  /* @__PURE__ */ jsx12(Text, { bold: true, children: "INTERNAL" }),
21069
21368
  /* @__PURE__ */ jsx12(Text, { children: " " }),
21070
- loading ? /* @__PURE__ */ jsx12(Text, { color: "#6B4C9A", children: " Loading team..." }) : dbError ? /* @__PURE__ */ jsx12(Text, { color: "#EF4444", children: " Database unavailable. Run exe-os setup to initialize." }) : members.length === 0 ? /* @__PURE__ */ jsx12(Text, { color: "#6B4C9A", children: " No employees found. Run /exe-new-employee to create one." }) : /* @__PURE__ */ jsx12(Box_default, { flexDirection: "row", flexWrap: "wrap", gap: 1, children: members.map((m, i) => {
21369
+ loading ? /* @__PURE__ */ jsx12(Text, { color: "#6B4C9A", children: " Loading employee roster..." }) : dbError ? /* @__PURE__ */ jsxs10(Text, { color: "#C91B00", children: [
21370
+ " Failed to load roster: ",
21371
+ dbError
21372
+ ] }) : members.length === 0 ? /* @__PURE__ */ jsx12(Text, { color: "#3D3660", children: " No employees configured. Run /exe-new-employee." }) : /* @__PURE__ */ jsx12(Box_default, { flexDirection: "row", flexWrap: "wrap", gap: 1, children: members.map((m, i) => {
21071
21373
  const isSelected = i === selectedIdx;
21072
21374
  const orchEmp = orchEmployeeMap.get(m.name);
21073
21375
  return /* @__PURE__ */ jsxs10(
@@ -21083,7 +21385,7 @@ function TeamView({ onBack }) {
21083
21385
  /* @__PURE__ */ jsxs10(Box_default, { gap: 1, children: [
21084
21386
  /* @__PURE__ */ jsx12(StatusDot, { status: m.status, showLabel: false }),
21085
21387
  /* @__PURE__ */ jsx12(Text, { bold: true, color: isSelected ? "#F5D76E" : "#F0EDE8", children: m.name }),
21086
- /* @__PURE__ */ jsxs10(Text, { color: isSelected ? "#F5D76E" : "#6B4C9A", children: [
21388
+ /* @__PURE__ */ jsxs10(Text, { color: isSelected ? "#F5D76E" : roleBadgeColor(m.role), children: [
21087
21389
  "(",
21088
21390
  m.role,
21089
21391
  ")"
@@ -21309,6 +21611,26 @@ var init_wiki_client = __esm({
21309
21611
  import React23, { useState as useState13, useEffect as useEffect15 } from "react";
21310
21612
  import TextInput2 from "ink-text-input";
21311
21613
  import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
21614
+ function agentColor(agentId) {
21615
+ switch (agentId.toLowerCase()) {
21616
+ case "exe":
21617
+ return "#F5D76E";
21618
+ case "yoshi":
21619
+ return "#3B82F6";
21620
+ case "mari":
21621
+ return "#6B4C9A";
21622
+ default:
21623
+ return "#F0EDE8";
21624
+ }
21625
+ }
21626
+ function formatTimeAgo(iso) {
21627
+ const diff2 = Date.now() - new Date(iso).getTime();
21628
+ const hours = Math.floor(diff2 / 36e5);
21629
+ if (hours < 1) return "just now";
21630
+ if (hours < 24) return `${hours}h ago`;
21631
+ const days = Math.floor(hours / 24);
21632
+ return `${days}d ago`;
21633
+ }
21312
21634
  function WikiView({ onBack }) {
21313
21635
  const demo = useDemo();
21314
21636
  const [activePanel, setActivePanel] = useState13("Workspaces");
@@ -21319,6 +21641,12 @@ function WikiView({ onBack }) {
21319
21641
  const [chatHistory, setChatHistory] = useState13([]);
21320
21642
  const [chatInput, setChatInput] = useState13("");
21321
21643
  const [chatFocused, setChatFocused] = useState13(false);
21644
+ const [searchMode, setSearchMode] = useState13(false);
21645
+ const [searchQuery, setSearchQuery] = useState13("");
21646
+ const [searchResults, setSearchResults] = useState13([]);
21647
+ const [searchLoading, setSearchLoading] = useState13(false);
21648
+ const [selectedResultIdx, setSelectedResultIdx] = useState13(0);
21649
+ const [expandedResultIdx, setExpandedResultIdx] = useState13(null);
21322
21650
  const [loading, setLoading] = useState13(true);
21323
21651
  const [connected, setConnected] = useState13(false);
21324
21652
  const [wikiUrl, setWikiUrl] = useState13("");
@@ -21412,6 +21740,55 @@ function WikiView({ onBack }) {
21412
21740
  setSending(false);
21413
21741
  }
21414
21742
  }
21743
+ async function searchMemories2(query) {
21744
+ if (!query.trim()) {
21745
+ setSearchResults([]);
21746
+ return;
21747
+ }
21748
+ if (demo) {
21749
+ const q = query.toLowerCase();
21750
+ setSearchResults(
21751
+ DEMO_SEARCH_RESULTS.filter((r) => r.rawText.toLowerCase().includes(q))
21752
+ );
21753
+ return;
21754
+ }
21755
+ setSearchLoading(true);
21756
+ try {
21757
+ const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
21758
+ const client = getClient2();
21759
+ const result = await client.execute({
21760
+ sql: `SELECT id, agent_id, raw_text, timestamp, project_name
21761
+ FROM memories
21762
+ WHERE raw_text LIKE ? AND COALESCE(status, 'active') = 'active'
21763
+ ORDER BY timestamp DESC LIMIT 20`,
21764
+ args: [`%${query}%`]
21765
+ });
21766
+ setSearchResults(
21767
+ result.rows.map((r) => ({
21768
+ id: String(r.id),
21769
+ agentId: String(r.agent_id),
21770
+ rawText: String(r.raw_text),
21771
+ timestamp: String(r.timestamp),
21772
+ projectName: String(r.project_name ?? "")
21773
+ }))
21774
+ );
21775
+ } catch {
21776
+ setSearchResults([]);
21777
+ } finally {
21778
+ setSearchLoading(false);
21779
+ }
21780
+ }
21781
+ useEffect15(() => {
21782
+ if (!searchMode) return;
21783
+ const timer = setTimeout(() => {
21784
+ searchMemories2(searchQuery);
21785
+ }, 300);
21786
+ return () => clearTimeout(timer);
21787
+ }, [searchQuery, searchMode]);
21788
+ useEffect15(() => {
21789
+ setSelectedResultIdx(0);
21790
+ setExpandedResultIdx(null);
21791
+ }, [searchResults]);
21415
21792
  async function selectWorkspace(idx) {
21416
21793
  setSelectedWorkspaceIdx(idx);
21417
21794
  const ws = workspaces[idx];
@@ -21432,6 +21809,32 @@ function WikiView({ onBack }) {
21432
21809
  }
21433
21810
  }
21434
21811
  use_input_default((input, key) => {
21812
+ if (searchMode && !chatFocused) {
21813
+ if (key.escape) {
21814
+ setSearchMode(false);
21815
+ setSearchQuery("");
21816
+ setSearchResults([]);
21817
+ setExpandedResultIdx(null);
21818
+ return;
21819
+ }
21820
+ if (key.upArrow && selectedResultIdx > 0) {
21821
+ setSelectedResultIdx(selectedResultIdx - 1);
21822
+ setExpandedResultIdx(null);
21823
+ return;
21824
+ }
21825
+ if (key.downArrow && selectedResultIdx < searchResults.length - 1) {
21826
+ setSelectedResultIdx(selectedResultIdx + 1);
21827
+ setExpandedResultIdx(null);
21828
+ return;
21829
+ }
21830
+ if (key.return && searchResults.length > 0) {
21831
+ setExpandedResultIdx(
21832
+ expandedResultIdx === selectedResultIdx ? null : selectedResultIdx
21833
+ );
21834
+ return;
21835
+ }
21836
+ return;
21837
+ }
21435
21838
  if (chatFocused) {
21436
21839
  if (key.escape) {
21437
21840
  setChatFocused(false);
@@ -21443,6 +21846,13 @@ function WikiView({ onBack }) {
21443
21846
  }
21444
21847
  return;
21445
21848
  }
21849
+ if (input === "/" && activePanel !== "Chat") {
21850
+ setSearchMode(true);
21851
+ setSearchQuery("");
21852
+ setSearchResults(demo ? DEMO_SEARCH_RESULTS : []);
21853
+ setExpandedResultIdx(null);
21854
+ return;
21855
+ }
21446
21856
  if (key.leftArrow) {
21447
21857
  const panelIdx = PANELS.indexOf(activePanel);
21448
21858
  if (panelIdx === 0) {
@@ -21520,9 +21930,53 @@ function WikiView({ onBack }) {
21520
21930
  /* @__PURE__ */ jsx13(Text, { bold: true, children: selectedWs.name })
21521
21931
  ] }) : null
21522
21932
  ] }),
21523
- /* @__PURE__ */ jsx13(Text, { color: "#6B4C9A", children: "\u2190\u2192 switch panels | \u2191\u2193 navigate | / chat | Enter select" }),
21933
+ /* @__PURE__ */ jsx13(Text, { color: "#6B4C9A", children: "\u2190\u2192 switch panels | \u2191\u2193 navigate | / search memories | Enter select" }),
21524
21934
  /* @__PURE__ */ jsx13(Text, { children: " " }),
21525
- /* @__PURE__ */ jsxs11(Box_default, { flexGrow: 1, gap: 1, children: [
21935
+ searchMode ? /* @__PURE__ */ jsxs11(Box_default, { flexDirection: "column", flexGrow: 1, children: [
21936
+ /* @__PURE__ */ jsxs11(Box_default, { paddingX: 1, children: [
21937
+ /* @__PURE__ */ jsx13(Text, { color: "#F5D76E", children: "Search: " }),
21938
+ /* @__PURE__ */ jsx13(
21939
+ TextInput2,
21940
+ {
21941
+ value: searchQuery,
21942
+ onChange: setSearchQuery,
21943
+ placeholder: "search memories...",
21944
+ focus: true
21945
+ }
21946
+ ),
21947
+ /* @__PURE__ */ jsx13(Text, { color: "#6B4C9A", children: " (esc to close)" })
21948
+ ] }),
21949
+ /* @__PURE__ */ jsx13(Text, { children: " " }),
21950
+ searchLoading ? /* @__PURE__ */ jsx13(Text, { color: "#6B4C9A", children: " Searching..." }) : searchResults.length === 0 && searchQuery.trim() ? /* @__PURE__ */ jsx13(Text, { color: "#6B4C9A", children: " No results found." }) : searchResults.length === 0 ? /* @__PURE__ */ jsx13(Text, { color: "#6B4C9A", children: " Type to search exe-os memories." }) : searchResults.map((result, i) => {
21951
+ const isSelected = i === selectedResultIdx;
21952
+ const isExpanded = i === expandedResultIdx;
21953
+ const snippet = result.rawText.slice(0, 80);
21954
+ return /* @__PURE__ */ jsxs11(Box_default, { flexDirection: "column", children: [
21955
+ /* @__PURE__ */ jsxs11(
21956
+ Text,
21957
+ {
21958
+ backgroundColor: isSelected ? "#6B4C9A" : void 0,
21959
+ color: isSelected ? "#F5D76E" : void 0,
21960
+ children: [
21961
+ " ",
21962
+ /* @__PURE__ */ jsx13(Text, { color: isSelected ? "#F5D76E" : agentColor(result.agentId), bold: true, children: result.agentId.padEnd(8) }),
21963
+ /* @__PURE__ */ jsxs11(Text, { color: isSelected ? "#F5D76E" : "#F0EDE8", children: [
21964
+ snippet,
21965
+ result.rawText.length > 80 ? "..." : ""
21966
+ ] }),
21967
+ " ",
21968
+ /* @__PURE__ */ jsx13(Text, { color: isSelected ? "#F5D76E" : "#3D3660", dimColor: !isSelected, children: formatTimeAgo(result.timestamp) })
21969
+ ]
21970
+ }
21971
+ ),
21972
+ isExpanded ? /* @__PURE__ */ jsx13(Box_default, { paddingX: 4, marginBottom: 1, children: /* @__PURE__ */ jsx13(Text, { color: "#F0EDE8", wrap: "wrap", children: result.rawText }) }) : null
21973
+ ] }, result.id);
21974
+ }),
21975
+ searchResults.length > 0 ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
21976
+ /* @__PURE__ */ jsx13(Text, { children: " " }),
21977
+ /* @__PURE__ */ jsx13(Text, { color: "#3D3660", children: " \u2191\u2193 navigate | Enter expand | Esc close" })
21978
+ ] }) : null
21979
+ ] }) : /* @__PURE__ */ jsxs11(Box_default, { flexGrow: 1, gap: 1, children: [
21526
21980
  /* @__PURE__ */ jsxs11(Box_default, { flexDirection: "column", width: "25%", children: [
21527
21981
  /* @__PURE__ */ jsxs11(Text, { bold: true, backgroundColor: panelHeaderBg("Workspaces"), color: panelHeaderColor("Workspaces"), children: [
21528
21982
  activePanel === "Workspaces" ? "\u25B8 " : " ",
@@ -21604,7 +22058,7 @@ function WikiView({ onBack }) {
21604
22058
  ] })
21605
22059
  ] });
21606
22060
  }
21607
- var PANELS, MAX_VISIBLE_MESSAGES;
22061
+ var PANELS, MAX_VISIBLE_MESSAGES, DEMO_SEARCH_RESULTS;
21608
22062
  var init_Wiki = __esm({
21609
22063
  async "src/tui/views/Wiki.tsx"() {
21610
22064
  "use strict";
@@ -21613,53 +22067,48 @@ var init_Wiki = __esm({
21613
22067
  init_demo_data();
21614
22068
  PANELS = ["Workspaces", "Documents", "Chat"];
21615
22069
  MAX_VISIBLE_MESSAGES = 12;
22070
+ DEMO_SEARCH_RESULTS = [
22071
+ { id: "1", agentId: "yoshi", rawText: "Reviewed PR #42 \u2014 approved with minor comment on error handling pattern", timestamp: new Date(Date.now() - 2 * 36e5).toISOString(), projectName: "exe-os" },
22072
+ { id: "2", agentId: "tom", rawText: "Implemented session routing with deterministic naming: agent-exeN convention", timestamp: new Date(Date.now() - 5 * 36e5).toISOString(), projectName: "exe-os" },
22073
+ { id: "3", agentId: "exe", rawText: "Status brief: 3 tasks completed, 1 blocked on wiki integration", timestamp: new Date(Date.now() - 8 * 36e5).toISOString(), projectName: "exe-os" },
22074
+ { id: "4", agentId: "mari", rawText: "Created brand guidelines document \u2014 Exe Foundry Bold design system", timestamp: new Date(Date.now() - 24 * 36e5).toISOString(), projectName: "exe-os" },
22075
+ { id: "5", agentId: "tom", rawText: "Fixed CommandCenter project filtering \u2014 DB-first, no random directories", timestamp: new Date(Date.now() - 48 * 36e5).toISOString(), projectName: "exe-os" }
22076
+ ];
21616
22077
  }
21617
22078
  });
21618
22079
 
21619
22080
  // src/tui/views/Settings.tsx
21620
- import React24, { useState as useState14, useEffect as useEffect16 } from "react";
22081
+ import React24, { useState as useState14, useEffect as useEffect16, useCallback as useCallback7 } from "react";
21621
22082
  import { Fragment as Fragment5, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
22083
+ function maskSecret(value) {
22084
+ if (value.length <= 10) return "****";
22085
+ return `${value.slice(0, 6)}...${value.slice(-4)}`;
22086
+ }
21622
22087
  function SettingsView({ onBack }) {
21623
22088
  const [providers, setProviders] = useState14([]);
21624
- const [memory, setMemory] = useState14({
21625
- encryption: "checking...",
21626
- embeddingModel: "checking...",
21627
- cloudSync: "checking...",
21628
- searchMode: "checking..."
21629
- });
21630
- const [runtime, setRuntime] = useState14({
21631
- consolidation: true,
21632
- consolidationModel: "...",
21633
- skillLearning: true,
21634
- skillThreshold: 3,
21635
- skillModel: "...",
21636
- failoverChain: ["anthropic", "opencode", "gemini", "openai"]
21637
- });
21638
- const [employeeModels, setEmployeeModels] = useState14([]);
22089
+ const [cloud, setCloud] = useState14({ configured: false, endpoint: "", apiKey: "" });
22090
+ const [license, setLicense] = useState14({ valid: false, plan: "checking...", expiresAt: null, deviceLimit: 0, employeeLimit: 0, memoryLimit: 0 });
22091
+ const [system, setSystem] = useState14({ daemon: "unknown", version: "...", dbPath: "...", embeddingModel: "...", encryption: "checking..." });
22092
+ const [gateway, setGateway] = useState14({ adapters: [] });
21639
22093
  const [selectedSection, setSelectedSection] = useState14(0);
21640
22094
  const [loading, setLoading] = useState14(true);
21641
- const [claudeCode, setClaudeCode] = useState14({ installed: false, hooksWired: false });
21642
- const [license, setLicense] = useState14({ valid: false, detail: "checking..." });
21643
- const [cloudSync, setCloudSync] = useState14({ configured: false, detail: "checking..." });
21644
- useEffect16(() => {
21645
- loadSettings().finally(() => setLoading(false));
21646
- }, []);
21647
- use_input_default((_input, key) => {
21648
- if (key.leftArrow) {
21649
- onBack?.();
21650
- return;
21651
- }
21652
- if (key.upArrow && selectedSection > 0) setSelectedSection(selectedSection - 1);
21653
- if (key.downArrow && selectedSection < SECTION_NAMES.length - 1) setSelectedSection(selectedSection + 1);
21654
- });
21655
- async function loadSettings() {
21656
- const providerList = [
21657
- { name: "Anthropic", configured: !!process.env.ANTHROPIC_API_KEY, detail: process.env.ANTHROPIC_API_KEY ? "ANTHROPIC_API_KEY set" : "not configured" },
21658
- { name: "OpenCode", configured: !!process.env.OPENCODE_API_KEY, detail: process.env.OPENCODE_API_KEY ? "OPENCODE_API_KEY set" : "not configured" },
21659
- { name: "Gemini", configured: !!process.env.GEMINI_API_KEY, detail: process.env.GEMINI_API_KEY ? "GEMINI_API_KEY set" : "not configured" },
21660
- { name: "OpenAI", configured: !!process.env.OPENAI_API_KEY, detail: process.env.OPENAI_API_KEY ? "OPENAI_API_KEY set" : "not configured" },
21661
- { name: "Chutes", configured: !!process.env.CHUTES_API_KEY, detail: process.env.CHUTES_API_KEY ? "CHUTES_API_KEY set" : "not configured" }
22095
+ const loadSettings = useCallback7(async () => {
22096
+ setLoading(true);
22097
+ const envKeys = [
22098
+ ["Anthropic", "ANTHROPIC_API_KEY"],
22099
+ ["OpenCode", "OPENCODE_API_KEY"],
22100
+ ["Gemini", "GEMINI_API_KEY"],
22101
+ ["OpenAI", "OPENAI_API_KEY"],
22102
+ ["Chutes", "CHUTES_API_KEY"]
21662
22103
  ];
22104
+ const providerList = envKeys.map(([name, envVar]) => {
22105
+ const val = process.env[envVar];
22106
+ return {
22107
+ name,
22108
+ configured: !!val,
22109
+ detail: val ? maskSecret(val) : "not configured"
22110
+ };
22111
+ });
21663
22112
  try {
21664
22113
  const { execSync: execSync13 } = await import("child_process");
21665
22114
  execSync13("curl -s --max-time 1 http://localhost:11434/api/tags", { timeout: 2e3 });
@@ -21671,112 +22120,101 @@ function SettingsView({ onBack }) {
21671
22120
  try {
21672
22121
  const { existsSync: existsSync22 } = await import("fs");
21673
22122
  const { join } = await import("path");
21674
- const home = process.env.HOME ?? "";
21675
- const hasKey = existsSync22(join(home, ".exe-os", "master.key"));
21676
22123
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
21677
22124
  const cfg = await loadConfig2();
21678
- setMemory({
21679
- encryption: hasKey ? "SQLCipher AES-256" : "not configured",
21680
- embeddingModel: cfg.modelFile ?? "unknown",
21681
- cloudSync: cfg.cloud ? "enabled" : "disabled",
21682
- searchMode: cfg.searchMode ?? "hybrid"
21683
- });
21684
- const rawCfg = cfg;
21685
- const chain = Array.isArray(rawCfg.failoverChain) ? rawCfg.failoverChain : ["anthropic", "opencode", "gemini", "openai"];
21686
- setRuntime({
21687
- consolidation: cfg.consolidationEnabled,
21688
- consolidationModel: cfg.consolidationModel,
21689
- skillLearning: cfg.skillLearning,
21690
- skillThreshold: cfg.skillThreshold,
21691
- skillModel: cfg.skillModel,
21692
- failoverChain: chain
21693
- });
21694
- } catch {
21695
- }
21696
- try {
21697
- const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
21698
- const roster = await loadEmployees2();
21699
- const { existsSync: existsSync22, readFileSync: readFileSync18 } = await import("fs");
21700
- const { join } = await import("path");
21701
22125
  const home = process.env.HOME ?? "";
21702
- const configPath = join(home, ".exe-os", "config.json");
21703
- let empConfig = {};
21704
- if (existsSync22(configPath)) {
21705
- try {
21706
- const raw = JSON.parse(readFileSync18(configPath, "utf8"));
21707
- if (raw.employees && typeof raw.employees === "object") {
21708
- empConfig = raw.employees;
21709
- }
21710
- } catch {
21711
- }
22126
+ const hasKey = existsSync22(join(home, ".exe-os", "master.key"));
22127
+ if (cfg.cloud) {
22128
+ setCloud({
22129
+ configured: true,
22130
+ endpoint: cfg.cloud.endpoint,
22131
+ apiKey: maskSecret(cfg.cloud.apiKey)
22132
+ });
22133
+ } else {
22134
+ setCloud({ configured: false, endpoint: "", apiKey: "" });
21712
22135
  }
21713
- setEmployeeModels(roster.filter((e) => e.name !== "exe").map((e) => ({
21714
- name: e.name,
21715
- model: empConfig[e.name]?.model ?? "claude-sonnet-4-6",
21716
- provider: empConfig[e.name]?.provider ?? "anthropic"
21717
- })));
21718
- } catch {
21719
- }
21720
- try {
21721
- const { existsSync: existsSync22, readFileSync: readFileSync18 } = await import("fs");
21722
- const { join } = await import("path");
21723
- const home = process.env.HOME ?? "";
21724
- const ccSettingsPath = join(home, ".claude", "settings.json");
21725
- const installed = existsSync22(ccSettingsPath);
21726
- let hooksWired = false;
21727
- if (installed) {
21728
- try {
21729
- const settings = JSON.parse(readFileSync18(ccSettingsPath, "utf8"));
21730
- const hooks = settings.hooks;
21731
- if (hooks) {
21732
- hooksWired = Object.values(hooks).flat().some((h) => h.command?.includes("exe-os") || h.command?.includes("exe-mem"));
21733
- }
21734
- } catch {
21735
- }
22136
+ const pidPath = join(home, ".exe-os", "exed.pid");
22137
+ let daemon = "unknown";
22138
+ try {
22139
+ daemon = existsSync22(pidPath) ? "running" : "stopped";
22140
+ } catch {
21736
22141
  }
21737
- setClaudeCode({ installed, hooksWired });
21738
- } catch {
21739
- }
21740
- try {
21741
- const { existsSync: existsSync22, readFileSync: readFileSync18 } = await import("fs");
21742
- const { join } = await import("path");
21743
- const home = process.env.HOME ?? "";
21744
- const licensePath = join(home, ".exe-os", "license.json");
21745
- if (existsSync22(licensePath)) {
22142
+ let version = "unknown";
22143
+ try {
22144
+ const { readFileSync: readFileSync18 } = await import("fs");
22145
+ const { createRequire } = await import("module");
22146
+ const require2 = createRequire(import.meta.url);
22147
+ const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
22148
+ const pkg = JSON.parse(readFileSync18(pkgPath, "utf8"));
22149
+ version = pkg.version;
22150
+ } catch {
21746
22151
  try {
21747
- const lic = JSON.parse(readFileSync18(licensePath, "utf8"));
21748
- setLicense({ valid: lic.valid !== false, detail: lic.plan ?? "licensed" });
22152
+ const { readFileSync: readFileSync18 } = await import("fs");
22153
+ const { join: joinPath } = await import("path");
22154
+ const pkg = JSON.parse(readFileSync18(joinPath(process.cwd(), "package.json"), "utf8"));
22155
+ version = pkg.version;
21749
22156
  } catch {
21750
- setLicense({ valid: false, detail: "invalid license file" });
21751
22157
  }
21752
- } else {
21753
- setLicense({ valid: false, detail: "no license" });
21754
22158
  }
22159
+ setSystem({
22160
+ daemon,
22161
+ version,
22162
+ dbPath: cfg.dbPath,
22163
+ embeddingModel: cfg.modelFile ?? "unknown",
22164
+ encryption: hasKey ? "SQLCipher AES-256" : "not configured"
22165
+ });
21755
22166
  } catch {
21756
22167
  }
21757
22168
  try {
21758
- const { existsSync: existsSync22 } = await import("fs");
21759
- const { join } = await import("path");
21760
- const home = process.env.HOME ?? "";
21761
- const cloudPath = join(home, ".exe-os", "cloud.json");
21762
- if (existsSync22(cloudPath)) {
21763
- setCloudSync({ configured: true, detail: "configured" });
21764
- } else {
21765
- setCloudSync({ configured: false, detail: "not configured" });
21766
- }
22169
+ const { checkLicense: checkLicense2 } = await Promise.resolve().then(() => (init_license(), license_exports));
22170
+ const lic = await checkLicense2();
22171
+ setLicense({
22172
+ valid: lic.valid,
22173
+ plan: lic.plan.toUpperCase(),
22174
+ expiresAt: lic.expiresAt,
22175
+ deviceLimit: lic.deviceLimit,
22176
+ employeeLimit: lic.employeeLimit,
22177
+ memoryLimit: lic.memoryLimit
22178
+ });
21767
22179
  } catch {
22180
+ setLicense({ valid: false, plan: "FREE", expiresAt: null, deviceLimit: 1, employeeLimit: 1, memoryLimit: 5e3 });
21768
22181
  }
21769
- }
21770
- const statusColor = (ok) => ok ? "#22C55E" : "#6B4C9A";
21771
- const sectionColor = (idx) => selectedSection === idx ? "#F5D76E" : void 0;
21772
- const sectionBg = (idx) => selectedSection === idx ? "#6B4C9A" : void 0;
22182
+ const gatewayAdapters = [
22183
+ { name: "WhatsApp", configured: !!process.env.WHATSAPP_API_TOKEN || !!process.env.WHATSAPP_PHONE_NUMBER_ID },
22184
+ { name: "Telegram", configured: !!process.env.TELEGRAM_BOT_TOKEN },
22185
+ { name: "Discord", configured: !!process.env.DISCORD_BOT_TOKEN },
22186
+ { name: "Slack", configured: !!process.env.SLACK_BOT_TOKEN }
22187
+ ];
22188
+ setGateway({ adapters: gatewayAdapters });
22189
+ setLoading(false);
22190
+ }, []);
22191
+ useEffect16(() => {
22192
+ loadSettings();
22193
+ }, [loadSettings]);
22194
+ use_input_default((input, key) => {
22195
+ if (key.leftArrow) {
22196
+ onBack?.();
22197
+ return;
22198
+ }
22199
+ if (key.upArrow && selectedSection > 0) setSelectedSection(selectedSection - 1);
22200
+ if (key.downArrow && selectedSection < SECTION_NAMES.length - 1) setSelectedSection(selectedSection + 1);
22201
+ if (input === "r") {
22202
+ loadSettings();
22203
+ }
22204
+ });
22205
+ const statusDot = (ok) => ok ? "\u2022" : "\u2022";
22206
+ const statusColor = (ok) => ok ? GREEN : DIM;
22207
+ const sectionColor = (idx) => selectedSection === idx ? YELLOW : void 0;
22208
+ const sectionBg = (idx) => selectedSection === idx ? DIM : void 0;
21773
22209
  const sectionMarker = (idx) => selectedSection === idx ? "\u25B8 " : " ";
22210
+ const anyGateway = gateway.adapters.some((a) => a.configured);
22211
+ const formatLimit = (n) => n === -1 ? "unlimited" : n.toLocaleString();
21774
22212
  return /* @__PURE__ */ jsxs12(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
21775
22213
  /* @__PURE__ */ jsx14(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx14(Text, { bold: true, children: "Settings" }) }),
21776
- /* @__PURE__ */ jsx14(Text, { color: "#6B4C9A", children: "\u2191\u2193 navigate sections" }),
22214
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: "\u2191\u2193 navigate sections r refresh" }),
21777
22215
  /* @__PURE__ */ jsx14(Text, { children: " " }),
21778
22216
  loading && /* @__PURE__ */ jsxs12(Fragment5, { children: [
21779
- /* @__PURE__ */ jsx14(Text, { color: "#6B4C9A", children: "Loading settings..." }),
22217
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: "Loading settings..." }),
21780
22218
  /* @__PURE__ */ jsx14(Text, { children: " " })
21781
22219
  ] }),
21782
22220
  /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(0), color: sectionColor(0), children: [
@@ -21786,6 +22224,8 @@ function SettingsView({ onBack }) {
21786
22224
  /* @__PURE__ */ jsx14(Text, { children: " " }),
21787
22225
  providers.map((p) => /* @__PURE__ */ jsxs12(Text, { children: [
21788
22226
  " ",
22227
+ /* @__PURE__ */ jsx14(Text, { color: statusColor(p.configured), children: statusDot(p.configured) }),
22228
+ " ",
21789
22229
  p.name,
21790
22230
  ": ",
21791
22231
  /* @__PURE__ */ jsx14(Text, { color: statusColor(p.configured), children: p.detail })
@@ -21793,131 +22233,274 @@ function SettingsView({ onBack }) {
21793
22233
  /* @__PURE__ */ jsx14(Text, { children: " " }),
21794
22234
  /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(1), color: sectionColor(1), children: [
21795
22235
  sectionMarker(1),
21796
- "Memory"
22236
+ "Cloud Sync"
21797
22237
  ] }),
21798
22238
  /* @__PURE__ */ jsx14(Text, { children: " " }),
21799
- /* @__PURE__ */ jsxs12(Text, { children: [
22239
+ cloud.configured ? /* @__PURE__ */ jsxs12(Fragment5, { children: [
22240
+ /* @__PURE__ */ jsxs12(Text, { children: [
22241
+ " ",
22242
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: statusDot(true) }),
22243
+ " Status: ",
22244
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: "connected" })
22245
+ ] }),
22246
+ /* @__PURE__ */ jsxs12(Text, { children: [
22247
+ " ",
22248
+ "Endpoint: ",
22249
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: cloud.endpoint })
22250
+ ] }),
22251
+ /* @__PURE__ */ jsxs12(Text, { children: [
22252
+ " ",
22253
+ "API key: ",
22254
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: cloud.apiKey })
22255
+ ] })
22256
+ ] }) : /* @__PURE__ */ jsxs12(Text, { children: [
21800
22257
  " ",
21801
- "Encryption: ",
21802
- /* @__PURE__ */ jsx14(Text, { color: memory.encryption.includes("AES") ? "#22C55E" : "#6B4C9A", children: memory.encryption })
22258
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: statusDot(false) }),
22259
+ " ",
22260
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: "Not configured \u2014 run /exe-cloud" })
21803
22261
  ] }),
21804
- /* @__PURE__ */ jsxs12(Text, { children: [
21805
- " ",
21806
- "Embedding: ",
21807
- /* @__PURE__ */ jsx14(Text, { color: "#22C55E", children: memory.embeddingModel })
22262
+ /* @__PURE__ */ jsx14(Text, { children: " " }),
22263
+ /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(2), color: sectionColor(2), children: [
22264
+ sectionMarker(2),
22265
+ "License"
21808
22266
  ] }),
22267
+ /* @__PURE__ */ jsx14(Text, { children: " " }),
21809
22268
  /* @__PURE__ */ jsxs12(Text, { children: [
21810
22269
  " ",
21811
- "Cloud sync: ",
21812
- /* @__PURE__ */ jsx14(Text, { color: memory.cloudSync === "enabled" ? "#22C55E" : "#6B4C9A", children: memory.cloudSync })
22270
+ "Plan: ",
22271
+ /* @__PURE__ */ jsx14(Text, { color: license.plan === "FREE" ? YELLOW : GREEN, children: license.plan })
21813
22272
  ] }),
21814
- /* @__PURE__ */ jsxs12(Text, { children: [
22273
+ license.expiresAt && /* @__PURE__ */ jsxs12(Text, { children: [
21815
22274
  " ",
21816
- "Search mode: ",
21817
- /* @__PURE__ */ jsx14(Text, { color: "#22C55E", children: memory.searchMode })
21818
- ] }),
21819
- /* @__PURE__ */ jsx14(Text, { children: " " }),
21820
- /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(2), color: sectionColor(2), children: [
21821
- sectionMarker(2),
21822
- "Runtime"
22275
+ "Expires: ",
22276
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: license.expiresAt.split("T")[0] })
21823
22277
  ] }),
21824
- /* @__PURE__ */ jsx14(Text, { children: " " }),
21825
22278
  /* @__PURE__ */ jsxs12(Text, { children: [
21826
22279
  " ",
21827
- "Consolidation: ",
21828
- /* @__PURE__ */ jsx14(Text, { color: runtime.consolidation ? "#22C55E" : "#6B4C9A", children: runtime.consolidation ? "enabled" : "disabled" }),
21829
- " (",
21830
- runtime.consolidationModel,
21831
- ")"
22280
+ "Devices: ",
22281
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: formatLimit(license.deviceLimit) })
21832
22282
  ] }),
21833
22283
  /* @__PURE__ */ jsxs12(Text, { children: [
21834
22284
  " ",
21835
- "Skill learning: ",
21836
- /* @__PURE__ */ jsx14(Text, { color: runtime.skillLearning ? "#22C55E" : "#6B4C9A", children: runtime.skillLearning ? "enabled" : "disabled" }),
21837
- " (threshold: ",
21838
- runtime.skillThreshold,
21839
- ")"
22285
+ "Employees: ",
22286
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: formatLimit(license.employeeLimit) })
21840
22287
  ] }),
21841
22288
  /* @__PURE__ */ jsxs12(Text, { children: [
21842
22289
  " ",
21843
- "Failover chain: ",
21844
- /* @__PURE__ */ jsx14(Text, { color: "#22C55E", children: runtime.failoverChain.join(" \u2192 ") })
22290
+ "Memory limit: ",
22291
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: formatLimit(license.memoryLimit) })
21845
22292
  ] }),
21846
22293
  /* @__PURE__ */ jsx14(Text, { children: " " }),
21847
22294
  /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(3), color: sectionColor(3), children: [
21848
22295
  sectionMarker(3),
21849
- "Employee Models"
22296
+ "System"
21850
22297
  ] }),
21851
22298
  /* @__PURE__ */ jsx14(Text, { children: " " }),
21852
- loading ? /* @__PURE__ */ jsxs12(Text, { color: "#6B4C9A", children: [
21853
- " ",
21854
- "Loading..."
21855
- ] }) : employeeModels.length === 0 ? /* @__PURE__ */ jsxs12(Text, { color: "#6B4C9A", children: [
22299
+ /* @__PURE__ */ jsxs12(Text, { children: [
21856
22300
  " ",
21857
- "No employees configured"
21858
- ] }) : employeeModels.map((e) => /* @__PURE__ */ jsxs12(Text, { children: [
22301
+ /* @__PURE__ */ jsx14(Text, { color: system.daemon === "running" ? GREEN : DIM, children: statusDot(system.daemon === "running") }),
22302
+ " Daemon: ",
22303
+ /* @__PURE__ */ jsx14(Text, { color: system.daemon === "running" ? GREEN : DIM, children: system.daemon })
22304
+ ] }),
22305
+ /* @__PURE__ */ jsxs12(Text, { children: [
21859
22306
  " ",
21860
- e.name,
21861
- ": ",
21862
- /* @__PURE__ */ jsx14(Text, { color: "#22C55E", children: e.model }),
21863
- " ",
21864
- /* @__PURE__ */ jsxs12(Text, { color: "#6B4C9A", children: [
21865
- "via ",
21866
- e.provider
21867
- ] })
21868
- ] }, e.name)),
21869
- /* @__PURE__ */ jsx14(Text, { children: " " }),
21870
- /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(4), color: sectionColor(4), children: [
21871
- sectionMarker(4),
21872
- "Integrations"
22307
+ "Version: ",
22308
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: system.version })
21873
22309
  ] }),
21874
- /* @__PURE__ */ jsx14(Text, { children: " " }),
21875
22310
  /* @__PURE__ */ jsxs12(Text, { children: [
21876
22311
  " ",
21877
- "Claude Code: ",
21878
- /* @__PURE__ */ jsx14(Text, { color: claudeCode.installed ? "#22C55E" : "#6B4C9A", children: claudeCode.installed ? "installed" : "not found" }),
21879
- claudeCode.installed && /* @__PURE__ */ jsxs12(Text, { color: claudeCode.hooksWired ? "#22C55E" : "#6B4C9A", children: [
21880
- " \u2014 hooks ",
21881
- claudeCode.hooksWired ? "wired" : "not wired"
21882
- ] })
22312
+ "Encryption: ",
22313
+ /* @__PURE__ */ jsx14(Text, { color: system.encryption.includes("AES") ? GREEN : DIM, children: system.encryption })
21883
22314
  ] }),
21884
22315
  /* @__PURE__ */ jsxs12(Text, { children: [
21885
22316
  " ",
21886
- "License: ",
21887
- /* @__PURE__ */ jsx14(Text, { color: license.valid ? "#22C55E" : "#6B4C9A", children: license.detail })
22317
+ "Embedding: ",
22318
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: system.embeddingModel })
21888
22319
  ] }),
21889
22320
  /* @__PURE__ */ jsxs12(Text, { children: [
21890
22321
  " ",
21891
- "Cloud sync: ",
21892
- /* @__PURE__ */ jsx14(Text, { color: cloudSync.configured ? "#22C55E" : "#6B4C9A", children: cloudSync.detail })
22322
+ "DB path: ",
22323
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: system.dbPath })
22324
+ ] }),
22325
+ /* @__PURE__ */ jsx14(Text, { children: " " }),
22326
+ /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(4), color: sectionColor(4), children: [
22327
+ sectionMarker(4),
22328
+ "Gateway"
22329
+ ] }),
22330
+ /* @__PURE__ */ jsx14(Text, { children: " " }),
22331
+ anyGateway ? gateway.adapters.map((a) => /* @__PURE__ */ jsxs12(Text, { children: [
22332
+ " ",
22333
+ /* @__PURE__ */ jsx14(Text, { color: statusColor(a.configured), children: statusDot(a.configured) }),
22334
+ " ",
22335
+ a.name,
22336
+ ": ",
22337
+ /* @__PURE__ */ jsx14(Text, { color: statusColor(a.configured), children: a.configured ? "configured" : "not configured" })
22338
+ ] }, a.name)) : /* @__PURE__ */ jsxs12(Text, { children: [
22339
+ " ",
22340
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: statusDot(false) }),
22341
+ " ",
22342
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: "No gateway configured" })
21893
22343
  ] })
21894
22344
  ] });
21895
22345
  }
21896
- var SECTION_NAMES;
22346
+ var SECTION_NAMES, GREEN, DIM, YELLOW;
21897
22347
  var init_Settings = __esm({
21898
22348
  async "src/tui/views/Settings.tsx"() {
21899
22349
  "use strict";
21900
22350
  await init_ink2();
21901
- SECTION_NAMES = ["Providers", "Memory", "Runtime", "Employee Models", "Integrations"];
22351
+ SECTION_NAMES = ["Providers", "Cloud Sync", "License", "System", "Gateway"];
22352
+ GREEN = "#22C55E";
22353
+ DIM = "#6B4C9A";
22354
+ YELLOW = "#F5D76E";
22355
+ }
22356
+ });
22357
+
22358
+ // src/tui/views/DebugPanel.tsx
22359
+ import React25, { useState as useState15, useEffect as useEffect17 } from "react";
22360
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
22361
+ function formatEvent(e) {
22362
+ switch (e.type) {
22363
+ case "task_completed":
22364
+ return `${e.employee}: "${e.result.slice(0, 60)}"`;
22365
+ case "review_created":
22366
+ return `${e.employee} \u2192 ${e.reviewer}`;
22367
+ case "employee_status":
22368
+ return `${e.employee}: ${e.status}`;
22369
+ case "memory_stored":
22370
+ return `${e.agentId} [${e.project}]`;
22371
+ case "session_started":
22372
+ return `${e.employee} (${e.sessionId.slice(0, 8)})`;
22373
+ case "session_ended":
22374
+ return `${e.employee} (${e.sessionId.slice(0, 8)})`;
22375
+ case "gateway_message":
22376
+ return `${e.platform}/${e.senderId} \u2192 ${e.botId}`;
22377
+ }
22378
+ }
22379
+ function DebugPanel() {
22380
+ const [events, setEvents] = useState15([]);
22381
+ useEffect17(() => {
22382
+ let counter = 0;
22383
+ const handler = (event) => {
22384
+ setEvents((prev) => {
22385
+ const next = [...prev, { event, id: counter++ }];
22386
+ return next.slice(-MAX_EVENTS);
22387
+ });
22388
+ };
22389
+ orgBus.onAny(handler);
22390
+ return () => {
22391
+ orgBus.offAny(handler);
22392
+ };
22393
+ }, []);
22394
+ return /* @__PURE__ */ jsxs13(Box_default, { flexDirection: "column", borderStyle: "single", borderColor: "#6B4C9A", flexGrow: 1, paddingX: 1, children: [
22395
+ /* @__PURE__ */ jsx15(Text, { bold: true, color: "#F5D76E", children: "Debug \u2014 Live Events" }),
22396
+ /* @__PURE__ */ jsx15(Text, { color: "#3D3660", children: "\u2500".repeat(40) }),
22397
+ events.length === 0 ? /* @__PURE__ */ jsx15(Text, { color: "#3D3660", children: "Waiting for events..." }) : events.slice(-DISPLAY_EVENTS).map(({ event, id }) => {
22398
+ const time = event.timestamp?.slice(11, 19) ?? "??:??:??";
22399
+ const color = EVENT_COLORS[event.type] ?? "#6B4C9A";
22400
+ const summary = formatEvent(event);
22401
+ return /* @__PURE__ */ jsxs13(Text, { color, children: [
22402
+ "[",
22403
+ time,
22404
+ "] ",
22405
+ event.type,
22406
+ " \u2014 ",
22407
+ summary
22408
+ ] }, id);
22409
+ })
22410
+ ] });
22411
+ }
22412
+ var MAX_EVENTS, DISPLAY_EVENTS, EVENT_COLORS;
22413
+ var init_DebugPanel = __esm({
22414
+ async "src/tui/views/DebugPanel.tsx"() {
22415
+ "use strict";
22416
+ await init_ink2();
22417
+ init_state_bus();
22418
+ MAX_EVENTS = 50;
22419
+ DISPLAY_EVENTS = 20;
22420
+ EVENT_COLORS = {
22421
+ task_completed: "#22C55E",
22422
+ review_created: "#F59E0B",
22423
+ employee_status: "#3B82F6",
22424
+ memory_stored: "#8B5CF6",
22425
+ session_started: "#06B6D4",
22426
+ session_ended: "#EF4444",
22427
+ gateway_message: "#EC4899"
22428
+ };
22429
+ }
22430
+ });
22431
+
22432
+ // src/tui/components/HelpOverlay.tsx
22433
+ import React26 from "react";
22434
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
22435
+ function HelpOverlay({ tabName, shortcuts }) {
22436
+ return /* @__PURE__ */ jsxs14(
22437
+ Box_default,
22438
+ {
22439
+ flexDirection: "column",
22440
+ borderStyle: "double",
22441
+ borderColor: "#6B4C9A",
22442
+ paddingX: 2,
22443
+ paddingY: 1,
22444
+ width: 50,
22445
+ alignSelf: "center",
22446
+ children: [
22447
+ /* @__PURE__ */ jsxs14(Text, { bold: true, color: "#F5D76E", children: [
22448
+ "Keyboard Shortcuts \u2014 ",
22449
+ tabName
22450
+ ] }),
22451
+ /* @__PURE__ */ jsx16(Text, { children: " " }),
22452
+ shortcuts.map((s, i) => /* @__PURE__ */ jsxs14(Box_default, { gap: 1, children: [
22453
+ /* @__PURE__ */ jsx16(Text, { color: "#F5D76E", bold: true, children: s.key.padEnd(10) }),
22454
+ /* @__PURE__ */ jsx16(Text, { color: "#F0EDE8", children: s.description })
22455
+ ] }, i)),
22456
+ /* @__PURE__ */ jsx16(Text, { children: " " }),
22457
+ /* @__PURE__ */ jsx16(Text, { bold: true, color: "#F5D76E", children: "Global" }),
22458
+ /* @__PURE__ */ jsx16(Text, { children: " " }),
22459
+ /* @__PURE__ */ jsxs14(Box_default, { gap: 1, children: [
22460
+ /* @__PURE__ */ jsx16(Text, { color: "#F5D76E", bold: true, children: "1-7".padEnd(10) }),
22461
+ /* @__PURE__ */ jsx16(Text, { color: "#F0EDE8", children: "Switch tabs" })
22462
+ ] }),
22463
+ /* @__PURE__ */ jsxs14(Box_default, { gap: 1, children: [
22464
+ /* @__PURE__ */ jsx16(Text, { color: "#F5D76E", bold: true, children: "q".padEnd(10) }),
22465
+ /* @__PURE__ */ jsx16(Text, { color: "#F0EDE8", children: "Quit" })
22466
+ ] }),
22467
+ /* @__PURE__ */ jsxs14(Box_default, { gap: 1, children: [
22468
+ /* @__PURE__ */ jsx16(Text, { color: "#F5D76E", bold: true, children: "?".padEnd(10) }),
22469
+ /* @__PURE__ */ jsx16(Text, { color: "#F0EDE8", children: "Toggle this help" })
22470
+ ] }),
22471
+ /* @__PURE__ */ jsxs14(Box_default, { gap: 1, children: [
22472
+ /* @__PURE__ */ jsx16(Text, { color: "#F5D76E", bold: true, children: "Esc".padEnd(10) }),
22473
+ /* @__PURE__ */ jsx16(Text, { color: "#F0EDE8", children: "Back to sidebar" })
22474
+ ] }),
22475
+ /* @__PURE__ */ jsx16(Text, { children: " " }),
22476
+ /* @__PURE__ */ jsx16(Text, { color: "#6B4C9A", children: "Press ? to close" })
22477
+ ]
22478
+ }
22479
+ );
22480
+ }
22481
+ var init_HelpOverlay = __esm({
22482
+ async "src/tui/components/HelpOverlay.tsx"() {
22483
+ "use strict";
22484
+ await init_ink2();
21902
22485
  }
21903
22486
  });
21904
22487
 
21905
22488
  // src/tui/App.tsx
21906
22489
  var App_exports = {};
21907
- import { useState as useState15, useEffect as useEffect17, useCallback as useCallback7 } from "react";
21908
- import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
22490
+ import { useState as useState16, useEffect as useEffect18, useCallback as useCallback8 } from "react";
22491
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
21909
22492
  function App2() {
21910
- const [section, setSection] = useState15("command-center");
22493
+ const [section, setSection] = useState16("command-center");
21911
22494
  const { exit } = use_app_default();
21912
- const [, forceUpdate] = useState15(0);
21913
- useEffect17(() => {
22495
+ const [, forceUpdate] = useState16(0);
22496
+ useEffect18(() => {
21914
22497
  const handleResize = () => forceUpdate((n) => n + 1);
21915
22498
  process.stdout.on("resize", handleResize);
21916
22499
  return () => {
21917
22500
  process.stdout.off("resize", handleResize);
21918
22501
  };
21919
22502
  }, []);
21920
- useMouseEvent(useCallback7((event) => {
22503
+ useMouseEvent(useCallback8((event) => {
21921
22504
  if (event.button !== 0) return;
21922
22505
  if (event.col <= 26) {
21923
22506
  const tabIdx = event.row - 4;
@@ -21929,22 +22512,33 @@ function App2() {
21929
22512
  setFocus("content");
21930
22513
  }
21931
22514
  }, []));
21932
- const [focus, setFocus] = useState15("sidebar");
21933
- const [focusedProject, setFocusedProject] = useState15(null);
21934
- const handleSelectProject = useCallback7((projectName) => {
22515
+ const [focus, setFocus] = useState16("sidebar");
22516
+ const [showDebug, setShowDebug] = useState16(false);
22517
+ const [showHelp, setShowHelp] = useState16(false);
22518
+ const [focusedProject, setFocusedProject] = useState16(null);
22519
+ const handleSelectProject = useCallback8((projectName) => {
21935
22520
  setFocusedProject(projectName);
21936
22521
  setSection("sessions");
21937
22522
  setFocus("content");
21938
22523
  }, []);
21939
- const handleBackToCommandCenter = useCallback7(() => {
22524
+ const handleBackToCommandCenter = useCallback8(() => {
21940
22525
  setSection("command-center");
21941
22526
  setFocus("sidebar");
21942
22527
  }, []);
21943
22528
  use_input_default((input, key) => {
22529
+ if (input === "?" || key.shift && input === "/") {
22530
+ setShowHelp((prev) => !prev);
22531
+ return;
22532
+ }
21944
22533
  const idx = parseInt(input, 10);
21945
22534
  if (idx >= 1 && idx <= SECTIONS.length) {
21946
22535
  setSection(SECTIONS[idx - 1].key);
21947
22536
  setFocus("sidebar");
22537
+ setShowHelp(false);
22538
+ return;
22539
+ }
22540
+ if (input === "d" && !key.ctrl) {
22541
+ setShowDebug((prev) => !prev);
21948
22542
  return;
21949
22543
  }
21950
22544
  if (input === "q" && !key.ctrl) {
@@ -21972,27 +22566,37 @@ function App2() {
21972
22566
  }
21973
22567
  }
21974
22568
  });
21975
- const consumeFocusedProject = useCallback7(() => {
22569
+ const consumeFocusedProject = useCallback8(() => {
21976
22570
  setFocusedProject(null);
21977
22571
  }, []);
21978
22572
  const views = {
21979
- "command-center": /* @__PURE__ */ jsx15(CommandCenterView, { onSelectProject: handleSelectProject, isFocused: focus === "content" && section === "command-center", onBack: () => setFocus("sidebar") }),
21980
- sessions: /* @__PURE__ */ jsx15(SessionsView, { initialProject: focusedProject, onConsumeInitialProject: consumeFocusedProject, onBackToCommandCenter: handleBackToCommandCenter, onBack: () => setFocus("sidebar") }),
21981
- tasks: /* @__PURE__ */ jsx15(TasksView, { onBack: () => setFocus("sidebar") }),
21982
- team: /* @__PURE__ */ jsx15(TeamView, { onBack: () => setFocus("sidebar") }),
21983
- gateway: /* @__PURE__ */ jsx15(GatewayView, { onBack: () => setFocus("sidebar") }),
21984
- wiki: /* @__PURE__ */ jsx15(WikiView, { onBack: () => setFocus("sidebar") }),
21985
- settings: /* @__PURE__ */ jsx15(SettingsView, { onBack: () => setFocus("sidebar") })
22573
+ "command-center": /* @__PURE__ */ jsx17(CommandCenterView, { onSelectProject: handleSelectProject, isFocused: focus === "content" && section === "command-center", onBack: () => setFocus("sidebar") }),
22574
+ sessions: /* @__PURE__ */ jsx17(SessionsView, { initialProject: focusedProject, onConsumeInitialProject: consumeFocusedProject, onBackToCommandCenter: handleBackToCommandCenter, onBack: () => setFocus("sidebar") }),
22575
+ tasks: /* @__PURE__ */ jsx17(TasksView, { onBack: () => setFocus("sidebar") }),
22576
+ team: /* @__PURE__ */ jsx17(TeamView, { onBack: () => setFocus("sidebar"), onViewSessions: (name) => {
22577
+ setFocusedProject(name);
22578
+ setSection("sessions");
22579
+ setFocus("content");
22580
+ } }),
22581
+ gateway: /* @__PURE__ */ jsx17(GatewayView, { onBack: () => setFocus("sidebar") }),
22582
+ wiki: /* @__PURE__ */ jsx17(WikiView, { onBack: () => setFocus("sidebar") }),
22583
+ settings: /* @__PURE__ */ jsx17(SettingsView, { onBack: () => setFocus("sidebar") })
21986
22584
  };
21987
- return /* @__PURE__ */ jsx15(ErrorBoundary2, { children: /* @__PURE__ */ jsx15(AlternateScreen, { children: /* @__PURE__ */ jsx15(DemoProvider, { demo: isDemo, children: /* @__PURE__ */ jsxs13(Box_default, { flexDirection: "column", flexGrow: 1, children: [
21988
- /* @__PURE__ */ jsxs13(Box_default, { flexGrow: 1, children: [
21989
- /* @__PURE__ */ jsx15(Sidebar, { active: section, onSelect: setSection, onQuit: exit, focused: focus === "sidebar" }),
21990
- views[section]
22585
+ return /* @__PURE__ */ jsx17(ErrorBoundary2, { children: /* @__PURE__ */ jsx17(AlternateScreen, { children: /* @__PURE__ */ jsx17(DemoProvider, { demo: isDemo, children: /* @__PURE__ */ jsxs15(Box_default, { flexDirection: "column", flexGrow: 1, children: [
22586
+ /* @__PURE__ */ jsxs15(Box_default, { flexGrow: 1, children: [
22587
+ /* @__PURE__ */ jsx17(Sidebar, { active: section, onSelect: setSection, onQuit: exit, focused: focus === "sidebar" }),
22588
+ showHelp ? /* @__PURE__ */ jsx17(
22589
+ HelpOverlay,
22590
+ {
22591
+ tabName: TAB_SHORTCUTS[section].name,
22592
+ shortcuts: TAB_SHORTCUTS[section].shortcuts
22593
+ }
22594
+ ) : showDebug ? /* @__PURE__ */ jsx17(DebugPanel, {}) : views[section]
21991
22595
  ] }),
21992
- /* @__PURE__ */ jsx15(Footer, {})
22596
+ /* @__PURE__ */ jsx17(Footer, {})
21993
22597
  ] }) }) }) });
21994
22598
  }
21995
- var isDemo;
22599
+ var TAB_SHORTCUTS, isDemo;
21996
22600
  var init_App2 = __esm({
21997
22601
  async "src/tui/App.tsx"() {
21998
22602
  "use strict";
@@ -22010,6 +22614,73 @@ var init_App2 = __esm({
22010
22614
  await init_Team();
22011
22615
  await init_Wiki();
22012
22616
  await init_Settings();
22617
+ await init_DebugPanel();
22618
+ await init_HelpOverlay();
22619
+ TAB_SHORTCUTS = {
22620
+ "command-center": {
22621
+ name: "Command Center",
22622
+ shortcuts: [
22623
+ { key: "\u2191\u2193", description: "Navigate projects" },
22624
+ { key: "Enter", description: "Select project" },
22625
+ { key: "/", description: "Enter chat mode" },
22626
+ { key: "n", description: "New task" }
22627
+ ]
22628
+ },
22629
+ sessions: {
22630
+ name: "Sessions",
22631
+ shortcuts: [
22632
+ { key: "\u2191\u2193", description: "Navigate sessions" },
22633
+ { key: "Enter", description: "View session output" },
22634
+ { key: "k", description: "Kill session" },
22635
+ { key: "r", description: "Restart agent" }
22636
+ ]
22637
+ },
22638
+ tasks: {
22639
+ name: "Tasks",
22640
+ shortcuts: [
22641
+ { key: "\u2191\u2193", description: "Navigate tasks" },
22642
+ { key: "Enter", description: "View task detail" },
22643
+ { key: "n", description: "Create new task" },
22644
+ { key: "f", description: "Cycle status filter" },
22645
+ { key: "p", description: "Filter by priority" }
22646
+ ]
22647
+ },
22648
+ team: {
22649
+ name: "Team",
22650
+ shortcuts: [
22651
+ { key: "\u2191\u2193", description: "Navigate employees" },
22652
+ { key: "Enter", description: "View detail" },
22653
+ { key: "a", description: "Add employee" },
22654
+ { key: "s", description: "View sessions" }
22655
+ ]
22656
+ },
22657
+ gateway: {
22658
+ name: "Gateway",
22659
+ shortcuts: [
22660
+ { key: "\u2191\u2193", description: "Navigate sections" },
22661
+ { key: "f", description: "Cycle platform filter" },
22662
+ { key: "\u2192", description: "Next conversation" },
22663
+ { key: "r", description: "Reconnect" }
22664
+ ]
22665
+ },
22666
+ wiki: {
22667
+ name: "Wiki",
22668
+ shortcuts: [
22669
+ { key: "\u2190\u2192", description: "Switch panels" },
22670
+ { key: "\u2191\u2193", description: "Navigate items" },
22671
+ { key: "/", description: "Search memories" },
22672
+ { key: "Enter", description: "Select" }
22673
+ ]
22674
+ },
22675
+ settings: {
22676
+ name: "Settings",
22677
+ shortcuts: [
22678
+ { key: "\u2191\u2193", description: "Navigate sections" },
22679
+ { key: "Enter", description: "Edit setting" },
22680
+ { key: "r", description: "Refresh status" }
22681
+ ]
22682
+ }
22683
+ };
22013
22684
  isDemo = process.argv.includes("--demo");
22014
22685
  process.stderr.write(`[exe-tui] Terminal: ${TERMINAL_TYPE}
22015
22686
  `);
@@ -22050,7 +22721,7 @@ var init_App2 = __esm({
22050
22721
  stdin.unref = () => stdin;
22051
22722
  }
22052
22723
  }
22053
- render_default(/* @__PURE__ */ jsx15(App2, {}));
22724
+ render_default(/* @__PURE__ */ jsx17(App2, {}));
22054
22725
  }
22055
22726
  });
22056
22727