@askexenow/exe-os 0.8.40 → 0.8.41
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/cli.js +48 -2
- package/dist/bin/exe-boot.js +75 -16
- package/dist/bin/exe-cloud.js +12 -2
- package/dist/bin/exe-doctor.js +1 -1
- package/dist/bin/exe-gateway.js +48 -2
- package/dist/bin/exe-link.js +35 -11
- package/dist/bin/exe-pending-reviews.js +29 -0
- package/dist/bin/exe-search.js +3 -1
- package/dist/bin/exe-session-cleanup.js +32 -1
- package/dist/bin/exe-settings.js +2 -1
- package/dist/bin/git-sweep.js +1 -1
- package/dist/bin/setup.js +3 -1
- package/dist/gateway/index.js +48 -2
- package/dist/hooks/commit-complete.js +1 -1
- package/dist/hooks/error-recall.js +3 -1
- package/dist/hooks/ingest-worker.js +48 -2
- package/dist/hooks/prompt-ingest-worker.js +3 -1
- package/dist/hooks/prompt-submit.js +33 -2
- package/dist/hooks/response-ingest-worker.js +3 -1
- package/dist/hooks/session-start.js +3 -1
- package/dist/hooks/subagent-stop.js +4 -2
- package/dist/hooks/summary-worker.js +30 -15
- package/dist/index.js +48 -2
- package/dist/lib/cloud-sync.js +22 -9
- package/dist/lib/config.js +2 -0
- package/dist/lib/embedder.js +3 -1
- package/dist/lib/exe-daemon.js +49 -2
- package/dist/lib/hybrid-search.js +3 -1
- package/dist/lib/tasks.js +45 -1
- package/dist/lib/tmux-routing.js +45 -1
- package/dist/mcp/server.js +48 -2
- package/dist/mcp/tools/create-task.js +3 -1
- package/dist/mcp/tools/list-tasks.js +1 -1
- package/dist/mcp/tools/update-task.js +15 -0
- package/dist/runtime/index.js +45 -1
- package/dist/tui/App.js +48 -2
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -31,6 +31,7 @@ var config_exports = {};
|
|
|
31
31
|
__export(config_exports, {
|
|
32
32
|
CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
|
|
33
33
|
CONFIG_PATH: () => CONFIG_PATH,
|
|
34
|
+
COO_AGENT_NAME: () => COO_AGENT_NAME,
|
|
34
35
|
CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
|
|
35
36
|
DB_PATH: () => DB_PATH,
|
|
36
37
|
EXE_AI_DIR: () => EXE_AI_DIR,
|
|
@@ -186,7 +187,7 @@ async function loadConfigFrom(configPath) {
|
|
|
186
187
|
return { ...DEFAULT_CONFIG };
|
|
187
188
|
}
|
|
188
189
|
}
|
|
189
|
-
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
190
|
+
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
190
191
|
var init_config = __esm({
|
|
191
192
|
"src/lib/config.ts"() {
|
|
192
193
|
"use strict";
|
|
@@ -194,6 +195,7 @@ var init_config = __esm({
|
|
|
194
195
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
195
196
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
196
197
|
CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
|
|
198
|
+
COO_AGENT_NAME = "exe";
|
|
197
199
|
LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
|
|
198
200
|
CURRENT_CONFIG_VERSION = 1;
|
|
199
201
|
DEFAULT_CONFIG = {
|
|
@@ -16901,7 +16903,7 @@ async function listTasks(input) {
|
|
|
16901
16903
|
}
|
|
16902
16904
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
16903
16905
|
const result = await client.execute({
|
|
16904
|
-
sql: `SELECT * FROM tasks ${where} ORDER BY CASE status WHEN 'blocked' THEN 0 WHEN 'in_progress' THEN 1 WHEN 'open' THEN 2 ELSE 3 END, priority ASC, created_at DESC`,
|
|
16906
|
+
sql: `SELECT * FROM tasks ${where} ORDER BY CASE status WHEN 'blocked' THEN 0 WHEN 'in_progress' THEN 1 WHEN 'open' THEN 2 ELSE 3 END, priority ASC, created_at DESC LIMIT 1000`,
|
|
16905
16907
|
args: args2
|
|
16906
16908
|
});
|
|
16907
16909
|
return result.rows.map((r) => ({
|
|
@@ -17122,6 +17124,34 @@ async function listPendingReviews(limit) {
|
|
|
17122
17124
|
});
|
|
17123
17125
|
return result.rows;
|
|
17124
17126
|
}
|
|
17127
|
+
async function cleanupOrphanedReviews() {
|
|
17128
|
+
const client = getClient();
|
|
17129
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
17130
|
+
const r1 = await client.execute({
|
|
17131
|
+
sql: `UPDATE tasks SET status = 'done', updated_at = ?
|
|
17132
|
+
WHERE status = 'needs_review'
|
|
17133
|
+
AND assigned_by = 'system'
|
|
17134
|
+
AND title LIKE 'Review:%'
|
|
17135
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
17136
|
+
args: [now]
|
|
17137
|
+
});
|
|
17138
|
+
const staleThreshold = new Date(Date.now() - 60 * 60 * 1e3).toISOString();
|
|
17139
|
+
const r2 = await client.execute({
|
|
17140
|
+
sql: `UPDATE tasks SET status = 'done', updated_at = ?
|
|
17141
|
+
WHERE status = 'needs_review'
|
|
17142
|
+
AND result IS NOT NULL
|
|
17143
|
+
AND updated_at < ?`,
|
|
17144
|
+
args: [now, staleThreshold]
|
|
17145
|
+
});
|
|
17146
|
+
const total = r1.rowsAffected + r2.rowsAffected;
|
|
17147
|
+
if (total > 0) {
|
|
17148
|
+
process.stderr.write(
|
|
17149
|
+
`[cleanup] Closed ${total} orphaned review(s): ${r1.rowsAffected} cascade + ${r2.rowsAffected} stale
|
|
17150
|
+
`
|
|
17151
|
+
);
|
|
17152
|
+
}
|
|
17153
|
+
return total;
|
|
17154
|
+
}
|
|
17125
17155
|
function getReviewChecklist(role, agent, taskSlug) {
|
|
17126
17156
|
const roleLower = role.toLowerCase();
|
|
17127
17157
|
if (roleLower.includes("engineer") || roleLower === "principal engineer") {
|
|
@@ -17801,6 +17831,7 @@ var init_skill_learning = __esm({
|
|
|
17801
17831
|
// src/lib/tasks.ts
|
|
17802
17832
|
var tasks_exports = {};
|
|
17803
17833
|
__export(tasks_exports, {
|
|
17834
|
+
cleanupOrphanedReviews: () => cleanupOrphanedReviews,
|
|
17804
17835
|
countNewPendingReviewsSince: () => countNewPendingReviewsSince,
|
|
17805
17836
|
countPendingReviews: () => countPendingReviews,
|
|
17806
17837
|
createTask: () => createTask,
|
|
@@ -17866,6 +17897,21 @@ async function updateTask(input) {
|
|
|
17866
17897
|
});
|
|
17867
17898
|
} catch {
|
|
17868
17899
|
}
|
|
17900
|
+
try {
|
|
17901
|
+
const client = getClient();
|
|
17902
|
+
const cascaded = await client.execute({
|
|
17903
|
+
sql: `UPDATE tasks SET status = 'done', updated_at = ?
|
|
17904
|
+
WHERE parent_task_id = ? AND status = 'needs_review'`,
|
|
17905
|
+
args: [now, taskId]
|
|
17906
|
+
});
|
|
17907
|
+
if (cascaded.rowsAffected > 0) {
|
|
17908
|
+
process.stderr.write(
|
|
17909
|
+
`[cascade] Closed ${cascaded.rowsAffected} orphaned review task(s) for parent ${taskId}
|
|
17910
|
+
`
|
|
17911
|
+
);
|
|
17912
|
+
}
|
|
17913
|
+
} catch {
|
|
17914
|
+
}
|
|
17869
17915
|
}
|
|
17870
17916
|
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
17871
17917
|
if (isTerminal) {
|
package/dist/bin/exe-boot.js
CHANGED
|
@@ -31,6 +31,7 @@ var config_exports = {};
|
|
|
31
31
|
__export(config_exports, {
|
|
32
32
|
CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
|
|
33
33
|
CONFIG_PATH: () => CONFIG_PATH,
|
|
34
|
+
COO_AGENT_NAME: () => COO_AGENT_NAME,
|
|
34
35
|
CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
|
|
35
36
|
DB_PATH: () => DB_PATH,
|
|
36
37
|
EXE_AI_DIR: () => EXE_AI_DIR,
|
|
@@ -186,7 +187,7 @@ async function loadConfigFrom(configPath) {
|
|
|
186
187
|
return { ...DEFAULT_CONFIG };
|
|
187
188
|
}
|
|
188
189
|
}
|
|
189
|
-
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
190
|
+
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
190
191
|
var init_config = __esm({
|
|
191
192
|
"src/lib/config.ts"() {
|
|
192
193
|
"use strict";
|
|
@@ -194,6 +195,7 @@ var init_config = __esm({
|
|
|
194
195
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
195
196
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
196
197
|
CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
|
|
198
|
+
COO_AGENT_NAME = "exe";
|
|
197
199
|
LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
|
|
198
200
|
CURRENT_CONFIG_VERSION = 1;
|
|
199
201
|
DEFAULT_CONFIG = {
|
|
@@ -3588,7 +3590,7 @@ async function listTasks(input) {
|
|
|
3588
3590
|
}
|
|
3589
3591
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
3590
3592
|
const result = await client.execute({
|
|
3591
|
-
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`,
|
|
3593
|
+
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`,
|
|
3592
3594
|
args
|
|
3593
3595
|
});
|
|
3594
3596
|
return result.rows.map((r) => ({
|
|
@@ -3809,6 +3811,34 @@ async function listPendingReviews(limit) {
|
|
|
3809
3811
|
});
|
|
3810
3812
|
return result.rows;
|
|
3811
3813
|
}
|
|
3814
|
+
async function cleanupOrphanedReviews() {
|
|
3815
|
+
const client = getClient();
|
|
3816
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3817
|
+
const r1 = await client.execute({
|
|
3818
|
+
sql: `UPDATE tasks SET status = 'done', updated_at = ?
|
|
3819
|
+
WHERE status = 'needs_review'
|
|
3820
|
+
AND assigned_by = 'system'
|
|
3821
|
+
AND title LIKE 'Review:%'
|
|
3822
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
3823
|
+
args: [now]
|
|
3824
|
+
});
|
|
3825
|
+
const staleThreshold = new Date(Date.now() - 60 * 60 * 1e3).toISOString();
|
|
3826
|
+
const r2 = await client.execute({
|
|
3827
|
+
sql: `UPDATE tasks SET status = 'done', updated_at = ?
|
|
3828
|
+
WHERE status = 'needs_review'
|
|
3829
|
+
AND result IS NOT NULL
|
|
3830
|
+
AND updated_at < ?`,
|
|
3831
|
+
args: [now, staleThreshold]
|
|
3832
|
+
});
|
|
3833
|
+
const total = r1.rowsAffected + r2.rowsAffected;
|
|
3834
|
+
if (total > 0) {
|
|
3835
|
+
process.stderr.write(
|
|
3836
|
+
`[cleanup] Closed ${total} orphaned review(s): ${r1.rowsAffected} cascade + ${r2.rowsAffected} stale
|
|
3837
|
+
`
|
|
3838
|
+
);
|
|
3839
|
+
}
|
|
3840
|
+
return total;
|
|
3841
|
+
}
|
|
3812
3842
|
function getReviewChecklist(role, agent, taskSlug) {
|
|
3813
3843
|
const roleLower = role.toLowerCase();
|
|
3814
3844
|
if (roleLower.includes("engineer") || roleLower === "principal engineer") {
|
|
@@ -4446,6 +4476,7 @@ var init_skill_learning = __esm({
|
|
|
4446
4476
|
// src/lib/tasks.ts
|
|
4447
4477
|
var tasks_exports = {};
|
|
4448
4478
|
__export(tasks_exports, {
|
|
4479
|
+
cleanupOrphanedReviews: () => cleanupOrphanedReviews,
|
|
4449
4480
|
countNewPendingReviewsSince: () => countNewPendingReviewsSince,
|
|
4450
4481
|
countPendingReviews: () => countPendingReviews,
|
|
4451
4482
|
createTask: () => createTask,
|
|
@@ -4511,6 +4542,21 @@ async function updateTask(input) {
|
|
|
4511
4542
|
});
|
|
4512
4543
|
} catch {
|
|
4513
4544
|
}
|
|
4545
|
+
try {
|
|
4546
|
+
const client = getClient();
|
|
4547
|
+
const cascaded = await client.execute({
|
|
4548
|
+
sql: `UPDATE tasks SET status = 'done', updated_at = ?
|
|
4549
|
+
WHERE parent_task_id = ? AND status = 'needs_review'`,
|
|
4550
|
+
args: [now, taskId]
|
|
4551
|
+
});
|
|
4552
|
+
if (cascaded.rowsAffected > 0) {
|
|
4553
|
+
process.stderr.write(
|
|
4554
|
+
`[cascade] Closed ${cascaded.rowsAffected} orphaned review task(s) for parent ${taskId}
|
|
4555
|
+
`
|
|
4556
|
+
);
|
|
4557
|
+
}
|
|
4558
|
+
} catch {
|
|
4559
|
+
}
|
|
4514
4560
|
}
|
|
4515
4561
|
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
4516
4562
|
if (isTerminal) {
|
|
@@ -4809,7 +4855,8 @@ __export(cloud_sync_exports, {
|
|
|
4809
4855
|
mergeRosterFromRemote: () => mergeRosterFromRemote,
|
|
4810
4856
|
recordRosterDeletion: () => recordRosterDeletion
|
|
4811
4857
|
});
|
|
4812
|
-
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, existsSync as existsSync14, readdirSync as readdirSync6, mkdirSync as mkdirSync8, appendFileSync as appendFileSync2, unlinkSync as unlinkSync6 } from "fs";
|
|
4858
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, existsSync as existsSync14, readdirSync as readdirSync6, mkdirSync as mkdirSync8, appendFileSync as appendFileSync2, unlinkSync as unlinkSync6, openSync, closeSync } from "fs";
|
|
4859
|
+
import crypto8 from "crypto";
|
|
4813
4860
|
import path18 from "path";
|
|
4814
4861
|
import { homedir } from "os";
|
|
4815
4862
|
function logError(msg) {
|
|
@@ -4821,17 +4868,29 @@ function logError(msg) {
|
|
|
4821
4868
|
}
|
|
4822
4869
|
}
|
|
4823
4870
|
async function withRosterLock(fn) {
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
|
|
4871
|
+
try {
|
|
4872
|
+
const fd = openSync(ROSTER_LOCK_PATH, "wx");
|
|
4873
|
+
closeSync(fd);
|
|
4874
|
+
writeFileSync7(ROSTER_LOCK_PATH, String(Date.now()));
|
|
4875
|
+
} catch (err) {
|
|
4876
|
+
if (err.code === "EEXIST") {
|
|
4877
|
+
try {
|
|
4878
|
+
const ts = parseInt(readFileSync12(ROSTER_LOCK_PATH, "utf-8"), 10);
|
|
4879
|
+
if (Date.now() - ts < LOCK_STALE_MS) {
|
|
4880
|
+
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
4881
|
+
}
|
|
4882
|
+
unlinkSync6(ROSTER_LOCK_PATH);
|
|
4883
|
+
const fd = openSync(ROSTER_LOCK_PATH, "wx");
|
|
4884
|
+
closeSync(fd);
|
|
4885
|
+
writeFileSync7(ROSTER_LOCK_PATH, String(Date.now()));
|
|
4886
|
+
} catch (retryErr) {
|
|
4887
|
+
if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
|
|
4828
4888
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
4829
4889
|
}
|
|
4830
|
-
}
|
|
4831
|
-
|
|
4890
|
+
} else {
|
|
4891
|
+
throw err;
|
|
4832
4892
|
}
|
|
4833
4893
|
}
|
|
4834
|
-
writeFileSync7(ROSTER_LOCK_PATH, String(Date.now()));
|
|
4835
4894
|
try {
|
|
4836
4895
|
return await fn();
|
|
4837
4896
|
} finally {
|
|
@@ -5153,7 +5212,7 @@ function buildRosterBlob(paths) {
|
|
|
5153
5212
|
}
|
|
5154
5213
|
const deletedNames = consumeRosterDeletions();
|
|
5155
5214
|
const content = JSON.stringify({ roster, identities, config, deletedNames });
|
|
5156
|
-
const hash =
|
|
5215
|
+
const hash = crypto8.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
5157
5216
|
return { roster, identities, config, deletedNames, version: hash };
|
|
5158
5217
|
}
|
|
5159
5218
|
async function cloudPushRoster(config) {
|
|
@@ -5682,7 +5741,7 @@ __export(schedules_exports, {
|
|
|
5682
5741
|
listSchedules: () => listSchedules,
|
|
5683
5742
|
parseHumanCron: () => parseHumanCron
|
|
5684
5743
|
});
|
|
5685
|
-
import
|
|
5744
|
+
import crypto9 from "crypto";
|
|
5686
5745
|
import { execSync as execSync10 } from "child_process";
|
|
5687
5746
|
async function ensureDb() {
|
|
5688
5747
|
if (!isInitialized()) {
|
|
@@ -5751,7 +5810,7 @@ function parseHumanCron(input) {
|
|
|
5751
5810
|
async function createSchedule(input) {
|
|
5752
5811
|
await ensureDb();
|
|
5753
5812
|
const client = getClient();
|
|
5754
|
-
const id =
|
|
5813
|
+
const id = crypto9.randomUUID().slice(0, 8);
|
|
5755
5814
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5756
5815
|
const prompt = input.prompt ?? input.description;
|
|
5757
5816
|
await client.execute({
|
|
@@ -7036,11 +7095,11 @@ async function boot(options) {
|
|
|
7036
7095
|
const thisFile = fileURLToPath3(import.meta.url);
|
|
7037
7096
|
const backfillPath = path19.resolve(path19.dirname(thisFile), "backfill-vectors.js");
|
|
7038
7097
|
if (existsSync15(backfillPath)) {
|
|
7039
|
-
const { openSync, closeSync } = await import("fs");
|
|
7098
|
+
const { openSync: openSync2, closeSync: closeSync2 } = await import("fs");
|
|
7040
7099
|
const workerLogPath = path19.join(EXE_AI_DIR, "workers.log");
|
|
7041
7100
|
let stderrFd = "ignore";
|
|
7042
7101
|
try {
|
|
7043
|
-
stderrFd =
|
|
7102
|
+
stderrFd = openSync2(workerLogPath, "a");
|
|
7044
7103
|
} catch {
|
|
7045
7104
|
}
|
|
7046
7105
|
const child = spawn(process.execPath, [backfillPath], {
|
|
@@ -7049,7 +7108,7 @@ async function boot(options) {
|
|
|
7049
7108
|
});
|
|
7050
7109
|
child.unref();
|
|
7051
7110
|
if (typeof stderrFd === "number") try {
|
|
7052
|
-
|
|
7111
|
+
closeSync2(stderrFd);
|
|
7053
7112
|
} catch {
|
|
7054
7113
|
}
|
|
7055
7114
|
briefData.embedding.backfillRunning = true;
|
package/dist/bin/exe-cloud.js
CHANGED
|
@@ -710,7 +710,8 @@ function isMainModule(importMetaUrl) {
|
|
|
710
710
|
}
|
|
711
711
|
|
|
712
712
|
// src/lib/cloud-sync.ts
|
|
713
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync6, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync } from "fs";
|
|
713
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync6, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync, openSync, closeSync } from "fs";
|
|
714
|
+
import crypto4 from "crypto";
|
|
714
715
|
import path6 from "path";
|
|
715
716
|
import { homedir } from "os";
|
|
716
717
|
|
|
@@ -897,8 +898,17 @@ async function syncMode() {
|
|
|
897
898
|
console.log("");
|
|
898
899
|
console.log("Run /exe-cloud on the new device and paste these when prompted:\n");
|
|
899
900
|
console.log(" 24-word recovery phrase:");
|
|
900
|
-
|
|
901
|
+
const showFull = process.argv.includes("--show-full");
|
|
902
|
+
if (showFull) {
|
|
903
|
+
console.log(` ${mnemonic}
|
|
901
904
|
`);
|
|
905
|
+
} else {
|
|
906
|
+
const words = mnemonic.split(" ");
|
|
907
|
+
const masked = words.length > 4 ? [...words.slice(0, 2), ...words.slice(2, -2).map(() => "****"), ...words.slice(-2)].join(" ") : mnemonic;
|
|
908
|
+
console.log(` ${masked}
|
|
909
|
+
`);
|
|
910
|
+
console.log(" To reveal full phrase: run with --show-full\n");
|
|
911
|
+
}
|
|
902
912
|
console.log(" \u26A0 Clear your terminal history after copying.\n");
|
|
903
913
|
if (config.cloud?.apiKey) {
|
|
904
914
|
const maskedKey = config.cloud.apiKey.slice(0, 10) + "..." + config.cloud.apiKey.slice(-4);
|
package/dist/bin/exe-doctor.js
CHANGED
|
@@ -1542,7 +1542,7 @@ async function auditDuplicates(client, flags) {
|
|
|
1542
1542
|
if (flags.agent) filterClause += " AND agent_id = ?";
|
|
1543
1543
|
if (flags.project) filterClause += " AND project_name = ?";
|
|
1544
1544
|
const ids = await client.execute({
|
|
1545
|
-
sql: `SELECT id FROM memories${filterClause} ORDER BY timestamp DESC`,
|
|
1545
|
+
sql: `SELECT id FROM memories${filterClause} ORDER BY timestamp DESC LIMIT 10000`,
|
|
1546
1546
|
args: filterArgs
|
|
1547
1547
|
});
|
|
1548
1548
|
const allIds = ids.rows.map((r) => r.id);
|
package/dist/bin/exe-gateway.js
CHANGED
|
@@ -1192,6 +1192,7 @@ var config_exports = {};
|
|
|
1192
1192
|
__export(config_exports, {
|
|
1193
1193
|
CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
|
|
1194
1194
|
CONFIG_PATH: () => CONFIG_PATH,
|
|
1195
|
+
COO_AGENT_NAME: () => COO_AGENT_NAME,
|
|
1195
1196
|
CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
|
|
1196
1197
|
DB_PATH: () => DB_PATH,
|
|
1197
1198
|
EXE_AI_DIR: () => EXE_AI_DIR,
|
|
@@ -1347,7 +1348,7 @@ async function loadConfigFrom(configPath) {
|
|
|
1347
1348
|
return { ...DEFAULT_CONFIG };
|
|
1348
1349
|
}
|
|
1349
1350
|
}
|
|
1350
|
-
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
1351
|
+
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
1351
1352
|
var init_config = __esm({
|
|
1352
1353
|
"src/lib/config.ts"() {
|
|
1353
1354
|
"use strict";
|
|
@@ -1355,6 +1356,7 @@ var init_config = __esm({
|
|
|
1355
1356
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
1356
1357
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
1357
1358
|
CONFIG_PATH = path.join(EXE_AI_DIR, "config.json");
|
|
1359
|
+
COO_AGENT_NAME = "exe";
|
|
1358
1360
|
LEGACY_LANCE_PATH = path.join(EXE_AI_DIR, "local.lance");
|
|
1359
1361
|
CURRENT_CONFIG_VERSION = 1;
|
|
1360
1362
|
DEFAULT_CONFIG = {
|
|
@@ -5849,7 +5851,7 @@ async function listTasks(input) {
|
|
|
5849
5851
|
}
|
|
5850
5852
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
5851
5853
|
const result = await client.execute({
|
|
5852
|
-
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`,
|
|
5854
|
+
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`,
|
|
5853
5855
|
args
|
|
5854
5856
|
});
|
|
5855
5857
|
return result.rows.map((r) => ({
|
|
@@ -6070,6 +6072,34 @@ async function listPendingReviews(limit) {
|
|
|
6070
6072
|
});
|
|
6071
6073
|
return result.rows;
|
|
6072
6074
|
}
|
|
6075
|
+
async function cleanupOrphanedReviews() {
|
|
6076
|
+
const client = getClient();
|
|
6077
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6078
|
+
const r1 = await client.execute({
|
|
6079
|
+
sql: `UPDATE tasks SET status = 'done', updated_at = ?
|
|
6080
|
+
WHERE status = 'needs_review'
|
|
6081
|
+
AND assigned_by = 'system'
|
|
6082
|
+
AND title LIKE 'Review:%'
|
|
6083
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
6084
|
+
args: [now]
|
|
6085
|
+
});
|
|
6086
|
+
const staleThreshold = new Date(Date.now() - 60 * 60 * 1e3).toISOString();
|
|
6087
|
+
const r2 = await client.execute({
|
|
6088
|
+
sql: `UPDATE tasks SET status = 'done', updated_at = ?
|
|
6089
|
+
WHERE status = 'needs_review'
|
|
6090
|
+
AND result IS NOT NULL
|
|
6091
|
+
AND updated_at < ?`,
|
|
6092
|
+
args: [now, staleThreshold]
|
|
6093
|
+
});
|
|
6094
|
+
const total = r1.rowsAffected + r2.rowsAffected;
|
|
6095
|
+
if (total > 0) {
|
|
6096
|
+
process.stderr.write(
|
|
6097
|
+
`[cleanup] Closed ${total} orphaned review(s): ${r1.rowsAffected} cascade + ${r2.rowsAffected} stale
|
|
6098
|
+
`
|
|
6099
|
+
);
|
|
6100
|
+
}
|
|
6101
|
+
return total;
|
|
6102
|
+
}
|
|
6073
6103
|
function getReviewChecklist(role, agent, taskSlug) {
|
|
6074
6104
|
const roleLower = role.toLowerCase();
|
|
6075
6105
|
if (roleLower.includes("engineer") || roleLower === "principal engineer") {
|
|
@@ -6749,6 +6779,7 @@ var init_skill_learning = __esm({
|
|
|
6749
6779
|
// src/lib/tasks.ts
|
|
6750
6780
|
var tasks_exports = {};
|
|
6751
6781
|
__export(tasks_exports, {
|
|
6782
|
+
cleanupOrphanedReviews: () => cleanupOrphanedReviews,
|
|
6752
6783
|
countNewPendingReviewsSince: () => countNewPendingReviewsSince,
|
|
6753
6784
|
countPendingReviews: () => countPendingReviews,
|
|
6754
6785
|
createTask: () => createTask,
|
|
@@ -6814,6 +6845,21 @@ async function updateTask(input) {
|
|
|
6814
6845
|
});
|
|
6815
6846
|
} catch {
|
|
6816
6847
|
}
|
|
6848
|
+
try {
|
|
6849
|
+
const client = getClient();
|
|
6850
|
+
const cascaded = await client.execute({
|
|
6851
|
+
sql: `UPDATE tasks SET status = 'done', updated_at = ?
|
|
6852
|
+
WHERE parent_task_id = ? AND status = 'needs_review'`,
|
|
6853
|
+
args: [now, taskId]
|
|
6854
|
+
});
|
|
6855
|
+
if (cascaded.rowsAffected > 0) {
|
|
6856
|
+
process.stderr.write(
|
|
6857
|
+
`[cascade] Closed ${cascaded.rowsAffected} orphaned review task(s) for parent ${taskId}
|
|
6858
|
+
`
|
|
6859
|
+
);
|
|
6860
|
+
}
|
|
6861
|
+
} catch {
|
|
6862
|
+
}
|
|
6817
6863
|
}
|
|
6818
6864
|
const isTerminal = input.status === "done" || input.status === "needs_review";
|
|
6819
6865
|
if (isTerminal) {
|
package/dist/bin/exe-link.js
CHANGED
|
@@ -20,6 +20,7 @@ var config_exports = {};
|
|
|
20
20
|
__export(config_exports, {
|
|
21
21
|
CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
|
|
22
22
|
CONFIG_PATH: () => CONFIG_PATH,
|
|
23
|
+
COO_AGENT_NAME: () => COO_AGENT_NAME,
|
|
23
24
|
CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
|
|
24
25
|
DB_PATH: () => DB_PATH,
|
|
25
26
|
EXE_AI_DIR: () => EXE_AI_DIR,
|
|
@@ -175,7 +176,7 @@ async function loadConfigFrom(configPath) {
|
|
|
175
176
|
return { ...DEFAULT_CONFIG };
|
|
176
177
|
}
|
|
177
178
|
}
|
|
178
|
-
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
179
|
+
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
179
180
|
var init_config = __esm({
|
|
180
181
|
"src/lib/config.ts"() {
|
|
181
182
|
"use strict";
|
|
@@ -183,6 +184,7 @@ var init_config = __esm({
|
|
|
183
184
|
DB_PATH = path2.join(EXE_AI_DIR, "memories.db");
|
|
184
185
|
MODELS_DIR = path2.join(EXE_AI_DIR, "models");
|
|
185
186
|
CONFIG_PATH = path2.join(EXE_AI_DIR, "config.json");
|
|
187
|
+
COO_AGENT_NAME = "exe";
|
|
186
188
|
LEGACY_LANCE_PATH = path2.join(EXE_AI_DIR, "local.lance");
|
|
187
189
|
CURRENT_CONFIG_VERSION = 1;
|
|
188
190
|
DEFAULT_CONFIG = {
|
|
@@ -671,7 +673,8 @@ __export(cloud_sync_exports, {
|
|
|
671
673
|
mergeRosterFromRemote: () => mergeRosterFromRemote,
|
|
672
674
|
recordRosterDeletion: () => recordRosterDeletion
|
|
673
675
|
});
|
|
674
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync6, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync } from "fs";
|
|
676
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync6, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync, openSync, closeSync } from "fs";
|
|
677
|
+
import crypto3 from "crypto";
|
|
675
678
|
import path6 from "path";
|
|
676
679
|
import { homedir } from "os";
|
|
677
680
|
function logError(msg) {
|
|
@@ -683,17 +686,29 @@ function logError(msg) {
|
|
|
683
686
|
}
|
|
684
687
|
}
|
|
685
688
|
async function withRosterLock(fn) {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
689
|
+
try {
|
|
690
|
+
const fd = openSync(ROSTER_LOCK_PATH, "wx");
|
|
691
|
+
closeSync(fd);
|
|
692
|
+
writeFileSync2(ROSTER_LOCK_PATH, String(Date.now()));
|
|
693
|
+
} catch (err) {
|
|
694
|
+
if (err.code === "EEXIST") {
|
|
695
|
+
try {
|
|
696
|
+
const ts = parseInt(readFileSync5(ROSTER_LOCK_PATH, "utf-8"), 10);
|
|
697
|
+
if (Date.now() - ts < LOCK_STALE_MS) {
|
|
698
|
+
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
699
|
+
}
|
|
700
|
+
unlinkSync(ROSTER_LOCK_PATH);
|
|
701
|
+
const fd = openSync(ROSTER_LOCK_PATH, "wx");
|
|
702
|
+
closeSync(fd);
|
|
703
|
+
writeFileSync2(ROSTER_LOCK_PATH, String(Date.now()));
|
|
704
|
+
} catch (retryErr) {
|
|
705
|
+
if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
|
|
690
706
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
691
707
|
}
|
|
692
|
-
}
|
|
693
|
-
|
|
708
|
+
} else {
|
|
709
|
+
throw err;
|
|
694
710
|
}
|
|
695
711
|
}
|
|
696
|
-
writeFileSync2(ROSTER_LOCK_PATH, String(Date.now()));
|
|
697
712
|
try {
|
|
698
713
|
return await fn();
|
|
699
714
|
} finally {
|
|
@@ -1015,7 +1030,7 @@ function buildRosterBlob(paths) {
|
|
|
1015
1030
|
}
|
|
1016
1031
|
const deletedNames = consumeRosterDeletions();
|
|
1017
1032
|
const content = JSON.stringify({ roster, identities, config, deletedNames });
|
|
1018
|
-
const hash =
|
|
1033
|
+
const hash = crypto3.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
1019
1034
|
return { roster, identities, config, deletedNames, version: hash };
|
|
1020
1035
|
}
|
|
1021
1036
|
async function cloudPushRoster(config) {
|
|
@@ -1684,8 +1699,17 @@ async function main() {
|
|
|
1684
1699
|
}
|
|
1685
1700
|
const mnemonic = exportMnemonic(key);
|
|
1686
1701
|
console.log("Your 24-word recovery phrase:\n");
|
|
1687
|
-
|
|
1702
|
+
const showFull = process.argv.includes("--show-full");
|
|
1703
|
+
if (showFull) {
|
|
1704
|
+
console.log(` ${mnemonic}
|
|
1688
1705
|
`);
|
|
1706
|
+
} else {
|
|
1707
|
+
const words = mnemonic.split(" ");
|
|
1708
|
+
const masked = words.length > 4 ? [...words.slice(0, 2), ...words.slice(2, -2).map(() => "****"), ...words.slice(-2)].join(" ") : mnemonic;
|
|
1709
|
+
console.log(` ${masked}
|
|
1710
|
+
`);
|
|
1711
|
+
console.log("To reveal full phrase: run with --show-full\n");
|
|
1712
|
+
}
|
|
1689
1713
|
console.log("Write this down and enter it on your new device with /exe-link import.");
|
|
1690
1714
|
console.log("Anyone with this phrase can decrypt your memories.");
|
|
1691
1715
|
console.log("\u26A0 Clear your terminal history after copying.");
|
|
@@ -1509,6 +1509,34 @@ async function listPendingReviews(limit) {
|
|
|
1509
1509
|
});
|
|
1510
1510
|
return result.rows;
|
|
1511
1511
|
}
|
|
1512
|
+
async function cleanupOrphanedReviews() {
|
|
1513
|
+
const client = getClient();
|
|
1514
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1515
|
+
const r1 = await client.execute({
|
|
1516
|
+
sql: `UPDATE tasks SET status = 'done', updated_at = ?
|
|
1517
|
+
WHERE status = 'needs_review'
|
|
1518
|
+
AND assigned_by = 'system'
|
|
1519
|
+
AND title LIKE 'Review:%'
|
|
1520
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
1521
|
+
args: [now]
|
|
1522
|
+
});
|
|
1523
|
+
const staleThreshold = new Date(Date.now() - 60 * 60 * 1e3).toISOString();
|
|
1524
|
+
const r2 = await client.execute({
|
|
1525
|
+
sql: `UPDATE tasks SET status = 'done', updated_at = ?
|
|
1526
|
+
WHERE status = 'needs_review'
|
|
1527
|
+
AND result IS NOT NULL
|
|
1528
|
+
AND updated_at < ?`,
|
|
1529
|
+
args: [now, staleThreshold]
|
|
1530
|
+
});
|
|
1531
|
+
const total = r1.rowsAffected + r2.rowsAffected;
|
|
1532
|
+
if (total > 0) {
|
|
1533
|
+
process.stderr.write(
|
|
1534
|
+
`[cleanup] Closed ${total} orphaned review(s): ${r1.rowsAffected} cascade + ${r2.rowsAffected} stale
|
|
1535
|
+
`
|
|
1536
|
+
);
|
|
1537
|
+
}
|
|
1538
|
+
return total;
|
|
1539
|
+
}
|
|
1512
1540
|
var init_tasks_review = __esm({
|
|
1513
1541
|
"src/lib/tasks-review.ts"() {
|
|
1514
1542
|
"use strict";
|
|
@@ -1662,6 +1690,7 @@ init_tasks_review();
|
|
|
1662
1690
|
var PENDING_REVIEW_LIMIT = 10;
|
|
1663
1691
|
async function main() {
|
|
1664
1692
|
await initStore();
|
|
1693
|
+
await cleanupOrphanedReviews();
|
|
1665
1694
|
const rows = await listPendingReviews(PENDING_REVIEW_LIMIT);
|
|
1666
1695
|
if (rows.length > 0) {
|
|
1667
1696
|
console.log("[REVIEW NOTIFICATIONS]");
|
package/dist/bin/exe-search.js
CHANGED
|
@@ -969,6 +969,7 @@ var config_exports = {};
|
|
|
969
969
|
__export(config_exports, {
|
|
970
970
|
CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
|
|
971
971
|
CONFIG_PATH: () => CONFIG_PATH,
|
|
972
|
+
COO_AGENT_NAME: () => COO_AGENT_NAME,
|
|
972
973
|
CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
|
|
973
974
|
DB_PATH: () => DB_PATH,
|
|
974
975
|
EXE_AI_DIR: () => EXE_AI_DIR,
|
|
@@ -1124,7 +1125,7 @@ async function loadConfigFrom(configPath) {
|
|
|
1124
1125
|
return { ...DEFAULT_CONFIG };
|
|
1125
1126
|
}
|
|
1126
1127
|
}
|
|
1127
|
-
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
1128
|
+
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
1128
1129
|
var init_config = __esm({
|
|
1129
1130
|
"src/lib/config.ts"() {
|
|
1130
1131
|
"use strict";
|
|
@@ -1132,6 +1133,7 @@ var init_config = __esm({
|
|
|
1132
1133
|
DB_PATH = path2.join(EXE_AI_DIR, "memories.db");
|
|
1133
1134
|
MODELS_DIR = path2.join(EXE_AI_DIR, "models");
|
|
1134
1135
|
CONFIG_PATH = path2.join(EXE_AI_DIR, "config.json");
|
|
1136
|
+
COO_AGENT_NAME = "exe";
|
|
1135
1137
|
LEGACY_LANCE_PATH = path2.join(EXE_AI_DIR, "local.lance");
|
|
1136
1138
|
CURRENT_CONFIG_VERSION = 1;
|
|
1137
1139
|
DEFAULT_CONFIG = {
|
|
@@ -922,6 +922,7 @@ var config_exports = {};
|
|
|
922
922
|
__export(config_exports, {
|
|
923
923
|
CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
|
|
924
924
|
CONFIG_PATH: () => CONFIG_PATH,
|
|
925
|
+
COO_AGENT_NAME: () => COO_AGENT_NAME,
|
|
925
926
|
CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
|
|
926
927
|
DB_PATH: () => DB_PATH,
|
|
927
928
|
EXE_AI_DIR: () => EXE_AI_DIR,
|
|
@@ -1077,7 +1078,7 @@ async function loadConfigFrom(configPath) {
|
|
|
1077
1078
|
return { ...DEFAULT_CONFIG };
|
|
1078
1079
|
}
|
|
1079
1080
|
}
|
|
1080
|
-
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
1081
|
+
var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, COO_AGENT_NAME, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
|
|
1081
1082
|
var init_config = __esm({
|
|
1082
1083
|
"src/lib/config.ts"() {
|
|
1083
1084
|
"use strict";
|
|
@@ -1085,6 +1086,7 @@ var init_config = __esm({
|
|
|
1085
1086
|
DB_PATH = path2.join(EXE_AI_DIR, "memories.db");
|
|
1086
1087
|
MODELS_DIR = path2.join(EXE_AI_DIR, "models");
|
|
1087
1088
|
CONFIG_PATH = path2.join(EXE_AI_DIR, "config.json");
|
|
1089
|
+
COO_AGENT_NAME = "exe";
|
|
1088
1090
|
LEGACY_LANCE_PATH = path2.join(EXE_AI_DIR, "local.lance");
|
|
1089
1091
|
CURRENT_CONFIG_VERSION = 1;
|
|
1090
1092
|
DEFAULT_CONFIG = {
|
|
@@ -2537,6 +2539,7 @@ var init_tmux_routing = __esm({
|
|
|
2537
2539
|
// src/lib/tasks-review.ts
|
|
2538
2540
|
var tasks_review_exports = {};
|
|
2539
2541
|
__export(tasks_review_exports, {
|
|
2542
|
+
cleanupOrphanedReviews: () => cleanupOrphanedReviews,
|
|
2540
2543
|
cleanupReviewFile: () => cleanupReviewFile,
|
|
2541
2544
|
countNewPendingReviewsSince: () => countNewPendingReviewsSince,
|
|
2542
2545
|
countPendingReviews: () => countPendingReviews,
|
|
@@ -2573,6 +2576,34 @@ async function listPendingReviews(limit) {
|
|
|
2573
2576
|
});
|
|
2574
2577
|
return result.rows;
|
|
2575
2578
|
}
|
|
2579
|
+
async function cleanupOrphanedReviews() {
|
|
2580
|
+
const client = getClient();
|
|
2581
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2582
|
+
const r1 = await client.execute({
|
|
2583
|
+
sql: `UPDATE tasks SET status = 'done', updated_at = ?
|
|
2584
|
+
WHERE status = 'needs_review'
|
|
2585
|
+
AND assigned_by = 'system'
|
|
2586
|
+
AND title LIKE 'Review:%'
|
|
2587
|
+
AND parent_task_id IN (SELECT id FROM tasks WHERE status IN ('done', 'cancelled'))`,
|
|
2588
|
+
args: [now]
|
|
2589
|
+
});
|
|
2590
|
+
const staleThreshold = new Date(Date.now() - 60 * 60 * 1e3).toISOString();
|
|
2591
|
+
const r2 = await client.execute({
|
|
2592
|
+
sql: `UPDATE tasks SET status = 'done', updated_at = ?
|
|
2593
|
+
WHERE status = 'needs_review'
|
|
2594
|
+
AND result IS NOT NULL
|
|
2595
|
+
AND updated_at < ?`,
|
|
2596
|
+
args: [now, staleThreshold]
|
|
2597
|
+
});
|
|
2598
|
+
const total = r1.rowsAffected + r2.rowsAffected;
|
|
2599
|
+
if (total > 0) {
|
|
2600
|
+
process.stderr.write(
|
|
2601
|
+
`[cleanup] Closed ${total} orphaned review(s): ${r1.rowsAffected} cascade + ${r2.rowsAffected} stale
|
|
2602
|
+
`
|
|
2603
|
+
);
|
|
2604
|
+
}
|
|
2605
|
+
return total;
|
|
2606
|
+
}
|
|
2576
2607
|
function getReviewChecklist(role, agent, taskSlug) {
|
|
2577
2608
|
const roleLower = role.toLowerCase();
|
|
2578
2609
|
if (roleLower.includes("engineer") || roleLower === "principal engineer") {
|
package/dist/bin/exe-settings.js
CHANGED
|
@@ -208,7 +208,8 @@ function isMainModule(importMetaUrl) {
|
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
// src/lib/cloud-sync.ts
|
|
211
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync5, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync } from "fs";
|
|
211
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync5, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync, openSync, closeSync } from "fs";
|
|
212
|
+
import crypto2 from "crypto";
|
|
212
213
|
import path5 from "path";
|
|
213
214
|
import { homedir } from "os";
|
|
214
215
|
|