@askexenow/exe-os 0.8.40 → 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 (78) 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 +1376 -659
  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 +2549 -1784
  9. package/dist/bin/exe-call.js +39 -1
  10. package/dist/bin/exe-cloud.js +12 -2
  11. package/dist/bin/exe-dispatch.js +39 -2
  12. package/dist/bin/exe-doctor.js +791 -634
  13. package/dist/bin/exe-export-behaviors.js +792 -637
  14. package/dist/bin/exe-forget.js +145 -0
  15. package/dist/bin/exe-gateway.js +2501 -1846
  16. package/dist/bin/exe-heartbeat.js +147 -1
  17. package/dist/bin/exe-kill.js +795 -640
  18. package/dist/bin/exe-launch-agent.js +2168 -2008
  19. package/dist/bin/exe-link.js +44 -12
  20. package/dist/bin/exe-new-employee.js +6 -2
  21. package/dist/bin/exe-pending-messages.js +146 -1
  22. package/dist/bin/exe-pending-notifications.js +788 -631
  23. package/dist/bin/exe-pending-reviews.js +176 -1
  24. package/dist/bin/exe-rename.js +23 -0
  25. package/dist/bin/exe-review.js +490 -327
  26. package/dist/bin/exe-search.js +157 -4
  27. package/dist/bin/exe-session-cleanup.js +2487 -403
  28. package/dist/bin/exe-settings.js +2 -1
  29. package/dist/bin/exe-status.js +474 -317
  30. package/dist/bin/exe-team.js +474 -317
  31. package/dist/bin/git-sweep.js +2691 -151
  32. package/dist/bin/graph-backfill.js +794 -637
  33. package/dist/bin/graph-export.js +798 -641
  34. package/dist/bin/scan-tasks.js +2951 -44
  35. package/dist/bin/setup.js +50 -26
  36. package/dist/bin/shard-migrate.js +792 -637
  37. package/dist/bin/wiki-sync.js +794 -637
  38. package/dist/gateway/index.js +2542 -1887
  39. package/dist/hooks/bug-report-worker.js +2118 -576
  40. package/dist/hooks/commit-complete.js +2690 -150
  41. package/dist/hooks/error-recall.js +157 -4
  42. package/dist/hooks/ingest-worker.js +1455 -803
  43. package/dist/hooks/instructions-loaded.js +151 -0
  44. package/dist/hooks/notification.js +153 -2
  45. package/dist/hooks/post-compact.js +164 -0
  46. package/dist/hooks/pre-compact.js +3073 -101
  47. package/dist/hooks/pre-tool-use.js +151 -0
  48. package/dist/hooks/prompt-ingest-worker.js +1670 -1509
  49. package/dist/hooks/prompt-submit.js +2650 -1074
  50. package/dist/hooks/response-ingest-worker.js +154 -6
  51. package/dist/hooks/session-end.js +153 -2
  52. package/dist/hooks/session-start.js +157 -4
  53. package/dist/hooks/stop.js +151 -0
  54. package/dist/hooks/subagent-stop.js +155 -2
  55. package/dist/hooks/summary-worker.js +190 -21
  56. package/dist/index.js +326 -102
  57. package/dist/lib/cloud-sync.js +31 -10
  58. package/dist/lib/config.js +2 -0
  59. package/dist/lib/consolidation.js +69 -2
  60. package/dist/lib/database.js +19 -0
  61. package/dist/lib/device-registry.js +19 -0
  62. package/dist/lib/embedder.js +3 -1
  63. package/dist/lib/employee-templates.js +20 -1
  64. package/dist/lib/exe-daemon.js +285 -18
  65. package/dist/lib/hybrid-search.js +157 -4
  66. package/dist/lib/messaging.js +39 -2
  67. package/dist/lib/schedules.js +792 -637
  68. package/dist/lib/store.js +796 -636
  69. package/dist/lib/tasks.js +1485 -918
  70. package/dist/lib/tmux-routing.js +194 -10
  71. package/dist/mcp/server.js +1643 -924
  72. package/dist/mcp/tools/create-task.js +2283 -829
  73. package/dist/mcp/tools/list-tasks.js +2788 -159
  74. package/dist/mcp/tools/send-message.js +39 -2
  75. package/dist/mcp/tools/update-task.js +79 -0
  76. package/dist/runtime/index.js +280 -68
  77. package/dist/tui/App.js +1485 -645
  78. package/package.json +3 -2
