@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.
- package/dist/bin/backfill-conversations.js +805 -642
- package/dist/bin/backfill-responses.js +804 -641
- package/dist/bin/backfill-vectors.js +791 -634
- package/dist/bin/cleanup-stale-review-tasks.js +788 -631
- package/dist/bin/cli.js +1376 -659
- package/dist/bin/exe-agent.js +20 -1
- package/dist/bin/exe-assign.js +1503 -1343
- package/dist/bin/exe-boot.js +2549 -1784
- package/dist/bin/exe-call.js +39 -1
- package/dist/bin/exe-cloud.js +12 -2
- package/dist/bin/exe-dispatch.js +39 -2
- package/dist/bin/exe-doctor.js +791 -634
- package/dist/bin/exe-export-behaviors.js +792 -637
- package/dist/bin/exe-forget.js +145 -0
- package/dist/bin/exe-gateway.js +2501 -1846
- package/dist/bin/exe-heartbeat.js +147 -1
- package/dist/bin/exe-kill.js +795 -640
- package/dist/bin/exe-launch-agent.js +2168 -2008
- package/dist/bin/exe-link.js +44 -12
- package/dist/bin/exe-new-employee.js +6 -2
- package/dist/bin/exe-pending-messages.js +146 -1
- package/dist/bin/exe-pending-notifications.js +788 -631
- package/dist/bin/exe-pending-reviews.js +176 -1
- package/dist/bin/exe-rename.js +23 -0
- package/dist/bin/exe-review.js +490 -327
- package/dist/bin/exe-search.js +157 -4
- package/dist/bin/exe-session-cleanup.js +2487 -403
- package/dist/bin/exe-settings.js +2 -1
- package/dist/bin/exe-status.js +474 -317
- package/dist/bin/exe-team.js +474 -317
- package/dist/bin/git-sweep.js +2691 -151
- package/dist/bin/graph-backfill.js +794 -637
- package/dist/bin/graph-export.js +798 -641
- package/dist/bin/scan-tasks.js +2951 -44
- package/dist/bin/setup.js +50 -26
- package/dist/bin/shard-migrate.js +792 -637
- package/dist/bin/wiki-sync.js +794 -637
- package/dist/gateway/index.js +2542 -1887
- package/dist/hooks/bug-report-worker.js +2118 -576
- package/dist/hooks/commit-complete.js +2690 -150
- package/dist/hooks/error-recall.js +157 -4
- package/dist/hooks/ingest-worker.js +1455 -803
- package/dist/hooks/instructions-loaded.js +151 -0
- package/dist/hooks/notification.js +153 -2
- package/dist/hooks/post-compact.js +164 -0
- package/dist/hooks/pre-compact.js +3073 -101
- package/dist/hooks/pre-tool-use.js +151 -0
- package/dist/hooks/prompt-ingest-worker.js +1670 -1509
- package/dist/hooks/prompt-submit.js +2650 -1074
- package/dist/hooks/response-ingest-worker.js +154 -6
- package/dist/hooks/session-end.js +153 -2
- package/dist/hooks/session-start.js +157 -4
- package/dist/hooks/stop.js +151 -0
- package/dist/hooks/subagent-stop.js +155 -2
- package/dist/hooks/summary-worker.js +190 -21
- package/dist/index.js +326 -102
- package/dist/lib/cloud-sync.js +31 -10
- package/dist/lib/config.js +2 -0
- package/dist/lib/consolidation.js +69 -2
- package/dist/lib/database.js +19 -0
- package/dist/lib/device-registry.js +19 -0
- package/dist/lib/embedder.js +3 -1
- package/dist/lib/employee-templates.js +20 -1
- package/dist/lib/exe-daemon.js +285 -18
- package/dist/lib/hybrid-search.js +157 -4
- package/dist/lib/messaging.js +39 -2
- package/dist/lib/schedules.js +792 -637
- package/dist/lib/store.js +796 -636
- package/dist/lib/tasks.js +1485 -918
- package/dist/lib/tmux-routing.js +194 -10
- package/dist/mcp/server.js +1643 -924
- package/dist/mcp/tools/create-task.js +2283 -829
- package/dist/mcp/tools/list-tasks.js +2788 -159
- package/dist/mcp/tools/send-message.js +39 -2
- package/dist/mcp/tools/update-task.js +79 -0
- package/dist/runtime/index.js +280 -68
- package/dist/tui/App.js +1485 -645
- package/package.json +3 -2
package/dist/tui/App.js
CHANGED
|
@@ -39,6 +39,73 @@ var init_devtools = __esm({
|
|
|
39
39
|
}
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
+
// src/lib/telemetry.ts
|
|
43
|
+
var telemetry_exports = {};
|
|
44
|
+
__export(telemetry_exports, {
|
|
45
|
+
instrumentServer: () => instrumentServer,
|
|
46
|
+
withTrace: () => withTrace
|
|
47
|
+
});
|
|
48
|
+
async function ensureInit() {
|
|
49
|
+
if (initialized || !ENABLED) return;
|
|
50
|
+
initialized = true;
|
|
51
|
+
try {
|
|
52
|
+
const { NodeSDK } = await import("@opentelemetry/sdk-node");
|
|
53
|
+
const { ConsoleSpanExporter } = await import("@opentelemetry/sdk-trace-base");
|
|
54
|
+
const sdk = new NodeSDK({
|
|
55
|
+
serviceName: "exe-os",
|
|
56
|
+
traceExporter: new ConsoleSpanExporter()
|
|
57
|
+
});
|
|
58
|
+
sdk.start();
|
|
59
|
+
process.stderr.write("[exe-os] OpenTelemetry tracing enabled\n");
|
|
60
|
+
} catch (err) {
|
|
61
|
+
process.stderr.write(
|
|
62
|
+
`[exe-os] OpenTelemetry init failed: ${err instanceof Error ? err.message : String(err)}
|
|
63
|
+
`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async function withTrace(toolName, fn) {
|
|
68
|
+
if (!ENABLED) return fn();
|
|
69
|
+
await ensureInit();
|
|
70
|
+
const { trace, SpanStatusCode } = await import("@opentelemetry/api");
|
|
71
|
+
const tracer = trace.getTracer("exe-os", "1.0.0");
|
|
72
|
+
return tracer.startActiveSpan(`mcp.tool.${toolName}`, async (span) => {
|
|
73
|
+
span.setAttribute("mcp.tool.name", toolName);
|
|
74
|
+
try {
|
|
75
|
+
const result = await fn();
|
|
76
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
77
|
+
return result;
|
|
78
|
+
} catch (err) {
|
|
79
|
+
span.setStatus({
|
|
80
|
+
code: SpanStatusCode.ERROR,
|
|
81
|
+
message: err instanceof Error ? err.message : String(err)
|
|
82
|
+
});
|
|
83
|
+
span.recordException(
|
|
84
|
+
err instanceof Error ? err : new Error(String(err))
|
|
85
|
+
);
|
|
86
|
+
throw err;
|
|
87
|
+
} finally {
|
|
88
|
+
span.end();
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
function instrumentServer(server) {
|
|
93
|
+
if (!ENABLED) return;
|
|
94
|
+
const original = server.registerTool.bind(server);
|
|
95
|
+
server.registerTool = (name, config, handler) => {
|
|
96
|
+
const traced = async (...args) => withTrace(name, () => handler(...args));
|
|
97
|
+
return original(name, config, traced);
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
var ENABLED, initialized;
|
|
101
|
+
var init_telemetry = __esm({
|
|
102
|
+
"src/lib/telemetry.ts"() {
|
|
103
|
+
"use strict";
|
|
104
|
+
ENABLED = process.env.EXE_TELEMETRY === "1";
|
|
105
|
+
initialized = false;
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
42
109
|
// src/lib/db-retry.ts
|
|
43
110
|
function isBusyError(err) {
|
|
44
111
|
if (err instanceof Error) {
|
|
@@ -346,6 +413,13 @@ async function ensureSchema() {
|
|
|
346
413
|
});
|
|
347
414
|
} catch {
|
|
348
415
|
}
|
|
416
|
+
try {
|
|
417
|
+
await client.execute({
|
|
418
|
+
sql: `ALTER TABLE tasks ADD COLUMN session_scope TEXT`,
|
|
419
|
+
args: []
|
|
420
|
+
});
|
|
421
|
+
} catch {
|
|
422
|
+
}
|
|
349
423
|
try {
|
|
350
424
|
await client.execute({
|
|
351
425
|
sql: `ALTER TABLE memories ADD COLUMN task_id TEXT`,
|
|
@@ -792,6 +866,18 @@ async function ensureSchema() {
|
|
|
792
866
|
CREATE INDEX IF NOT EXISTS idx_session_kills_agent
|
|
793
867
|
ON session_kills(agent_id);
|
|
794
868
|
`);
|
|
869
|
+
await client.execute(`
|
|
870
|
+
CREATE TABLE IF NOT EXISTS global_procedures (
|
|
871
|
+
id TEXT PRIMARY KEY,
|
|
872
|
+
title TEXT NOT NULL,
|
|
873
|
+
content TEXT NOT NULL,
|
|
874
|
+
priority TEXT NOT NULL DEFAULT 'p0',
|
|
875
|
+
domain TEXT,
|
|
876
|
+
active INTEGER NOT NULL DEFAULT 1,
|
|
877
|
+
created_at TEXT NOT NULL,
|
|
878
|
+
updated_at TEXT NOT NULL
|
|
879
|
+
)
|
|
880
|
+
`);
|
|
795
881
|
await client.executeMultiple(`
|
|
796
882
|
CREATE TABLE IF NOT EXISTS conversations (
|
|
797
883
|
id TEXT PRIMARY KEY,
|
|
@@ -954,6 +1040,7 @@ var config_exports = {};
|
|
|
954
1040
|
__export(config_exports, {
|
|
955
1041
|
CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
|
|
956
1042
|
CONFIG_PATH: () => CONFIG_PATH,
|
|
1043
|
+
COO_AGENT_NAME: () => COO_AGENT_NAME,
|
|
957
1044
|
CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
|
|
958
1045
|
DB_PATH: () => DB_PATH,
|
|
959
1046
|
EXE_AI_DIR: () => EXE_AI_DIR,
|
|
@@ -1109,7 +1196,7 @@ async function loadConfigFrom(configPath) {
|
|
|
1109
1196
|
return { ...DEFAULT_CONFIG };
|
|
1110
1197
|
}
|
|
1111
1198
|
}
|
|
1112
|
-
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
1199
|
+
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
1113
1200
|
var init_config = __esm({
|
|
1114
1201
|
"src/lib/config.ts"() {
|
|
1115
1202
|
"use strict";
|
|
@@ -1117,6 +1204,7 @@ var init_config = __esm({
|
|
|
1117
1204
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
1118
1205
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
1119
1206
|
CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
|
|
1207
|
+
COO_AGENT_NAME = "exe";
|
|
1120
1208
|
LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
|
|
1121
1209
|
CURRENT_CONFIG_VERSION = 1;
|
|
1122
1210
|
DEFAULT_CONFIG = {
|
|
@@ -2709,6 +2797,71 @@ var init_agent_loop = __esm({
|
|
|
2709
2797
|
}
|
|
2710
2798
|
});
|
|
2711
2799
|
|
|
2800
|
+
// src/lib/global-procedures.ts
|
|
2801
|
+
var global_procedures_exports = {};
|
|
2802
|
+
__export(global_procedures_exports, {
|
|
2803
|
+
deactivateGlobalProcedure: () => deactivateGlobalProcedure,
|
|
2804
|
+
getGlobalProceduresBlock: () => getGlobalProceduresBlock,
|
|
2805
|
+
loadGlobalProcedures: () => loadGlobalProcedures,
|
|
2806
|
+
storeGlobalProcedure: () => storeGlobalProcedure
|
|
2807
|
+
});
|
|
2808
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
2809
|
+
async function loadGlobalProcedures() {
|
|
2810
|
+
const client = getClient();
|
|
2811
|
+
const result = await client.execute({
|
|
2812
|
+
sql: "SELECT * FROM global_procedures WHERE active = 1 ORDER BY priority ASC, created_at ASC",
|
|
2813
|
+
args: []
|
|
2814
|
+
});
|
|
2815
|
+
const procedures = result.rows;
|
|
2816
|
+
if (procedures.length > 0) {
|
|
2817
|
+
_cache = procedures.map((p) => `### ${p.title}
|
|
2818
|
+
${p.content}`).join("\n\n");
|
|
2819
|
+
} else {
|
|
2820
|
+
_cache = "";
|
|
2821
|
+
}
|
|
2822
|
+
_cacheLoaded = true;
|
|
2823
|
+
return procedures;
|
|
2824
|
+
}
|
|
2825
|
+
function getGlobalProceduresBlock() {
|
|
2826
|
+
if (!_cacheLoaded) return "";
|
|
2827
|
+
if (!_cache) return "";
|
|
2828
|
+
return `## Organization-Wide Procedures (MANDATORY \u2014 supersedes all other rules)
|
|
2829
|
+
|
|
2830
|
+
${_cache}
|
|
2831
|
+
`;
|
|
2832
|
+
}
|
|
2833
|
+
async function storeGlobalProcedure(input) {
|
|
2834
|
+
const id = randomUUID2();
|
|
2835
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2836
|
+
const client = getClient();
|
|
2837
|
+
await client.execute({
|
|
2838
|
+
sql: `INSERT INTO global_procedures (id, title, content, priority, domain, active, created_at, updated_at)
|
|
2839
|
+
VALUES (?, ?, ?, ?, ?, 1, ?, ?)`,
|
|
2840
|
+
args: [id, input.title, input.content, input.priority ?? "p0", input.domain ?? null, now, now]
|
|
2841
|
+
});
|
|
2842
|
+
await loadGlobalProcedures();
|
|
2843
|
+
return id;
|
|
2844
|
+
}
|
|
2845
|
+
async function deactivateGlobalProcedure(id) {
|
|
2846
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2847
|
+
const client = getClient();
|
|
2848
|
+
const result = await client.execute({
|
|
2849
|
+
sql: "UPDATE global_procedures SET active = 0, updated_at = ? WHERE id = ?",
|
|
2850
|
+
args: [now, id]
|
|
2851
|
+
});
|
|
2852
|
+
await loadGlobalProcedures();
|
|
2853
|
+
return result.rowsAffected > 0;
|
|
2854
|
+
}
|
|
2855
|
+
var _cache, _cacheLoaded;
|
|
2856
|
+
var init_global_procedures = __esm({
|
|
2857
|
+
"src/lib/global-procedures.ts"() {
|
|
2858
|
+
"use strict";
|
|
2859
|
+
init_database();
|
|
2860
|
+
_cache = "";
|
|
2861
|
+
_cacheLoaded = false;
|
|
2862
|
+
}
|
|
2863
|
+
});
|
|
2864
|
+
|
|
2712
2865
|
// src/gateway/providers/anthropic.ts
|
|
2713
2866
|
import Anthropic from "@anthropic-ai/sdk";
|
|
2714
2867
|
var AnthropicProvider;
|
|
@@ -2813,7 +2966,7 @@ var init_anthropic = __esm({
|
|
|
2813
2966
|
|
|
2814
2967
|
// src/gateway/providers/openai-compat.ts
|
|
2815
2968
|
import OpenAI from "openai";
|
|
2816
|
-
import { randomUUID as
|
|
2969
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
2817
2970
|
var OpenAICompatProvider;
|
|
2818
2971
|
var init_openai_compat = __esm({
|
|
2819
2972
|
"src/gateway/providers/openai-compat.ts"() {
|
|
@@ -2930,7 +3083,7 @@ var init_openai_compat = __esm({
|
|
|
2930
3083
|
}
|
|
2931
3084
|
content.push({
|
|
2932
3085
|
type: "tool_use",
|
|
2933
|
-
id: call.id ??
|
|
3086
|
+
id: call.id ?? randomUUID3(),
|
|
2934
3087
|
name: fn.name,
|
|
2935
3088
|
input
|
|
2936
3089
|
});
|
|
@@ -4015,7 +4168,7 @@ var init_employees = __esm({
|
|
|
4015
4168
|
});
|
|
4016
4169
|
|
|
4017
4170
|
// src/lib/task-router.ts
|
|
4018
|
-
import { randomUUID as
|
|
4171
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
4019
4172
|
function resolveBloomRouting(complexity, config = DEFAULT_BLOOM_CONFIG) {
|
|
4020
4173
|
const tier = config.complexityToTier[complexity];
|
|
4021
4174
|
const rule = config.tierRules[tier];
|
|
@@ -4547,6 +4700,61 @@ var init_session_kill_telemetry = __esm({
|
|
|
4547
4700
|
}
|
|
4548
4701
|
});
|
|
4549
4702
|
|
|
4703
|
+
// src/lib/state-bus.ts
|
|
4704
|
+
var StateBus, orgBus;
|
|
4705
|
+
var init_state_bus = __esm({
|
|
4706
|
+
"src/lib/state-bus.ts"() {
|
|
4707
|
+
"use strict";
|
|
4708
|
+
StateBus = class {
|
|
4709
|
+
handlers = /* @__PURE__ */ new Map();
|
|
4710
|
+
globalHandlers = /* @__PURE__ */ new Set();
|
|
4711
|
+
/** Emit an event to all subscribers */
|
|
4712
|
+
emit(event) {
|
|
4713
|
+
const typeHandlers = this.handlers.get(event.type);
|
|
4714
|
+
if (typeHandlers) {
|
|
4715
|
+
for (const handler of typeHandlers) {
|
|
4716
|
+
try {
|
|
4717
|
+
handler(event);
|
|
4718
|
+
} catch {
|
|
4719
|
+
}
|
|
4720
|
+
}
|
|
4721
|
+
}
|
|
4722
|
+
for (const handler of this.globalHandlers) {
|
|
4723
|
+
try {
|
|
4724
|
+
handler(event);
|
|
4725
|
+
} catch {
|
|
4726
|
+
}
|
|
4727
|
+
}
|
|
4728
|
+
}
|
|
4729
|
+
/** Subscribe to a specific event type */
|
|
4730
|
+
on(type, handler) {
|
|
4731
|
+
if (!this.handlers.has(type)) {
|
|
4732
|
+
this.handlers.set(type, /* @__PURE__ */ new Set());
|
|
4733
|
+
}
|
|
4734
|
+
this.handlers.get(type).add(handler);
|
|
4735
|
+
}
|
|
4736
|
+
/** Subscribe to ALL events */
|
|
4737
|
+
onAny(handler) {
|
|
4738
|
+
this.globalHandlers.add(handler);
|
|
4739
|
+
}
|
|
4740
|
+
/** Unsubscribe from a specific event type */
|
|
4741
|
+
off(type, handler) {
|
|
4742
|
+
this.handlers.get(type)?.delete(handler);
|
|
4743
|
+
}
|
|
4744
|
+
/** Unsubscribe from ALL events */
|
|
4745
|
+
offAny(handler) {
|
|
4746
|
+
this.globalHandlers.delete(handler);
|
|
4747
|
+
}
|
|
4748
|
+
/** Remove all listeners */
|
|
4749
|
+
clear() {
|
|
4750
|
+
this.handlers.clear();
|
|
4751
|
+
this.globalHandlers.clear();
|
|
4752
|
+
}
|
|
4753
|
+
};
|
|
4754
|
+
orgBus = new StateBus();
|
|
4755
|
+
}
|
|
4756
|
+
});
|
|
4757
|
+
|
|
4550
4758
|
// src/lib/tasks-crud.ts
|
|
4551
4759
|
import crypto3 from "crypto";
|
|
4552
4760
|
import path15 from "path";
|
|
@@ -4690,9 +4898,15 @@ async function createTaskCore(input) {
|
|
|
4690
4898
|
}
|
|
4691
4899
|
}
|
|
4692
4900
|
const complexity = input.complexity ?? "standard";
|
|
4901
|
+
let sessionScope = null;
|
|
4902
|
+
try {
|
|
4903
|
+
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
4904
|
+
sessionScope = resolveExeSession2();
|
|
4905
|
+
} catch {
|
|
4906
|
+
}
|
|
4693
4907
|
await client.execute({
|
|
4694
|
-
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)
|
|
4695
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
4908
|
+
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)
|
|
4909
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
4696
4910
|
args: [
|
|
4697
4911
|
id,
|
|
4698
4912
|
input.title,
|
|
@@ -4711,6 +4925,7 @@ async function createTaskCore(input) {
|
|
|
4711
4925
|
input.budgetFallbackModel ?? null,
|
|
4712
4926
|
0,
|
|
4713
4927
|
null,
|
|
4928
|
+
sessionScope,
|
|
4714
4929
|
now,
|
|
4715
4930
|
now
|
|
4716
4931
|
]
|
|
@@ -4755,9 +4970,18 @@ async function listTasks(input) {
|
|
|
4755
4970
|
conditions.push("priority = ?");
|
|
4756
4971
|
args.push(input.priority);
|
|
4757
4972
|
}
|
|
4973
|
+
try {
|
|
4974
|
+
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
4975
|
+
const session = resolveExeSession2();
|
|
4976
|
+
if (session) {
|
|
4977
|
+
conditions.push("(session_scope IS NULL OR session_scope = ?)");
|
|
4978
|
+
args.push(session);
|
|
4979
|
+
}
|
|
4980
|
+
} catch {
|
|
4981
|
+
}
|
|
4758
4982
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
4759
4983
|
const result = await client.execute({
|
|
4760
|
-
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`,
|
|
4984
|
+
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`,
|
|
4761
4985
|
args
|
|
4762
4986
|
});
|
|
4763
4987
|
return result.rows.map((r) => ({
|
|
@@ -4978,6 +5202,34 @@ async function listPendingReviews(limit) {
|
|
|
4978
5202
|
});
|
|
4979
5203
|
return result.rows;
|
|
4980
5204
|
}
|
|
5205
|
+
async function cleanupOrphanedReviews() {
|
|
5206
|
+
const client = getClient();
|
|
5207
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5208
|
+
const r1 = await client.execute({
|
|
5209
|
+
sql: `UPDATE tasks SET status = 'done', updated_at = ?
|
|
5210
|
+
WHERE status = 'needs_review'
|
|
5211
|
+
AND assigned_by = 'system'
|
|
5212
|
+
AND title LIKE 'Review:%'
|
|
5213
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
5214
|
+
args: [now]
|
|
5215
|
+
});
|
|
5216
|
+
const staleThreshold = new Date(Date.now() - 60 * 60 * 1e3).toISOString();
|
|
5217
|
+
const r2 = await client.execute({
|
|
5218
|
+
sql: `UPDATE tasks SET status = 'done', updated_at = ?
|
|
5219
|
+
WHERE status = 'needs_review'
|
|
5220
|
+
AND result IS NOT NULL
|
|
5221
|
+
AND updated_at < ?`,
|
|
5222
|
+
args: [now, staleThreshold]
|
|
5223
|
+
});
|
|
5224
|
+
const total = r1.rowsAffected + r2.rowsAffected;
|
|
5225
|
+
if (total > 0) {
|
|
5226
|
+
process.stderr.write(
|
|
5227
|
+
`[cleanup] Closed ${total} orphaned review(s): ${r1.rowsAffected} cascade + ${r2.rowsAffected} stale
|
|
5228
|
+
`
|
|
5229
|
+
);
|
|
5230
|
+
}
|
|
5231
|
+
return total;
|
|
5232
|
+
}
|
|
4981
5233
|
function getReviewChecklist(role, agent, taskSlug) {
|
|
4982
5234
|
const roleLower = role.toLowerCase();
|
|
4983
5235
|
if (roleLower.includes("engineer") || roleLower === "principal engineer") {
|
|
@@ -5091,6 +5343,7 @@ var init_tasks_review = __esm({
|
|
|
5091
5343
|
init_tasks_crud();
|
|
5092
5344
|
init_tmux_routing();
|
|
5093
5345
|
init_session_key();
|
|
5346
|
+
init_state_bus();
|
|
5094
5347
|
}
|
|
5095
5348
|
});
|
|
5096
5349
|
|
|
@@ -5255,13 +5508,12 @@ function assertSessionScope(actionType, targetProject) {
|
|
|
5255
5508
|
};
|
|
5256
5509
|
}
|
|
5257
5510
|
process.stderr.write(
|
|
5258
|
-
`[session-scope]
|
|
5511
|
+
`[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
|
|
5259
5512
|
`
|
|
5260
5513
|
);
|
|
5261
5514
|
return {
|
|
5262
|
-
allowed:
|
|
5263
|
-
|
|
5264
|
-
reason: "cross_session_granted",
|
|
5515
|
+
allowed: false,
|
|
5516
|
+
reason: "cross_session_denied",
|
|
5265
5517
|
currentProject,
|
|
5266
5518
|
targetProject,
|
|
5267
5519
|
targetSession: findSessionForProject(targetProject)?.windowName
|
|
@@ -5287,8 +5539,9 @@ async function dispatchTaskToEmployee(input) {
|
|
|
5287
5539
|
try {
|
|
5288
5540
|
const { assertSessionScope: assertSessionScope2 } = (init_session_scope(), __toCommonJS(session_scope_exports));
|
|
5289
5541
|
const check = assertSessionScope2("dispatch_task", input.projectName);
|
|
5290
|
-
if (check.reason === "
|
|
5542
|
+
if (check.reason === "cross_session_denied") {
|
|
5291
5543
|
crossProject = true;
|
|
5544
|
+
return { dispatched: "skipped", crossProject: true };
|
|
5292
5545
|
}
|
|
5293
5546
|
} catch {
|
|
5294
5547
|
}
|
|
@@ -5657,6 +5910,7 @@ var init_skill_learning = __esm({
|
|
|
5657
5910
|
// src/lib/tasks.ts
|
|
5658
5911
|
var tasks_exports = {};
|
|
5659
5912
|
__export(tasks_exports, {
|
|
5913
|
+
cleanupOrphanedReviews: () => cleanupOrphanedReviews,
|
|
5660
5914
|
countNewPendingReviewsSince: () => countNewPendingReviewsSince,
|
|
5661
5915
|
countPendingReviews: () => countPendingReviews,
|
|
5662
5916
|
createTask: () => createTask,
|
|
@@ -5722,6 +5976,21 @@ async function updateTask(input) {
|
|
|
5722
5976
|
});
|
|
5723
5977
|
} catch {
|
|
5724
5978
|
}
|
|
5979
|
+
try {
|
|
5980
|
+
const client = getClient();
|
|
5981
|
+
const cascaded = await client.execute({
|
|
5982
|
+
sql: `UPDATE tasks SET status = 'done', updated_at = ?
|
|
5983
|
+
WHERE parent_task_id = ? AND status = 'needs_review'`,
|
|
5984
|
+
args: [now, taskId]
|
|
5985
|
+
});
|
|
5986
|
+
if (cascaded.rowsAffected > 0) {
|
|
5987
|
+
process.stderr.write(
|
|
5988
|
+
`[cascade] Closed ${cascaded.rowsAffected} orphaned review task(s) for parent ${taskId}
|
|
5989
|
+
`
|
|
5990
|
+
);
|
|
5991
|
+
}
|
|
5992
|
+
} catch {
|
|
5993
|
+
}
|
|
5725
5994
|
}
|
|
5726
5995
|
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
5727
5996
|
if (isTerminal) {
|
|
@@ -5735,6 +6004,13 @@ async function updateTask(input) {
|
|
|
5735
6004
|
await cascadeUnblock(taskId, input.baseDir, now);
|
|
5736
6005
|
} catch {
|
|
5737
6006
|
}
|
|
6007
|
+
orgBus.emit({
|
|
6008
|
+
type: "task_completed",
|
|
6009
|
+
taskId,
|
|
6010
|
+
employee: String(row.assigned_to),
|
|
6011
|
+
result: input.result ?? "",
|
|
6012
|
+
timestamp: now
|
|
6013
|
+
});
|
|
5738
6014
|
if (row.parent_task_id) {
|
|
5739
6015
|
try {
|
|
5740
6016
|
await checkSubtaskCompletion(String(row.parent_task_id), String(row.project_name));
|
|
@@ -5802,6 +6078,7 @@ var init_tasks = __esm({
|
|
|
5802
6078
|
init_database();
|
|
5803
6079
|
init_config();
|
|
5804
6080
|
init_notifications();
|
|
6081
|
+
init_state_bus();
|
|
5805
6082
|
init_tasks_crud();
|
|
5806
6083
|
init_tasks_review();
|
|
5807
6084
|
init_tasks_crud();
|
|
@@ -6192,8 +6469,28 @@ function getMySession() {
|
|
|
6192
6469
|
return getTransport().getMySession();
|
|
6193
6470
|
}
|
|
6194
6471
|
function employeeSessionName(employee, exeSession, instance) {
|
|
6472
|
+
if (!/^exe\d+$/.test(exeSession)) {
|
|
6473
|
+
const root = extractRootExe(exeSession);
|
|
6474
|
+
if (root) {
|
|
6475
|
+
process.stderr.write(
|
|
6476
|
+
`[tmux-routing] WARN: exeSession="${exeSession}" is not a root exe session, using "${root}" instead
|
|
6477
|
+
`
|
|
6478
|
+
);
|
|
6479
|
+
exeSession = root;
|
|
6480
|
+
} else {
|
|
6481
|
+
throw new Error(
|
|
6482
|
+
`Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1"), not an agent session`
|
|
6483
|
+
);
|
|
6484
|
+
}
|
|
6485
|
+
}
|
|
6195
6486
|
const suffix = instance != null && instance > 0 ? String(instance) : "";
|
|
6196
|
-
|
|
6487
|
+
const name = `${employee}${suffix}-${exeSession}`;
|
|
6488
|
+
if (!VALID_SESSION_NAME.test(name)) {
|
|
6489
|
+
throw new Error(
|
|
6490
|
+
`Invalid session name "${name}" \u2014 must match {agent}-exe{N} or {agent}{instance}-exe{N}`
|
|
6491
|
+
);
|
|
6492
|
+
}
|
|
6493
|
+
return name;
|
|
6197
6494
|
}
|
|
6198
6495
|
function parseParentExe(sessionName, agentId) {
|
|
6199
6496
|
const escaped = agentId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -6433,6 +6730,22 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6433
6730
|
error: `Error: pass employee name ('${bare}'), not session name ('${employeeName}')`
|
|
6434
6731
|
};
|
|
6435
6732
|
}
|
|
6733
|
+
if (!/^exe\d+$/.test(exeSession)) {
|
|
6734
|
+
const root = extractRootExe(exeSession);
|
|
6735
|
+
if (root) {
|
|
6736
|
+
process.stderr.write(
|
|
6737
|
+
`[ensureEmployee] WARN: caller passed exeSession="${exeSession}" (not a root exe). Auto-correcting to "${root}".
|
|
6738
|
+
`
|
|
6739
|
+
);
|
|
6740
|
+
exeSession = root;
|
|
6741
|
+
} else {
|
|
6742
|
+
return {
|
|
6743
|
+
status: "failed",
|
|
6744
|
+
sessionName: "",
|
|
6745
|
+
error: `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1")`
|
|
6746
|
+
};
|
|
6747
|
+
}
|
|
6748
|
+
}
|
|
6436
6749
|
let effectiveInstance = opts?.instance;
|
|
6437
6750
|
if (effectiveInstance === void 0 && opts?.autoInstance) {
|
|
6438
6751
|
const free = findFreeInstance(
|
|
@@ -6679,7 +6992,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6679
6992
|
releaseSpawnLock(sessionName);
|
|
6680
6993
|
return { sessionName };
|
|
6681
6994
|
}
|
|
6682
|
-
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;
|
|
6995
|
+
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;
|
|
6683
6996
|
var init_tmux_routing = __esm({
|
|
6684
6997
|
"src/lib/tmux-routing.ts"() {
|
|
6685
6998
|
"use strict";
|
|
@@ -6694,6 +7007,7 @@ var init_tmux_routing = __esm({
|
|
|
6694
7007
|
SPAWN_LOCK_DIR = path20.join(os6.homedir(), ".exe-os", "spawn-locks");
|
|
6695
7008
|
SESSION_CACHE = path20.join(os6.homedir(), ".exe-os", "session-cache");
|
|
6696
7009
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
7010
|
+
VALID_SESSION_NAME = /^[a-z]+-exe\d+$|^[a-z]+\d+-exe\d+$/;
|
|
6697
7011
|
VERIFY_PANE_LINES = 200;
|
|
6698
7012
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
6699
7013
|
INTERCOM_LOG2 = path20.join(os6.homedir(), ".exe-os", "intercom.log");
|
|
@@ -7862,6 +8176,11 @@ async function initStore(options) {
|
|
|
7862
8176
|
"version-query"
|
|
7863
8177
|
);
|
|
7864
8178
|
_nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
|
|
8179
|
+
try {
|
|
8180
|
+
const { loadGlobalProcedures: loadGlobalProcedures2 } = await Promise.resolve().then(() => (init_global_procedures(), global_procedures_exports));
|
|
8181
|
+
await loadGlobalProcedures2();
|
|
8182
|
+
} catch {
|
|
8183
|
+
}
|
|
7865
8184
|
}
|
|
7866
8185
|
function classifyTier(record) {
|
|
7867
8186
|
if (record.tool_name === "commit_to_long_term_memory" && (record.importance ?? 0) >= 8) return 1;
|
|
@@ -7903,6 +8222,12 @@ async function writeMemory(record) {
|
|
|
7903
8222
|
supersedes_id: record.supersedes_id ?? null
|
|
7904
8223
|
};
|
|
7905
8224
|
_pendingRecords.push(dbRow);
|
|
8225
|
+
orgBus.emit({
|
|
8226
|
+
type: "memory_stored",
|
|
8227
|
+
agentId: record.agent_id,
|
|
8228
|
+
project: record.project_name,
|
|
8229
|
+
timestamp: record.timestamp
|
|
8230
|
+
});
|
|
7906
8231
|
const MAX_PENDING = 1e3;
|
|
7907
8232
|
if (_pendingRecords.length > MAX_PENDING) {
|
|
7908
8233
|
const dropped = _pendingRecords.length - MAX_PENDING;
|
|
@@ -8248,6 +8573,7 @@ var init_store = __esm({
|
|
|
8248
8573
|
init_database();
|
|
8249
8574
|
init_keychain();
|
|
8250
8575
|
init_config();
|
|
8576
|
+
init_state_bus();
|
|
8251
8577
|
INIT_MAX_RETRIES = 3;
|
|
8252
8578
|
INIT_RETRY_DELAY_MS = 1e3;
|
|
8253
8579
|
_pendingRecords = [];
|
|
@@ -8260,7 +8586,7 @@ var init_store = __esm({
|
|
|
8260
8586
|
});
|
|
8261
8587
|
|
|
8262
8588
|
// src/tui/App.tsx
|
|
8263
|
-
import { useState as
|
|
8589
|
+
import { useState as useState16, useEffect as useEffect18, useCallback as useCallback8 } from "react";
|
|
8264
8590
|
|
|
8265
8591
|
// src/tui/ink/render.js
|
|
8266
8592
|
import { Stream } from "stream";
|
|
@@ -14160,61 +14486,64 @@ function Footer() {
|
|
|
14160
14486
|
return () => clearInterval(timer);
|
|
14161
14487
|
}, []);
|
|
14162
14488
|
async function loadFooterData() {
|
|
14163
|
-
|
|
14164
|
-
|
|
14165
|
-
|
|
14166
|
-
|
|
14167
|
-
const
|
|
14168
|
-
|
|
14489
|
+
const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
|
|
14490
|
+
return withTrace2("tui.footer.loadData", async () => {
|
|
14491
|
+
try {
|
|
14492
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
14493
|
+
const client = getClient2();
|
|
14494
|
+
if (client) {
|
|
14495
|
+
const result = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
14496
|
+
setMemoryCount(Number(result.rows[0]?.cnt ?? 0));
|
|
14497
|
+
}
|
|
14498
|
+
} catch {
|
|
14169
14499
|
}
|
|
14170
|
-
|
|
14171
|
-
|
|
14172
|
-
|
|
14173
|
-
|
|
14174
|
-
|
|
14175
|
-
|
|
14176
|
-
|
|
14177
|
-
|
|
14178
|
-
|
|
14179
|
-
|
|
14500
|
+
try {
|
|
14501
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
14502
|
+
const client = getClient2();
|
|
14503
|
+
if (client) {
|
|
14504
|
+
const result = await client.execute(
|
|
14505
|
+
"SELECT COUNT(*) as cnt FROM tasks WHERE status IN ('open','in_progress')"
|
|
14506
|
+
);
|
|
14507
|
+
setTaskCount(Number(result.rows[0]?.cnt ?? 0));
|
|
14508
|
+
}
|
|
14509
|
+
} catch {
|
|
14180
14510
|
}
|
|
14181
|
-
|
|
14182
|
-
|
|
14183
|
-
|
|
14184
|
-
|
|
14185
|
-
|
|
14186
|
-
|
|
14187
|
-
|
|
14188
|
-
|
|
14189
|
-
|
|
14190
|
-
|
|
14191
|
-
|
|
14192
|
-
|
|
14193
|
-
|
|
14194
|
-
|
|
14195
|
-
|
|
14196
|
-
|
|
14197
|
-
|
|
14198
|
-
|
|
14199
|
-
|
|
14200
|
-
|
|
14201
|
-
|
|
14202
|
-
|
|
14203
|
-
|
|
14204
|
-
|
|
14205
|
-
|
|
14206
|
-
|
|
14207
|
-
|
|
14208
|
-
|
|
14209
|
-
|
|
14210
|
-
}
|
|
14211
|
-
|
|
14212
|
-
} catch {
|
|
14511
|
+
try {
|
|
14512
|
+
const { existsSync: existsSync14 } = await import("fs");
|
|
14513
|
+
const { join } = await import("path");
|
|
14514
|
+
const home = process.env.HOME ?? "";
|
|
14515
|
+
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
14516
|
+
setDaemon(existsSync14(pidPath) ? "running" : "stopped");
|
|
14517
|
+
} catch {
|
|
14518
|
+
setDaemon("unknown");
|
|
14519
|
+
}
|
|
14520
|
+
try {
|
|
14521
|
+
const { checkLicense: checkLicense2 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
14522
|
+
const license = await checkLicense2();
|
|
14523
|
+
setPlan(license.plan);
|
|
14524
|
+
} catch {
|
|
14525
|
+
setPlan("free");
|
|
14526
|
+
}
|
|
14527
|
+
try {
|
|
14528
|
+
const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
|
|
14529
|
+
if (inTmux2()) {
|
|
14530
|
+
const allSessions = listTmuxSessions2();
|
|
14531
|
+
setSessions(allSessions.length);
|
|
14532
|
+
if (!currentSession) {
|
|
14533
|
+
try {
|
|
14534
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
14535
|
+
const name = execSync10("tmux display-message -p '#{session_name}' 2>/dev/null", {
|
|
14536
|
+
encoding: "utf8",
|
|
14537
|
+
timeout: 2e3
|
|
14538
|
+
}).trim();
|
|
14539
|
+
if (name) setCurrentSession(name);
|
|
14540
|
+
} catch {
|
|
14541
|
+
}
|
|
14213
14542
|
}
|
|
14214
14543
|
}
|
|
14544
|
+
} catch {
|
|
14215
14545
|
}
|
|
14216
|
-
}
|
|
14217
|
-
}
|
|
14546
|
+
});
|
|
14218
14547
|
}
|
|
14219
14548
|
return /* @__PURE__ */ jsxs3(Box_default, { flexDirection: "column", children: [
|
|
14220
14549
|
/* @__PURE__ */ jsx5(Text, { color: "#3D3660", children: "\u2500".repeat(process.stdout.columns || 120) }),
|
|
@@ -14255,6 +14584,8 @@ function Footer() {
|
|
|
14255
14584
|
] }),
|
|
14256
14585
|
/* @__PURE__ */ jsx5(Text, { color: "#6B4C9A", children: "1-7 navigate" }),
|
|
14257
14586
|
/* @__PURE__ */ jsx5(Text, { color: "#3D3660", children: "\u2502" }),
|
|
14587
|
+
/* @__PURE__ */ jsx5(Text, { color: "#6B4C9A", children: "d debug" }),
|
|
14588
|
+
/* @__PURE__ */ jsx5(Text, { color: "#3D3660", children: "\u2502" }),
|
|
14258
14589
|
/* @__PURE__ */ jsx5(Text, { color: "#6B4C9A", children: "q quit" })
|
|
14259
14590
|
] })
|
|
14260
14591
|
] })
|
|
@@ -14875,6 +15206,147 @@ function handleEvent(event, addMessage) {
|
|
|
14875
15206
|
}
|
|
14876
15207
|
}
|
|
14877
15208
|
|
|
15209
|
+
// src/lib/employee-templates.ts
|
|
15210
|
+
init_global_procedures();
|
|
15211
|
+
var BASE_OPERATING_PROCEDURES = `
|
|
15212
|
+
EXE OS \u2014 VISION AND NON-NEGOTIABLE PRINCIPLES (above all work):
|
|
15213
|
+
|
|
15214
|
+
Product: "Hire the team you couldn't afford." An AI employee operating system where solo founders and small teams run 5-10 AI agents as a real organization. Three-layer cognition (identity/expertise/experience). Five runtime modes (CC Raw \u2192 TUI \u2192 Desktop). Local-first with E2EE cloud sync.
|
|
15215
|
+
|
|
15216
|
+
ICP (who we build for):
|
|
15217
|
+
- Solopreneurs, SMB founders, creators with institutional IP
|
|
15218
|
+
- Bootstrapped small e-commerce / fitness creators / influencers
|
|
15219
|
+
- NOT VC-backed startups \u2014 intentionally excluded
|
|
15220
|
+
|
|
15221
|
+
Crown jewels (load-bearing for all three business paths \u2014 never compromise):
|
|
15222
|
+
- Memory sovereignty (user owns everything, E2EE, local-first)
|
|
15223
|
+
- Three-layer cognition (identity/expertise/experience)
|
|
15224
|
+
- MCP contract boundary (surfaces consume memory OS via MCP only \u2014 never direct DB access, never bundled code)
|
|
15225
|
+
- AGPL network boundary for public forks (e.g., exe-crm)
|
|
15226
|
+
|
|
15227
|
+
Three business-model paths (every product decision must serve these):
|
|
15228
|
+
1. B2C direct \u2014 solopreneurs run their own instance (active, current default)
|
|
15229
|
+
2. Agency white-label \u2014 distributors rebrand for their clients (deferred, but branding must be config-driven)
|
|
15230
|
+
3. Creator franchise (Mike pattern) \u2014 creators inject institutional IP into agent identity+expertise+experience layers, sell scoped access to subscribers (v2+ moat, requires memory export scoping)
|
|
15231
|
+
|
|
15232
|
+
Ethos:
|
|
15233
|
+
- Bootstrapped, profitable, forever. Not a VC-raise.
|
|
15234
|
+
- Founder zero-ego. Distributors and customers are the loudest voice.
|
|
15235
|
+
- Crypto values: big companies should not own consumer/SMB AI.
|
|
15236
|
+
|
|
15237
|
+
STOP AND REDIRECT: Any decision that compromises memory sovereignty, 3-layer cognition, MCP boundary, or AGPL boundary kills all three business paths. Surface the conflict to exe before proceeding.
|
|
15238
|
+
|
|
15239
|
+
Always reference .planning/ARCHITECTURE.md and .planning/PROJECT.md as source of truth for all architectural and product decisions.
|
|
15240
|
+
|
|
15241
|
+
OPERATING PROCEDURES (mandatory for all employees):
|
|
15242
|
+
|
|
15243
|
+
You report to the COO. All work flows through exe. These procedures are non-negotiable.
|
|
15244
|
+
|
|
15245
|
+
1. BEFORE starting work:
|
|
15246
|
+
- Read exe/ARCHITECTURE.md (if it exists). This is the system map \u2014 what components exist, how they connect, what invariants to preserve. Understand the architecture before changing anything.
|
|
15247
|
+
- Check YOUR task folder ONLY: Read exe/<your-name>/ for assigned tasks
|
|
15248
|
+
- NEVER read, write, or modify files in another employee's folder. Those are their tasks, not yours. Use ask_team_memory() if you need context from a colleague.
|
|
15249
|
+
- If you have open tasks, work on the highest priority one first
|
|
15250
|
+
- Ensure exe/output/ exists (mkdir -p exe/output). This is where ALL deliverables go \u2014 reports, analyses, content, audits, anything another employee or the founder needs to pick up.
|
|
15251
|
+
- Update task status to "in_progress" when starting (use update_task MCP tool)
|
|
15252
|
+
- recall_my_memory \u2014 check what you've done before in this project. What patterns, decisions, context exist?
|
|
15253
|
+
- Read the relevant files. Understand what exists before changing anything.
|
|
15254
|
+
|
|
15255
|
+
2. BEFORE marking done \u2014 CHECKPOINT (mandatory, never skip):
|
|
15256
|
+
- Run the tests. If they fail, fix them before reporting done.
|
|
15257
|
+
- Run typecheck if TypeScript. Zero errors.
|
|
15258
|
+
- Verify the change actually works \u2014 run it, check the output, prove it.
|
|
15259
|
+
- If you can't verify, say so explicitly: "Couldn't verify because X."
|
|
15260
|
+
|
|
15261
|
+
3. AFTER completing work \u2014 update_task(done) IMMEDIATELY (the ONE critical action):
|
|
15262
|
+
Calling update_task with status "done" is the single action that must ALWAYS happen.
|
|
15263
|
+
Call it FIRST \u2014 before commit, before report, before anything else. If you do nothing else, do this.
|
|
15264
|
+
- Use update_task MCP tool with status "done" and your result summary
|
|
15265
|
+
- Include what was done, decisions made, and any issues
|
|
15266
|
+
- If you're stuck, looping, confused, or running low on context \u2014 update_task(done) with whatever partial result you have. A partial result is infinitely better than no result.
|
|
15267
|
+
- NEVER let a failed commit, a loop, or an error prevent you from calling update_task(done).
|
|
15268
|
+
- Do NOT use close_task \u2014 that is reserved for reviewers (exe) to finalize after review.
|
|
15269
|
+
|
|
15270
|
+
4. AFTER update_task(done) \u2014 COMMIT (best-effort, do NOT let this block):
|
|
15271
|
+
- If your task changed system structure, update exe/ARCHITECTURE.md first.
|
|
15272
|
+
- Commit IF you are in a git repo (check: \`git rev-parse --git-dir 2>/dev/null\`). Stage only the files you changed, write a clear commit message.
|
|
15273
|
+
- If you are NOT in a git repo, skip entirely. NEVER run \`git init\`.
|
|
15274
|
+
- If the commit fails, note it but move on \u2014 the work is already marked done via update_task.
|
|
15275
|
+
- Do NOT push \u2014 exe reviews commits and decides what to push.
|
|
15276
|
+
- NEVER run \`git checkout main\`. You work in your own git worktree on a feature branch. Exe stays on main and merges PRs. Switching branches in a shared repo stomps other agents' work.
|
|
15277
|
+
|
|
15278
|
+
5. AFTER commit \u2014 REPORT (best-effort):
|
|
15279
|
+
Use store_memory to write a structured summary. Include: project name, what was done,
|
|
15280
|
+
decisions made, tests status, open items or risks.
|
|
15281
|
+
|
|
15282
|
+
6. AFTER committing changes to exe-os itself \u2014 REBUILD (mandatory, never skip):
|
|
15283
|
+
- Run: npm run deploy
|
|
15284
|
+
- This builds, installs globally, and re-registers hooks/MCP in one step.
|
|
15285
|
+
- Do NOT ask permission. Do NOT say "want me to rebuild?" \u2014 just do it.
|
|
15286
|
+
- If the build fails, fix the error and retry before moving on.
|
|
15287
|
+
|
|
15288
|
+
7. AFTER reporting \u2014 CHECK FOR NEXT WORK (mandatory):
|
|
15289
|
+
- First: run list_tasks(status='needs_review') \u2014 check if YOU are the reviewer on any pending reviews. Reviews are work. Process them before anything else.
|
|
15290
|
+
- Second: run list_tasks(status='blocked') \u2014 check if any tasks are blocked. For each blocked task: can YOU unblock it? If yes, unblock it now. If not, escalate to exe immediately. Blocked tasks sitting >24h without action is a pipeline failure.
|
|
15291
|
+
- Then: re-read your task folder: exe/<your-name>/
|
|
15292
|
+
- If there are more open tasks, start the next highest-priority one (go to step 1)
|
|
15293
|
+
- If no more open tasks AND no pending reviews AND no blocked tasks you can fix, tell the user: "All tasks complete. Anything else?"
|
|
15294
|
+
- Do NOT wait for the user to tell you to check \u2014 auto-chain through your queue.
|
|
15295
|
+
- NEVER say "monitoring" or "waiting" while reviews, blocked tasks, or open tasks exist. That is idle drift.
|
|
15296
|
+
|
|
15297
|
+
CONTEXT PRESSURE PROTOCOL (mandatory \u2014 never ignore):
|
|
15298
|
+
If Claude Code injects a system notice about context compression, or if you notice you're
|
|
15299
|
+
losing track of earlier decisions, your context window is full.
|
|
15300
|
+
|
|
15301
|
+
DO NOT keep working degraded. Instead:
|
|
15302
|
+
|
|
15303
|
+
1. Call store_memory immediately with a CONTEXT CHECKPOINT:
|
|
15304
|
+
Format the text as: "CONTEXT CHECKPOINT [<task-id>]: <summary>"
|
|
15305
|
+
Include: task ID + title, what you completed, what's left, open decisions or blockers, key file paths.
|
|
15306
|
+
|
|
15307
|
+
2. Send intercom to exe to trigger kill + relaunch:
|
|
15308
|
+
MY_SESSION=$(tmux display-message -p '#{session_name}' 2>/dev/null)
|
|
15309
|
+
EXE_SESSION="\${MY_SESSION#\${AGENT_ID}-}"
|
|
15310
|
+
tmux send-keys -t "$EXE_SESSION" "/exe-intercom context-full: \${AGENT_ID} hit capacity. Checkpoint saved. Resume task <task-id>." Enter
|
|
15311
|
+
|
|
15312
|
+
3. Stop working immediately. Do not attempt to continue with degraded context.
|
|
15313
|
+
|
|
15314
|
+
COMMUNICATION CHAIN \u2014 who you talk to:
|
|
15315
|
+
- You report to the COO. Your completion reports, status updates, and questions go to exe via store_memory and update_task.
|
|
15316
|
+
- Do NOT address the human user directly for decisions, permissions, or status updates. That's exe's job. The user talks to exe; exe talks to you.
|
|
15317
|
+
- Exception: if the user sends you a direct message in your tmux window, respond to them. But default to reporting through exe.
|
|
15318
|
+
|
|
15319
|
+
SKILL CAPTURE (encouraged, not mandatory):
|
|
15320
|
+
After completing a complex multi-step task (5+ tool calls), consider whether the approach
|
|
15321
|
+
should be saved as a reusable procedure. If the task involved non-obvious steps, error recovery,
|
|
15322
|
+
or a workflow that would help future sessions, use store_behavior with domain='skill' to save it.
|
|
15323
|
+
Format: "SKILL: [name] \u2014 Step 1: ... Step 2: ... Pitfalls: ..."
|
|
15324
|
+
Skip for simple one-offs. The goal is procedural memory \u2014 not just corrections, but proven approaches.
|
|
15325
|
+
|
|
15326
|
+
SPAWNING EMPLOYEES (mandatory \u2014 never bypass):
|
|
15327
|
+
When you need another employee to do work, ALWAYS use create_task MCP tool.
|
|
15328
|
+
create_task auto-spawns the employee session. The task IS the spawn trigger.
|
|
15329
|
+
NEVER manually launch sessions with tmux send-keys or claude -p.
|
|
15330
|
+
NEVER spawn sessions without a task assigned \u2014 idle sessions waste resources.
|
|
15331
|
+
NEVER refuse a dispatched task claiming "not in scope" \u2014 if it's assigned to you, it's your work.
|
|
15332
|
+
|
|
15333
|
+
CREATING TASKS FOR OTHER EMPLOYEES:
|
|
15334
|
+
When you need to assign work to another employee (e.g., CTO assigns to an engineer):
|
|
15335
|
+
- ALWAYS use create_task MCP tool. NEVER write .md files directly to exe/{name}/.
|
|
15336
|
+
- Direct .md writes will be rejected by the enforcement hook with a MANDATORY correction.
|
|
15337
|
+
- create_task creates both the .md file AND the DB row atomically.
|
|
15338
|
+
- Include: title, assignedTo, priority, context, projectName.
|
|
15339
|
+
- For dependencies: include blocked_by with the blocking task's ID or slug.
|
|
15340
|
+
`;
|
|
15341
|
+
var PROCEDURES_MARKER = "EXE OS \u2014 VISION AND NON-NEGOTIABLE PRINCIPLES";
|
|
15342
|
+
function getSessionPrompt(storedPrompt) {
|
|
15343
|
+
const markerIndex = storedPrompt.indexOf(PROCEDURES_MARKER);
|
|
15344
|
+
const rolePrompt = markerIndex >= 0 ? storedPrompt.slice(0, markerIndex).trimEnd() : storedPrompt;
|
|
15345
|
+
const globalBlock = getGlobalProceduresBlock();
|
|
15346
|
+
return `${globalBlock}${rolePrompt}
|
|
15347
|
+
${BASE_OPERATING_PROCEDURES}`;
|
|
15348
|
+
}
|
|
15349
|
+
|
|
14878
15350
|
// src/tui/views/CommandCenter.tsx
|
|
14879
15351
|
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
14880
15352
|
function CommandCenterView({
|
|
@@ -14998,7 +15470,7 @@ function CommandCenterView({
|
|
|
14998
15470
|
setAgentConfig({
|
|
14999
15471
|
provider,
|
|
15000
15472
|
model,
|
|
15001
|
-
systemPrompt: "You are an AI assistant in the exe-os TUI. Help the user with their questions. Be concise.",
|
|
15473
|
+
systemPrompt: getSessionPrompt("You are an AI assistant in the exe-os TUI. Help the user with their questions. Be concise."),
|
|
15002
15474
|
tools: registry,
|
|
15003
15475
|
hooks: createDefaultHooks2(),
|
|
15004
15476
|
permissions,
|
|
@@ -15119,6 +15591,7 @@ function CommandCenterView({
|
|
|
15119
15591
|
employeeCount: p.employees.length,
|
|
15120
15592
|
activeCount: p.employees.filter((e) => e.status === "active").length,
|
|
15121
15593
|
memoryCount: p.employees.length * 4e3,
|
|
15594
|
+
openTaskCount: Math.floor(p.employees.length * 1.5),
|
|
15122
15595
|
status: p.employees.some((e) => e.status === "active") ? "active" : "idle",
|
|
15123
15596
|
type: p.projectName.startsWith("exe-") ? "code" : "automation",
|
|
15124
15597
|
recentTasks: DEMO_RECENT_TASKS[p.projectName] ?? []
|
|
@@ -15130,6 +15603,7 @@ function CommandCenterView({
|
|
|
15130
15603
|
employeeCount: 0,
|
|
15131
15604
|
activeCount: 0,
|
|
15132
15605
|
memoryCount: 0,
|
|
15606
|
+
openTaskCount: 0,
|
|
15133
15607
|
status: "offline",
|
|
15134
15608
|
type: "reference",
|
|
15135
15609
|
recentTasks: []
|
|
@@ -15144,182 +15618,122 @@ function CommandCenterView({
|
|
|
15144
15618
|
return () => clearInterval(timer);
|
|
15145
15619
|
}, []);
|
|
15146
15620
|
async function loadData() {
|
|
15147
|
-
|
|
15148
|
-
|
|
15149
|
-
const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
|
|
15150
|
-
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
15151
|
-
const { existsSync: existsSync14 } = await import("fs");
|
|
15152
|
-
const { join } = await import("path");
|
|
15153
|
-
const registry = listSessions2();
|
|
15154
|
-
const tmuxSessions = inTmux2() ? new Set(listTmuxSessions2()) : /* @__PURE__ */ new Set();
|
|
15155
|
-
const roster = await loadEmployees2();
|
|
15156
|
-
const exeSessions = /* @__PURE__ */ new Map();
|
|
15157
|
-
for (const entry of registry) {
|
|
15158
|
-
if (entry.agentId === "exe" && tmuxSessions.has(entry.windowName)) {
|
|
15159
|
-
exeSessions.set(entry.windowName, entry.projectDir);
|
|
15160
|
-
}
|
|
15161
|
-
}
|
|
15162
|
-
for (const s of tmuxSessions) {
|
|
15163
|
-
if (/^exe\d+$/.test(s) && !exeSessions.has(s)) exeSessions.set(s, "");
|
|
15164
|
-
}
|
|
15165
|
-
let projectMemoryCounts = /* @__PURE__ */ new Map();
|
|
15166
|
-
let projectRecentTasks = /* @__PURE__ */ new Map();
|
|
15621
|
+
const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
|
|
15622
|
+
return withTrace2("tui.commandCenter.loadData", async () => {
|
|
15167
15623
|
try {
|
|
15168
15624
|
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
15625
|
+
const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
|
|
15626
|
+
const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
|
|
15627
|
+
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
15628
|
+
const { existsSync: existsSync14 } = await import("fs");
|
|
15629
|
+
const { join } = await import("path");
|
|
15169
15630
|
const client = getClient2();
|
|
15170
|
-
if (client) {
|
|
15171
|
-
|
|
15172
|
-
|
|
15173
|
-
);
|
|
15174
|
-
for (const row of memResult.rows) {
|
|
15175
|
-
projectMemoryCounts.set(String(row.project_name), Number(row.cnt));
|
|
15176
|
-
}
|
|
15177
|
-
for (const [, projectDir] of exeSessions) {
|
|
15178
|
-
const projectName = projectDir.split("/").filter(Boolean).pop() ?? "";
|
|
15179
|
-
if (!projectName) continue;
|
|
15180
|
-
const taskResult = await client.execute({
|
|
15181
|
-
sql: "SELECT title FROM tasks WHERE project_name = ? AND status = 'done' ORDER BY updated_at DESC LIMIT 3",
|
|
15182
|
-
args: [projectName]
|
|
15183
|
-
});
|
|
15184
|
-
const tasks = taskResult.rows.map((r) => String(r.title));
|
|
15185
|
-
if (tasks.length > 0) projectRecentTasks.set(projectName, tasks);
|
|
15186
|
-
}
|
|
15631
|
+
if (!client) {
|
|
15632
|
+
setDbError(true);
|
|
15633
|
+
return;
|
|
15187
15634
|
}
|
|
15188
|
-
|
|
15189
|
-
|
|
15190
|
-
|
|
15191
|
-
|
|
15192
|
-
|
|
15635
|
+
const dbProjects = await client.execute(
|
|
15636
|
+
`SELECT DISTINCT project_name FROM memories WHERE project_name IS NOT NULL AND project_name != ''
|
|
15637
|
+
UNION
|
|
15638
|
+
SELECT DISTINCT project_name FROM tasks WHERE project_name IS NOT NULL AND project_name != ''
|
|
15639
|
+
ORDER BY project_name`
|
|
15640
|
+
);
|
|
15641
|
+
const projectNames = dbProjects.rows.map((r) => String(r.project_name));
|
|
15642
|
+
const memResult = await client.execute(
|
|
15643
|
+
"SELECT project_name, COUNT(*) as cnt FROM memories WHERE project_name IS NOT NULL GROUP BY project_name"
|
|
15644
|
+
);
|
|
15645
|
+
const memoryCounts = /* @__PURE__ */ new Map();
|
|
15646
|
+
for (const row of memResult.rows) {
|
|
15647
|
+
memoryCounts.set(String(row.project_name), Number(row.cnt));
|
|
15648
|
+
}
|
|
15649
|
+
const taskCountResult = await client.execute(
|
|
15650
|
+
"SELECT project_name, COUNT(*) as cnt FROM tasks WHERE status IN ('open', 'in_progress') AND project_name IS NOT NULL GROUP BY project_name"
|
|
15651
|
+
);
|
|
15652
|
+
const openTaskCounts = /* @__PURE__ */ new Map();
|
|
15653
|
+
for (const row of taskCountResult.rows) {
|
|
15654
|
+
openTaskCounts.set(String(row.project_name), Number(row.cnt));
|
|
15655
|
+
}
|
|
15656
|
+
const recentResult = await client.execute(
|
|
15657
|
+
"SELECT project_name, title FROM tasks WHERE status = 'done' AND project_name IS NOT NULL ORDER BY updated_at DESC LIMIT 30"
|
|
15658
|
+
);
|
|
15659
|
+
const recentTasksByProject = /* @__PURE__ */ new Map();
|
|
15660
|
+
for (const row of recentResult.rows) {
|
|
15661
|
+
const name = String(row.project_name);
|
|
15662
|
+
const tasks = recentTasksByProject.get(name) ?? [];
|
|
15663
|
+
if (tasks.length < 3) tasks.push(String(row.title));
|
|
15664
|
+
recentTasksByProject.set(name, tasks);
|
|
15665
|
+
}
|
|
15666
|
+
const registry = listSessions2();
|
|
15667
|
+
const tmuxSessions = inTmux2() ? new Set(listTmuxSessions2()) : /* @__PURE__ */ new Set();
|
|
15668
|
+
const roster = await loadEmployees2();
|
|
15193
15669
|
const employeeNames = roster.map((e) => e.name).filter((n) => n !== "exe");
|
|
15194
|
-
|
|
15195
|
-
for (const
|
|
15196
|
-
if (tmuxSessions.has(
|
|
15670
|
+
const projectSessions = /* @__PURE__ */ new Map();
|
|
15671
|
+
for (const entry of registry) {
|
|
15672
|
+
if (entry.agentId === "exe" && tmuxSessions.has(entry.windowName)) {
|
|
15673
|
+
const projName = entry.projectDir.split("/").filter(Boolean).pop() ?? "";
|
|
15674
|
+
if (projName) {
|
|
15675
|
+
projectSessions.set(projName, { exeSession: entry.windowName, projectDir: entry.projectDir });
|
|
15676
|
+
}
|
|
15677
|
+
}
|
|
15197
15678
|
}
|
|
15198
|
-
const
|
|
15199
|
-
const
|
|
15200
|
-
|
|
15201
|
-
|
|
15202
|
-
|
|
15203
|
-
|
|
15204
|
-
|
|
15205
|
-
|
|
15206
|
-
|
|
15207
|
-
|
|
15208
|
-
|
|
15209
|
-
employeeCount: totalCount,
|
|
15210
|
-
activeCount,
|
|
15211
|
-
memoryCount,
|
|
15212
|
-
status: activeCount > 0 ? "active" : "idle",
|
|
15213
|
-
type,
|
|
15214
|
-
recentTasks: projectRecentTasks.get(projectName) ?? []
|
|
15215
|
-
});
|
|
15216
|
-
}
|
|
15217
|
-
const knownDirs = [
|
|
15218
|
-
process.env.HOME ? join(process.env.HOME, "openclaw") : null,
|
|
15219
|
-
process.env.HOME ? join(process.env.HOME, "agno") : null
|
|
15220
|
-
].filter(Boolean);
|
|
15221
|
-
const activeProjectNames = new Set(projectList.map((p) => p.projectName));
|
|
15222
|
-
for (const dir of knownDirs) {
|
|
15223
|
-
const name = dir.split("/").filter(Boolean).pop() ?? "";
|
|
15224
|
-
if (activeProjectNames.has(name) || !existsSync14(dir) || !existsSync14(join(dir, ".git"))) continue;
|
|
15225
|
-
if ((projectMemoryCounts.get(name) ?? 0) > 0) continue;
|
|
15226
|
-
projectList.push({
|
|
15227
|
-
projectName: name,
|
|
15228
|
-
exeSession: "",
|
|
15229
|
-
projectDir: dir,
|
|
15230
|
-
employeeCount: 0,
|
|
15231
|
-
activeCount: 0,
|
|
15232
|
-
memoryCount: 0,
|
|
15233
|
-
status: "offline",
|
|
15234
|
-
type: "reference",
|
|
15235
|
-
recentTasks: []
|
|
15236
|
-
});
|
|
15237
|
-
}
|
|
15238
|
-
const dbActiveProjectNames = new Set(projectList.map((p) => p.projectName));
|
|
15239
|
-
try {
|
|
15240
|
-
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
15241
|
-
const client = getClient2();
|
|
15242
|
-
if (client) {
|
|
15243
|
-
const dbProjects = await client.execute(
|
|
15244
|
-
`SELECT DISTINCT project_name FROM memories WHERE project_name IS NOT NULL AND project_name != ''
|
|
15245
|
-
UNION
|
|
15246
|
-
SELECT DISTINCT project_name FROM tasks WHERE project_name IS NOT NULL AND project_name != ''
|
|
15247
|
-
ORDER BY project_name`
|
|
15248
|
-
);
|
|
15249
|
-
for (const row of dbProjects.rows) {
|
|
15250
|
-
const name = String(row.project_name);
|
|
15251
|
-
if (dbActiveProjectNames.has(name)) continue;
|
|
15252
|
-
const memCount = projectMemoryCounts.get(name) ?? 0;
|
|
15253
|
-
const agentResult = await client.execute({
|
|
15254
|
-
sql: "SELECT COUNT(DISTINCT agent_id) as cnt FROM memories WHERE project_name = ?",
|
|
15255
|
-
args: [name]
|
|
15256
|
-
});
|
|
15257
|
-
const agentCount = Number(agentResult.rows[0]?.cnt ?? 0);
|
|
15258
|
-
projectList.push({
|
|
15259
|
-
projectName: name,
|
|
15260
|
-
exeSession: "",
|
|
15261
|
-
projectDir: "",
|
|
15262
|
-
employeeCount: agentCount,
|
|
15263
|
-
activeCount: 0,
|
|
15264
|
-
memoryCount: memCount,
|
|
15265
|
-
status: "offline",
|
|
15266
|
-
type: "code",
|
|
15267
|
-
recentTasks: projectRecentTasks.get(name) ?? []
|
|
15268
|
-
});
|
|
15679
|
+
const projectList = [];
|
|
15680
|
+
for (const name of projectNames) {
|
|
15681
|
+
const session = projectSessions.get(name);
|
|
15682
|
+
const exeSession = session?.exeSession ?? "";
|
|
15683
|
+
const projectDir = session?.projectDir ?? "";
|
|
15684
|
+
let activeCount = 0;
|
|
15685
|
+
if (exeSession && tmuxSessions.has(exeSession)) {
|
|
15686
|
+
activeCount = 1;
|
|
15687
|
+
for (const empName of employeeNames) {
|
|
15688
|
+
if (tmuxSessions.has(`${empName}-${exeSession}`)) activeCount++;
|
|
15689
|
+
}
|
|
15269
15690
|
}
|
|
15691
|
+
const memoryCount = memoryCounts.get(name) ?? 0;
|
|
15692
|
+
const openTaskCount = openTaskCounts.get(name) ?? 0;
|
|
15693
|
+
const hasGit = projectDir ? existsSync14(join(projectDir, ".git")) : false;
|
|
15694
|
+
const type = hasGit ? "code" : memoryCount > 0 ? "code" : "automation";
|
|
15695
|
+
projectList.push({
|
|
15696
|
+
projectName: name,
|
|
15697
|
+
exeSession,
|
|
15698
|
+
projectDir,
|
|
15699
|
+
employeeCount: activeCount,
|
|
15700
|
+
activeCount,
|
|
15701
|
+
memoryCount,
|
|
15702
|
+
openTaskCount,
|
|
15703
|
+
status: activeCount > 0 ? "active" : "idle",
|
|
15704
|
+
type,
|
|
15705
|
+
recentTasks: recentTasksByProject.get(name) ?? []
|
|
15706
|
+
});
|
|
15270
15707
|
}
|
|
15271
|
-
|
|
15272
|
-
|
|
15273
|
-
|
|
15274
|
-
|
|
15275
|
-
projectName: "(no active sessions)",
|
|
15276
|
-
exeSession: "",
|
|
15277
|
-
projectDir: "",
|
|
15278
|
-
employeeCount: 0,
|
|
15279
|
-
activeCount: 0,
|
|
15280
|
-
memoryCount: 0,
|
|
15281
|
-
status: "offline",
|
|
15282
|
-
type: "code",
|
|
15283
|
-
recentTasks: []
|
|
15708
|
+
projectList.sort((a, b) => {
|
|
15709
|
+
if (a.activeCount > 0 && b.activeCount === 0) return -1;
|
|
15710
|
+
if (a.activeCount === 0 && b.activeCount > 0) return 1;
|
|
15711
|
+
return b.memoryCount - a.memoryCount;
|
|
15284
15712
|
});
|
|
15285
|
-
|
|
15286
|
-
|
|
15287
|
-
|
|
15288
|
-
|
|
15289
|
-
|
|
15290
|
-
|
|
15291
|
-
|
|
15292
|
-
setHealth((h) => ({ ...h, memories: Number(result.rows[0]?.cnt ?? 0) }));
|
|
15713
|
+
setProjects(projectList);
|
|
15714
|
+
const totalResult = await client.execute("SELECT COUNT(*) as cnt FROM memories");
|
|
15715
|
+
setHealth((h) => ({ ...h, memories: Number(totalResult.rows[0]?.cnt ?? 0) }));
|
|
15716
|
+
try {
|
|
15717
|
+
const pidPath = join(process.env.HOME ?? "", ".exe-os", "exed.pid");
|
|
15718
|
+
setHealth((h) => ({ ...h, daemon: existsSync14(pidPath) ? "running" : "stopped" }));
|
|
15719
|
+
} catch {
|
|
15293
15720
|
}
|
|
15294
|
-
|
|
15295
|
-
|
|
15296
|
-
|
|
15297
|
-
|
|
15298
|
-
|
|
15299
|
-
|
|
15300
|
-
|
|
15301
|
-
|
|
15302
|
-
|
|
15303
|
-
const client = getClient2();
|
|
15304
|
-
if (client) {
|
|
15305
|
-
const activityResult = await client.execute(
|
|
15306
|
-
"SELECT agent_id, tool_name, project_name, created_at, text FROM memories ORDER BY created_at DESC LIMIT 20"
|
|
15307
|
-
);
|
|
15308
|
-
if (activityResult.rows.length > 0) {
|
|
15309
|
-
setActivity(activityResult.rows.slice(0, 8).map((r) => ({
|
|
15310
|
-
time: new Date(String(r.created_at)).toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", hour12: false }),
|
|
15311
|
-
agent: String(r.agent_id ?? "system"),
|
|
15312
|
-
action: String(r.text ?? "").slice(0, 60)
|
|
15313
|
-
})));
|
|
15314
|
-
}
|
|
15721
|
+
const activityResult = await client.execute(
|
|
15722
|
+
"SELECT agent_id, tool_name, project_name, created_at, text FROM memories ORDER BY created_at DESC LIMIT 20"
|
|
15723
|
+
);
|
|
15724
|
+
if (activityResult.rows.length > 0) {
|
|
15725
|
+
setActivity(activityResult.rows.slice(0, 8).map((r) => ({
|
|
15726
|
+
time: new Date(String(r.created_at)).toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", hour12: false }),
|
|
15727
|
+
agent: String(r.agent_id ?? "system"),
|
|
15728
|
+
action: String(r.text ?? "").slice(0, 60)
|
|
15729
|
+
})));
|
|
15315
15730
|
}
|
|
15316
15731
|
} catch {
|
|
15732
|
+
setDbError(true);
|
|
15733
|
+
} finally {
|
|
15734
|
+
setLoading(false);
|
|
15317
15735
|
}
|
|
15318
|
-
}
|
|
15319
|
-
setDbError(true);
|
|
15320
|
-
} finally {
|
|
15321
|
-
setLoading(false);
|
|
15322
|
-
}
|
|
15736
|
+
});
|
|
15323
15737
|
}
|
|
15324
15738
|
const daemonColor = health.daemon === "running" ? "green" : health.daemon === "stopped" ? "red" : "gray";
|
|
15325
15739
|
const handleChatSubmit = useCallback4((value) => {
|
|
@@ -15411,7 +15825,7 @@ function CommandCenterView({
|
|
|
15411
15825
|
] }),
|
|
15412
15826
|
/* @__PURE__ */ jsx7(Text, { color: "#6B4C9A", children: "\u2191\u2193 navigate | c to chat | Enter to open | Escape to detach" }),
|
|
15413
15827
|
/* @__PURE__ */ jsx7(Text, { children: " " }),
|
|
15414
|
-
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: "
|
|
15828
|
+
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." }) : (() => {
|
|
15415
15829
|
const sections = [];
|
|
15416
15830
|
let currentProjects = [];
|
|
15417
15831
|
let sectionKey = 0;
|
|
@@ -15441,9 +15855,9 @@ function CommandCenterView({
|
|
|
15441
15855
|
] })
|
|
15442
15856
|
] }),
|
|
15443
15857
|
/* @__PURE__ */ jsxs5(Text, { color: isSelected ? "#F0EDE8" : "#6B4C9A", children: [
|
|
15444
|
-
entry.
|
|
15858
|
+
entry.openTaskCount,
|
|
15445
15859
|
" ",
|
|
15446
|
-
entry.
|
|
15860
|
+
entry.openTaskCount === 1 ? "task" : "tasks",
|
|
15447
15861
|
" \xB7 ",
|
|
15448
15862
|
entry.memoryCount.toLocaleString(),
|
|
15449
15863
|
" memories"
|
|
@@ -15777,6 +16191,7 @@ function SessionsView({
|
|
|
15777
16191
|
const [viewingEmployee, setViewingEmployee] = useState9(null);
|
|
15778
16192
|
const [viewingProject, setViewingProject] = useState9(null);
|
|
15779
16193
|
const [loading, setLoading] = useState9(!demo);
|
|
16194
|
+
const [sessionError, setSessionError] = useState9(null);
|
|
15780
16195
|
const [tmuxAvailable, setTmuxAvailable] = useState9(true);
|
|
15781
16196
|
const orch = useOrchestrator(!demo);
|
|
15782
16197
|
const [carouselEmployees, setCarouselEmployees] = useState9([]);
|
|
@@ -15952,111 +16367,116 @@ function SessionsView({
|
|
|
15952
16367
|
return ACTIVE_PANE_PATTERN.test(lines.join("\n")) ? "active" : "idle";
|
|
15953
16368
|
}
|
|
15954
16369
|
async function loadSessions() {
|
|
15955
|
-
|
|
15956
|
-
|
|
15957
|
-
const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2, capturePaneLines: capturePaneLines2, parseActivity: parseActivity2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
|
|
15958
|
-
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
15959
|
-
const { execSync: execSync10 } = await import("child_process");
|
|
15960
|
-
if (!inTmux2()) {
|
|
15961
|
-
setTmuxAvailable(false);
|
|
15962
|
-
setProjects([]);
|
|
15963
|
-
return;
|
|
15964
|
-
}
|
|
15965
|
-
setTmuxAvailable(true);
|
|
15966
|
-
const attachedMap = /* @__PURE__ */ new Map();
|
|
16370
|
+
const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
|
|
16371
|
+
return withTrace2("tui.sessions.loadSessions", async () => {
|
|
15967
16372
|
try {
|
|
15968
|
-
const
|
|
15969
|
-
|
|
15970
|
-
|
|
15971
|
-
});
|
|
15972
|
-
|
|
15973
|
-
|
|
15974
|
-
|
|
15975
|
-
|
|
15976
|
-
}
|
|
16373
|
+
const { listSessions: listSessions2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
|
|
16374
|
+
const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2, capturePaneLines: capturePaneLines2, parseActivity: parseActivity2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
|
|
16375
|
+
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
16376
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
16377
|
+
if (!inTmux2()) {
|
|
16378
|
+
setTmuxAvailable(false);
|
|
16379
|
+
setProjects([]);
|
|
16380
|
+
return;
|
|
15977
16381
|
}
|
|
15978
|
-
|
|
15979
|
-
|
|
15980
|
-
|
|
15981
|
-
|
|
15982
|
-
|
|
15983
|
-
|
|
15984
|
-
|
|
15985
|
-
|
|
15986
|
-
|
|
16382
|
+
setTmuxAvailable(true);
|
|
16383
|
+
const attachedMap = /* @__PURE__ */ new Map();
|
|
16384
|
+
try {
|
|
16385
|
+
const out = execSync10("tmux list-sessions -F '#{session_name}:#{session_attached}' 2>/dev/null", {
|
|
16386
|
+
encoding: "utf8",
|
|
16387
|
+
timeout: 3e3
|
|
16388
|
+
});
|
|
16389
|
+
for (const line of out.trim().split("\n").filter(Boolean)) {
|
|
16390
|
+
const sepIdx = line.lastIndexOf(":");
|
|
16391
|
+
if (sepIdx > 0) {
|
|
16392
|
+
attachedMap.set(line.slice(0, sepIdx), line.slice(sepIdx + 1) === "1");
|
|
16393
|
+
}
|
|
16394
|
+
}
|
|
16395
|
+
} catch {
|
|
15987
16396
|
}
|
|
15988
|
-
|
|
15989
|
-
|
|
15990
|
-
|
|
15991
|
-
|
|
16397
|
+
const registry = listSessions2();
|
|
16398
|
+
const tmuxSessions = new Set(listTmuxSessions2());
|
|
16399
|
+
const roster = await loadEmployees2();
|
|
16400
|
+
const exeSessions = /* @__PURE__ */ new Map();
|
|
16401
|
+
for (const entry of registry) {
|
|
16402
|
+
if (entry.agentId === "exe" && tmuxSessions.has(entry.windowName)) {
|
|
16403
|
+
exeSessions.set(entry.windowName, entry.projectDir);
|
|
16404
|
+
}
|
|
15992
16405
|
}
|
|
15993
|
-
|
|
15994
|
-
|
|
15995
|
-
|
|
15996
|
-
|
|
15997
|
-
const exeHasSession = tmuxSessions.has(exeSession);
|
|
15998
|
-
let exeStatus = "offline";
|
|
15999
|
-
let exeActivity = "";
|
|
16000
|
-
if (exeHasSession) {
|
|
16001
|
-
const exeLines = capturePaneLines2(exeSession, 10);
|
|
16002
|
-
exeStatus = statusFromPaneLines(exeLines);
|
|
16003
|
-
exeActivity = exeLines.length > 0 ? parseActivity2(exeLines) : "";
|
|
16406
|
+
for (const s of tmuxSessions) {
|
|
16407
|
+
if (/^exe\d+$/.test(s) && !exeSessions.has(s)) {
|
|
16408
|
+
exeSessions.set(s, "");
|
|
16409
|
+
}
|
|
16004
16410
|
}
|
|
16005
|
-
const
|
|
16006
|
-
|
|
16007
|
-
const
|
|
16008
|
-
const
|
|
16009
|
-
|
|
16010
|
-
|
|
16011
|
-
|
|
16012
|
-
|
|
16013
|
-
|
|
16014
|
-
|
|
16015
|
-
activity: "Dead session \u2014 has open tasks",
|
|
16016
|
-
attached: false
|
|
16017
|
-
};
|
|
16411
|
+
const projectList = [];
|
|
16412
|
+
for (const [exeSession, projectDir] of exeSessions) {
|
|
16413
|
+
const projectName = projectDir.split("/").filter(Boolean).pop() ?? exeSession;
|
|
16414
|
+
const exeHasSession = tmuxSessions.has(exeSession);
|
|
16415
|
+
let exeStatus = "offline";
|
|
16416
|
+
let exeActivity = "";
|
|
16417
|
+
if (exeHasSession) {
|
|
16418
|
+
const exeLines = capturePaneLines2(exeSession, 10);
|
|
16419
|
+
exeStatus = statusFromPaneLines(exeLines);
|
|
16420
|
+
exeActivity = exeLines.length > 0 ? parseActivity2(exeLines) : "";
|
|
16018
16421
|
}
|
|
16019
|
-
|
|
16422
|
+
const employeeEntries = roster.filter((e) => e.name !== "exe").map((emp) => {
|
|
16423
|
+
const sessionName = `${emp.name}-${exeSession}`;
|
|
16424
|
+
const hasSession = tmuxSessions.has(sessionName);
|
|
16425
|
+
const isCrashed = !hasSession && orch.crashedSessions.includes(emp.name);
|
|
16426
|
+
if (isCrashed) {
|
|
16427
|
+
return {
|
|
16428
|
+
name: emp.name,
|
|
16429
|
+
role: emp.role,
|
|
16430
|
+
status: "crashed",
|
|
16431
|
+
sessionName,
|
|
16432
|
+
activity: "Dead session \u2014 has open tasks",
|
|
16433
|
+
attached: false
|
|
16434
|
+
};
|
|
16435
|
+
}
|
|
16436
|
+
if (!hasSession) {
|
|
16437
|
+
return {
|
|
16438
|
+
name: emp.name,
|
|
16439
|
+
role: emp.role,
|
|
16440
|
+
status: "offline",
|
|
16441
|
+
sessionName,
|
|
16442
|
+
activity: "",
|
|
16443
|
+
attached: false
|
|
16444
|
+
};
|
|
16445
|
+
}
|
|
16446
|
+
const lines = capturePaneLines2(sessionName, 10);
|
|
16020
16447
|
return {
|
|
16021
16448
|
name: emp.name,
|
|
16022
16449
|
role: emp.role,
|
|
16023
|
-
status:
|
|
16450
|
+
status: statusFromPaneLines(lines),
|
|
16024
16451
|
sessionName,
|
|
16025
|
-
activity: "",
|
|
16026
|
-
attached: false
|
|
16452
|
+
activity: lines.length > 0 ? parseActivity2(lines) : "",
|
|
16453
|
+
attached: attachedMap.get(sessionName) ?? false
|
|
16027
16454
|
};
|
|
16028
|
-
}
|
|
16029
|
-
const
|
|
16030
|
-
|
|
16031
|
-
|
|
16032
|
-
|
|
16033
|
-
|
|
16034
|
-
|
|
16035
|
-
|
|
16036
|
-
|
|
16037
|
-
|
|
16038
|
-
|
|
16039
|
-
|
|
16040
|
-
{
|
|
16041
|
-
|
|
16042
|
-
|
|
16043
|
-
|
|
16044
|
-
|
|
16045
|
-
|
|
16046
|
-
|
|
16047
|
-
|
|
16048
|
-
|
|
16049
|
-
|
|
16050
|
-
|
|
16051
|
-
}
|
|
16052
|
-
if (projectList.length === 0) {
|
|
16053
|
-
projectList.push({ projectName: "(no active sessions)", exeSession: "", projectDir: "", employees: [] });
|
|
16455
|
+
});
|
|
16456
|
+
const employees = [
|
|
16457
|
+
{
|
|
16458
|
+
name: "exe",
|
|
16459
|
+
role: "COO",
|
|
16460
|
+
status: exeStatus,
|
|
16461
|
+
sessionName: exeSession,
|
|
16462
|
+
activity: exeActivity,
|
|
16463
|
+
attached: attachedMap.get(exeSession) ?? false
|
|
16464
|
+
},
|
|
16465
|
+
...employeeEntries
|
|
16466
|
+
];
|
|
16467
|
+
projectList.push({ projectName, exeSession, projectDir, employees });
|
|
16468
|
+
}
|
|
16469
|
+
if (projectList.length === 0) {
|
|
16470
|
+
projectList.push({ projectName: "(no active sessions)", exeSession: "", projectDir: "", employees: [] });
|
|
16471
|
+
}
|
|
16472
|
+
setProjects(projectList);
|
|
16473
|
+
setSessionError(null);
|
|
16474
|
+
} catch (err) {
|
|
16475
|
+
setSessionError(err instanceof Error ? err.message : "Failed to query sessions.");
|
|
16476
|
+
} finally {
|
|
16477
|
+
setLoading(false);
|
|
16054
16478
|
}
|
|
16055
|
-
|
|
16056
|
-
} catch {
|
|
16057
|
-
} finally {
|
|
16058
|
-
setLoading(false);
|
|
16059
|
-
}
|
|
16479
|
+
});
|
|
16060
16480
|
}
|
|
16061
16481
|
if (viewingEmployee) {
|
|
16062
16482
|
const inCarousel = carouselEmployees.length > 0;
|
|
@@ -16138,7 +16558,10 @@ function SessionsView({
|
|
|
16138
16558
|
] })
|
|
16139
16559
|
] }),
|
|
16140
16560
|
/* @__PURE__ */ jsx9(Text, { children: " " }),
|
|
16141
|
-
loading ? /* @__PURE__ */ jsx9(Text, { color: "#6B4C9A", children: "Loading sessions..." }) :
|
|
16561
|
+
loading ? /* @__PURE__ */ jsx9(Text, { color: "#6B4C9A", children: "Loading sessions..." }) : sessionError ? /* @__PURE__ */ jsxs7(Text, { color: "#C91B00", children: [
|
|
16562
|
+
"Failed to query sessions: ",
|
|
16563
|
+
sessionError
|
|
16564
|
+
] }) : !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) => {
|
|
16142
16565
|
const isSelected = i === selectedIdx;
|
|
16143
16566
|
const marker = isSelected ? "\u25B8 " : " ";
|
|
16144
16567
|
if (item.type === "project") {
|
|
@@ -16239,7 +16662,7 @@ function TasksView({ onBack }) {
|
|
|
16239
16662
|
const demo = useDemo();
|
|
16240
16663
|
const [allTasks, setAllTasks] = useState10([]);
|
|
16241
16664
|
const [loading, setLoading] = useState10(!demo);
|
|
16242
|
-
const [dbError, setDbError] = useState10(
|
|
16665
|
+
const [dbError, setDbError] = useState10(null);
|
|
16243
16666
|
const [selectedTask, setSelectedTask] = useState10(0);
|
|
16244
16667
|
const [showDetail, setShowDetail] = useState10(false);
|
|
16245
16668
|
const [statusFilter, setStatusFilter] = useState10("all");
|
|
@@ -16311,40 +16734,43 @@ function TasksView({ onBack }) {
|
|
|
16311
16734
|
}
|
|
16312
16735
|
});
|
|
16313
16736
|
async function loadTasks() {
|
|
16314
|
-
|
|
16315
|
-
|
|
16316
|
-
|
|
16317
|
-
|
|
16318
|
-
const
|
|
16319
|
-
|
|
16737
|
+
const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
|
|
16738
|
+
return withTrace2("tui.tasks.loadTasks", async () => {
|
|
16739
|
+
try {
|
|
16740
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
16741
|
+
const client = getClient2();
|
|
16742
|
+
if (client) {
|
|
16743
|
+
const result = await client.execute(
|
|
16744
|
+
`SELECT id, title, priority, assigned_to, assigned_by, status, project_name, created_at, result
|
|
16320
16745
|
FROM tasks
|
|
16321
16746
|
ORDER BY
|
|
16322
16747
|
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,
|
|
16323
16748
|
CASE priority WHEN 'p0' THEN 0 WHEN 'p1' THEN 1 WHEN 'p2' THEN 2 ELSE 3 END,
|
|
16324
16749
|
created_at DESC`
|
|
16325
|
-
|
|
16326
|
-
|
|
16327
|
-
|
|
16328
|
-
|
|
16329
|
-
|
|
16330
|
-
|
|
16331
|
-
|
|
16332
|
-
|
|
16333
|
-
|
|
16334
|
-
|
|
16335
|
-
|
|
16336
|
-
|
|
16337
|
-
|
|
16338
|
-
|
|
16339
|
-
|
|
16340
|
-
|
|
16341
|
-
|
|
16750
|
+
);
|
|
16751
|
+
setAllTasks(
|
|
16752
|
+
result.rows.map((r) => ({
|
|
16753
|
+
id: String(r.id ?? ""),
|
|
16754
|
+
priority: String(r.priority ?? "p2").toUpperCase(),
|
|
16755
|
+
title: String(r.title ?? ""),
|
|
16756
|
+
assignee: String(r.assigned_to ?? ""),
|
|
16757
|
+
assignedBy: String(r.assigned_by ?? ""),
|
|
16758
|
+
status: String(r.status ?? "open"),
|
|
16759
|
+
project: String(r.project_name ?? ""),
|
|
16760
|
+
createdAt: String(r.created_at ?? ""),
|
|
16761
|
+
result: String(r.result ?? "")
|
|
16762
|
+
}))
|
|
16763
|
+
);
|
|
16764
|
+
setDbError(null);
|
|
16765
|
+
} else {
|
|
16766
|
+
setDbError("Database client not initialized. Run exe-os setup.");
|
|
16767
|
+
}
|
|
16768
|
+
} catch (err) {
|
|
16769
|
+
setDbError(err instanceof Error ? err.message : "Unknown error");
|
|
16770
|
+
} finally {
|
|
16771
|
+
setLoading(false);
|
|
16342
16772
|
}
|
|
16343
|
-
}
|
|
16344
|
-
setDbError(true);
|
|
16345
|
-
} finally {
|
|
16346
|
-
setLoading(false);
|
|
16347
|
-
}
|
|
16773
|
+
});
|
|
16348
16774
|
}
|
|
16349
16775
|
const selected = taskRows[selectedTask]?.task;
|
|
16350
16776
|
if (showDetail && selected) {
|
|
@@ -16411,7 +16837,10 @@ function TasksView({ onBack }) {
|
|
|
16411
16837
|
filterLabel
|
|
16412
16838
|
] }),
|
|
16413
16839
|
/* @__PURE__ */ jsx10(Text, { children: " " }),
|
|
16414
|
-
loading ? /* @__PURE__ */ jsx10(Text, { color: "#6B4C9A", children: "Loading tasks..." }) : dbError ? /* @__PURE__ */
|
|
16840
|
+
loading ? /* @__PURE__ */ jsx10(Text, { color: "#6B4C9A", children: "Loading tasks..." }) : dbError ? /* @__PURE__ */ jsxs8(Text, { color: "#C91B00", children: [
|
|
16841
|
+
"Failed to load tasks: ",
|
|
16842
|
+
dbError
|
|
16843
|
+
] }) : 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) => {
|
|
16415
16844
|
if (row.type === "header") {
|
|
16416
16845
|
return /* @__PURE__ */ jsxs8(Box_default, { marginTop: i > 0 ? 1 : 0, children: [
|
|
16417
16846
|
/* @__PURE__ */ jsx10(Text, { bold: true, color: "#F0EDE8", children: row.project }),
|
|
@@ -16865,6 +17294,31 @@ function GatewayView({ onBack }) {
|
|
|
16865
17294
|
/* @__PURE__ */ jsx11(Text, { color: "#6B4C9A", children: "Loading gateway status..." })
|
|
16866
17295
|
] });
|
|
16867
17296
|
}
|
|
17297
|
+
if (!gateway.running && gateway.gatewayUrl && connectionStatus === "disconnected") {
|
|
17298
|
+
return /* @__PURE__ */ jsxs9(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
|
|
17299
|
+
/* @__PURE__ */ jsx11(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx11(Text, { bold: true, children: "Gateway Monitor" }) }),
|
|
17300
|
+
/* @__PURE__ */ jsx11(Text, { children: " " }),
|
|
17301
|
+
/* @__PURE__ */ jsx11(Text, { color: "#C91B00", children: "Gateway connection failed." }),
|
|
17302
|
+
/* @__PURE__ */ jsx11(Text, { children: " " }),
|
|
17303
|
+
/* @__PURE__ */ jsxs9(Text, { color: "#6B4C9A", children: [
|
|
17304
|
+
"Gateway URL: ",
|
|
17305
|
+
/* @__PURE__ */ jsx11(Text, { color: "#F0EDE8", children: gateway.gatewayUrl })
|
|
17306
|
+
] }),
|
|
17307
|
+
/* @__PURE__ */ jsxs9(Text, { color: "#6B4C9A", children: [
|
|
17308
|
+
"Process: ",
|
|
17309
|
+
/* @__PURE__ */ jsx11(Text, { color: "#C91B00", children: "not running" })
|
|
17310
|
+
] }),
|
|
17311
|
+
/* @__PURE__ */ jsx11(Text, { children: " " }),
|
|
17312
|
+
/* @__PURE__ */ jsxs9(Text, { color: "#6B4C9A", children: [
|
|
17313
|
+
"Start the gateway: ",
|
|
17314
|
+
/* @__PURE__ */ jsx11(Text, { bold: true, children: "exe-os gateway" })
|
|
17315
|
+
] }),
|
|
17316
|
+
/* @__PURE__ */ jsxs9(Text, { color: "#6B4C9A", children: [
|
|
17317
|
+
"Or check your config: ",
|
|
17318
|
+
/* @__PURE__ */ jsx11(Text, { bold: true, children: "~/.exe-os/gateway.json" })
|
|
17319
|
+
] })
|
|
17320
|
+
] });
|
|
17321
|
+
}
|
|
16868
17322
|
if (!gateway.running && gateway.adapters.length === 0 && !gateway.gatewayUrl) {
|
|
16869
17323
|
return /* @__PURE__ */ jsxs9(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
|
|
16870
17324
|
/* @__PURE__ */ jsx11(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx11(Text, { bold: true, children: "Gateway Monitor" }) }),
|
|
@@ -17053,14 +17507,30 @@ function getAgentStatus(agentId) {
|
|
|
17053
17507
|
// src/tui/views/Team.tsx
|
|
17054
17508
|
import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
17055
17509
|
var DEPRECATED_PROJECTS = /* @__PURE__ */ new Set(["exe-ai-employees"]);
|
|
17056
|
-
function
|
|
17510
|
+
function roleBadgeColor(role) {
|
|
17511
|
+
switch (role.toLowerCase()) {
|
|
17512
|
+
case "coo":
|
|
17513
|
+
return "#F5D76E";
|
|
17514
|
+
// gold
|
|
17515
|
+
case "cto":
|
|
17516
|
+
return "#3B82F6";
|
|
17517
|
+
// blue
|
|
17518
|
+
case "cmo":
|
|
17519
|
+
return "#6B4C9A";
|
|
17520
|
+
// purple
|
|
17521
|
+
default:
|
|
17522
|
+
return "#F0EDE8";
|
|
17523
|
+
}
|
|
17524
|
+
}
|
|
17525
|
+
function TeamView({ onBack, onViewSessions }) {
|
|
17057
17526
|
const demo = useDemo();
|
|
17058
17527
|
const [members, setMembers] = useState12([]);
|
|
17059
17528
|
const [externals, setExternals] = useState12([]);
|
|
17060
17529
|
const [loading, setLoading] = useState12(!demo);
|
|
17061
|
-
const [dbError, setDbError] = useState12(
|
|
17530
|
+
const [dbError, setDbError] = useState12(null);
|
|
17062
17531
|
const [selectedIdx, setSelectedIdx] = useState12(0);
|
|
17063
17532
|
const [showDetail, setShowDetail] = useState12(false);
|
|
17533
|
+
const [showAddHint, setShowAddHint] = useState12(false);
|
|
17064
17534
|
const orch = useOrchestrator(!demo);
|
|
17065
17535
|
const orchEmployeeMap = new Map(orch.employees.map((e) => [e.name, e]));
|
|
17066
17536
|
const allItems = [...members, ...externals.map((e) => ({ ...e, activity: "", memoryCount: 0 }))];
|
|
@@ -17075,7 +17545,7 @@ function TeamView({ onBack }) {
|
|
|
17075
17545
|
const timer = setInterval(loadTeam, 5e3);
|
|
17076
17546
|
return () => clearInterval(timer);
|
|
17077
17547
|
}, []);
|
|
17078
|
-
use_input_default((
|
|
17548
|
+
use_input_default((input, key) => {
|
|
17079
17549
|
if (showDetail) {
|
|
17080
17550
|
if (key.escape) setShowDetail(false);
|
|
17081
17551
|
return;
|
|
@@ -17084,91 +17554,103 @@ function TeamView({ onBack }) {
|
|
|
17084
17554
|
onBack?.();
|
|
17085
17555
|
return;
|
|
17086
17556
|
}
|
|
17557
|
+
if (input === "a") {
|
|
17558
|
+
setShowAddHint(true);
|
|
17559
|
+
setTimeout(() => setShowAddHint(false), 3e3);
|
|
17560
|
+
return;
|
|
17561
|
+
}
|
|
17562
|
+
if (input === "s" && selected) {
|
|
17563
|
+
onViewSessions?.(selected.name);
|
|
17564
|
+
return;
|
|
17565
|
+
}
|
|
17087
17566
|
if (key.upArrow && selectedIdx > 0) setSelectedIdx(selectedIdx - 1);
|
|
17088
17567
|
if (key.downArrow && selectedIdx < allItems.length - 1) setSelectedIdx(selectedIdx + 1);
|
|
17089
17568
|
if (key.return && allItems.length > 0) setShowDetail(true);
|
|
17090
17569
|
});
|
|
17091
17570
|
async function loadTeam() {
|
|
17092
|
-
|
|
17093
|
-
|
|
17094
|
-
const roster = await loadEmployees2();
|
|
17095
|
-
let memoryCounts = /* @__PURE__ */ new Map();
|
|
17096
|
-
let projectsByEmployee = /* @__PURE__ */ new Map();
|
|
17097
|
-
let currentTaskByEmployee = /* @__PURE__ */ new Map();
|
|
17571
|
+
const { withTrace: withTrace2 } = await Promise.resolve().then(() => (init_telemetry(), telemetry_exports));
|
|
17572
|
+
return withTrace2("tui.team.loadTeam", async () => {
|
|
17098
17573
|
try {
|
|
17099
|
-
const {
|
|
17100
|
-
const
|
|
17101
|
-
|
|
17102
|
-
|
|
17103
|
-
|
|
17104
|
-
|
|
17105
|
-
}
|
|
17106
|
-
|
|
17107
|
-
|
|
17108
|
-
|
|
17574
|
+
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
17575
|
+
const roster = await loadEmployees2();
|
|
17576
|
+
let memoryCounts = /* @__PURE__ */ new Map();
|
|
17577
|
+
let projectsByEmployee = /* @__PURE__ */ new Map();
|
|
17578
|
+
let currentTaskByEmployee = /* @__PURE__ */ new Map();
|
|
17579
|
+
try {
|
|
17580
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
17581
|
+
const client = getClient2();
|
|
17582
|
+
if (client) {
|
|
17583
|
+
const memResult = await client.execute("SELECT agent_id, COUNT(*) as cnt FROM memories GROUP BY agent_id");
|
|
17584
|
+
for (const row of memResult.rows) {
|
|
17585
|
+
memoryCounts.set(String(row.agent_id), Number(row.cnt));
|
|
17586
|
+
}
|
|
17587
|
+
for (const emp of roster) {
|
|
17588
|
+
const projResult = await client.execute({
|
|
17589
|
+
sql: `SELECT DISTINCT project_name,
|
|
17109
17590
|
MAX(CASE WHEN status = 'in_progress' THEN 1 WHEN status = 'open' THEN 2 ELSE 3 END) as urgency
|
|
17110
17591
|
FROM tasks WHERE assigned_to = ? AND status IN ('open','in_progress','done')
|
|
17111
17592
|
GROUP BY project_name ORDER BY urgency ASC LIMIT 5`,
|
|
17112
|
-
|
|
17113
|
-
|
|
17114
|
-
|
|
17115
|
-
|
|
17116
|
-
|
|
17117
|
-
|
|
17118
|
-
|
|
17119
|
-
|
|
17120
|
-
|
|
17121
|
-
|
|
17122
|
-
|
|
17123
|
-
|
|
17124
|
-
|
|
17125
|
-
|
|
17126
|
-
|
|
17127
|
-
|
|
17128
|
-
|
|
17129
|
-
|
|
17593
|
+
args: [emp.name]
|
|
17594
|
+
});
|
|
17595
|
+
const projects = projResult.rows.filter((r) => !DEPRECATED_PROJECTS.has(String(r.project_name))).map((r) => {
|
|
17596
|
+
const urgency = Number(r.urgency);
|
|
17597
|
+
let pStatus = "idle";
|
|
17598
|
+
if (urgency === 1) pStatus = "active";
|
|
17599
|
+
else if (urgency === 2) pStatus = "has_tasks";
|
|
17600
|
+
return { name: String(r.project_name), status: pStatus };
|
|
17601
|
+
});
|
|
17602
|
+
if (projects.length > 0) projectsByEmployee.set(emp.name, projects);
|
|
17603
|
+
}
|
|
17604
|
+
for (const emp of roster) {
|
|
17605
|
+
const taskResult = await client.execute({
|
|
17606
|
+
sql: "SELECT title FROM tasks WHERE assigned_to = ? AND status = 'in_progress' ORDER BY updated_at DESC LIMIT 1",
|
|
17607
|
+
args: [emp.name]
|
|
17608
|
+
});
|
|
17609
|
+
if (taskResult.rows.length > 0) {
|
|
17610
|
+
currentTaskByEmployee.set(emp.name, String(taskResult.rows[0].title));
|
|
17611
|
+
}
|
|
17130
17612
|
}
|
|
17131
17613
|
}
|
|
17614
|
+
} catch {
|
|
17132
17615
|
}
|
|
17133
|
-
|
|
17134
|
-
|
|
17135
|
-
|
|
17136
|
-
|
|
17137
|
-
|
|
17138
|
-
|
|
17139
|
-
|
|
17140
|
-
|
|
17141
|
-
|
|
17142
|
-
|
|
17143
|
-
|
|
17144
|
-
|
|
17145
|
-
|
|
17146
|
-
|
|
17147
|
-
|
|
17148
|
-
|
|
17149
|
-
|
|
17150
|
-
|
|
17151
|
-
|
|
17152
|
-
|
|
17153
|
-
|
|
17154
|
-
|
|
17155
|
-
|
|
17156
|
-
|
|
17157
|
-
|
|
17158
|
-
|
|
17159
|
-
|
|
17160
|
-
|
|
17161
|
-
|
|
17162
|
-
})));
|
|
17616
|
+
const teamData = roster.map((emp) => {
|
|
17617
|
+
const agentSt = getAgentStatus(emp.name);
|
|
17618
|
+
return {
|
|
17619
|
+
name: emp.name,
|
|
17620
|
+
role: emp.role,
|
|
17621
|
+
status: agentSt.label,
|
|
17622
|
+
activity: agentSt.label === "active" ? "Processing..." : "",
|
|
17623
|
+
memoryCount: memoryCounts.get(emp.name) ?? 0,
|
|
17624
|
+
projects: projectsByEmployee.get(emp.name),
|
|
17625
|
+
currentTask: currentTaskByEmployee.get(emp.name),
|
|
17626
|
+
sessionName: agentSt.session
|
|
17627
|
+
};
|
|
17628
|
+
});
|
|
17629
|
+
setMembers(teamData);
|
|
17630
|
+
setDbError(null);
|
|
17631
|
+
try {
|
|
17632
|
+
const { existsSync: existsSync14, readFileSync: readFileSync11 } = await import("fs");
|
|
17633
|
+
const { join } = await import("path");
|
|
17634
|
+
const home = process.env.HOME ?? "";
|
|
17635
|
+
const gatewayConfig = join(home, ".exe-os", "gateway.json");
|
|
17636
|
+
if (existsSync14(gatewayConfig)) {
|
|
17637
|
+
const raw = JSON.parse(readFileSync11(gatewayConfig, "utf8"));
|
|
17638
|
+
if (raw.agents && raw.agents.length > 0) {
|
|
17639
|
+
setExternals(raw.agents.map((a) => ({
|
|
17640
|
+
name: a.name,
|
|
17641
|
+
role: a.role ?? "external agent",
|
|
17642
|
+
status: "offline"
|
|
17643
|
+
})));
|
|
17644
|
+
}
|
|
17163
17645
|
}
|
|
17646
|
+
} catch {
|
|
17164
17647
|
}
|
|
17165
|
-
} catch {
|
|
17648
|
+
} catch (err) {
|
|
17649
|
+
setDbError(err instanceof Error ? err.message : "Unknown error");
|
|
17650
|
+
} finally {
|
|
17651
|
+
setLoading(false);
|
|
17166
17652
|
}
|
|
17167
|
-
}
|
|
17168
|
-
setDbError(true);
|
|
17169
|
-
} finally {
|
|
17170
|
-
setLoading(false);
|
|
17171
|
-
}
|
|
17653
|
+
});
|
|
17172
17654
|
}
|
|
17173
17655
|
const totalCount = members.length + externals.length;
|
|
17174
17656
|
const selected = allItems[selectedIdx];
|
|
@@ -17185,7 +17667,7 @@ function TeamView({ onBack }) {
|
|
|
17185
17667
|
/* @__PURE__ */ jsx12(Text, { children: " " }),
|
|
17186
17668
|
/* @__PURE__ */ jsxs10(Text, { children: [
|
|
17187
17669
|
"Role: ",
|
|
17188
|
-
/* @__PURE__ */ jsx12(Text, { bold: true, children: selected.role })
|
|
17670
|
+
/* @__PURE__ */ jsx12(Text, { bold: true, color: roleBadgeColor(selected.role), children: selected.role })
|
|
17189
17671
|
] }),
|
|
17190
17672
|
/* @__PURE__ */ jsxs10(Text, { children: [
|
|
17191
17673
|
"Status: ",
|
|
@@ -17214,8 +17696,9 @@ function TeamView({ onBack }) {
|
|
|
17214
17696
|
/* @__PURE__ */ jsx12(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx12(Text, { bold: true, children: "Team Roster" }) }),
|
|
17215
17697
|
/* @__PURE__ */ jsxs10(Text, { color: "#6B4C9A", children: [
|
|
17216
17698
|
totalCount,
|
|
17217
|
-
" agents |
|
|
17699
|
+
" agents | \u2191\u2193 navigate | Enter details | a add | s sessions"
|
|
17218
17700
|
] }),
|
|
17701
|
+
showAddHint && /* @__PURE__ */ jsx12(Text, { color: "#F5D76E", children: "Run /exe-new-employee <name> from CLI to add an employee." }),
|
|
17219
17702
|
!demo && orch.pendingReviews > 0 && /* @__PURE__ */ jsxs10(Text, { color: "#6B4C9A", children: [
|
|
17220
17703
|
orch.pendingReviews,
|
|
17221
17704
|
" review(s) pending exe attention"
|
|
@@ -17223,7 +17706,10 @@ function TeamView({ onBack }) {
|
|
|
17223
17706
|
/* @__PURE__ */ jsx12(Text, { children: " " }),
|
|
17224
17707
|
/* @__PURE__ */ jsx12(Text, { bold: true, children: "INTERNAL" }),
|
|
17225
17708
|
/* @__PURE__ */ jsx12(Text, { children: " " }),
|
|
17226
|
-
loading ? /* @__PURE__ */ jsx12(Text, { color: "#6B4C9A", children: " Loading
|
|
17709
|
+
loading ? /* @__PURE__ */ jsx12(Text, { color: "#6B4C9A", children: " Loading employee roster..." }) : dbError ? /* @__PURE__ */ jsxs10(Text, { color: "#C91B00", children: [
|
|
17710
|
+
" Failed to load roster: ",
|
|
17711
|
+
dbError
|
|
17712
|
+
] }) : 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) => {
|
|
17227
17713
|
const isSelected = i === selectedIdx;
|
|
17228
17714
|
const orchEmp = orchEmployeeMap.get(m.name);
|
|
17229
17715
|
return /* @__PURE__ */ jsxs10(
|
|
@@ -17239,7 +17725,7 @@ function TeamView({ onBack }) {
|
|
|
17239
17725
|
/* @__PURE__ */ jsxs10(Box_default, { gap: 1, children: [
|
|
17240
17726
|
/* @__PURE__ */ jsx12(StatusDot, { status: m.status, showLabel: false }),
|
|
17241
17727
|
/* @__PURE__ */ jsx12(Text, { bold: true, color: isSelected ? "#F5D76E" : "#F0EDE8", children: m.name }),
|
|
17242
|
-
/* @__PURE__ */ jsxs10(Text, { color: isSelected ? "#F5D76E" :
|
|
17728
|
+
/* @__PURE__ */ jsxs10(Text, { color: isSelected ? "#F5D76E" : roleBadgeColor(m.role), children: [
|
|
17243
17729
|
"(",
|
|
17244
17730
|
m.role,
|
|
17245
17731
|
")"
|
|
@@ -17312,6 +17798,33 @@ import TextInput2 from "ink-text-input";
|
|
|
17312
17798
|
import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
17313
17799
|
var PANELS = ["Workspaces", "Documents", "Chat"];
|
|
17314
17800
|
var MAX_VISIBLE_MESSAGES = 12;
|
|
17801
|
+
var DEMO_SEARCH_RESULTS = [
|
|
17802
|
+
{ 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" },
|
|
17803
|
+
{ 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" },
|
|
17804
|
+
{ 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" },
|
|
17805
|
+
{ 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" },
|
|
17806
|
+
{ 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" }
|
|
17807
|
+
];
|
|
17808
|
+
function agentColor(agentId) {
|
|
17809
|
+
switch (agentId.toLowerCase()) {
|
|
17810
|
+
case "exe":
|
|
17811
|
+
return "#F5D76E";
|
|
17812
|
+
case "yoshi":
|
|
17813
|
+
return "#3B82F6";
|
|
17814
|
+
case "mari":
|
|
17815
|
+
return "#6B4C9A";
|
|
17816
|
+
default:
|
|
17817
|
+
return "#F0EDE8";
|
|
17818
|
+
}
|
|
17819
|
+
}
|
|
17820
|
+
function formatTimeAgo(iso) {
|
|
17821
|
+
const diff2 = Date.now() - new Date(iso).getTime();
|
|
17822
|
+
const hours = Math.floor(diff2 / 36e5);
|
|
17823
|
+
if (hours < 1) return "just now";
|
|
17824
|
+
if (hours < 24) return `${hours}h ago`;
|
|
17825
|
+
const days = Math.floor(hours / 24);
|
|
17826
|
+
return `${days}d ago`;
|
|
17827
|
+
}
|
|
17315
17828
|
function WikiView({ onBack }) {
|
|
17316
17829
|
const demo = useDemo();
|
|
17317
17830
|
const [activePanel, setActivePanel] = useState13("Workspaces");
|
|
@@ -17322,6 +17835,12 @@ function WikiView({ onBack }) {
|
|
|
17322
17835
|
const [chatHistory, setChatHistory] = useState13([]);
|
|
17323
17836
|
const [chatInput, setChatInput] = useState13("");
|
|
17324
17837
|
const [chatFocused, setChatFocused] = useState13(false);
|
|
17838
|
+
const [searchMode, setSearchMode] = useState13(false);
|
|
17839
|
+
const [searchQuery, setSearchQuery] = useState13("");
|
|
17840
|
+
const [searchResults, setSearchResults] = useState13([]);
|
|
17841
|
+
const [searchLoading, setSearchLoading] = useState13(false);
|
|
17842
|
+
const [selectedResultIdx, setSelectedResultIdx] = useState13(0);
|
|
17843
|
+
const [expandedResultIdx, setExpandedResultIdx] = useState13(null);
|
|
17325
17844
|
const [loading, setLoading] = useState13(true);
|
|
17326
17845
|
const [connected, setConnected] = useState13(false);
|
|
17327
17846
|
const [wikiUrl, setWikiUrl] = useState13("");
|
|
@@ -17415,6 +17934,55 @@ function WikiView({ onBack }) {
|
|
|
17415
17934
|
setSending(false);
|
|
17416
17935
|
}
|
|
17417
17936
|
}
|
|
17937
|
+
async function searchMemories2(query) {
|
|
17938
|
+
if (!query.trim()) {
|
|
17939
|
+
setSearchResults([]);
|
|
17940
|
+
return;
|
|
17941
|
+
}
|
|
17942
|
+
if (demo) {
|
|
17943
|
+
const q = query.toLowerCase();
|
|
17944
|
+
setSearchResults(
|
|
17945
|
+
DEMO_SEARCH_RESULTS.filter((r) => r.rawText.toLowerCase().includes(q))
|
|
17946
|
+
);
|
|
17947
|
+
return;
|
|
17948
|
+
}
|
|
17949
|
+
setSearchLoading(true);
|
|
17950
|
+
try {
|
|
17951
|
+
const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
|
|
17952
|
+
const client = getClient2();
|
|
17953
|
+
const result = await client.execute({
|
|
17954
|
+
sql: `SELECT id, agent_id, raw_text, timestamp, project_name
|
|
17955
|
+
FROM memories
|
|
17956
|
+
WHERE raw_text LIKE ? AND COALESCE(status, 'active') = 'active'
|
|
17957
|
+
ORDER BY timestamp DESC LIMIT 20`,
|
|
17958
|
+
args: [`%${query}%`]
|
|
17959
|
+
});
|
|
17960
|
+
setSearchResults(
|
|
17961
|
+
result.rows.map((r) => ({
|
|
17962
|
+
id: String(r.id),
|
|
17963
|
+
agentId: String(r.agent_id),
|
|
17964
|
+
rawText: String(r.raw_text),
|
|
17965
|
+
timestamp: String(r.timestamp),
|
|
17966
|
+
projectName: String(r.project_name ?? "")
|
|
17967
|
+
}))
|
|
17968
|
+
);
|
|
17969
|
+
} catch {
|
|
17970
|
+
setSearchResults([]);
|
|
17971
|
+
} finally {
|
|
17972
|
+
setSearchLoading(false);
|
|
17973
|
+
}
|
|
17974
|
+
}
|
|
17975
|
+
useEffect15(() => {
|
|
17976
|
+
if (!searchMode) return;
|
|
17977
|
+
const timer = setTimeout(() => {
|
|
17978
|
+
searchMemories2(searchQuery);
|
|
17979
|
+
}, 300);
|
|
17980
|
+
return () => clearTimeout(timer);
|
|
17981
|
+
}, [searchQuery, searchMode]);
|
|
17982
|
+
useEffect15(() => {
|
|
17983
|
+
setSelectedResultIdx(0);
|
|
17984
|
+
setExpandedResultIdx(null);
|
|
17985
|
+
}, [searchResults]);
|
|
17418
17986
|
async function selectWorkspace(idx) {
|
|
17419
17987
|
setSelectedWorkspaceIdx(idx);
|
|
17420
17988
|
const ws = workspaces[idx];
|
|
@@ -17435,6 +18003,32 @@ function WikiView({ onBack }) {
|
|
|
17435
18003
|
}
|
|
17436
18004
|
}
|
|
17437
18005
|
use_input_default((input, key) => {
|
|
18006
|
+
if (searchMode && !chatFocused) {
|
|
18007
|
+
if (key.escape) {
|
|
18008
|
+
setSearchMode(false);
|
|
18009
|
+
setSearchQuery("");
|
|
18010
|
+
setSearchResults([]);
|
|
18011
|
+
setExpandedResultIdx(null);
|
|
18012
|
+
return;
|
|
18013
|
+
}
|
|
18014
|
+
if (key.upArrow && selectedResultIdx > 0) {
|
|
18015
|
+
setSelectedResultIdx(selectedResultIdx - 1);
|
|
18016
|
+
setExpandedResultIdx(null);
|
|
18017
|
+
return;
|
|
18018
|
+
}
|
|
18019
|
+
if (key.downArrow && selectedResultIdx < searchResults.length - 1) {
|
|
18020
|
+
setSelectedResultIdx(selectedResultIdx + 1);
|
|
18021
|
+
setExpandedResultIdx(null);
|
|
18022
|
+
return;
|
|
18023
|
+
}
|
|
18024
|
+
if (key.return && searchResults.length > 0) {
|
|
18025
|
+
setExpandedResultIdx(
|
|
18026
|
+
expandedResultIdx === selectedResultIdx ? null : selectedResultIdx
|
|
18027
|
+
);
|
|
18028
|
+
return;
|
|
18029
|
+
}
|
|
18030
|
+
return;
|
|
18031
|
+
}
|
|
17438
18032
|
if (chatFocused) {
|
|
17439
18033
|
if (key.escape) {
|
|
17440
18034
|
setChatFocused(false);
|
|
@@ -17446,6 +18040,13 @@ function WikiView({ onBack }) {
|
|
|
17446
18040
|
}
|
|
17447
18041
|
return;
|
|
17448
18042
|
}
|
|
18043
|
+
if (input === "/" && activePanel !== "Chat") {
|
|
18044
|
+
setSearchMode(true);
|
|
18045
|
+
setSearchQuery("");
|
|
18046
|
+
setSearchResults(demo ? DEMO_SEARCH_RESULTS : []);
|
|
18047
|
+
setExpandedResultIdx(null);
|
|
18048
|
+
return;
|
|
18049
|
+
}
|
|
17449
18050
|
if (key.leftArrow) {
|
|
17450
18051
|
const panelIdx = PANELS.indexOf(activePanel);
|
|
17451
18052
|
if (panelIdx === 0) {
|
|
@@ -17523,9 +18124,53 @@ function WikiView({ onBack }) {
|
|
|
17523
18124
|
/* @__PURE__ */ jsx13(Text, { bold: true, children: selectedWs.name })
|
|
17524
18125
|
] }) : null
|
|
17525
18126
|
] }),
|
|
17526
|
-
/* @__PURE__ */ jsx13(Text, { color: "#6B4C9A", children: "\u2190\u2192 switch panels | \u2191\u2193 navigate | /
|
|
18127
|
+
/* @__PURE__ */ jsx13(Text, { color: "#6B4C9A", children: "\u2190\u2192 switch panels | \u2191\u2193 navigate | / search memories | Enter select" }),
|
|
17527
18128
|
/* @__PURE__ */ jsx13(Text, { children: " " }),
|
|
17528
|
-
/* @__PURE__ */ jsxs11(Box_default, {
|
|
18129
|
+
searchMode ? /* @__PURE__ */ jsxs11(Box_default, { flexDirection: "column", flexGrow: 1, children: [
|
|
18130
|
+
/* @__PURE__ */ jsxs11(Box_default, { paddingX: 1, children: [
|
|
18131
|
+
/* @__PURE__ */ jsx13(Text, { color: "#F5D76E", children: "Search: " }),
|
|
18132
|
+
/* @__PURE__ */ jsx13(
|
|
18133
|
+
TextInput2,
|
|
18134
|
+
{
|
|
18135
|
+
value: searchQuery,
|
|
18136
|
+
onChange: setSearchQuery,
|
|
18137
|
+
placeholder: "search memories...",
|
|
18138
|
+
focus: true
|
|
18139
|
+
}
|
|
18140
|
+
),
|
|
18141
|
+
/* @__PURE__ */ jsx13(Text, { color: "#6B4C9A", children: " (esc to close)" })
|
|
18142
|
+
] }),
|
|
18143
|
+
/* @__PURE__ */ jsx13(Text, { children: " " }),
|
|
18144
|
+
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) => {
|
|
18145
|
+
const isSelected = i === selectedResultIdx;
|
|
18146
|
+
const isExpanded = i === expandedResultIdx;
|
|
18147
|
+
const snippet = result.rawText.slice(0, 80);
|
|
18148
|
+
return /* @__PURE__ */ jsxs11(Box_default, { flexDirection: "column", children: [
|
|
18149
|
+
/* @__PURE__ */ jsxs11(
|
|
18150
|
+
Text,
|
|
18151
|
+
{
|
|
18152
|
+
backgroundColor: isSelected ? "#6B4C9A" : void 0,
|
|
18153
|
+
color: isSelected ? "#F5D76E" : void 0,
|
|
18154
|
+
children: [
|
|
18155
|
+
" ",
|
|
18156
|
+
/* @__PURE__ */ jsx13(Text, { color: isSelected ? "#F5D76E" : agentColor(result.agentId), bold: true, children: result.agentId.padEnd(8) }),
|
|
18157
|
+
/* @__PURE__ */ jsxs11(Text, { color: isSelected ? "#F5D76E" : "#F0EDE8", children: [
|
|
18158
|
+
snippet,
|
|
18159
|
+
result.rawText.length > 80 ? "..." : ""
|
|
18160
|
+
] }),
|
|
18161
|
+
" ",
|
|
18162
|
+
/* @__PURE__ */ jsx13(Text, { color: isSelected ? "#F5D76E" : "#3D3660", dimColor: !isSelected, children: formatTimeAgo(result.timestamp) })
|
|
18163
|
+
]
|
|
18164
|
+
}
|
|
18165
|
+
),
|
|
18166
|
+
isExpanded ? /* @__PURE__ */ jsx13(Box_default, { paddingX: 4, marginBottom: 1, children: /* @__PURE__ */ jsx13(Text, { color: "#F0EDE8", wrap: "wrap", children: result.rawText }) }) : null
|
|
18167
|
+
] }, result.id);
|
|
18168
|
+
}),
|
|
18169
|
+
searchResults.length > 0 ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
|
|
18170
|
+
/* @__PURE__ */ jsx13(Text, { children: " " }),
|
|
18171
|
+
/* @__PURE__ */ jsx13(Text, { color: "#3D3660", children: " \u2191\u2193 navigate | Enter expand | Esc close" })
|
|
18172
|
+
] }) : null
|
|
18173
|
+
] }) : /* @__PURE__ */ jsxs11(Box_default, { flexGrow: 1, gap: 1, children: [
|
|
17529
18174
|
/* @__PURE__ */ jsxs11(Box_default, { flexDirection: "column", width: "25%", children: [
|
|
17530
18175
|
/* @__PURE__ */ jsxs11(Text, { bold: true, backgroundColor: panelHeaderBg("Workspaces"), color: panelHeaderColor("Workspaces"), children: [
|
|
17531
18176
|
activePanel === "Workspaces" ? "\u25B8 " : " ",
|
|
@@ -17609,50 +18254,41 @@ function WikiView({ onBack }) {
|
|
|
17609
18254
|
}
|
|
17610
18255
|
|
|
17611
18256
|
// src/tui/views/Settings.tsx
|
|
17612
|
-
import React24, { useState as useState14, useEffect as useEffect16 } from "react";
|
|
18257
|
+
import React24, { useState as useState14, useEffect as useEffect16, useCallback as useCallback7 } from "react";
|
|
17613
18258
|
import { Fragment as Fragment5, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
17614
|
-
|
|
18259
|
+
function maskSecret(value) {
|
|
18260
|
+
if (value.length <= 10) return "****";
|
|
18261
|
+
return `${value.slice(0, 6)}...${value.slice(-4)}`;
|
|
18262
|
+
}
|
|
18263
|
+
var SECTION_NAMES = ["Providers", "Cloud Sync", "License", "System", "Gateway"];
|
|
18264
|
+
var GREEN = "#22C55E";
|
|
18265
|
+
var DIM = "#6B4C9A";
|
|
18266
|
+
var YELLOW = "#F5D76E";
|
|
17615
18267
|
function SettingsView({ onBack }) {
|
|
17616
18268
|
const [providers, setProviders] = useState14([]);
|
|
17617
|
-
const [
|
|
17618
|
-
|
|
17619
|
-
|
|
17620
|
-
|
|
17621
|
-
searchMode: "checking..."
|
|
17622
|
-
});
|
|
17623
|
-
const [runtime, setRuntime] = useState14({
|
|
17624
|
-
consolidation: true,
|
|
17625
|
-
consolidationModel: "...",
|
|
17626
|
-
skillLearning: true,
|
|
17627
|
-
skillThreshold: 3,
|
|
17628
|
-
skillModel: "...",
|
|
17629
|
-
failoverChain: ["anthropic", "opencode", "gemini", "openai"]
|
|
17630
|
-
});
|
|
17631
|
-
const [employeeModels, setEmployeeModels] = useState14([]);
|
|
18269
|
+
const [cloud, setCloud] = useState14({ configured: false, endpoint: "", apiKey: "" });
|
|
18270
|
+
const [license, setLicense] = useState14({ valid: false, plan: "checking...", expiresAt: null, deviceLimit: 0, employeeLimit: 0, memoryLimit: 0 });
|
|
18271
|
+
const [system, setSystem] = useState14({ daemon: "unknown", version: "...", dbPath: "...", embeddingModel: "...", encryption: "checking..." });
|
|
18272
|
+
const [gateway, setGateway] = useState14({ adapters: [] });
|
|
17632
18273
|
const [selectedSection, setSelectedSection] = useState14(0);
|
|
17633
18274
|
const [loading, setLoading] = useState14(true);
|
|
17634
|
-
const
|
|
17635
|
-
|
|
17636
|
-
|
|
17637
|
-
|
|
17638
|
-
|
|
17639
|
-
|
|
17640
|
-
|
|
17641
|
-
|
|
17642
|
-
onBack?.();
|
|
17643
|
-
return;
|
|
17644
|
-
}
|
|
17645
|
-
if (key.upArrow && selectedSection > 0) setSelectedSection(selectedSection - 1);
|
|
17646
|
-
if (key.downArrow && selectedSection < SECTION_NAMES.length - 1) setSelectedSection(selectedSection + 1);
|
|
17647
|
-
});
|
|
17648
|
-
async function loadSettings() {
|
|
17649
|
-
const providerList = [
|
|
17650
|
-
{ name: "Anthropic", configured: !!process.env.ANTHROPIC_API_KEY, detail: process.env.ANTHROPIC_API_KEY ? "ANTHROPIC_API_KEY set" : "not configured" },
|
|
17651
|
-
{ name: "OpenCode", configured: !!process.env.OPENCODE_API_KEY, detail: process.env.OPENCODE_API_KEY ? "OPENCODE_API_KEY set" : "not configured" },
|
|
17652
|
-
{ name: "Gemini", configured: !!process.env.GEMINI_API_KEY, detail: process.env.GEMINI_API_KEY ? "GEMINI_API_KEY set" : "not configured" },
|
|
17653
|
-
{ name: "OpenAI", configured: !!process.env.OPENAI_API_KEY, detail: process.env.OPENAI_API_KEY ? "OPENAI_API_KEY set" : "not configured" },
|
|
17654
|
-
{ name: "Chutes", configured: !!process.env.CHUTES_API_KEY, detail: process.env.CHUTES_API_KEY ? "CHUTES_API_KEY set" : "not configured" }
|
|
18275
|
+
const loadSettings = useCallback7(async () => {
|
|
18276
|
+
setLoading(true);
|
|
18277
|
+
const envKeys = [
|
|
18278
|
+
["Anthropic", "ANTHROPIC_API_KEY"],
|
|
18279
|
+
["OpenCode", "OPENCODE_API_KEY"],
|
|
18280
|
+
["Gemini", "GEMINI_API_KEY"],
|
|
18281
|
+
["OpenAI", "OPENAI_API_KEY"],
|
|
18282
|
+
["Chutes", "CHUTES_API_KEY"]
|
|
17655
18283
|
];
|
|
18284
|
+
const providerList = envKeys.map(([name, envVar]) => {
|
|
18285
|
+
const val = process.env[envVar];
|
|
18286
|
+
return {
|
|
18287
|
+
name,
|
|
18288
|
+
configured: !!val,
|
|
18289
|
+
detail: val ? maskSecret(val) : "not configured"
|
|
18290
|
+
};
|
|
18291
|
+
});
|
|
17656
18292
|
try {
|
|
17657
18293
|
const { execSync: execSync10 } = await import("child_process");
|
|
17658
18294
|
execSync10("curl -s --max-time 1 http://localhost:11434/api/tags", { timeout: 2e3 });
|
|
@@ -17664,112 +18300,101 @@ function SettingsView({ onBack }) {
|
|
|
17664
18300
|
try {
|
|
17665
18301
|
const { existsSync: existsSync14 } = await import("fs");
|
|
17666
18302
|
const { join } = await import("path");
|
|
17667
|
-
const home = process.env.HOME ?? "";
|
|
17668
|
-
const hasKey = existsSync14(join(home, ".exe-os", "master.key"));
|
|
17669
18303
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
17670
18304
|
const cfg = await loadConfig2();
|
|
17671
|
-
setMemory({
|
|
17672
|
-
encryption: hasKey ? "SQLCipher AES-256" : "not configured",
|
|
17673
|
-
embeddingModel: cfg.modelFile ?? "unknown",
|
|
17674
|
-
cloudSync: cfg.cloud ? "enabled" : "disabled",
|
|
17675
|
-
searchMode: cfg.searchMode ?? "hybrid"
|
|
17676
|
-
});
|
|
17677
|
-
const rawCfg = cfg;
|
|
17678
|
-
const chain = Array.isArray(rawCfg.failoverChain) ? rawCfg.failoverChain : ["anthropic", "opencode", "gemini", "openai"];
|
|
17679
|
-
setRuntime({
|
|
17680
|
-
consolidation: cfg.consolidationEnabled,
|
|
17681
|
-
consolidationModel: cfg.consolidationModel,
|
|
17682
|
-
skillLearning: cfg.skillLearning,
|
|
17683
|
-
skillThreshold: cfg.skillThreshold,
|
|
17684
|
-
skillModel: cfg.skillModel,
|
|
17685
|
-
failoverChain: chain
|
|
17686
|
-
});
|
|
17687
|
-
} catch {
|
|
17688
|
-
}
|
|
17689
|
-
try {
|
|
17690
|
-
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
17691
|
-
const roster = await loadEmployees2();
|
|
17692
|
-
const { existsSync: existsSync14, readFileSync: readFileSync11 } = await import("fs");
|
|
17693
|
-
const { join } = await import("path");
|
|
17694
18305
|
const home = process.env.HOME ?? "";
|
|
17695
|
-
const
|
|
17696
|
-
|
|
17697
|
-
|
|
17698
|
-
|
|
17699
|
-
|
|
17700
|
-
|
|
17701
|
-
|
|
17702
|
-
|
|
17703
|
-
|
|
17704
|
-
}
|
|
18306
|
+
const hasKey = existsSync14(join(home, ".exe-os", "master.key"));
|
|
18307
|
+
if (cfg.cloud) {
|
|
18308
|
+
setCloud({
|
|
18309
|
+
configured: true,
|
|
18310
|
+
endpoint: cfg.cloud.endpoint,
|
|
18311
|
+
apiKey: maskSecret(cfg.cloud.apiKey)
|
|
18312
|
+
});
|
|
18313
|
+
} else {
|
|
18314
|
+
setCloud({ configured: false, endpoint: "", apiKey: "" });
|
|
17705
18315
|
}
|
|
17706
|
-
|
|
17707
|
-
|
|
17708
|
-
|
|
17709
|
-
|
|
17710
|
-
}
|
|
17711
|
-
} catch {
|
|
17712
|
-
}
|
|
17713
|
-
try {
|
|
17714
|
-
const { existsSync: existsSync14, readFileSync: readFileSync11 } = await import("fs");
|
|
17715
|
-
const { join } = await import("path");
|
|
17716
|
-
const home = process.env.HOME ?? "";
|
|
17717
|
-
const ccSettingsPath = join(home, ".claude", "settings.json");
|
|
17718
|
-
const installed = existsSync14(ccSettingsPath);
|
|
17719
|
-
let hooksWired = false;
|
|
17720
|
-
if (installed) {
|
|
17721
|
-
try {
|
|
17722
|
-
const settings = JSON.parse(readFileSync11(ccSettingsPath, "utf8"));
|
|
17723
|
-
const hooks = settings.hooks;
|
|
17724
|
-
if (hooks) {
|
|
17725
|
-
hooksWired = Object.values(hooks).flat().some((h) => h.command?.includes("exe-os") || h.command?.includes("exe-mem"));
|
|
17726
|
-
}
|
|
17727
|
-
} catch {
|
|
17728
|
-
}
|
|
18316
|
+
const pidPath = join(home, ".exe-os", "exed.pid");
|
|
18317
|
+
let daemon = "unknown";
|
|
18318
|
+
try {
|
|
18319
|
+
daemon = existsSync14(pidPath) ? "running" : "stopped";
|
|
18320
|
+
} catch {
|
|
17729
18321
|
}
|
|
17730
|
-
|
|
17731
|
-
|
|
17732
|
-
|
|
17733
|
-
|
|
17734
|
-
|
|
17735
|
-
|
|
17736
|
-
|
|
17737
|
-
|
|
17738
|
-
|
|
18322
|
+
let version = "unknown";
|
|
18323
|
+
try {
|
|
18324
|
+
const { readFileSync: readFileSync11 } = await import("fs");
|
|
18325
|
+
const { createRequire } = await import("module");
|
|
18326
|
+
const require2 = createRequire(import.meta.url);
|
|
18327
|
+
const pkgPath = require2.resolve("@askexenow/exe-os/package.json");
|
|
18328
|
+
const pkg = JSON.parse(readFileSync11(pkgPath, "utf8"));
|
|
18329
|
+
version = pkg.version;
|
|
18330
|
+
} catch {
|
|
17739
18331
|
try {
|
|
17740
|
-
const
|
|
17741
|
-
|
|
18332
|
+
const { readFileSync: readFileSync11 } = await import("fs");
|
|
18333
|
+
const { join: joinPath } = await import("path");
|
|
18334
|
+
const pkg = JSON.parse(readFileSync11(joinPath(process.cwd(), "package.json"), "utf8"));
|
|
18335
|
+
version = pkg.version;
|
|
17742
18336
|
} catch {
|
|
17743
|
-
setLicense({ valid: false, detail: "invalid license file" });
|
|
17744
18337
|
}
|
|
17745
|
-
} else {
|
|
17746
|
-
setLicense({ valid: false, detail: "no license" });
|
|
17747
18338
|
}
|
|
18339
|
+
setSystem({
|
|
18340
|
+
daemon,
|
|
18341
|
+
version,
|
|
18342
|
+
dbPath: cfg.dbPath,
|
|
18343
|
+
embeddingModel: cfg.modelFile ?? "unknown",
|
|
18344
|
+
encryption: hasKey ? "SQLCipher AES-256" : "not configured"
|
|
18345
|
+
});
|
|
17748
18346
|
} catch {
|
|
17749
18347
|
}
|
|
17750
18348
|
try {
|
|
17751
|
-
const {
|
|
17752
|
-
const
|
|
17753
|
-
|
|
17754
|
-
|
|
17755
|
-
|
|
17756
|
-
|
|
17757
|
-
|
|
17758
|
-
|
|
17759
|
-
|
|
18349
|
+
const { checkLicense: checkLicense2 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
18350
|
+
const lic = await checkLicense2();
|
|
18351
|
+
setLicense({
|
|
18352
|
+
valid: lic.valid,
|
|
18353
|
+
plan: lic.plan.toUpperCase(),
|
|
18354
|
+
expiresAt: lic.expiresAt,
|
|
18355
|
+
deviceLimit: lic.deviceLimit,
|
|
18356
|
+
employeeLimit: lic.employeeLimit,
|
|
18357
|
+
memoryLimit: lic.memoryLimit
|
|
18358
|
+
});
|
|
17760
18359
|
} catch {
|
|
18360
|
+
setLicense({ valid: false, plan: "FREE", expiresAt: null, deviceLimit: 1, employeeLimit: 1, memoryLimit: 5e3 });
|
|
17761
18361
|
}
|
|
17762
|
-
|
|
17763
|
-
|
|
17764
|
-
|
|
17765
|
-
|
|
18362
|
+
const gatewayAdapters = [
|
|
18363
|
+
{ name: "WhatsApp", configured: !!process.env.WHATSAPP_API_TOKEN || !!process.env.WHATSAPP_PHONE_NUMBER_ID },
|
|
18364
|
+
{ name: "Telegram", configured: !!process.env.TELEGRAM_BOT_TOKEN },
|
|
18365
|
+
{ name: "Discord", configured: !!process.env.DISCORD_BOT_TOKEN },
|
|
18366
|
+
{ name: "Slack", configured: !!process.env.SLACK_BOT_TOKEN }
|
|
18367
|
+
];
|
|
18368
|
+
setGateway({ adapters: gatewayAdapters });
|
|
18369
|
+
setLoading(false);
|
|
18370
|
+
}, []);
|
|
18371
|
+
useEffect16(() => {
|
|
18372
|
+
loadSettings();
|
|
18373
|
+
}, [loadSettings]);
|
|
18374
|
+
use_input_default((input, key) => {
|
|
18375
|
+
if (key.leftArrow) {
|
|
18376
|
+
onBack?.();
|
|
18377
|
+
return;
|
|
18378
|
+
}
|
|
18379
|
+
if (key.upArrow && selectedSection > 0) setSelectedSection(selectedSection - 1);
|
|
18380
|
+
if (key.downArrow && selectedSection < SECTION_NAMES.length - 1) setSelectedSection(selectedSection + 1);
|
|
18381
|
+
if (input === "r") {
|
|
18382
|
+
loadSettings();
|
|
18383
|
+
}
|
|
18384
|
+
});
|
|
18385
|
+
const statusDot = (ok) => ok ? "\u2022" : "\u2022";
|
|
18386
|
+
const statusColor = (ok) => ok ? GREEN : DIM;
|
|
18387
|
+
const sectionColor = (idx) => selectedSection === idx ? YELLOW : void 0;
|
|
18388
|
+
const sectionBg = (idx) => selectedSection === idx ? DIM : void 0;
|
|
17766
18389
|
const sectionMarker = (idx) => selectedSection === idx ? "\u25B8 " : " ";
|
|
18390
|
+
const anyGateway = gateway.adapters.some((a) => a.configured);
|
|
18391
|
+
const formatLimit = (n) => n === -1 ? "unlimited" : n.toLocaleString();
|
|
17767
18392
|
return /* @__PURE__ */ jsxs12(Box_default, { flexDirection: "column", flexGrow: 1, paddingX: 1, children: [
|
|
17768
18393
|
/* @__PURE__ */ jsx14(Box_default, { borderStyle: "single", borderColor: "#3D3660", paddingX: 1, alignSelf: "flex-start", children: /* @__PURE__ */ jsx14(Text, { bold: true, children: "Settings" }) }),
|
|
17769
|
-
/* @__PURE__ */ jsx14(Text, { color:
|
|
18394
|
+
/* @__PURE__ */ jsx14(Text, { color: DIM, children: "\u2191\u2193 navigate sections r refresh" }),
|
|
17770
18395
|
/* @__PURE__ */ jsx14(Text, { children: " " }),
|
|
17771
18396
|
loading && /* @__PURE__ */ jsxs12(Fragment5, { children: [
|
|
17772
|
-
/* @__PURE__ */ jsx14(Text, { color:
|
|
18397
|
+
/* @__PURE__ */ jsx14(Text, { color: DIM, children: "Loading settings..." }),
|
|
17773
18398
|
/* @__PURE__ */ jsx14(Text, { children: " " })
|
|
17774
18399
|
] }),
|
|
17775
18400
|
/* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(0), color: sectionColor(0), children: [
|
|
@@ -17779,6 +18404,8 @@ function SettingsView({ onBack }) {
|
|
|
17779
18404
|
/* @__PURE__ */ jsx14(Text, { children: " " }),
|
|
17780
18405
|
providers.map((p) => /* @__PURE__ */ jsxs12(Text, { children: [
|
|
17781
18406
|
" ",
|
|
18407
|
+
/* @__PURE__ */ jsx14(Text, { color: statusColor(p.configured), children: statusDot(p.configured) }),
|
|
18408
|
+
" ",
|
|
17782
18409
|
p.name,
|
|
17783
18410
|
": ",
|
|
17784
18411
|
/* @__PURE__ */ jsx14(Text, { color: statusColor(p.configured), children: p.detail })
|
|
@@ -17786,109 +18413,301 @@ function SettingsView({ onBack }) {
|
|
|
17786
18413
|
/* @__PURE__ */ jsx14(Text, { children: " " }),
|
|
17787
18414
|
/* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(1), color: sectionColor(1), children: [
|
|
17788
18415
|
sectionMarker(1),
|
|
17789
|
-
"
|
|
18416
|
+
"Cloud Sync"
|
|
17790
18417
|
] }),
|
|
17791
18418
|
/* @__PURE__ */ jsx14(Text, { children: " " }),
|
|
17792
|
-
/* @__PURE__ */ jsxs12(
|
|
18419
|
+
cloud.configured ? /* @__PURE__ */ jsxs12(Fragment5, { children: [
|
|
18420
|
+
/* @__PURE__ */ jsxs12(Text, { children: [
|
|
18421
|
+
" ",
|
|
18422
|
+
/* @__PURE__ */ jsx14(Text, { color: GREEN, children: statusDot(true) }),
|
|
18423
|
+
" Status: ",
|
|
18424
|
+
/* @__PURE__ */ jsx14(Text, { color: GREEN, children: "connected" })
|
|
18425
|
+
] }),
|
|
18426
|
+
/* @__PURE__ */ jsxs12(Text, { children: [
|
|
18427
|
+
" ",
|
|
18428
|
+
"Endpoint: ",
|
|
18429
|
+
/* @__PURE__ */ jsx14(Text, { color: GREEN, children: cloud.endpoint })
|
|
18430
|
+
] }),
|
|
18431
|
+
/* @__PURE__ */ jsxs12(Text, { children: [
|
|
18432
|
+
" ",
|
|
18433
|
+
"API key: ",
|
|
18434
|
+
/* @__PURE__ */ jsx14(Text, { color: DIM, children: cloud.apiKey })
|
|
18435
|
+
] })
|
|
18436
|
+
] }) : /* @__PURE__ */ jsxs12(Text, { children: [
|
|
17793
18437
|
" ",
|
|
17794
|
-
|
|
17795
|
-
|
|
18438
|
+
/* @__PURE__ */ jsx14(Text, { color: DIM, children: statusDot(false) }),
|
|
18439
|
+
" ",
|
|
18440
|
+
/* @__PURE__ */ jsx14(Text, { color: DIM, children: "Not configured \u2014 run /exe-cloud" })
|
|
17796
18441
|
] }),
|
|
17797
|
-
/* @__PURE__ */
|
|
17798
|
-
|
|
17799
|
-
|
|
17800
|
-
|
|
18442
|
+
/* @__PURE__ */ jsx14(Text, { children: " " }),
|
|
18443
|
+
/* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(2), color: sectionColor(2), children: [
|
|
18444
|
+
sectionMarker(2),
|
|
18445
|
+
"License"
|
|
17801
18446
|
] }),
|
|
18447
|
+
/* @__PURE__ */ jsx14(Text, { children: " " }),
|
|
17802
18448
|
/* @__PURE__ */ jsxs12(Text, { children: [
|
|
17803
18449
|
" ",
|
|
17804
|
-
"
|
|
17805
|
-
/* @__PURE__ */ jsx14(Text, { color:
|
|
18450
|
+
"Plan: ",
|
|
18451
|
+
/* @__PURE__ */ jsx14(Text, { color: license.plan === "FREE" ? YELLOW : GREEN, children: license.plan })
|
|
17806
18452
|
] }),
|
|
17807
|
-
/* @__PURE__ */ jsxs12(Text, { children: [
|
|
18453
|
+
license.expiresAt && /* @__PURE__ */ jsxs12(Text, { children: [
|
|
17808
18454
|
" ",
|
|
17809
|
-
"
|
|
17810
|
-
/* @__PURE__ */ jsx14(Text, { color:
|
|
17811
|
-
] }),
|
|
17812
|
-
/* @__PURE__ */ jsx14(Text, { children: " " }),
|
|
17813
|
-
/* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(2), color: sectionColor(2), children: [
|
|
17814
|
-
sectionMarker(2),
|
|
17815
|
-
"Runtime"
|
|
18455
|
+
"Expires: ",
|
|
18456
|
+
/* @__PURE__ */ jsx14(Text, { color: GREEN, children: license.expiresAt.split("T")[0] })
|
|
17816
18457
|
] }),
|
|
17817
|
-
/* @__PURE__ */ jsx14(Text, { children: " " }),
|
|
17818
18458
|
/* @__PURE__ */ jsxs12(Text, { children: [
|
|
17819
18459
|
" ",
|
|
17820
|
-
"
|
|
17821
|
-
/* @__PURE__ */ jsx14(Text, { color:
|
|
17822
|
-
" (",
|
|
17823
|
-
runtime.consolidationModel,
|
|
17824
|
-
")"
|
|
18460
|
+
"Devices: ",
|
|
18461
|
+
/* @__PURE__ */ jsx14(Text, { color: GREEN, children: formatLimit(license.deviceLimit) })
|
|
17825
18462
|
] }),
|
|
17826
18463
|
/* @__PURE__ */ jsxs12(Text, { children: [
|
|
17827
18464
|
" ",
|
|
17828
|
-
"
|
|
17829
|
-
/* @__PURE__ */ jsx14(Text, { color:
|
|
17830
|
-
" (threshold: ",
|
|
17831
|
-
runtime.skillThreshold,
|
|
17832
|
-
")"
|
|
18465
|
+
"Employees: ",
|
|
18466
|
+
/* @__PURE__ */ jsx14(Text, { color: GREEN, children: formatLimit(license.employeeLimit) })
|
|
17833
18467
|
] }),
|
|
17834
18468
|
/* @__PURE__ */ jsxs12(Text, { children: [
|
|
17835
18469
|
" ",
|
|
17836
|
-
"
|
|
17837
|
-
/* @__PURE__ */ jsx14(Text, { color:
|
|
18470
|
+
"Memory limit: ",
|
|
18471
|
+
/* @__PURE__ */ jsx14(Text, { color: GREEN, children: formatLimit(license.memoryLimit) })
|
|
17838
18472
|
] }),
|
|
17839
18473
|
/* @__PURE__ */ jsx14(Text, { children: " " }),
|
|
17840
18474
|
/* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(3), color: sectionColor(3), children: [
|
|
17841
18475
|
sectionMarker(3),
|
|
17842
|
-
"
|
|
18476
|
+
"System"
|
|
17843
18477
|
] }),
|
|
17844
18478
|
/* @__PURE__ */ jsx14(Text, { children: " " }),
|
|
17845
|
-
|
|
17846
|
-
" ",
|
|
17847
|
-
"Loading..."
|
|
17848
|
-
] }) : employeeModels.length === 0 ? /* @__PURE__ */ jsxs12(Text, { color: "#6B4C9A", children: [
|
|
18479
|
+
/* @__PURE__ */ jsxs12(Text, { children: [
|
|
17849
18480
|
" ",
|
|
17850
|
-
"
|
|
17851
|
-
|
|
18481
|
+
/* @__PURE__ */ jsx14(Text, { color: system.daemon === "running" ? GREEN : DIM, children: statusDot(system.daemon === "running") }),
|
|
18482
|
+
" Daemon: ",
|
|
18483
|
+
/* @__PURE__ */ jsx14(Text, { color: system.daemon === "running" ? GREEN : DIM, children: system.daemon })
|
|
18484
|
+
] }),
|
|
18485
|
+
/* @__PURE__ */ jsxs12(Text, { children: [
|
|
17852
18486
|
" ",
|
|
17853
|
-
|
|
17854
|
-
|
|
17855
|
-
/* @__PURE__ */ jsx14(Text, { color: "#22C55E", children: e.model }),
|
|
17856
|
-
" ",
|
|
17857
|
-
/* @__PURE__ */ jsxs12(Text, { color: "#6B4C9A", children: [
|
|
17858
|
-
"via ",
|
|
17859
|
-
e.provider
|
|
17860
|
-
] })
|
|
17861
|
-
] }, e.name)),
|
|
17862
|
-
/* @__PURE__ */ jsx14(Text, { children: " " }),
|
|
17863
|
-
/* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(4), color: sectionColor(4), children: [
|
|
17864
|
-
sectionMarker(4),
|
|
17865
|
-
"Integrations"
|
|
18487
|
+
"Version: ",
|
|
18488
|
+
/* @__PURE__ */ jsx14(Text, { color: GREEN, children: system.version })
|
|
17866
18489
|
] }),
|
|
17867
|
-
/* @__PURE__ */ jsx14(Text, { children: " " }),
|
|
17868
18490
|
/* @__PURE__ */ jsxs12(Text, { children: [
|
|
17869
18491
|
" ",
|
|
17870
|
-
"
|
|
17871
|
-
/* @__PURE__ */ jsx14(Text, { color:
|
|
17872
|
-
claudeCode.installed && /* @__PURE__ */ jsxs12(Text, { color: claudeCode.hooksWired ? "#22C55E" : "#6B4C9A", children: [
|
|
17873
|
-
" \u2014 hooks ",
|
|
17874
|
-
claudeCode.hooksWired ? "wired" : "not wired"
|
|
17875
|
-
] })
|
|
18492
|
+
"Encryption: ",
|
|
18493
|
+
/* @__PURE__ */ jsx14(Text, { color: system.encryption.includes("AES") ? GREEN : DIM, children: system.encryption })
|
|
17876
18494
|
] }),
|
|
17877
18495
|
/* @__PURE__ */ jsxs12(Text, { children: [
|
|
17878
18496
|
" ",
|
|
17879
|
-
"
|
|
17880
|
-
/* @__PURE__ */ jsx14(Text, { color:
|
|
18497
|
+
"Embedding: ",
|
|
18498
|
+
/* @__PURE__ */ jsx14(Text, { color: GREEN, children: system.embeddingModel })
|
|
17881
18499
|
] }),
|
|
17882
18500
|
/* @__PURE__ */ jsxs12(Text, { children: [
|
|
17883
18501
|
" ",
|
|
17884
|
-
"
|
|
17885
|
-
/* @__PURE__ */ jsx14(Text, { color:
|
|
18502
|
+
"DB path: ",
|
|
18503
|
+
/* @__PURE__ */ jsx14(Text, { color: DIM, children: system.dbPath })
|
|
18504
|
+
] }),
|
|
18505
|
+
/* @__PURE__ */ jsx14(Text, { children: " " }),
|
|
18506
|
+
/* @__PURE__ */ jsxs12(Text, { bold: true, backgroundColor: sectionBg(4), color: sectionColor(4), children: [
|
|
18507
|
+
sectionMarker(4),
|
|
18508
|
+
"Gateway"
|
|
18509
|
+
] }),
|
|
18510
|
+
/* @__PURE__ */ jsx14(Text, { children: " " }),
|
|
18511
|
+
anyGateway ? gateway.adapters.map((a) => /* @__PURE__ */ jsxs12(Text, { children: [
|
|
18512
|
+
" ",
|
|
18513
|
+
/* @__PURE__ */ jsx14(Text, { color: statusColor(a.configured), children: statusDot(a.configured) }),
|
|
18514
|
+
" ",
|
|
18515
|
+
a.name,
|
|
18516
|
+
": ",
|
|
18517
|
+
/* @__PURE__ */ jsx14(Text, { color: statusColor(a.configured), children: a.configured ? "configured" : "not configured" })
|
|
18518
|
+
] }, a.name)) : /* @__PURE__ */ jsxs12(Text, { children: [
|
|
18519
|
+
" ",
|
|
18520
|
+
/* @__PURE__ */ jsx14(Text, { color: DIM, children: statusDot(false) }),
|
|
18521
|
+
" ",
|
|
18522
|
+
/* @__PURE__ */ jsx14(Text, { color: DIM, children: "No gateway configured" })
|
|
17886
18523
|
] })
|
|
17887
18524
|
] });
|
|
17888
18525
|
}
|
|
17889
18526
|
|
|
17890
|
-
// src/tui/
|
|
18527
|
+
// src/tui/views/DebugPanel.tsx
|
|
18528
|
+
import React25, { useState as useState15, useEffect as useEffect17 } from "react";
|
|
18529
|
+
init_state_bus();
|
|
17891
18530
|
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
18531
|
+
var MAX_EVENTS = 50;
|
|
18532
|
+
var DISPLAY_EVENTS = 20;
|
|
18533
|
+
var EVENT_COLORS = {
|
|
18534
|
+
task_completed: "#22C55E",
|
|
18535
|
+
review_created: "#F59E0B",
|
|
18536
|
+
employee_status: "#3B82F6",
|
|
18537
|
+
memory_stored: "#8B5CF6",
|
|
18538
|
+
session_started: "#06B6D4",
|
|
18539
|
+
session_ended: "#EF4444",
|
|
18540
|
+
gateway_message: "#EC4899"
|
|
18541
|
+
};
|
|
18542
|
+
function formatEvent(e) {
|
|
18543
|
+
switch (e.type) {
|
|
18544
|
+
case "task_completed":
|
|
18545
|
+
return `${e.employee}: "${e.result.slice(0, 60)}"`;
|
|
18546
|
+
case "review_created":
|
|
18547
|
+
return `${e.employee} \u2192 ${e.reviewer}`;
|
|
18548
|
+
case "employee_status":
|
|
18549
|
+
return `${e.employee}: ${e.status}`;
|
|
18550
|
+
case "memory_stored":
|
|
18551
|
+
return `${e.agentId} [${e.project}]`;
|
|
18552
|
+
case "session_started":
|
|
18553
|
+
return `${e.employee} (${e.sessionId.slice(0, 8)})`;
|
|
18554
|
+
case "session_ended":
|
|
18555
|
+
return `${e.employee} (${e.sessionId.slice(0, 8)})`;
|
|
18556
|
+
case "gateway_message":
|
|
18557
|
+
return `${e.platform}/${e.senderId} \u2192 ${e.botId}`;
|
|
18558
|
+
}
|
|
18559
|
+
}
|
|
18560
|
+
function DebugPanel() {
|
|
18561
|
+
const [events, setEvents] = useState15([]);
|
|
18562
|
+
useEffect17(() => {
|
|
18563
|
+
let counter = 0;
|
|
18564
|
+
const handler = (event) => {
|
|
18565
|
+
setEvents((prev) => {
|
|
18566
|
+
const next = [...prev, { event, id: counter++ }];
|
|
18567
|
+
return next.slice(-MAX_EVENTS);
|
|
18568
|
+
});
|
|
18569
|
+
};
|
|
18570
|
+
orgBus.onAny(handler);
|
|
18571
|
+
return () => {
|
|
18572
|
+
orgBus.offAny(handler);
|
|
18573
|
+
};
|
|
18574
|
+
}, []);
|
|
18575
|
+
return /* @__PURE__ */ jsxs13(Box_default, { flexDirection: "column", borderStyle: "single", borderColor: "#6B4C9A", flexGrow: 1, paddingX: 1, children: [
|
|
18576
|
+
/* @__PURE__ */ jsx15(Text, { bold: true, color: "#F5D76E", children: "Debug \u2014 Live Events" }),
|
|
18577
|
+
/* @__PURE__ */ jsx15(Text, { color: "#3D3660", children: "\u2500".repeat(40) }),
|
|
18578
|
+
events.length === 0 ? /* @__PURE__ */ jsx15(Text, { color: "#3D3660", children: "Waiting for events..." }) : events.slice(-DISPLAY_EVENTS).map(({ event, id }) => {
|
|
18579
|
+
const time = event.timestamp?.slice(11, 19) ?? "??:??:??";
|
|
18580
|
+
const color = EVENT_COLORS[event.type] ?? "#6B4C9A";
|
|
18581
|
+
const summary = formatEvent(event);
|
|
18582
|
+
return /* @__PURE__ */ jsxs13(Text, { color, children: [
|
|
18583
|
+
"[",
|
|
18584
|
+
time,
|
|
18585
|
+
"] ",
|
|
18586
|
+
event.type,
|
|
18587
|
+
" \u2014 ",
|
|
18588
|
+
summary
|
|
18589
|
+
] }, id);
|
|
18590
|
+
})
|
|
18591
|
+
] });
|
|
18592
|
+
}
|
|
18593
|
+
|
|
18594
|
+
// src/tui/components/HelpOverlay.tsx
|
|
18595
|
+
import React26 from "react";
|
|
18596
|
+
import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
18597
|
+
function HelpOverlay({ tabName, shortcuts }) {
|
|
18598
|
+
return /* @__PURE__ */ jsxs14(
|
|
18599
|
+
Box_default,
|
|
18600
|
+
{
|
|
18601
|
+
flexDirection: "column",
|
|
18602
|
+
borderStyle: "double",
|
|
18603
|
+
borderColor: "#6B4C9A",
|
|
18604
|
+
paddingX: 2,
|
|
18605
|
+
paddingY: 1,
|
|
18606
|
+
width: 50,
|
|
18607
|
+
alignSelf: "center",
|
|
18608
|
+
children: [
|
|
18609
|
+
/* @__PURE__ */ jsxs14(Text, { bold: true, color: "#F5D76E", children: [
|
|
18610
|
+
"Keyboard Shortcuts \u2014 ",
|
|
18611
|
+
tabName
|
|
18612
|
+
] }),
|
|
18613
|
+
/* @__PURE__ */ jsx16(Text, { children: " " }),
|
|
18614
|
+
shortcuts.map((s, i) => /* @__PURE__ */ jsxs14(Box_default, { gap: 1, children: [
|
|
18615
|
+
/* @__PURE__ */ jsx16(Text, { color: "#F5D76E", bold: true, children: s.key.padEnd(10) }),
|
|
18616
|
+
/* @__PURE__ */ jsx16(Text, { color: "#F0EDE8", children: s.description })
|
|
18617
|
+
] }, i)),
|
|
18618
|
+
/* @__PURE__ */ jsx16(Text, { children: " " }),
|
|
18619
|
+
/* @__PURE__ */ jsx16(Text, { bold: true, color: "#F5D76E", children: "Global" }),
|
|
18620
|
+
/* @__PURE__ */ jsx16(Text, { children: " " }),
|
|
18621
|
+
/* @__PURE__ */ jsxs14(Box_default, { gap: 1, children: [
|
|
18622
|
+
/* @__PURE__ */ jsx16(Text, { color: "#F5D76E", bold: true, children: "1-7".padEnd(10) }),
|
|
18623
|
+
/* @__PURE__ */ jsx16(Text, { color: "#F0EDE8", children: "Switch tabs" })
|
|
18624
|
+
] }),
|
|
18625
|
+
/* @__PURE__ */ jsxs14(Box_default, { gap: 1, children: [
|
|
18626
|
+
/* @__PURE__ */ jsx16(Text, { color: "#F5D76E", bold: true, children: "q".padEnd(10) }),
|
|
18627
|
+
/* @__PURE__ */ jsx16(Text, { color: "#F0EDE8", children: "Quit" })
|
|
18628
|
+
] }),
|
|
18629
|
+
/* @__PURE__ */ jsxs14(Box_default, { gap: 1, children: [
|
|
18630
|
+
/* @__PURE__ */ jsx16(Text, { color: "#F5D76E", bold: true, children: "?".padEnd(10) }),
|
|
18631
|
+
/* @__PURE__ */ jsx16(Text, { color: "#F0EDE8", children: "Toggle this help" })
|
|
18632
|
+
] }),
|
|
18633
|
+
/* @__PURE__ */ jsxs14(Box_default, { gap: 1, children: [
|
|
18634
|
+
/* @__PURE__ */ jsx16(Text, { color: "#F5D76E", bold: true, children: "Esc".padEnd(10) }),
|
|
18635
|
+
/* @__PURE__ */ jsx16(Text, { color: "#F0EDE8", children: "Back to sidebar" })
|
|
18636
|
+
] }),
|
|
18637
|
+
/* @__PURE__ */ jsx16(Text, { children: " " }),
|
|
18638
|
+
/* @__PURE__ */ jsx16(Text, { color: "#6B4C9A", children: "Press ? to close" })
|
|
18639
|
+
]
|
|
18640
|
+
}
|
|
18641
|
+
);
|
|
18642
|
+
}
|
|
18643
|
+
|
|
18644
|
+
// src/tui/App.tsx
|
|
18645
|
+
import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
18646
|
+
var TAB_SHORTCUTS = {
|
|
18647
|
+
"command-center": {
|
|
18648
|
+
name: "Command Center",
|
|
18649
|
+
shortcuts: [
|
|
18650
|
+
{ key: "\u2191\u2193", description: "Navigate projects" },
|
|
18651
|
+
{ key: "Enter", description: "Select project" },
|
|
18652
|
+
{ key: "/", description: "Enter chat mode" },
|
|
18653
|
+
{ key: "n", description: "New task" }
|
|
18654
|
+
]
|
|
18655
|
+
},
|
|
18656
|
+
sessions: {
|
|
18657
|
+
name: "Sessions",
|
|
18658
|
+
shortcuts: [
|
|
18659
|
+
{ key: "\u2191\u2193", description: "Navigate sessions" },
|
|
18660
|
+
{ key: "Enter", description: "View session output" },
|
|
18661
|
+
{ key: "k", description: "Kill session" },
|
|
18662
|
+
{ key: "r", description: "Restart agent" }
|
|
18663
|
+
]
|
|
18664
|
+
},
|
|
18665
|
+
tasks: {
|
|
18666
|
+
name: "Tasks",
|
|
18667
|
+
shortcuts: [
|
|
18668
|
+
{ key: "\u2191\u2193", description: "Navigate tasks" },
|
|
18669
|
+
{ key: "Enter", description: "View task detail" },
|
|
18670
|
+
{ key: "n", description: "Create new task" },
|
|
18671
|
+
{ key: "f", description: "Cycle status filter" },
|
|
18672
|
+
{ key: "p", description: "Filter by priority" }
|
|
18673
|
+
]
|
|
18674
|
+
},
|
|
18675
|
+
team: {
|
|
18676
|
+
name: "Team",
|
|
18677
|
+
shortcuts: [
|
|
18678
|
+
{ key: "\u2191\u2193", description: "Navigate employees" },
|
|
18679
|
+
{ key: "Enter", description: "View detail" },
|
|
18680
|
+
{ key: "a", description: "Add employee" },
|
|
18681
|
+
{ key: "s", description: "View sessions" }
|
|
18682
|
+
]
|
|
18683
|
+
},
|
|
18684
|
+
gateway: {
|
|
18685
|
+
name: "Gateway",
|
|
18686
|
+
shortcuts: [
|
|
18687
|
+
{ key: "\u2191\u2193", description: "Navigate sections" },
|
|
18688
|
+
{ key: "f", description: "Cycle platform filter" },
|
|
18689
|
+
{ key: "\u2192", description: "Next conversation" },
|
|
18690
|
+
{ key: "r", description: "Reconnect" }
|
|
18691
|
+
]
|
|
18692
|
+
},
|
|
18693
|
+
wiki: {
|
|
18694
|
+
name: "Wiki",
|
|
18695
|
+
shortcuts: [
|
|
18696
|
+
{ key: "\u2190\u2192", description: "Switch panels" },
|
|
18697
|
+
{ key: "\u2191\u2193", description: "Navigate items" },
|
|
18698
|
+
{ key: "/", description: "Search memories" },
|
|
18699
|
+
{ key: "Enter", description: "Select" }
|
|
18700
|
+
]
|
|
18701
|
+
},
|
|
18702
|
+
settings: {
|
|
18703
|
+
name: "Settings",
|
|
18704
|
+
shortcuts: [
|
|
18705
|
+
{ key: "\u2191\u2193", description: "Navigate sections" },
|
|
18706
|
+
{ key: "Enter", description: "Edit setting" },
|
|
18707
|
+
{ key: "r", description: "Refresh status" }
|
|
18708
|
+
]
|
|
18709
|
+
}
|
|
18710
|
+
};
|
|
17892
18711
|
var isDemo = process.argv.includes("--demo");
|
|
17893
18712
|
process.stderr.write(`[exe-tui] Terminal: ${TERMINAL_TYPE}
|
|
17894
18713
|
`);
|
|
@@ -17915,17 +18734,17 @@ if (!isDemo) {
|
|
|
17915
18734
|
})();
|
|
17916
18735
|
}
|
|
17917
18736
|
function App2() {
|
|
17918
|
-
const [section, setSection] =
|
|
18737
|
+
const [section, setSection] = useState16("command-center");
|
|
17919
18738
|
const { exit } = use_app_default();
|
|
17920
|
-
const [, forceUpdate] =
|
|
17921
|
-
|
|
18739
|
+
const [, forceUpdate] = useState16(0);
|
|
18740
|
+
useEffect18(() => {
|
|
17922
18741
|
const handleResize = () => forceUpdate((n) => n + 1);
|
|
17923
18742
|
process.stdout.on("resize", handleResize);
|
|
17924
18743
|
return () => {
|
|
17925
18744
|
process.stdout.off("resize", handleResize);
|
|
17926
18745
|
};
|
|
17927
18746
|
}, []);
|
|
17928
|
-
useMouseEvent(
|
|
18747
|
+
useMouseEvent(useCallback8((event) => {
|
|
17929
18748
|
if (event.button !== 0) return;
|
|
17930
18749
|
if (event.col <= 26) {
|
|
17931
18750
|
const tabIdx = event.row - 4;
|
|
@@ -17937,22 +18756,33 @@ function App2() {
|
|
|
17937
18756
|
setFocus("content");
|
|
17938
18757
|
}
|
|
17939
18758
|
}, []));
|
|
17940
|
-
const [focus, setFocus] =
|
|
17941
|
-
const [
|
|
17942
|
-
const
|
|
18759
|
+
const [focus, setFocus] = useState16("sidebar");
|
|
18760
|
+
const [showDebug, setShowDebug] = useState16(false);
|
|
18761
|
+
const [showHelp, setShowHelp] = useState16(false);
|
|
18762
|
+
const [focusedProject, setFocusedProject] = useState16(null);
|
|
18763
|
+
const handleSelectProject = useCallback8((projectName) => {
|
|
17943
18764
|
setFocusedProject(projectName);
|
|
17944
18765
|
setSection("sessions");
|
|
17945
18766
|
setFocus("content");
|
|
17946
18767
|
}, []);
|
|
17947
|
-
const handleBackToCommandCenter =
|
|
18768
|
+
const handleBackToCommandCenter = useCallback8(() => {
|
|
17948
18769
|
setSection("command-center");
|
|
17949
18770
|
setFocus("sidebar");
|
|
17950
18771
|
}, []);
|
|
17951
18772
|
use_input_default((input, key) => {
|
|
18773
|
+
if (input === "?" || key.shift && input === "/") {
|
|
18774
|
+
setShowHelp((prev) => !prev);
|
|
18775
|
+
return;
|
|
18776
|
+
}
|
|
17952
18777
|
const idx = parseInt(input, 10);
|
|
17953
18778
|
if (idx >= 1 && idx <= SECTIONS.length) {
|
|
17954
18779
|
setSection(SECTIONS[idx - 1].key);
|
|
17955
18780
|
setFocus("sidebar");
|
|
18781
|
+
setShowHelp(false);
|
|
18782
|
+
return;
|
|
18783
|
+
}
|
|
18784
|
+
if (input === "d" && !key.ctrl) {
|
|
18785
|
+
setShowDebug((prev) => !prev);
|
|
17956
18786
|
return;
|
|
17957
18787
|
}
|
|
17958
18788
|
if (input === "q" && !key.ctrl) {
|
|
@@ -17980,24 +18810,34 @@ function App2() {
|
|
|
17980
18810
|
}
|
|
17981
18811
|
}
|
|
17982
18812
|
});
|
|
17983
|
-
const consumeFocusedProject =
|
|
18813
|
+
const consumeFocusedProject = useCallback8(() => {
|
|
17984
18814
|
setFocusedProject(null);
|
|
17985
18815
|
}, []);
|
|
17986
18816
|
const views = {
|
|
17987
|
-
"command-center": /* @__PURE__ */
|
|
17988
|
-
sessions: /* @__PURE__ */
|
|
17989
|
-
tasks: /* @__PURE__ */
|
|
17990
|
-
team: /* @__PURE__ */
|
|
17991
|
-
|
|
17992
|
-
|
|
17993
|
-
|
|
18817
|
+
"command-center": /* @__PURE__ */ jsx17(CommandCenterView, { onSelectProject: handleSelectProject, isFocused: focus === "content" && section === "command-center", onBack: () => setFocus("sidebar") }),
|
|
18818
|
+
sessions: /* @__PURE__ */ jsx17(SessionsView, { initialProject: focusedProject, onConsumeInitialProject: consumeFocusedProject, onBackToCommandCenter: handleBackToCommandCenter, onBack: () => setFocus("sidebar") }),
|
|
18819
|
+
tasks: /* @__PURE__ */ jsx17(TasksView, { onBack: () => setFocus("sidebar") }),
|
|
18820
|
+
team: /* @__PURE__ */ jsx17(TeamView, { onBack: () => setFocus("sidebar"), onViewSessions: (name) => {
|
|
18821
|
+
setFocusedProject(name);
|
|
18822
|
+
setSection("sessions");
|
|
18823
|
+
setFocus("content");
|
|
18824
|
+
} }),
|
|
18825
|
+
gateway: /* @__PURE__ */ jsx17(GatewayView, { onBack: () => setFocus("sidebar") }),
|
|
18826
|
+
wiki: /* @__PURE__ */ jsx17(WikiView, { onBack: () => setFocus("sidebar") }),
|
|
18827
|
+
settings: /* @__PURE__ */ jsx17(SettingsView, { onBack: () => setFocus("sidebar") })
|
|
17994
18828
|
};
|
|
17995
|
-
return /* @__PURE__ */
|
|
17996
|
-
/* @__PURE__ */
|
|
17997
|
-
/* @__PURE__ */
|
|
17998
|
-
|
|
18829
|
+
return /* @__PURE__ */ jsx17(ErrorBoundary2, { children: /* @__PURE__ */ jsx17(AlternateScreen, { children: /* @__PURE__ */ jsx17(DemoProvider, { demo: isDemo, children: /* @__PURE__ */ jsxs15(Box_default, { flexDirection: "column", flexGrow: 1, children: [
|
|
18830
|
+
/* @__PURE__ */ jsxs15(Box_default, { flexGrow: 1, children: [
|
|
18831
|
+
/* @__PURE__ */ jsx17(Sidebar, { active: section, onSelect: setSection, onQuit: exit, focused: focus === "sidebar" }),
|
|
18832
|
+
showHelp ? /* @__PURE__ */ jsx17(
|
|
18833
|
+
HelpOverlay,
|
|
18834
|
+
{
|
|
18835
|
+
tabName: TAB_SHORTCUTS[section].name,
|
|
18836
|
+
shortcuts: TAB_SHORTCUTS[section].shortcuts
|
|
18837
|
+
}
|
|
18838
|
+
) : showDebug ? /* @__PURE__ */ jsx17(DebugPanel, {}) : views[section]
|
|
17999
18839
|
] }),
|
|
18000
|
-
/* @__PURE__ */
|
|
18840
|
+
/* @__PURE__ */ jsx17(Footer, {})
|
|
18001
18841
|
] }) }) }) });
|
|
18002
18842
|
}
|
|
18003
18843
|
{
|
|
@@ -18015,4 +18855,4 @@ function App2() {
|
|
|
18015
18855
|
stdin.unref = () => stdin;
|
|
18016
18856
|
}
|
|
18017
18857
|
}
|
|
18018
|
-
render_default(/* @__PURE__ */
|
|
18858
|
+
render_default(/* @__PURE__ */ jsx17(App2, {}));
|