@askexenow/exe-os 0.8.83 → 0.8.85
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 +746 -595
- package/dist/bin/backfill-responses.js +745 -594
- package/dist/bin/backfill-vectors.js +312 -226
- package/dist/bin/cleanup-stale-review-tasks.js +97 -2
- package/dist/bin/cli.js +14350 -12518
- package/dist/bin/exe-agent.js +97 -88
- package/dist/bin/exe-assign.js +1003 -854
- package/dist/bin/exe-boot.js +1257 -320
- package/dist/bin/exe-call.js +10 -0
- package/dist/bin/exe-cloud.js +29 -6
- package/dist/bin/exe-dispatch.js +210 -34
- package/dist/bin/exe-doctor.js +403 -6
- package/dist/bin/exe-export-behaviors.js +175 -72
- package/dist/bin/exe-forget.js +97 -2
- package/dist/bin/exe-gateway.js +550 -171
- package/dist/bin/exe-healthcheck.js +1 -0
- package/dist/bin/exe-heartbeat.js +100 -5
- package/dist/bin/exe-kill.js +175 -72
- package/dist/bin/exe-launch-agent.js +189 -76
- package/dist/bin/exe-link.js +902 -80
- package/dist/bin/exe-new-employee.js +38 -8
- package/dist/bin/exe-pending-messages.js +96 -2
- package/dist/bin/exe-pending-notifications.js +97 -2
- package/dist/bin/exe-pending-reviews.js +98 -3
- package/dist/bin/exe-rename.js +564 -23
- package/dist/bin/exe-review.js +231 -73
- package/dist/bin/exe-search.js +989 -226
- package/dist/bin/exe-session-cleanup.js +4806 -1665
- package/dist/bin/exe-settings.js +20 -5
- package/dist/bin/exe-status.js +97 -2
- package/dist/bin/exe-team.js +97 -2
- package/dist/bin/git-sweep.js +899 -207
- package/dist/bin/graph-backfill.js +175 -72
- package/dist/bin/graph-export.js +175 -72
- package/dist/bin/install.js +38 -7
- package/dist/bin/list-providers.js +1 -0
- package/dist/bin/scan-tasks.js +904 -211
- package/dist/bin/setup.js +867 -268
- package/dist/bin/shard-migrate.js +175 -72
- package/dist/bin/update.js +1 -0
- package/dist/bin/wiki-sync.js +175 -72
- package/dist/gateway/index.js +548 -166
- package/dist/hooks/bug-report-worker.js +208 -23
- package/dist/hooks/commit-complete.js +897 -205
- package/dist/hooks/error-recall.js +988 -226
- package/dist/hooks/ingest-worker.js +1638 -1194
- package/dist/hooks/ingest.js +3 -0
- package/dist/hooks/instructions-loaded.js +707 -97
- package/dist/hooks/notification.js +699 -89
- package/dist/hooks/post-compact.js +714 -104
- package/dist/hooks/pre-compact.js +897 -205
- package/dist/hooks/pre-tool-use.js +742 -123
- package/dist/hooks/prompt-ingest-worker.js +242 -101
- package/dist/hooks/prompt-submit.js +995 -233
- package/dist/hooks/response-ingest-worker.js +242 -101
- package/dist/hooks/session-end.js +3941 -400
- package/dist/hooks/session-start.js +1001 -226
- package/dist/hooks/stop.js +725 -115
- package/dist/hooks/subagent-stop.js +714 -104
- package/dist/hooks/summary-worker.js +1964 -1330
- package/dist/index.js +1651 -1053
- package/dist/lib/cloud-sync.js +907 -86
- package/dist/lib/consolidation.js +2 -1
- package/dist/lib/database.js +642 -87
- package/dist/lib/db-daemon-client.js +503 -0
- package/dist/lib/device-registry.js +547 -7
- package/dist/lib/embedder.js +14 -28
- package/dist/lib/employee-templates.js +84 -74
- package/dist/lib/employees.js +9 -0
- package/dist/lib/exe-daemon-client.js +16 -29
- package/dist/lib/exe-daemon.js +1955 -922
- package/dist/lib/hybrid-search.js +988 -226
- package/dist/lib/identity.js +87 -67
- package/dist/lib/keychain.js +9 -1
- package/dist/lib/messaging.js +8 -1
- package/dist/lib/reminders.js +91 -74
- package/dist/lib/schedules.js +96 -2
- package/dist/lib/skill-learning.js +103 -85
- package/dist/lib/store.js +234 -73
- package/dist/lib/tasks.js +111 -22
- package/dist/lib/tmux-routing.js +120 -31
- package/dist/lib/token-spend.js +273 -0
- package/dist/lib/ws-client.js +11 -0
- package/dist/mcp/server.js +5222 -475
- package/dist/mcp/tools/complete-reminder.js +94 -77
- package/dist/mcp/tools/create-reminder.js +94 -77
- package/dist/mcp/tools/create-task.js +120 -22
- package/dist/mcp/tools/deactivate-behavior.js +95 -77
- package/dist/mcp/tools/list-reminders.js +94 -77
- package/dist/mcp/tools/list-tasks.js +31 -1
- package/dist/mcp/tools/send-message.js +8 -1
- package/dist/mcp/tools/update-task.js +39 -10
- package/dist/runtime/index.js +911 -219
- package/dist/tui/App.js +997 -295
- package/package.json +6 -1
|
@@ -1,18 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import crypto from "crypto";
|
|
6
|
-
|
|
7
|
-
// src/lib/database.ts
|
|
8
|
-
import { createClient } from "@libsql/client";
|
|
9
|
-
|
|
10
|
-
// src/lib/employees.ts
|
|
11
|
-
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
12
|
-
import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
13
|
-
import { execSync } from "child_process";
|
|
14
|
-
import path2 from "path";
|
|
15
|
-
import os2 from "os";
|
|
1
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
2
|
+
var __esm = (fn, res) => function __init() {
|
|
3
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
4
|
+
};
|
|
16
5
|
|
|
17
6
|
// src/lib/config.ts
|
|
18
7
|
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
@@ -35,79 +24,107 @@ function resolveDataDir() {
|
|
|
35
24
|
}
|
|
36
25
|
return newDir;
|
|
37
26
|
}
|
|
38
|
-
var EXE_AI_DIR
|
|
39
|
-
var
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
27
|
+
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG;
|
|
28
|
+
var init_config = __esm({
|
|
29
|
+
"src/lib/config.ts"() {
|
|
30
|
+
"use strict";
|
|
31
|
+
EXE_AI_DIR = resolveDataDir();
|
|
32
|
+
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
33
|
+
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
34
|
+
CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
|
|
35
|
+
LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
|
|
36
|
+
CURRENT_CONFIG_VERSION = 1;
|
|
37
|
+
DEFAULT_CONFIG = {
|
|
38
|
+
config_version: CURRENT_CONFIG_VERSION,
|
|
39
|
+
dbPath: DB_PATH,
|
|
40
|
+
modelFile: "jina-embeddings-v5-small-q4_k_m.gguf",
|
|
41
|
+
embeddingDim: 1024,
|
|
42
|
+
batchSize: 20,
|
|
43
|
+
flushIntervalMs: 1e4,
|
|
44
|
+
autoIngestion: true,
|
|
45
|
+
autoRetrieval: true,
|
|
46
|
+
searchMode: "hybrid",
|
|
47
|
+
hookSearchMode: "hybrid",
|
|
48
|
+
fileGrepEnabled: true,
|
|
49
|
+
splashEffect: true,
|
|
50
|
+
consolidationEnabled: true,
|
|
51
|
+
consolidationIntervalMs: 6 * 60 * 60 * 1e3,
|
|
52
|
+
consolidationModel: "claude-haiku-4-5-20251001",
|
|
53
|
+
consolidationMaxCallsPerRun: 20,
|
|
54
|
+
selfQueryRouter: true,
|
|
55
|
+
selfQueryModel: "claude-haiku-4-5-20251001",
|
|
56
|
+
rerankerEnabled: true,
|
|
57
|
+
scalingRoadmap: {
|
|
58
|
+
rerankerAutoTrigger: {
|
|
59
|
+
enabled: true,
|
|
60
|
+
broadQueryMinCardinality: 5e4,
|
|
61
|
+
fetchTopK: 150,
|
|
62
|
+
returnTopK: 5
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
graphRagEnabled: true,
|
|
66
|
+
wikiEnabled: false,
|
|
67
|
+
wikiUrl: "",
|
|
68
|
+
wikiApiKey: "",
|
|
69
|
+
wikiSyncIntervalMs: 30 * 60 * 1e3,
|
|
70
|
+
wikiWorkspaceMapping: {},
|
|
71
|
+
wikiAutoUpdate: true,
|
|
72
|
+
wikiAutoUpdateThreshold: 0.5,
|
|
73
|
+
wikiAutoUpdateCreateNew: true,
|
|
74
|
+
skillLearning: true,
|
|
75
|
+
skillThreshold: 3,
|
|
76
|
+
skillModel: "claude-haiku-4-5-20251001",
|
|
77
|
+
exeHeartbeat: {
|
|
78
|
+
enabled: true,
|
|
79
|
+
intervalSeconds: 60,
|
|
80
|
+
staleInProgressThresholdHours: 2
|
|
81
|
+
},
|
|
82
|
+
sessionLifecycle: {
|
|
83
|
+
idleKillEnabled: true,
|
|
84
|
+
idleKillTicksRequired: 3,
|
|
85
|
+
idleKillIntercomAckWindowMs: 1e4,
|
|
86
|
+
maxAutoInstances: 10
|
|
87
|
+
},
|
|
88
|
+
autoUpdate: {
|
|
89
|
+
checkOnBoot: true,
|
|
90
|
+
autoInstall: false,
|
|
91
|
+
checkIntervalMs: 24 * 60 * 60 * 1e3
|
|
92
|
+
}
|
|
93
|
+
};
|
|
99
94
|
}
|
|
100
|
-
};
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// src/mcp/tools/list-reminders.ts
|
|
98
|
+
import { z } from "zod";
|
|
99
|
+
|
|
100
|
+
// src/lib/reminders.ts
|
|
101
|
+
import crypto from "crypto";
|
|
102
|
+
|
|
103
|
+
// src/lib/database.ts
|
|
104
|
+
import { createClient } from "@libsql/client";
|
|
101
105
|
|
|
102
106
|
// src/lib/employees.ts
|
|
107
|
+
init_config();
|
|
108
|
+
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
109
|
+
import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
|
|
110
|
+
import { execSync } from "child_process";
|
|
111
|
+
import path2 from "path";
|
|
112
|
+
import os2 from "os";
|
|
103
113
|
var EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
|
|
104
114
|
|
|
105
115
|
// src/lib/database.ts
|
|
106
116
|
var _resilientClient = null;
|
|
117
|
+
var _daemonClient = null;
|
|
107
118
|
function getClient() {
|
|
108
119
|
if (!_resilientClient) {
|
|
109
120
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
110
121
|
}
|
|
122
|
+
if (process.env.EXE_IS_DAEMON === "1") {
|
|
123
|
+
return _resilientClient;
|
|
124
|
+
}
|
|
125
|
+
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
126
|
+
return _daemonClient;
|
|
127
|
+
}
|
|
111
128
|
return _resilientClient;
|
|
112
129
|
}
|
|
113
130
|
|
|
@@ -138,15 +138,22 @@ function getClient() {
|
|
|
138
138
|
if (!_resilientClient) {
|
|
139
139
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
140
140
|
}
|
|
141
|
+
if (process.env.EXE_IS_DAEMON === "1") {
|
|
142
|
+
return _resilientClient;
|
|
143
|
+
}
|
|
144
|
+
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
145
|
+
return _daemonClient;
|
|
146
|
+
}
|
|
141
147
|
return _resilientClient;
|
|
142
148
|
}
|
|
143
|
-
var _resilientClient;
|
|
149
|
+
var _resilientClient, _daemonClient;
|
|
144
150
|
var init_database = __esm({
|
|
145
151
|
"src/lib/database.ts"() {
|
|
146
152
|
"use strict";
|
|
147
153
|
init_db_retry();
|
|
148
154
|
init_employees();
|
|
149
155
|
_resilientClient = null;
|
|
156
|
+
_daemonClient = null;
|
|
150
157
|
}
|
|
151
158
|
});
|
|
152
159
|
|
|
@@ -533,9 +540,21 @@ var init_task_scope = __esm({
|
|
|
533
540
|
// src/lib/tasks-crud.ts
|
|
534
541
|
import crypto2 from "crypto";
|
|
535
542
|
import path9 from "path";
|
|
543
|
+
import os7 from "os";
|
|
536
544
|
import { execSync as execSync4 } from "child_process";
|
|
537
545
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
538
546
|
import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
|
|
547
|
+
function buildKeywordIndex() {
|
|
548
|
+
const idx = /* @__PURE__ */ new Map();
|
|
549
|
+
for (const [role, keywords] of Object.entries(LANE_KEYWORDS)) {
|
|
550
|
+
for (const kw of keywords) {
|
|
551
|
+
const existing = idx.get(kw) ?? [];
|
|
552
|
+
existing.push(role);
|
|
553
|
+
idx.set(kw, existing);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return idx;
|
|
557
|
+
}
|
|
539
558
|
async function listTasks(input) {
|
|
540
559
|
const client = getClient();
|
|
541
560
|
const conditions = [];
|
|
@@ -586,11 +605,22 @@ async function listTasks(input) {
|
|
|
586
605
|
tokensWarnedAt: r.tokens_warned_at !== null ? Number(r.tokens_warned_at) : null
|
|
587
606
|
}));
|
|
588
607
|
}
|
|
608
|
+
var LANE_KEYWORDS, KEYWORD_INDEX;
|
|
589
609
|
var init_tasks_crud = __esm({
|
|
590
610
|
"src/lib/tasks-crud.ts"() {
|
|
591
611
|
"use strict";
|
|
592
612
|
init_database();
|
|
593
613
|
init_task_scope();
|
|
614
|
+
init_employees();
|
|
615
|
+
LANE_KEYWORDS = {
|
|
616
|
+
CMO: ["sales", "script", "pitch", "offer", "copy", "objection", "brand", "content", "seo", "marketing", "newsletter", "carousel", "social", "campaign"],
|
|
617
|
+
CTO: ["spec", "architecture", "migration", "schema", "database", "design doc", "adr", "security audit", "tech stack"],
|
|
618
|
+
"Principal Engineer": ["implement", "build", "fix", "commit", "refactor", "bug", "feature", "wire", "integration"],
|
|
619
|
+
"Staff Code Reviewer": ["critique", "verdict", "review", "audit", "code quality"],
|
|
620
|
+
"Content Production Specialist": ["render", "video", "image", "b-roll", "remotion", "animation", "thumbnail"],
|
|
621
|
+
"AI Product Lead": ["competitive", "analysis", "benchmark", "compare", "scout", "evaluate", "poc"]
|
|
622
|
+
};
|
|
623
|
+
KEYWORD_INDEX = buildKeywordIndex();
|
|
594
624
|
}
|
|
595
625
|
});
|
|
596
626
|
|
|
@@ -163,15 +163,22 @@ function getClient() {
|
|
|
163
163
|
if (!_resilientClient) {
|
|
164
164
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
165
165
|
}
|
|
166
|
+
if (process.env.EXE_IS_DAEMON === "1") {
|
|
167
|
+
return _resilientClient;
|
|
168
|
+
}
|
|
169
|
+
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
170
|
+
return _daemonClient;
|
|
171
|
+
}
|
|
166
172
|
return _resilientClient;
|
|
167
173
|
}
|
|
168
|
-
var _resilientClient;
|
|
174
|
+
var _resilientClient, _daemonClient;
|
|
169
175
|
var init_database = __esm({
|
|
170
176
|
"src/lib/database.ts"() {
|
|
171
177
|
"use strict";
|
|
172
178
|
init_db_retry();
|
|
173
179
|
init_employees();
|
|
174
180
|
_resilientClient = null;
|
|
181
|
+
_daemonClient = null;
|
|
175
182
|
}
|
|
176
183
|
});
|
|
177
184
|
|
|
@@ -262,15 +262,22 @@ function getClient() {
|
|
|
262
262
|
if (!_resilientClient) {
|
|
263
263
|
throw new Error("Database client not initialized. Call initDatabase() first.");
|
|
264
264
|
}
|
|
265
|
+
if (process.env.EXE_IS_DAEMON === "1") {
|
|
266
|
+
return _resilientClient;
|
|
267
|
+
}
|
|
268
|
+
if (_daemonClient && _daemonClient._isDaemonActive()) {
|
|
269
|
+
return _daemonClient;
|
|
270
|
+
}
|
|
265
271
|
return _resilientClient;
|
|
266
272
|
}
|
|
267
|
-
var _resilientClient;
|
|
273
|
+
var _resilientClient, _daemonClient;
|
|
268
274
|
var init_database = __esm({
|
|
269
275
|
"src/lib/database.ts"() {
|
|
270
276
|
"use strict";
|
|
271
277
|
init_db_retry();
|
|
272
278
|
init_employees();
|
|
273
279
|
_resilientClient = null;
|
|
280
|
+
_daemonClient = null;
|
|
274
281
|
}
|
|
275
282
|
});
|
|
276
283
|
|
|
@@ -864,6 +871,7 @@ var init_task_scope = __esm({
|
|
|
864
871
|
// src/lib/tasks-crud.ts
|
|
865
872
|
import crypto2 from "crypto";
|
|
866
873
|
import path9 from "path";
|
|
874
|
+
import os7 from "os";
|
|
867
875
|
import { execSync as execSync4 } from "child_process";
|
|
868
876
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
869
877
|
import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
|
|
@@ -897,6 +905,17 @@ async function writeCheckpoint(input) {
|
|
|
897
905
|
const checkpointCount = Number(countResult.rows[0]?.checkpoint_count ?? 1);
|
|
898
906
|
return { checkpointCount };
|
|
899
907
|
}
|
|
908
|
+
function buildKeywordIndex() {
|
|
909
|
+
const idx = /* @__PURE__ */ new Map();
|
|
910
|
+
for (const [role, keywords] of Object.entries(LANE_KEYWORDS)) {
|
|
911
|
+
for (const kw of keywords) {
|
|
912
|
+
const existing = idx.get(kw) ?? [];
|
|
913
|
+
existing.push(role);
|
|
914
|
+
idx.set(kw, existing);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
return idx;
|
|
918
|
+
}
|
|
900
919
|
async function resolveTask(client, identifier, scopeSession) {
|
|
901
920
|
const scope = sessionScopeFilter(scopeSession);
|
|
902
921
|
let result = await client.execute({
|
|
@@ -1065,7 +1084,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
1065
1084
|
return { row, taskFile, now, taskId };
|
|
1066
1085
|
}
|
|
1067
1086
|
}
|
|
1068
|
-
if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || input.callerAgentId
|
|
1087
|
+
if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || isCoordinatorName(input.callerAgentId))) {
|
|
1069
1088
|
process.stderr.write(
|
|
1070
1089
|
`[tasks] Assigner override: ${input.callerAgentId} reclaiming ${taskId}
|
|
1071
1090
|
`
|
|
@@ -1118,12 +1137,22 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
|
|
|
1118
1137
|
}
|
|
1119
1138
|
return { row, taskFile, now, taskId };
|
|
1120
1139
|
}
|
|
1121
|
-
var DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
|
|
1140
|
+
var LANE_KEYWORDS, KEYWORD_INDEX, DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
|
|
1122
1141
|
var init_tasks_crud = __esm({
|
|
1123
1142
|
"src/lib/tasks-crud.ts"() {
|
|
1124
1143
|
"use strict";
|
|
1125
1144
|
init_database();
|
|
1126
1145
|
init_task_scope();
|
|
1146
|
+
init_employees();
|
|
1147
|
+
LANE_KEYWORDS = {
|
|
1148
|
+
CMO: ["sales", "script", "pitch", "offer", "copy", "objection", "brand", "content", "seo", "marketing", "newsletter", "carousel", "social", "campaign"],
|
|
1149
|
+
CTO: ["spec", "architecture", "migration", "schema", "database", "design doc", "adr", "security audit", "tech stack"],
|
|
1150
|
+
"Principal Engineer": ["implement", "build", "fix", "commit", "refactor", "bug", "feature", "wire", "integration"],
|
|
1151
|
+
"Staff Code Reviewer": ["critique", "verdict", "review", "audit", "code quality"],
|
|
1152
|
+
"Content Production Specialist": ["render", "video", "image", "b-roll", "remotion", "animation", "thumbnail"],
|
|
1153
|
+
"AI Product Lead": ["competitive", "analysis", "benchmark", "compare", "scout", "evaluate", "poc"]
|
|
1154
|
+
};
|
|
1155
|
+
KEYWORD_INDEX = buildKeywordIndex();
|
|
1127
1156
|
DELEGATION_KEYWORDS = /parallel|delegate|wave|worktree|multi-instance/i;
|
|
1128
1157
|
TASK_ALREADY_CLAIMED_PREFIX = "TASK_ALREADY_CLAIMED";
|
|
1129
1158
|
}
|
|
@@ -1156,14 +1185,14 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
1156
1185
|
if (parts.length >= 3 && parts[0] === "review") {
|
|
1157
1186
|
const agent = parts[1];
|
|
1158
1187
|
const slug = parts.slice(2).join("-");
|
|
1159
|
-
const
|
|
1188
|
+
const legacyTaskFile = `exe/${agent}/${slug}.md`;
|
|
1160
1189
|
const result = await client.execute({
|
|
1161
|
-
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE task_file = ? AND status = 'needs_review'",
|
|
1162
|
-
args: [now,
|
|
1190
|
+
sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE (task_file = ? OR task_file LIKE ?) AND status = 'needs_review'",
|
|
1191
|
+
args: [now, legacyTaskFile, `tasks/%/${agent}/${slug}.md`]
|
|
1163
1192
|
});
|
|
1164
1193
|
if (result.rowsAffected > 0) {
|
|
1165
1194
|
process.stderr.write(
|
|
1166
|
-
`[review-cleanup] Cascaded original task to done
|
|
1195
|
+
`[review-cleanup] Cascaded original task to done: ${agent}/${slug}.md
|
|
1167
1196
|
`
|
|
1168
1197
|
);
|
|
1169
1198
|
}
|
|
@@ -1691,7 +1720,7 @@ async function updateTask(input) {
|
|
|
1691
1720
|
}
|
|
1692
1721
|
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
1693
1722
|
if (isTerminal) {
|
|
1694
|
-
const isCoordinator =
|
|
1723
|
+
const isCoordinator = isCoordinatorName(String(row.assigned_to));
|
|
1695
1724
|
if (!isCoordinator) {
|
|
1696
1725
|
notifyTaskDone();
|
|
1697
1726
|
}
|
|
@@ -1716,7 +1745,7 @@ async function updateTask(input) {
|
|
|
1716
1745
|
}
|
|
1717
1746
|
}
|
|
1718
1747
|
}
|
|
1719
|
-
if (input.status === "done" &&
|
|
1748
|
+
if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
|
|
1720
1749
|
Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
|
|
1721
1750
|
({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
|
|
1722
1751
|
taskId,
|
|
@@ -1732,7 +1761,7 @@ async function updateTask(input) {
|
|
|
1732
1761
|
});
|
|
1733
1762
|
}
|
|
1734
1763
|
let nextTask;
|
|
1735
|
-
if (isTerminal &&
|
|
1764
|
+
if (isTerminal && !isCoordinatorName(String(row.assigned_to))) {
|
|
1736
1765
|
try {
|
|
1737
1766
|
nextTask = await findNextTask(String(row.assigned_to));
|
|
1738
1767
|
} catch {
|