package/dist/bin/cli.js CHANGED
@@ -31,6 +31,7 @@ var config_exports = {};
31
31
  __export(config_exports, {
32
32
  CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
33
33
  CONFIG_PATH: () => CONFIG_PATH,
34
+ COO_AGENT_NAME: () => COO_AGENT_NAME,
34
35
  CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
35
36
  DB_PATH: () => DB_PATH,
36
37
  EXE_AI_DIR: () => EXE_AI_DIR,
@@ -186,7 +187,7 @@ async function loadConfigFrom(configPath) {
186
187
  return { ...DEFAULT_CONFIG };
187
188
  }
188
189
  }
189
- var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
190
+ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
190
191
  var init_config = __esm({
191
192
  "src/lib/config.ts"() {
192
193
  "use strict";
@@ -194,6 +195,7 @@ var init_config = __esm({
194
195
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
195
196
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
196
197
  CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
198
+ COO_AGENT_NAME = "exe";
197
199
  LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
198
200
  CURRENT_CONFIG_VERSION = 1;
199
201
  DEFAULT_CONFIG = {
@@ -1309,6 +1311,13 @@ async function ensureSchema() {
1309
1311
  });
1310
1312
  } catch {
1311
1313
  }
1314
+ try {
1315
+ await client.execute({
1316
+ sql: `ALTER TABLE tasks ADD COLUMN session_scope TEXT`,
1317
+ args: []
1318
+ });
1319
+ } catch {
1320
+ }
1312
1321
  try {
1313
1322
  await client.execute({
1314
1323
  sql: `ALTER TABLE memories ADD COLUMN task_id TEXT`,
@@ -1755,6 +1764,18 @@ async function ensureSchema() {
1755
1764
  CREATE INDEX IF NOT EXISTS idx_session_kills_agent
1756
1765
  ON session_kills(agent_id);
1757
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
+ `);
1758
1779
  await client.executeMultiple(`
1759
1780
  CREATE TABLE IF NOT EXISTS conversations (
1760
1781
  id TEXT PRIMARY KEY,
@@ -2059,6 +2080,61 @@ var init_keychain = __esm({
2059
2080
  }
2060
2081
  });
2061
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
+
2062
2138
  // src/lib/shard-manager.ts
2063
2139
  var shard_manager_exports = {};
2064
2140
  __export(shard_manager_exports, {
@@ -2300,6 +2376,71 @@ var init_shard_manager = __esm({
2300
2376
  }
2301
2377
  });
2302
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
+
2303
2444
  // src/lib/store.ts
2304
2445
  var store_exports = {};
2305
2446
  __export(store_exports, {
@@ -2379,6 +2520,11 @@ async function initStore(options) {
2379
2520
  "version-query"
2380
2521
  );
2381
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
+ }
2382
2528
  }
2383
2529
  function classifyTier(record) {
2384
2530
  if (record.tool_name === "commit_to_long_term_memory" && (record.importance ?? 0) >= 8) return 1;
@@ -2420,6 +2566,12 @@ async function writeMemory(record) {
2420
2566
  supersedes_id: record.supersedes_id ?? null
2421
2567
  };
2422
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
+ });
2423
2575
  const MAX_PENDING = 1e3;
2424
2576
  if (_pendingRecords.length > MAX_PENDING) {
2425
2577
  const dropped = _pendingRecords.length - MAX_PENDING;
@@ -2765,6 +2917,7 @@ var init_store = __esm({
2765
2917
  init_database();
2766
2918
  init_keychain();
2767
2919
  init_config();
2920
+ init_state_bus();
2768
2921
  INIT_MAX_RETRIES = 3;
2769
2922
  INIT_RETRY_DELAY_MS = 1e3;
2770
2923
  _pendingRecords = [];
@@ -2779,7 +2932,7 @@ var init_store = __esm({
2779
2932
  // src/lib/exe-daemon-client.ts
2780
2933
  import net from "net";
2781
2934
  import { spawn } from "child_process";
2782
- import { randomUUID } from "crypto";
2935
+ import { randomUUID as randomUUID2 } from "crypto";
2783
2936
  import { existsSync as existsSync7, unlinkSync, readFileSync as readFileSync4, openSync, closeSync, statSync } from "fs";
2784
2937
  import path7 from "path";
2785
2938
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -2971,7 +3124,7 @@ function sendRequest(texts, priority) {
2971
3124
  resolve({ error: "Not connected" });
2972
3125
  return;
2973
3126
  }
2974
- const id = randomUUID();
3127
+ const id = randomUUID2();
2975
3128
  const timer = setTimeout(() => {
2976
3129
  _pending.delete(id);
2977
3130
  resolve({ error: "Request timeout" });
@@ -2989,7 +3142,7 @@ function sendRequest(texts, priority) {
2989
3142
  async function pingDaemon() {
2990
3143
  if (!_socket || !_connected) return null;
2991
3144
  return new Promise((resolve) => {
2992
- const id = randomUUID();
3145
+ const id = randomUUID2();
2993
3146
  const timer = setTimeout(() => {
2994
3147
  _pending.delete(id);
2995
3148
  resolve(null);
@@ -3535,7 +3688,8 @@ __export(employee_templates_exports, {
3535
3688
  function getSessionPrompt(storedPrompt) {
3536
3689
  const markerIndex = storedPrompt.indexOf(PROCEDURES_MARKER);
3537
3690
  const rolePrompt = markerIndex >= 0 ? storedPrompt.slice(0, markerIndex).trimEnd() : storedPrompt;
3538
- return `${rolePrompt}
3691
+ const globalBlock = getGlobalProceduresBlock();
3692
+ return `${globalBlock}${rolePrompt}
3539
3693
  ${BASE_OPERATING_PROCEDURES}`;
3540
3694
  }
3541
3695
  function buildCustomEmployeePrompt(name, role) {
@@ -3575,6 +3729,7 @@ var BASE_OPERATING_PROCEDURES, DEFAULT_EXE, TEMPLATE_VERSION, PROCEDURES_MARKER,
3575
3729
  var init_employee_templates = __esm({
3576
3730
  "src/lib/employee-templates.ts"() {
3577
3731
  "use strict";
3732
+ init_global_procedures();
3578
3733
  BASE_OPERATING_PROCEDURES = `
3579
3734
  EXE OS \u2014 VISION AND NON-NEGOTIABLE PRINCIPLES (above all work):
3580
3735
 
@@ -4508,7 +4663,7 @@ __export(license_exports, {
4508
4663
  validateLicense: () => validateLicense
4509
4664
  });
4510
4665
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, existsSync as existsSync10, mkdirSync as mkdirSync3 } from "fs";
4511
- import { randomUUID as randomUUID2 } from "crypto";
4666
+ import { randomUUID as randomUUID3 } from "crypto";
4512
4667
  import path11 from "path";
4513
4668
  import { jwtVerify, importSPKI } from "jose";
4514
4669
  async function fetchRetry(url, init) {
@@ -4535,7 +4690,7 @@ function loadDeviceId() {
4535
4690
  }
4536
4691
  } catch {
4537
4692
  }
4538
- const id = randomUUID2();
4693
+ const id = randomUUID3();
4539
4694
  mkdirSync3(EXE_AI_DIR, { recursive: true });
4540
4695
  writeFileSync2(DEVICE_ID_PATH, id, "utf8");
4541
4696
  return id;
@@ -12307,6 +12462,73 @@ var init_Sidebar = __esm({
12307
12462
  }
12308
12463
  });
12309
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
+
12310
12532
  // src/lib/tmux-status.ts
12311
12533
  var tmux_status_exports = {};
12312
12534
  __export(tmux_status_exports, {
@@ -12601,61 +12823,64 @@ function Footer() {
12601
12823
  return () => clearInterval(timer);
12602
12824
  }, []);
12603
12825
  async function loadFooterData() {
12604
- try {
12605
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
12606
- const client = getClient2();
12607
- if (client) {
12608
- const result = await client.execute("SELECT COUNT(*) as cnt FROM memories");
12609
- 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 {
12610
12836
  }
12611
- } catch {
12612
- }
12613
- try {
12614
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
12615
- const client = getClient2();
12616
- if (client) {
12617
- const result = await client.execute(
12618
- "SELECT COUNT(*) as cnt FROM tasks WHERE status IN ('open','in_progress')"
12619
- );
12620
- 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 {
12621
12847
  }
12622
- } catch {
12623
- }
12624
- try {
12625
- const { existsSync: existsSync22 } = await import("fs");
12626
- const { join } = await import("path");
12627
- const home = process.env.HOME ?? "";
12628
- const pidPath = join(home, ".exe-os", "exed.pid");
12629
- setDaemon(existsSync22(pidPath) ? "running" : "stopped");
12630
- } catch {
12631
- setDaemon("unknown");
12632
- }
12633
- try {
12634
- const { checkLicense: checkLicense2 } = await Promise.resolve().then(() => (init_license(), license_exports));
12635
- const license = await checkLicense2();
12636
- setPlan(license.plan);
12637
- } catch {
12638
- setPlan("free");
12639
- }
12640
- try {
12641
- const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
12642
- if (inTmux2()) {
12643
- const allSessions = listTmuxSessions2();
12644
- setSessions(allSessions.length);
12645
- if (!currentSession) {
12646
- try {
12647
- const { execSync: execSync13 } = await import("child_process");
12648
- const name = execSync13("tmux display-message -p '#{session_name}' 2>/dev/null", {
12649
- encoding: "utf8",
12650
- timeout: 2e3
12651
- }).trim();
12652
- if (name) setCurrentSession(name);
12653
- } 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
+ }
12654
12879
  }
12655
12880
  }
12881
+ } catch {
12656
12882
  }
12657
- } catch {
12658
- }
12883
+ });
12659
12884
  }
12660
12885
  return /* @__PURE__ */ jsxs3(Box_default, { flexDirection: "column", children: [
12661
12886
  /* @__PURE__ */ jsx5(Text, { color: "#3D3660", children: "\u2500".repeat(process.stdout.columns || 120) }),
@@ -12696,6 +12921,8 @@ function Footer() {
12696
12921
  ] }),
12697
12922
  /* @__PURE__ */ jsx5(Text, { color: "#6B4C9A", children: "1-7 navigate" }),
12698
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" }),
12699
12926
  /* @__PURE__ */ jsx5(Text, { color: "#6B4C9A", children: "q quit" })
12700
12927
  ] })
12701
12928
  ] })
@@ -14344,7 +14571,7 @@ var init_anthropic = __esm({
14344
14571
 
14345
14572
  // src/gateway/providers/openai-compat.ts
14346
14573
  import OpenAI from "openai";
14347
- import { randomUUID as randomUUID3 } from "crypto";
14574
+ import { randomUUID as randomUUID4 } from "crypto";
14348
14575
  var OpenAICompatProvider;
14349
14576
  var init_openai_compat = __esm({
14350
14577
  "src/gateway/providers/openai-compat.ts"() {
@@ -14461,7 +14688,7 @@ var init_openai_compat = __esm({
14461
14688
  }
14462
14689
  content.push({
14463
14690
  type: "tool_use",
14464
- id: call.id ?? randomUUID3(),
14691
+ id: call.id ?? randomUUID4(),
14465
14692
  name: fn.name,
14466
14693
  input
14467
14694
  });
@@ -15535,7 +15762,7 @@ function CommandCenterView({
15535
15762
  setAgentConfig({
15536
15763
  provider,
15537
15764
  model,
15538
- 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."),
15539
15766
  tools: registry,
15540
15767
  hooks: createDefaultHooks2(),
15541
15768
  permissions,
@@ -15656,6 +15883,7 @@ function CommandCenterView({
15656
15883
  employeeCount: p.employees.length,
15657
15884
  activeCount: p.employees.filter((e) => e.status === "active").length,
15658
15885
  memoryCount: p.employees.length * 4e3,
15886
+ openTaskCount: Math.floor(p.employees.length * 1.5),
15659
15887
  status: p.employees.some((e) => e.status === "active") ? "active" : "idle",
15660
15888
  type: p.projectName.startsWith("exe-") ? "code" : "automation",
15661
15889
  recentTasks: DEMO_RECENT_TASKS[p.projectName] ?? []
@@ -15667,6 +15895,7 @@ function CommandCenterView({
15667
15895
  employeeCount: 0,
15668
15896
  activeCount: 0,
15669
15897
  memoryCount: 0,
15898
+ openTaskCount: 0,
15670
15899
  status: "offline",
15671
15900
  type: "reference",
15672
15901
  recentTasks: []
@@ -15681,182 +15910,122 @@ function CommandCenterView({
15681
15910
  return () => clearInterval(timer);
15682
15911
  }, []);
15683
15912
  async function loadData() {
15684
- try {
15685
- const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
15686
- const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
15687
- const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
15688
- const { existsSync: existsSync22 } = await import("fs");
15689
- const { join } = await import("path");
15690
- const registry = listSessions2();
15691
- const tmuxSessions = inTmux2() ? new Set(listTmuxSessions2()) : /* @__PURE__ */ new Set();
15692
- const roster = await loadEmployees2();
15693
- const exeSessions = /* @__PURE__ */ new Map();
15694
- for (const entry of registry) {
15695
- if (entry.agentId === "exe" && tmuxSessions.has(entry.windowName)) {
15696
- exeSessions.set(entry.windowName, entry.projectDir);
15697
- }
15698
- }
15699
- for (const s of tmuxSessions) {
15700
- if (/^exe\d+$/.test(s) && !exeSessions.has(s)) exeSessions.set(s, "");
15701
- }
15702
- let projectMemoryCounts = /* @__PURE__ */ new Map();
15703
- let projectRecentTasks = /* @__PURE__ */ new Map();
15913
+ const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
15914
+ return withTrace2("tui.commandCenter.loadData", async () => {
15704
15915
  try {
15705
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");
15706
15922
  const client = getClient2();
15707
- if (client) {
15708
- const memResult = await client.execute(
15709
- "SELECT project_name, COUNT(*) as cnt FROM memories GROUP BY project_name"
15710
- );
15711
- for (const row of memResult.rows) {
15712
- projectMemoryCounts.set(String(row.project_name), Number(row.cnt));
15713
- }
15714
- for (const [, projectDir] of exeSessions) {
15715
- const projectName = projectDir.split("/").filter(Boolean).pop() ?? "";
15716
- if (!projectName) continue;
15717
- const taskResult = await client.execute({
15718
- sql: "SELECT title FROM tasks WHERE project_name = ? AND status = 'done' ORDER BY updated_at DESC LIMIT 3",
15719
- args: [projectName]
15720
- });
15721
- const tasks = taskResult.rows.map((r) => String(r.title));
15722
- if (tasks.length > 0) projectRecentTasks.set(projectName, tasks);
15723
- }
15923
+ if (!client) {
15924
+ setDbError(true);
15925
+ return;
15724
15926
  }
15725
- } catch {
15726
- }
15727
- const projectList = [];
15728
- for (const [exeSession, projectDir] of exeSessions) {
15729
- 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();
15730
15961
  const employeeNames = roster.map((e) => e.name).filter((n) => n !== "exe");
15731
- let activeCount = tmuxSessions.has(exeSession) ? 1 : 0;
15732
- for (const empName of employeeNames) {
15733
- if (tmuxSessions.has(`${empName}-${exeSession}`)) activeCount++;
15734
- }
15735
- const totalCount = 1 + employeeNames.length;
15736
- const memoryCount = projectMemoryCounts.get(projectName) ?? 0;
15737
- const hasGit = projectDir ? existsSync22(join(projectDir, ".git")) : false;
15738
- const hasActivity = memoryCount > 0;
15739
- let type = "automation";
15740
- if (hasGit && hasActivity) type = "code";
15741
- else if (hasGit && !hasActivity) type = "reference";
15742
- projectList.push({
15743
- projectName,
15744
- exeSession,
15745
- projectDir,
15746
- employeeCount: totalCount,
15747
- activeCount,
15748
- memoryCount,
15749
- status: activeCount > 0 ? "active" : "idle",
15750
- type,
15751
- recentTasks: projectRecentTasks.get(projectName) ?? []
15752
- });
15753
- }
15754
- const knownDirs = [
15755
- process.env.HOME ? join(process.env.HOME, "openclaw") : null,
15756
- process.env.HOME ? join(process.env.HOME, "agno") : null
15757
- ].filter(Boolean);
15758
- const activeProjectNames = new Set(projectList.map((p) => p.projectName));
15759
- for (const dir of knownDirs) {
15760
- const name = dir.split("/").filter(Boolean).pop() ?? "";
15761
- if (activeProjectNames.has(name) || !existsSync22(dir) || !existsSync22(join(dir, ".git"))) continue;
15762
- if ((projectMemoryCounts.get(name) ?? 0) > 0) continue;
15763
- projectList.push({
15764
- projectName: name,
15765
- exeSession: "",
15766
- projectDir: dir,
15767
- employeeCount: 0,
15768
- activeCount: 0,
15769
- memoryCount: 0,
15770
- status: "offline",
15771
- type: "reference",
15772
- recentTasks: []
15773
- });
15774
- }
15775
- const dbActiveProjectNames = new Set(projectList.map((p) => p.projectName));
15776
- try {
15777
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
15778
- const client = getClient2();
15779
- if (client) {
15780
- const dbProjects = await client.execute(
15781
- `SELECT DISTINCT project_name FROM memories WHERE project_name IS NOT NULL AND project_name != ''
15782
- UNION
15783
- SELECT DISTINCT project_name FROM tasks WHERE project_name IS NOT NULL AND project_name != ''
15784
- ORDER BY project_name`
15785
- );
15786
- for (const row of dbProjects.rows) {
15787
- const name = String(row.project_name);
15788
- if (dbActiveProjectNames.has(name)) continue;
15789
- const memCount = projectMemoryCounts.get(name) ?? 0;
15790
- const agentResult = await client.execute({
15791
- sql: "SELECT COUNT(DISTINCT agent_id) as cnt FROM memories WHERE project_name = ?",
15792
- args: [name]
15793
- });
15794
- const agentCount = Number(agentResult.rows[0]?.cnt ?? 0);
15795
- projectList.push({
15796
- projectName: name,
15797
- exeSession: "",
15798
- projectDir: "",
15799
- employeeCount: agentCount,
15800
- activeCount: 0,
15801
- memoryCount: memCount,
15802
- status: "offline",
15803
- type: "code",
15804
- recentTasks: projectRecentTasks.get(name) ?? []
15805
- });
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
+ }
15806
15969
  }
15807
15970
  }
15808
- } catch {
15809
- }
15810
- if (projectList.filter((p) => p.type !== "reference").length === 0) {
15811
- projectList.unshift({
15812
- projectName: "(no active sessions)",
15813
- exeSession: "",
15814
- projectDir: "",
15815
- employeeCount: 0,
15816
- activeCount: 0,
15817
- memoryCount: 0,
15818
- status: "offline",
15819
- type: "code",
15820
- 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;
15821
16004
  });
15822
- }
15823
- setProjects(projectList);
15824
- try {
15825
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
15826
- const client = getClient2();
15827
- if (client) {
15828
- const result = await client.execute("SELECT COUNT(*) as cnt FROM memories");
15829
- 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 {
15830
16012
  }
15831
- } catch {
15832
- }
15833
- try {
15834
- const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
15835
- setHealth((h) => ({ ...h, daemon: existsSync22(pidPath) ? "running" : "stopped" }));
15836
- } catch {
15837
- }
15838
- try {
15839
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
15840
- const client = getClient2();
15841
- if (client) {
15842
- const activityResult = await client.execute(
15843
- "SELECT agent_id, tool_name, project_name, created_at, text FROM memories ORDER BY created_at DESC LIMIT 20"
15844
- );
15845
- if (activityResult.rows.length > 0) {
15846
- setActivity(activityResult.rows.slice(0, 8).map((r) => ({
15847
- time: new Date(String(r.created_at)).toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", hour12: false }),
15848
- agent: String(r.agent_id ?? "system"),
15849
- action: String(r.text ?? "").slice(0, 60)
15850
- })));
15851
- }
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
+ })));
15852
16022
  }
15853
16023
  } catch {
16024
+ setDbError(true);
16025
+ } finally {
16026
+ setLoading(false);
15854
16027
  }
15855
- } catch {
15856
- setDbError(true);
15857
- } finally {
15858
- setLoading(false);
15859
- }
16028
+ });
15860
16029
  }
15861
16030
  const daemonColor = health.daemon === "running" ? "green" : health.daemon === "stopped" ? "red" : "gray";
15862
16031
  const handleChatSubmit = useCallback4((value) => {
@@ -15948,7 +16117,7 @@ function CommandCenterView({
15948
16117
  ] }),
15949
16118
  /* @__PURE__ */ jsx7(Text, { color: "#6B4C9A", children: "\u2191\u2193 navigate | c to chat | Enter to open | Escape to detach" }),
15950
16119
  /* @__PURE__ */ jsx7(Text, { children: " " }),
15951
- 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." }) : (() => {
15952
16121
  const sections = [];
15953
16122
  let currentProjects = [];
15954
16123
  let sectionKey = 0;
@@ -15978,9 +16147,9 @@ function CommandCenterView({
15978
16147
  ] })
15979
16148
  ] }),
15980
16149
  /* @__PURE__ */ jsxs5(Text, { color: isSelected ? "#F0EDE8" : "#6B4C9A", children: [
15981
- entry.employeeCount,
16150
+ entry.openTaskCount,
15982
16151
  " ",
15983
- entry.employeeCount === 1 ? "employee" : "employees",
16152
+ entry.openTaskCount === 1 ? "task" : "tasks",
15984
16153
  " \xB7 ",
15985
16154
  entry.memoryCount.toLocaleString(),
15986
16155
  " memories"
@@ -16058,6 +16227,7 @@ var init_CommandCenter = __esm({
16058
16227
  init_DemoContext();
16059
16228
  init_demo_data();
16060
16229
  init_useAgentLoop();
16230
+ init_employee_templates();
16061
16231
  }
16062
16232
  });
16063
16233
 
@@ -16182,7 +16352,7 @@ var init_TmuxPane = __esm({
16182
16352
  });
16183
16353
 
16184
16354
  // src/lib/task-router.ts
16185
- import { randomUUID as randomUUID4 } from "crypto";
16355
+ import { randomUUID as randomUUID5 } from "crypto";
16186
16356
  function resolveBloomRouting(complexity, config = DEFAULT_BLOOM_CONFIG) {
16187
16357
  const tier = config.complexityToTier[complexity];
16188
16358
  const rule = config.tierRules[tier];
@@ -16834,9 +17004,15 @@ async function createTaskCore(input) {
16834
17004
  }
16835
17005
  }
16836
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
+ }
16837
17013
  await client.execute({
16838
- 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)
16839
- 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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
16840
17016
  args: [
16841
17017
  id,
16842
17018
  input.title,
@@ -16855,6 +17031,7 @@ async function createTaskCore(input) {
16855
17031
  input.budgetFallbackModel ?? null,
16856
17032
  0,
16857
17033
  null,
17034
+ sessionScope,
16858
17035
  now,
16859
17036
  now
16860
17037
  ]
@@ -16899,9 +17076,18 @@ async function listTasks(input) {
16899
17076
  conditions.push("priority = ?");
16900
17077
  args2.push(input.priority);
16901
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
+ }
16902
17088
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
16903
17089
  const result = await client.execute({
16904
- 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`,
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`,
16905
17091
  args: args2
16906
17092
  });
16907
17093
  return result.rows.map((r) => ({
@@ -17122,6 +17308,34 @@ async function listPendingReviews(limit) {
17122
17308
  });
17123
17309
  return result.rows;
17124
17310
  }
17311
+ async function cleanupOrphanedReviews() {
17312
+ const client = getClient();
17313
+ const now = (/* @__PURE__ */ new Date()).toISOString();
17314
+ const r1 = await client.execute({
17315
+ sql: `UPDATE tasks SET status = 'done', updated_at = ?
17316
+ WHERE status = 'needs_review'
17317
+ AND assigned_by = 'system'
17318
+ AND title LIKE 'Review:%'
17319
+ AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
17320
+ args: [now]
17321
+ });
17322
+ const staleThreshold = new Date(Date.now() - 60 * 60 * 1e3).toISOString();
17323
+ const r2 = await client.execute({
17324
+ sql: `UPDATE tasks SET status = 'done', updated_at = ?
17325
+ WHERE status = 'needs_review'
17326
+ AND result IS NOT NULL
17327
+ AND updated_at < ?`,
17328
+ args: [now, staleThreshold]
17329
+ });
17330
+ const total = r1.rowsAffected + r2.rowsAffected;
17331
+ if (total > 0) {
17332
+ process.stderr.write(
17333
+ `[cleanup] Closed ${total} orphaned review(s): ${r1.rowsAffected} cascade + ${r2.rowsAffected} stale
17334
+ `
17335
+ );
17336
+ }
17337
+ return total;
17338
+ }
17125
17339
  function getReviewChecklist(role, agent, taskSlug) {
17126
17340
  const roleLower = role.toLowerCase();
17127
17341
  if (roleLower.includes("engineer") || roleLower === "principal engineer") {
@@ -17235,6 +17449,7 @@ var init_tasks_review = __esm({
17235
17449
  init_tasks_crud();
17236
17450
  init_tmux_routing();
17237
17451
  init_session_key();
17452
+ init_state_bus();
17238
17453
  }
17239
17454
  });
17240
17455
 
@@ -17399,13 +17614,12 @@ function assertSessionScope(actionType, targetProject) {
17399
17614
  };
17400
17615
  }
17401
17616
  process.stderr.write(
17402
- `[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}"
17403
17618
  `
17404
17619
  );
17405
17620
  return {
17406
- allowed: true,
17407
- // v1: warn-only, don't block
17408
- reason: "cross_session_granted",
17621
+ allowed: false,
17622
+ reason: "cross_session_denied",
17409
17623
  currentProject,
17410
17624
  targetProject,
17411
17625
  targetSession: findSessionForProject(targetProject)?.windowName
@@ -17431,8 +17645,9 @@ async function dispatchTaskToEmployee(input) {
17431
17645
  try {
17432
17646
  const { assertSessionScope: assertSessionScope2 } = (init_session_scope(), __toCommonJS(session_scope_exports));
17433
17647
  const check = assertSessionScope2("dispatch_task", input.projectName);
17434
- if (check.reason === "cross_session_granted") {
17648
+ if (check.reason === "cross_session_denied") {
17435
17649
  crossProject = true;
17650
+ return { dispatched: "skipped", crossProject: true };
17436
17651
  }
17437
17652
  } catch {
17438
17653
  }
@@ -17801,6 +18016,7 @@ var init_skill_learning = __esm({
17801
18016
  // src/lib/tasks.ts
17802
18017
  var tasks_exports = {};
17803
18018
  __export(tasks_exports, {
18019
+ cleanupOrphanedReviews: () => cleanupOrphanedReviews,
17804
18020
  countNewPendingReviewsSince: () => countNewPendingReviewsSince,
17805
18021
  countPendingReviews: () => countPendingReviews,
17806
18022
  createTask: () => createTask,
@@ -17866,6 +18082,21 @@ async function updateTask(input) {
17866
18082
  });
17867
18083
  } catch {
17868
18084
  }
18085
+ try {
18086
+ const client = getClient();
18087
+ const cascaded = await client.execute({
18088
+ sql: `UPDATE tasks SET status = 'done', updated_at = ?
18089
+ WHERE parent_task_id = ? AND status = 'needs_review'`,
18090
+ args: [now, taskId]
18091
+ });
18092
+ if (cascaded.rowsAffected > 0) {
18093
+ process.stderr.write(
18094
+ `[cascade] Closed ${cascaded.rowsAffected} orphaned review task(s) for parent ${taskId}
18095
+ `
18096
+ );
18097
+ }
18098
+ } catch {
18099
+ }
17869
18100
  }
17870
18101
  const isTerminal = input.status === "done" || input.status === "needs_review";
17871
18102
  if (isTerminal) {
@@ -17879,6 +18110,13 @@ async function updateTask(input) {
17879
18110
  await cascadeUnblock(taskId, input.baseDir, now);
17880
18111
  } catch {
17881
18112
  }
18113
+ orgBus.emit({
18114
+ type: "task_completed",
18115
+ taskId,
18116
+ employee: String(row.assigned_to),
18117
+ result: input.result ?? "",
18118
+ timestamp: now
18119
+ });
17882
18120
  if (row.parent_task_id) {
17883
18121
  try {
17884
18122
  await checkSubtaskCompletion(String(row.parent_task_id), String(row.project_name));
@@ -17946,6 +18184,7 @@ var init_tasks = __esm({
17946
18184
  init_database();
17947
18185
  init_config();
17948
18186
  init_notifications();
18187
+ init_state_bus();
17949
18188
  init_tasks_crud();
17950
18189
  init_tasks_review();
17951
18190
  init_tasks_crud();
@@ -18336,9 +18575,29 @@ function getMySession() {
18336
18575
  return getTransport().getMySession();
18337
18576
  }
18338
18577
  function employeeSessionName(employee, exeSession, instance) {
18339
- const suffix = instance != null && instance > 0 ? String(instance) : "";
18340
- return `${employee}${suffix}-${exeSession}`;
18341
- }
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
+ }
18592
+ const suffix = instance != null && instance > 0 ? String(instance) : "";
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;
18600
+ }
18342
18601
  function parseParentExe(sessionName, agentId) {
18343
18602
  const escaped = agentId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
18344
18603
  const regex = new RegExp(`^${escaped}\\d*-(.+)$`);
@@ -18577,6 +18836,22 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
18577
18836
  error: `Error: pass employee name ('${bare}'), not session name ('${employeeName}')`
18578
18837
  };
18579
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
+ }
18580
18855
  let effectiveInstance = opts?.instance;
18581
18856
  if (effectiveInstance === void 0 && opts?.autoInstance) {
18582
18857
  const free = findFreeInstance(
@@ -18823,7 +19098,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
18823
19098
  releaseSpawnLock2(sessionName);
18824
19099
  return { sessionName };
18825
19100
  }
18826
- 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;
18827
19102
  var init_tmux_routing = __esm({
18828
19103
  "src/lib/tmux-routing.ts"() {
18829
19104
  "use strict";
@@ -18838,6 +19113,7 @@ var init_tmux_routing = __esm({
18838
19113
  SPAWN_LOCK_DIR = path30.join(os10.homedir(), ".exe-os", "spawn-locks");
18839
19114
  SESSION_CACHE = path30.join(os10.homedir(), ".exe-os", "session-cache");
18840
19115
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
19116
+ VALID_SESSION_NAME = /^[a-z]+-exe\d+$|^[a-z]+\d+-exe\d+$/;
18841
19117
  VERIFY_PANE_LINES = 200;
18842
19118
  INTERCOM_DEBOUNCE_MS = 3e4;
18843
19119
  INTERCOM_LOG2 = path30.join(os10.homedir(), ".exe-os", "intercom.log");
@@ -19244,6 +19520,7 @@ function SessionsView({
19244
19520
  const [viewingEmployee, setViewingEmployee] = useState9(null);
19245
19521
  const [viewingProject, setViewingProject] = useState9(null);
19246
19522
  const [loading, setLoading] = useState9(!demo);
19523
+ const [sessionError, setSessionError] = useState9(null);
19247
19524
  const [tmuxAvailable, setTmuxAvailable] = useState9(true);
19248
19525
  const orch = useOrchestrator(!demo);
19249
19526
  const [carouselEmployees, setCarouselEmployees] = useState9([]);
@@ -19419,111 +19696,116 @@ function SessionsView({
19419
19696
  return ACTIVE_PANE_PATTERN.test(lines.join("\n")) ? "active" : "idle";
19420
19697
  }
19421
19698
  async function loadSessions() {
19422
- try {
19423
- const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
19424
- const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2, capturePaneLines: capturePaneLines2, parseActivity: parseActivity2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
19425
- const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
19426
- const { execSync: execSync13 } = await import("child_process");
19427
- if (!inTmux2()) {
19428
- setTmuxAvailable(false);
19429
- setProjects([]);
19430
- return;
19431
- }
19432
- setTmuxAvailable(true);
19433
- const attachedMap = /* @__PURE__ */ new Map();
19699
+ const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
19700
+ return withTrace2("tui.sessions.loadSessions", async () => {
19434
19701
  try {
19435
- const out = execSync13("tmux list-sessions -F '#{session_name}:#{session_attached}' 2>/dev/null", {
19436
- encoding: "utf8",
19437
- timeout: 3e3
19438
- });
19439
- for (const line of out.trim().split("\n").filter(Boolean)) {
19440
- const sepIdx = line.lastIndexOf(":");
19441
- if (sepIdx > 0) {
19442
- 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
+ }
19443
19723
  }
19724
+ } catch {
19444
19725
  }
19445
- } catch {
19446
- }
19447
- const registry = listSessions2();
19448
- const tmuxSessions = new Set(listTmuxSessions2());
19449
- const roster = await loadEmployees2();
19450
- const exeSessions = /* @__PURE__ */ new Map();
19451
- for (const entry of registry) {
19452
- if (entry.agentId === "exe" && tmuxSessions.has(entry.windowName)) {
19453
- exeSessions.set(entry.windowName, entry.projectDir);
19454
- }
19455
- }
19456
- for (const s of tmuxSessions) {
19457
- if (/^exe\d+$/.test(s) && !exeSessions.has(s)) {
19458
- exeSessions.set(s, "");
19459
- }
19460
- }
19461
- const projectList = [];
19462
- for (const [exeSession, projectDir] of exeSessions) {
19463
- const projectName = projectDir.split("/").filter(Boolean).pop() ?? exeSession;
19464
- const exeHasSession = tmuxSessions.has(exeSession);
19465
- let exeStatus = "offline";
19466
- let exeActivity = "";
19467
- if (exeHasSession) {
19468
- const exeLines = capturePaneLines2(exeSession, 10);
19469
- exeStatus = statusFromPaneLines(exeLines);
19470
- exeActivity = exeLines.length > 0 ? parseActivity2(exeLines) : "";
19471
- }
19472
- const employeeEntries = roster.filter((e) => e.name !== "exe").map((emp) => {
19473
- const sessionName = `${emp.name}-${exeSession}`;
19474
- const hasSession = tmuxSessions.has(sessionName);
19475
- const isCrashed = !hasSession && orch.crashedSessions.includes(emp.name);
19476
- if (isCrashed) {
19477
- return {
19478
- name: emp.name,
19479
- role: emp.role,
19480
- status: "crashed",
19481
- sessionName,
19482
- activity: "Dead session \u2014 has open tasks",
19483
- attached: false
19484
- };
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);
19733
+ }
19734
+ }
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) : "";
19485
19750
  }
19486
- 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);
19487
19776
  return {
19488
19777
  name: emp.name,
19489
19778
  role: emp.role,
19490
- status: "offline",
19779
+ status: statusFromPaneLines(lines),
19491
19780
  sessionName,
19492
- activity: "",
19493
- attached: false
19781
+ activity: lines.length > 0 ? parseActivity2(lines) : "",
19782
+ attached: attachedMap.get(sessionName) ?? false
19494
19783
  };
19495
- }
19496
- const lines = capturePaneLines2(sessionName, 10);
19497
- return {
19498
- name: emp.name,
19499
- role: emp.role,
19500
- status: statusFromPaneLines(lines),
19501
- sessionName,
19502
- activity: lines.length > 0 ? parseActivity2(lines) : "",
19503
- attached: attachedMap.get(sessionName) ?? false
19504
- };
19505
- });
19506
- const employees = [
19507
- {
19508
- name: "exe",
19509
- role: "COO",
19510
- status: exeStatus,
19511
- sessionName: exeSession,
19512
- activity: exeActivity,
19513
- attached: attachedMap.get(exeSession) ?? false
19514
- },
19515
- ...employeeEntries
19516
- ];
19517
- projectList.push({ projectName, exeSession, projectDir, employees });
19518
- }
19519
- if (projectList.length === 0) {
19520
- 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);
19521
19807
  }
19522
- setProjects(projectList);
19523
- } catch {
19524
- } finally {
19525
- setLoading(false);
19526
- }
19808
+ });
19527
19809
  }
19528
19810
  if (viewingEmployee) {
19529
19811
  const inCarousel = carouselEmployees.length > 0;
@@ -19605,7 +19887,10 @@ function SessionsView({
19605
19887
  ] })
19606
19888
  ] }),
19607
19889
  /* @__PURE__ */ jsx9(Text, { children: " " }),
19608
- 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) => {
19609
19894
  const isSelected = i === selectedIdx;
19610
19895
  const marker = isSelected ? "\u25B8 " : " ";
19611
19896
  if (item.type === "project") {
@@ -19706,7 +19991,7 @@ function TasksView({ onBack }) {
19706
19991
  const demo = useDemo();
19707
19992
  const [allTasks, setAllTasks] = useState10([]);
19708
19993
  const [loading, setLoading] = useState10(!demo);
19709
- const [dbError, setDbError] = useState10(false);
19994
+ const [dbError, setDbError] = useState10(null);
19710
19995
  const [selectedTask, setSelectedTask] = useState10(0);
19711
19996
  const [showDetail, setShowDetail] = useState10(false);
19712
19997
  const [statusFilter, setStatusFilter] = useState10("all");
@@ -19778,40 +20063,43 @@ function TasksView({ onBack }) {
19778
20063
  }
19779
20064
  });
19780
20065
  async function loadTasks() {
19781
- try {
19782
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
19783
- const client = getClient2();
19784
- if (client) {
19785
- const result = await client.execute(
19786
- `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
19787
20074
  FROM tasks
19788
20075
  ORDER BY
19789
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,
19790
20077
  CASE priority WHEN 'p0' THEN 0 WHEN 'p1' THEN 1 WHEN 'p2' THEN 2 ELSE 3 END,
19791
20078
  created_at DESC`
19792
- );
19793
- setAllTasks(
19794
- result.rows.map((r) => ({
19795
- id: String(r.id ?? ""),
19796
- priority: String(r.priority ?? "p2").toUpperCase(),
19797
- title: String(r.title ?? ""),
19798
- assignee: String(r.assigned_to ?? ""),
19799
- assignedBy: String(r.assigned_by ?? ""),
19800
- status: String(r.status ?? "open"),
19801
- project: String(r.project_name ?? ""),
19802
- createdAt: String(r.created_at ?? ""),
19803
- result: String(r.result ?? "")
19804
- }))
19805
- );
19806
- setDbError(false);
19807
- } else {
19808
- 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);
19809
20101
  }
19810
- } catch {
19811
- setDbError(true);
19812
- } finally {
19813
- setLoading(false);
19814
- }
20102
+ });
19815
20103
  }
19816
20104
  const selected = taskRows[selectedTask]?.task;
19817
20105
  if (showDetail && selected) {
@@ -19878,7 +20166,10 @@ function TasksView({ onBack }) {
19878
20166
  filterLabel
19879
20167
  ] }),
19880
20168
  /* @__PURE__ */ jsx10(Text, { children: " " }),
19881
- 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) => {
19882
20173
  if (row.type === "header") {
19883
20174
  return /* @__PURE__ */ jsxs8(Box_default, { marginTop: i > 0 ? 1 : 0, children: [
19884
20175
  /* @__PURE__ */ jsx10(Text, { bold: true, color: "#F0EDE8", children: row.project }),
@@ -20565,6 +20856,31 @@ function GatewayView({ onBack }) {
20565
20856
  /* @__PURE__ */ jsx11(Text, { color: "#6B4C9A", children: "Loading gateway status..." })
20566
20857
  ] });
20567
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
+ }
20568
20884
  if (!gateway.running && gateway.adapters.length === 0 && !gateway.gatewayUrl) {
20569
20885
  return /* @__PURE__ */ jsxs9(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
20570
20886
  /* @__PURE__ */ jsx11(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx11(Text, { bold: true, children: "Gateway Monitor" }) }),
@@ -20851,14 +21167,30 @@ var init_agent_status = __esm({
20851
21167
  // src/tui/views/Team.tsx
20852
21168
  import React22, { useState as useState12, useEffect as useEffect14 } from "react";
20853
21169
  import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
20854
- 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 }) {
20855
21186
  const demo = useDemo();
20856
21187
  const [members, setMembers] = useState12([]);
20857
21188
  const [externals, setExternals] = useState12([]);
20858
21189
  const [loading, setLoading] = useState12(!demo);
20859
- const [dbError, setDbError] = useState12(false);
21190
+ const [dbError, setDbError] = useState12(null);
20860
21191
  const [selectedIdx, setSelectedIdx] = useState12(0);
20861
21192
  const [showDetail, setShowDetail] = useState12(false);
21193
+ const [showAddHint, setShowAddHint] = useState12(false);
20862
21194
  const orch = useOrchestrator(!demo);
20863
21195
  const orchEmployeeMap = new Map(orch.employees.map((e) => [e.name, e]));
20864
21196
  const allItems = [...members, ...externals.map((e) => ({ ...e, activity: "", memoryCount: 0 }))];
@@ -20873,7 +21205,7 @@ function TeamView({ onBack }) {
20873
21205
  const timer = setInterval(loadTeam, 5e3);
20874
21206
  return () => clearInterval(timer);
20875
21207
  }, []);
20876
- use_input_default((_input, key) => {
21208
+ use_input_default((input, key) => {
20877
21209
  if (showDetail) {
20878
21210
  if (key.escape) setShowDetail(false);
20879
21211
  return;
@@ -20882,91 +21214,103 @@ function TeamView({ onBack }) {
20882
21214
  onBack?.();
20883
21215
  return;
20884
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
+ }
20885
21226
  if (key.upArrow && selectedIdx > 0) setSelectedIdx(selectedIdx - 1);
20886
21227
  if (key.downArrow && selectedIdx < allItems.length - 1) setSelectedIdx(selectedIdx + 1);
20887
21228
  if (key.return && allItems.length > 0) setShowDetail(true);
20888
21229
  });
20889
21230
  async function loadTeam() {
20890
- try {
20891
- const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
20892
- const roster = await loadEmployees2();
20893
- let memoryCounts = /* @__PURE__ */ new Map();
20894
- let projectsByEmployee = /* @__PURE__ */ new Map();
20895
- let currentTaskByEmployee = /* @__PURE__ */ new Map();
21231
+ const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
21232
+ return withTrace2("tui.team.loadTeam", async () => {
20896
21233
  try {
20897
- const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
20898
- const client = getClient2();
20899
- if (client) {
20900
- const memResult = await client.execute("SELECT agent_id, COUNT(*) as cnt FROM memories GROUP BY agent_id");
20901
- for (const row of memResult.rows) {
20902
- memoryCounts.set(String(row.agent_id), Number(row.cnt));
20903
- }
20904
- for (const emp of roster) {
20905
- const projResult = await client.execute({
20906
- 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,
20907
21250
  MAX(CASE WHEN status = 'in_progress' THEN 1 WHEN status = 'open' THEN 2 ELSE 3 END) as urgency
20908
21251
  FROM tasks WHERE assigned_to = ? AND status IN ('open','in_progress','done')
20909
21252
  GROUP BY project_name ORDER BY urgency ASC LIMIT 5`,
20910
- args: [emp.name]
20911
- });
20912
- const projects = projResult.rows.filter((r) => !DEPRECATED_PROJECTS.has(String(r.project_name))).map((r) => {
20913
- const urgency = Number(r.urgency);
20914
- let pStatus = "idle";
20915
- if (urgency === 1) pStatus = "active";
20916
- else if (urgency === 2) pStatus = "has_tasks";
20917
- return { name: String(r.project_name), status: pStatus };
20918
- });
20919
- if (projects.length > 0) projectsByEmployee.set(emp.name, projects);
20920
- }
20921
- for (const emp of roster) {
20922
- const taskResult = await client.execute({
20923
- sql: "SELECT title FROM tasks WHERE assigned_to = ? AND status = 'in_progress' ORDER BY updated_at DESC LIMIT 1",
20924
- args: [emp.name]
20925
- });
20926
- if (taskResult.rows.length > 0) {
20927
- 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
+ }
20928
21272
  }
20929
21273
  }
21274
+ } catch {
20930
21275
  }
20931
- } catch {
20932
- }
20933
- const teamData = roster.map((emp) => {
20934
- const agentSt = getAgentStatus(emp.name);
20935
- return {
20936
- name: emp.name,
20937
- role: emp.role,
20938
- status: agentSt.label,
20939
- activity: agentSt.label === "active" ? "Processing..." : "",
20940
- memoryCount: memoryCounts.get(emp.name) ?? 0,
20941
- projects: projectsByEmployee.get(emp.name),
20942
- currentTask: currentTaskByEmployee.get(emp.name),
20943
- sessionName: agentSt.session
20944
- };
20945
- });
20946
- setMembers(teamData);
20947
- setDbError(false);
20948
- try {
20949
- const { existsSync: existsSync22, readFileSync: readFileSync18 } = await import("fs");
20950
- const { join } = await import("path");
20951
- const home = process.env.HOME ?? "";
20952
- const gatewayConfig = join(home, ".exe-os", "gateway.json");
20953
- if (existsSync22(gatewayConfig)) {
20954
- const raw = JSON.parse(readFileSync18(gatewayConfig, "utf8"));
20955
- if (raw.agents && raw.agents.length > 0) {
20956
- setExternals(raw.agents.map((a) => ({
20957
- name: a.name,
20958
- role: a.role ?? "external agent",
20959
- status: "offline"
20960
- })));
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
+ }
20961
21305
  }
21306
+ } catch {
20962
21307
  }
20963
- } catch {
21308
+ } catch (err) {
21309
+ setDbError(err instanceof Error ? err.message : "Unknown error");
21310
+ } finally {
21311
+ setLoading(false);
20964
21312
  }
20965
- } catch {
20966
- setDbError(true);
20967
- } finally {
20968
- setLoading(false);
20969
- }
21313
+ });
20970
21314
  }
20971
21315
  const totalCount = members.length + externals.length;
20972
21316
  const selected = allItems[selectedIdx];
@@ -20983,7 +21327,7 @@ function TeamView({ onBack }) {
20983
21327
  /* @__PURE__ */ jsx12(Text, { children: " " }),
20984
21328
  /* @__PURE__ */ jsxs10(Text, { children: [
20985
21329
  "Role: ",
20986
- /* @__PURE__ */ jsx12(Text, { bold: true, children: selected.role })
21330
+ /* @__PURE__ */ jsx12(Text, { bold: true, color: roleBadgeColor(selected.role), children: selected.role })
20987
21331
  ] }),
20988
21332
  /* @__PURE__ */ jsxs10(Text, { children: [
20989
21333
  "Status: ",
@@ -21012,8 +21356,9 @@ function TeamView({ onBack }) {
21012
21356
  /* @__PURE__ */ jsx12(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx12(Text, { bold: true, children: "Team Roster" }) }),
21013
21357
  /* @__PURE__ */ jsxs10(Text, { color: "#6B4C9A", children: [
21014
21358
  totalCount,
21015
- " agents | up/down navigate | Enter for details"
21359
+ " agents | \u2191\u2193 navigate | Enter details | a add | s sessions"
21016
21360
  ] }),
21361
+ showAddHint && /* @__PURE__ */ jsx12(Text, { color: "#F5D76E", children: "Run /exe-new-employee <name> from CLI to add an employee." }),
21017
21362
  !demo && orch.pendingReviews > 0 && /* @__PURE__ */ jsxs10(Text, { color: "#6B4C9A", children: [
21018
21363
  orch.pendingReviews,
21019
21364
  " review(s) pending exe attention"
@@ -21021,7 +21366,10 @@ function TeamView({ onBack }) {
21021
21366
  /* @__PURE__ */ jsx12(Text, { children: " " }),
21022
21367
  /* @__PURE__ */ jsx12(Text, { bold: true, children: "INTERNAL" }),
21023
21368
  /* @__PURE__ */ jsx12(Text, { children: " " }),
21024
- 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) => {
21025
21373
  const isSelected = i === selectedIdx;
21026
21374
  const orchEmp = orchEmployeeMap.get(m.name);
21027
21375
  return /* @__PURE__ */ jsxs10(
@@ -21037,7 +21385,7 @@ function TeamView({ onBack }) {
21037
21385
  /* @__PURE__ */ jsxs10(Box_default, { gap: 1, children: [
21038
21386
  /* @__PURE__ */ jsx12(StatusDot, { status: m.status, showLabel: false }),
21039
21387
  /* @__PURE__ */ jsx12(Text, { bold: true, color: isSelected ? "#F5D76E" : "#F0EDE8", children: m.name }),
21040
- /* @__PURE__ */ jsxs10(Text, { color: isSelected ? "#F5D76E" : "#6B4C9A", children: [
21388
+ /* @__PURE__ */ jsxs10(Text, { color: isSelected ? "#F5D76E" : roleBadgeColor(m.role), children: [
21041
21389
  "(",
21042
21390
  m.role,
21043
21391
  ")"
@@ -21263,6 +21611,26 @@ var init_wiki_client = __esm({
21263
21611
  import React23, { useState as useState13, useEffect as useEffect15 } from "react";
21264
21612
  import TextInput2 from "ink-text-input";
21265
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
+ }
21266
21634
  function WikiView({ onBack }) {
21267
21635
  const demo = useDemo();
21268
21636
  const [activePanel, setActivePanel] = useState13("Workspaces");
@@ -21273,6 +21641,12 @@ function WikiView({ onBack }) {
21273
21641
  const [chatHistory, setChatHistory] = useState13([]);
21274
21642
  const [chatInput, setChatInput] = useState13("");
21275
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);
21276
21650
  const [loading, setLoading] = useState13(true);
21277
21651
  const [connected, setConnected] = useState13(false);
21278
21652
  const [wikiUrl, setWikiUrl] = useState13("");
@@ -21366,6 +21740,55 @@ function WikiView({ onBack }) {
21366
21740
  setSending(false);
21367
21741
  }
21368
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]);
21369
21792
  async function selectWorkspace(idx) {
21370
21793
  setSelectedWorkspaceIdx(idx);
21371
21794
  const ws = workspaces[idx];
@@ -21386,6 +21809,32 @@ function WikiView({ onBack }) {
21386
21809
  }
21387
21810
  }
21388
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
+ }
21389
21838
  if (chatFocused) {
21390
21839
  if (key.escape) {
21391
21840
  setChatFocused(false);
@@ -21397,6 +21846,13 @@ function WikiView({ onBack }) {
21397
21846
  }
21398
21847
  return;
21399
21848
  }
21849
+ if (input === "/" && activePanel !== "Chat") {
21850
+ setSearchMode(true);
21851
+ setSearchQuery("");
21852
+ setSearchResults(demo ? DEMO_SEARCH_RESULTS : []);
21853
+ setExpandedResultIdx(null);
21854
+ return;
21855
+ }
21400
21856
  if (key.leftArrow) {
21401
21857
  const panelIdx = PANELS.indexOf(activePanel);
21402
21858
  if (panelIdx === 0) {
@@ -21474,9 +21930,53 @@ function WikiView({ onBack }) {
21474
21930
  /* @__PURE__ */ jsx13(Text, { bold: true, children: selectedWs.name })
21475
21931
  ] }) : null
21476
21932
  ] }),
21477
- /* @__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" }),
21478
21934
  /* @__PURE__ */ jsx13(Text, { children: " " }),
21479
- /* @__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: [
21480
21980
  /* @__PURE__ */ jsxs11(Box_default, { flexDirection: "column", width: "25%", children: [
21481
21981
  /* @__PURE__ */ jsxs11(Text, { bold: true, backgroundColor: panelHeaderBg("Workspaces"), color: panelHeaderColor("Workspaces"), children: [
21482
21982
  activePanel === "Workspaces" ? "\u25B8 " : " ",
@@ -21558,7 +22058,7 @@ function WikiView({ onBack }) {
21558
22058
  ] })
21559
22059
  ] });
21560
22060
  }
21561
- var PANELS, MAX_VISIBLE_MESSAGES;
22061
+ var PANELS, MAX_VISIBLE_MESSAGES, DEMO_SEARCH_RESULTS;
21562
22062
  var init_Wiki = __esm({
21563
22063
  async "src/tui/views/Wiki.tsx"() {
21564
22064
  "use strict";
@@ -21567,53 +22067,48 @@ var init_Wiki = __esm({
21567
22067
  init_demo_data();
21568
22068
  PANELS = ["Workspaces", "Documents", "Chat"];
21569
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
+ ];
21570
22077
  }
21571
22078
  });
21572
22079
 
21573
22080
  // src/tui/views/Settings.tsx
21574
- import React24, { useState as useState14, useEffect as useEffect16 } from "react";
22081
+ import React24, { useState as useState14, useEffect as useEffect16, useCallback as useCallback7 } from "react";
21575
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
+ }
21576
22087
  function SettingsView({ onBack }) {
21577
22088
  const [providers, setProviders] = useState14([]);
21578
- const [memory, setMemory] = useState14({
21579
- encryption: "checking...",
21580
- embeddingModel: "checking...",
21581
- cloudSync: "checking...",
21582
- searchMode: "checking..."
21583
- });
21584
- const [runtime, setRuntime] = useState14({
21585
- consolidation: true,
21586
- consolidationModel: "...",
21587
- skillLearning: true,
21588
- skillThreshold: 3,
21589
- skillModel: "...",
21590
- failoverChain: ["anthropic", "opencode", "gemini", "openai"]
21591
- });
21592
- 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: [] });
21593
22093
  const [selectedSection, setSelectedSection] = useState14(0);
21594
22094
  const [loading, setLoading] = useState14(true);
21595
- const [claudeCode, setClaudeCode] = useState14({ installed: false, hooksWired: false });
21596
- const [license, setLicense] = useState14({ valid: false, detail: "checking..." });
21597
- const [cloudSync, setCloudSync] = useState14({ configured: false, detail: "checking..." });
21598
- useEffect16(() => {
21599
- loadSettings().finally(() => setLoading(false));
21600
- }, []);
21601
- use_input_default((_input, key) => {
21602
- if (key.leftArrow) {
21603
- onBack?.();
21604
- return;
21605
- }
21606
- if (key.upArrow && selectedSection > 0) setSelectedSection(selectedSection - 1);
21607
- if (key.downArrow && selectedSection < SECTION_NAMES.length - 1) setSelectedSection(selectedSection + 1);
21608
- });
21609
- async function loadSettings() {
21610
- const providerList = [
21611
- { name: "Anthropic", configured: !!process.env.ANTHROPIC_API_KEY, detail: process.env.ANTHROPIC_API_KEY ? "ANTHROPIC_API_KEY set" : "not configured" },
21612
- { name: "OpenCode", configured: !!process.env.OPENCODE_API_KEY, detail: process.env.OPENCODE_API_KEY ? "OPENCODE_API_KEY set" : "not configured" },
21613
- { name: "Gemini", configured: !!process.env.GEMINI_API_KEY, detail: process.env.GEMINI_API_KEY ? "GEMINI_API_KEY set" : "not configured" },
21614
- { name: "OpenAI", configured: !!process.env.OPENAI_API_KEY, detail: process.env.OPENAI_API_KEY ? "OPENAI_API_KEY set" : "not configured" },
21615
- { 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"]
21616
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
+ });
21617
22112
  try {
21618
22113
  const { execSync: execSync13 } = await import("child_process");
21619
22114
  execSync13("curl -s --max-time 1 http://localhost:11434/api/tags", { timeout: 2e3 });
@@ -21625,112 +22120,101 @@ function SettingsView({ onBack }) {
21625
22120
  try {
21626
22121
  const { existsSync: existsSync22 } = await import("fs");
21627
22122
  const { join } = await import("path");
21628
- const home = process.env.HOME ?? "";
21629
- const hasKey = existsSync22(join(home, ".exe-os", "master.key"));
21630
22123
  const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
21631
22124
  const cfg = await loadConfig2();
21632
- setMemory({
21633
- encryption: hasKey ? "SQLCipher AES-256" : "not configured",
21634
- embeddingModel: cfg.modelFile ?? "unknown",
21635
- cloudSync: cfg.cloud ? "enabled" : "disabled",
21636
- searchMode: cfg.searchMode ?? "hybrid"
21637
- });
21638
- const rawCfg = cfg;
21639
- const chain = Array.isArray(rawCfg.failoverChain) ? rawCfg.failoverChain : ["anthropic", "opencode", "gemini", "openai"];
21640
- setRuntime({
21641
- consolidation: cfg.consolidationEnabled,
21642
- consolidationModel: cfg.consolidationModel,
21643
- skillLearning: cfg.skillLearning,
21644
- skillThreshold: cfg.skillThreshold,
21645
- skillModel: cfg.skillModel,
21646
- failoverChain: chain
21647
- });
21648
- } catch {
21649
- }
21650
- try {
21651
- const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
21652
- const roster = await loadEmployees2();
21653
- const { existsSync: existsSync22, readFileSync: readFileSync18 } = await import("fs");
21654
- const { join } = await import("path");
21655
22125
  const home = process.env.HOME ?? "";
21656
- const configPath = join(home, ".exe-os", "config.json");
21657
- let empConfig = {};
21658
- if (existsSync22(configPath)) {
21659
- try {
21660
- const raw = JSON.parse(readFileSync18(configPath, "utf8"));
21661
- if (raw.employees && typeof raw.employees === "object") {
21662
- empConfig = raw.employees;
21663
- }
21664
- } catch {
21665
- }
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: "" });
21666
22135
  }
21667
- setEmployeeModels(roster.filter((e) => e.name !== "exe").map((e) => ({
21668
- name: e.name,
21669
- model: empConfig[e.name]?.model ?? "claude-sonnet-4-6",
21670
- provider: empConfig[e.name]?.provider ?? "anthropic"
21671
- })));
21672
- } catch {
21673
- }
21674
- try {
21675
- const { existsSync: existsSync22, readFileSync: readFileSync18 } = await import("fs");
21676
- const { join } = await import("path");
21677
- const home = process.env.HOME ?? "";
21678
- const ccSettingsPath = join(home, ".claude", "settings.json");
21679
- const installed = existsSync22(ccSettingsPath);
21680
- let hooksWired = false;
21681
- if (installed) {
21682
- try {
21683
- const settings = JSON.parse(readFileSync18(ccSettingsPath, "utf8"));
21684
- const hooks = settings.hooks;
21685
- if (hooks) {
21686
- hooksWired = Object.values(hooks).flat().some((h) => h.command?.includes("exe-os") || h.command?.includes("exe-mem"));
21687
- }
21688
- } catch {
21689
- }
22136
+ const pidPath = join(home, ".exe-os", "exed.pid");
22137
+ let daemon = "unknown";
22138
+ try {
22139
+ daemon = existsSync22(pidPath) ? "running" : "stopped";
22140
+ } catch {
21690
22141
  }
21691
- setClaudeCode({ installed, hooksWired });
21692
- } catch {
21693
- }
21694
- try {
21695
- const { existsSync: existsSync22, readFileSync: readFileSync18 } = await import("fs");
21696
- const { join } = await import("path");
21697
- const home = process.env.HOME ?? "";
21698
- const licensePath = join(home, ".exe-os", "license.json");
21699
- 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 {
21700
22151
  try {
21701
- const lic = JSON.parse(readFileSync18(licensePath, "utf8"));
21702
- 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;
21703
22156
  } catch {
21704
- setLicense({ valid: false, detail: "invalid license file" });
21705
22157
  }
21706
- } else {
21707
- setLicense({ valid: false, detail: "no license" });
21708
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
+ });
21709
22166
  } catch {
21710
22167
  }
21711
22168
  try {
21712
- const { existsSync: existsSync22 } = await import("fs");
21713
- const { join } = await import("path");
21714
- const home = process.env.HOME ?? "";
21715
- const cloudPath = join(home, ".exe-os", "cloud.json");
21716
- if (existsSync22(cloudPath)) {
21717
- setCloudSync({ configured: true, detail: "configured" });
21718
- } else {
21719
- setCloudSync({ configured: false, detail: "not configured" });
21720
- }
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
+ });
21721
22179
  } catch {
22180
+ setLicense({ valid: false, plan: "FREE", expiresAt: null, deviceLimit: 1, employeeLimit: 1, memoryLimit: 5e3 });
21722
22181
  }
21723
- }
21724
- const statusColor = (ok) => ok ? "#22C55E" : "#6B4C9A";
21725
- const sectionColor = (idx) => selectedSection === idx ? "#F5D76E" : void 0;
21726
- 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;
21727
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();
21728
22212
  return /* @__PURE__ */ jsxs12(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
21729
22213
  /* @__PURE__ */ jsx14(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx14(Text, { bold: true, children: "Settings" }) }),
21730
- /* @__PURE__ */ jsx14(Text, { color: "#6B4C9A", children: "\u2191\u2193 navigate sections" }),
22214
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: "\u2191\u2193 navigate sections r refresh" }),
21731
22215
  /* @__PURE__ */ jsx14(Text, { children: " " }),
21732
22216
  loading && /* @__PURE__ */ jsxs12(Fragment5, { children: [
21733
- /* @__PURE__ */ jsx14(Text, { color: "#6B4C9A", children: "Loading settings..." }),
22217
+ /* @__PURE__ */ jsx14(Text, { color: DIM, children: "Loading settings..." }),
21734
22218
  /* @__PURE__ */ jsx14(Text, { children: " " })
21735
22219
  ] }),
21736
22220
  /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(0), color: sectionColor(0), children: [
@@ -21740,6 +22224,8 @@ function SettingsView({ onBack }) {
21740
22224
  /* @__PURE__ */ jsx14(Text, { children: " " }),
21741
22225
  providers.map((p) => /* @__PURE__ */ jsxs12(Text, { children: [
21742
22226
  " ",
22227
+ /* @__PURE__ */ jsx14(Text, { color: statusColor(p.configured), children: statusDot(p.configured) }),
22228
+ " ",
21743
22229
  p.name,
21744
22230
  ": ",
21745
22231
  /* @__PURE__ */ jsx14(Text, { color: statusColor(p.configured), children: p.detail })
@@ -21747,131 +22233,274 @@ function SettingsView({ onBack }) {
21747
22233
  /* @__PURE__ */ jsx14(Text, { children: " " }),
21748
22234
  /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(1), color: sectionColor(1), children: [
21749
22235
  sectionMarker(1),
21750
- "Memory"
22236
+ "Cloud Sync"
21751
22237
  ] }),
21752
22238
  /* @__PURE__ */ jsx14(Text, { children: " " }),
21753
- /* @__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: [
21754
22257
  " ",
21755
- "Encryption: ",
21756
- /* @__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" })
21757
22261
  ] }),
21758
- /* @__PURE__ */ jsxs12(Text, { children: [
21759
- " ",
21760
- "Embedding: ",
21761
- /* @__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"
21762
22266
  ] }),
22267
+ /* @__PURE__ */ jsx14(Text, { children: " " }),
21763
22268
  /* @__PURE__ */ jsxs12(Text, { children: [
21764
22269
  " ",
21765
- "Cloud sync: ",
21766
- /* @__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 })
21767
22272
  ] }),
21768
- /* @__PURE__ */ jsxs12(Text, { children: [
22273
+ license.expiresAt && /* @__PURE__ */ jsxs12(Text, { children: [
21769
22274
  " ",
21770
- "Search mode: ",
21771
- /* @__PURE__ */ jsx14(Text, { color: "#22C55E", children: memory.searchMode })
21772
- ] }),
21773
- /* @__PURE__ */ jsx14(Text, { children: " " }),
21774
- /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(2), color: sectionColor(2), children: [
21775
- sectionMarker(2),
21776
- "Runtime"
22275
+ "Expires: ",
22276
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: license.expiresAt.split("T")[0] })
21777
22277
  ] }),
21778
- /* @__PURE__ */ jsx14(Text, { children: " " }),
21779
22278
  /* @__PURE__ */ jsxs12(Text, { children: [
21780
22279
  " ",
21781
- "Consolidation: ",
21782
- /* @__PURE__ */ jsx14(Text, { color: runtime.consolidation ? "#22C55E" : "#6B4C9A", children: runtime.consolidation ? "enabled" : "disabled" }),
21783
- " (",
21784
- runtime.consolidationModel,
21785
- ")"
22280
+ "Devices: ",
22281
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: formatLimit(license.deviceLimit) })
21786
22282
  ] }),
21787
22283
  /* @__PURE__ */ jsxs12(Text, { children: [
21788
22284
  " ",
21789
- "Skill learning: ",
21790
- /* @__PURE__ */ jsx14(Text, { color: runtime.skillLearning ? "#22C55E" : "#6B4C9A", children: runtime.skillLearning ? "enabled" : "disabled" }),
21791
- " (threshold: ",
21792
- runtime.skillThreshold,
21793
- ")"
22285
+ "Employees: ",
22286
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: formatLimit(license.employeeLimit) })
21794
22287
  ] }),
21795
22288
  /* @__PURE__ */ jsxs12(Text, { children: [
21796
22289
  " ",
21797
- "Failover chain: ",
21798
- /* @__PURE__ */ jsx14(Text, { color: "#22C55E", children: runtime.failoverChain.join(" \u2192 ") })
22290
+ "Memory limit: ",
22291
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: formatLimit(license.memoryLimit) })
21799
22292
  ] }),
21800
22293
  /* @__PURE__ */ jsx14(Text, { children: " " }),
21801
22294
  /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(3), color: sectionColor(3), children: [
21802
22295
  sectionMarker(3),
21803
- "Employee Models"
22296
+ "System"
21804
22297
  ] }),
21805
22298
  /* @__PURE__ */ jsx14(Text, { children: " " }),
21806
- loading ? /* @__PURE__ */ jsxs12(Text, { color: "#6B4C9A", children: [
21807
- " ",
21808
- "Loading..."
21809
- ] }) : employeeModels.length === 0 ? /* @__PURE__ */ jsxs12(Text, { color: "#6B4C9A", children: [
22299
+ /* @__PURE__ */ jsxs12(Text, { children: [
21810
22300
  " ",
21811
- "No employees configured"
21812
- ] }) : 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: [
21813
22306
  " ",
21814
- e.name,
21815
- ": ",
21816
- /* @__PURE__ */ jsx14(Text, { color: "#22C55E", children: e.model }),
21817
- " ",
21818
- /* @__PURE__ */ jsxs12(Text, { color: "#6B4C9A", children: [
21819
- "via ",
21820
- e.provider
21821
- ] })
21822
- ] }, e.name)),
21823
- /* @__PURE__ */ jsx14(Text, { children: " " }),
21824
- /* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(4), color: sectionColor(4), children: [
21825
- sectionMarker(4),
21826
- "Integrations"
22307
+ "Version: ",
22308
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: system.version })
21827
22309
  ] }),
21828
- /* @__PURE__ */ jsx14(Text, { children: " " }),
21829
22310
  /* @__PURE__ */ jsxs12(Text, { children: [
21830
22311
  " ",
21831
- "Claude Code: ",
21832
- /* @__PURE__ */ jsx14(Text, { color: claudeCode.installed ? "#22C55E" : "#6B4C9A", children: claudeCode.installed ? "installed" : "not found" }),
21833
- claudeCode.installed && /* @__PURE__ */ jsxs12(Text, { color: claudeCode.hooksWired ? "#22C55E" : "#6B4C9A", children: [
21834
- " \u2014 hooks ",
21835
- claudeCode.hooksWired ? "wired" : "not wired"
21836
- ] })
22312
+ "Encryption: ",
22313
+ /* @__PURE__ */ jsx14(Text, { color: system.encryption.includes("AES") ? GREEN : DIM, children: system.encryption })
21837
22314
  ] }),
21838
22315
  /* @__PURE__ */ jsxs12(Text, { children: [
21839
22316
  " ",
21840
- "License: ",
21841
- /* @__PURE__ */ jsx14(Text, { color: license.valid ? "#22C55E" : "#6B4C9A", children: license.detail })
22317
+ "Embedding: ",
22318
+ /* @__PURE__ */ jsx14(Text, { color: GREEN, children: system.embeddingModel })
21842
22319
  ] }),
21843
22320
  /* @__PURE__ */ jsxs12(Text, { children: [
21844
22321
  " ",
21845
- "Cloud sync: ",
21846
- /* @__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" })
21847
22343
  ] })
21848
22344
  ] });
21849
22345
  }
21850
- var SECTION_NAMES;
22346
+ var SECTION_NAMES, GREEN, DIM, YELLOW;
21851
22347
  var init_Settings = __esm({
21852
22348
  async "src/tui/views/Settings.tsx"() {
21853
22349
  "use strict";
21854
22350
  await init_ink2();
21855
- 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();
21856
22485
  }
21857
22486
  });
21858
22487
 
21859
22488
  // src/tui/App.tsx
21860
22489
  var App_exports = {};
21861
- import { useState as useState15, useEffect as useEffect17, useCallback as useCallback7 } from "react";
21862
- 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";
21863
22492
  function App2() {
21864
- const [section, setSection] = useState15("command-center");
22493
+ const [section, setSection] = useState16("command-center");
21865
22494
  const { exit } = use_app_default();
21866
- const [, forceUpdate] = useState15(0);
21867
- useEffect17(() => {
22495
+ const [, forceUpdate] = useState16(0);
22496
+ useEffect18(() => {
21868
22497
  const handleResize = () => forceUpdate((n) => n + 1);
21869
22498
  process.stdout.on("resize", handleResize);
21870
22499
  return () => {
21871
22500
  process.stdout.off("resize", handleResize);
21872
22501
  };
21873
22502
  }, []);
21874
- useMouseEvent(useCallback7((event) => {
22503
+ useMouseEvent(useCallback8((event) => {
21875
22504
  if (event.button !== 0) return;
21876
22505
  if (event.col <= 26) {
21877
22506
  const tabIdx = event.row - 4;
@@ -21883,22 +22512,33 @@ function App2() {
21883
22512
  setFocus("content");
21884
22513
  }
21885
22514
  }, []));
21886
- const [focus, setFocus] = useState15("sidebar");
21887
- const [focusedProject, setFocusedProject] = useState15(null);
21888
- 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) => {
21889
22520
  setFocusedProject(projectName);
21890
22521
  setSection("sessions");
21891
22522
  setFocus("content");
21892
22523
  }, []);
21893
- const handleBackToCommandCenter = useCallback7(() => {
22524
+ const handleBackToCommandCenter = useCallback8(() => {
21894
22525
  setSection("command-center");
21895
22526
  setFocus("sidebar");
21896
22527
  }, []);
21897
22528
  use_input_default((input, key) => {
22529
+ if (input === "?" || key.shift && input === "/") {
22530
+ setShowHelp((prev) => !prev);
22531
+ return;
22532
+ }
21898
22533
  const idx = parseInt(input, 10);
21899
22534
  if (idx >= 1 && idx <= SECTIONS.length) {
21900
22535
  setSection(SECTIONS[idx - 1].key);
21901
22536
  setFocus("sidebar");
22537
+ setShowHelp(false);
22538
+ return;
22539
+ }
22540
+ if (input === "d" && !key.ctrl) {
22541
+ setShowDebug((prev) => !prev);
21902
22542
  return;
21903
22543
  }
21904
22544
  if (input === "q" && !key.ctrl) {
@@ -21926,27 +22566,37 @@ function App2() {
21926
22566
  }
21927
22567
  }
21928
22568
  });
21929
- const consumeFocusedProject = useCallback7(() => {
22569
+ const consumeFocusedProject = useCallback8(() => {
21930
22570
  setFocusedProject(null);
21931
22571
  }, []);
21932
22572
  const views = {
21933
- "command-center": /* @__PURE__ */ jsx15(CommandCenterView, { onSelectProject: handleSelectProject, isFocused: focus === "content" && section === "command-center", onBack: () => setFocus("sidebar") }),
21934
- sessions: /* @__PURE__ */ jsx15(SessionsView, { initialProject: focusedProject, onConsumeInitialProject: consumeFocusedProject, onBackToCommandCenter: handleBackToCommandCenter, onBack: () => setFocus("sidebar") }),
21935
- tasks: /* @__PURE__ */ jsx15(TasksView, { onBack: () => setFocus("sidebar") }),
21936
- team: /* @__PURE__ */ jsx15(TeamView, { onBack: () => setFocus("sidebar") }),
21937
- gateway: /* @__PURE__ */ jsx15(GatewayView, { onBack: () => setFocus("sidebar") }),
21938
- wiki: /* @__PURE__ */ jsx15(WikiView, { onBack: () => setFocus("sidebar") }),
21939
- 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") })
21940
22584
  };
21941
- return /* @__PURE__ */ jsx15(ErrorBoundary2, { children: /* @__PURE__ */ jsx15(AlternateScreen, { children: /* @__PURE__ */ jsx15(DemoProvider, { demo: isDemo, children: /* @__PURE__ */ jsxs13(Box_default, { flexDirection: "column", flexGrow: 1, children: [
21942
- /* @__PURE__ */ jsxs13(Box_default, { flexGrow: 1, children: [
21943
- /* @__PURE__ */ jsx15(Sidebar, { active: section, onSelect: setSection, onQuit: exit, focused: focus === "sidebar" }),
21944
- 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]
21945
22595
  ] }),
21946
- /* @__PURE__ */ jsx15(Footer, {})
22596
+ /* @__PURE__ */ jsx17(Footer, {})
21947
22597
  ] }) }) }) });
21948
22598
  }
21949
- var isDemo;
22599
+ var TAB_SHORTCUTS, isDemo;
21950
22600
  var init_App2 = __esm({
21951
22601
  async "src/tui/App.tsx"() {
21952
22602
  "use strict";
@@ -21964,6 +22614,73 @@ var init_App2 = __esm({
21964
22614
  await init_Team();
21965
22615
  await init_Wiki();
21966
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
+ };
21967
22684
  isDemo = process.argv.includes("--demo");
21968
22685
  process.stderr.write(`[exe-tui] Terminal: ${TERMINAL_TYPE}
21969
22686
  `);
@@ -22004,7 +22721,7 @@ var init_App2 = __esm({
22004
22721
  stdin.unref = () => stdin;
22005
22722
  }
22006
22723
  }
22007
- render_default(/* @__PURE__ */ jsx15(App2, {}));
22724
+ render_default(/* @__PURE__ */ jsx17(App2, {}));
22008
22725
  }
22009
22726
  });
22010
22727