@askexenow/exe-os 0.9.9 → 0.9.10
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 +143 -124
- package/dist/bin/exe-boot.js +152 -133
- package/dist/bin/exe-dispatch.js +143 -124
- package/dist/bin/exe-gateway.js +143 -124
- package/dist/bin/exe-session-cleanup.js +143 -124
- package/dist/bin/git-sweep.js +143 -124
- package/dist/bin/scan-tasks.js +152 -133
- package/dist/gateway/index.js +143 -124
- package/dist/hooks/bug-report-worker.js +143 -124
- package/dist/hooks/commit-complete.js +143 -124
- package/dist/hooks/ingest-worker.js +82 -63
- package/dist/hooks/pre-compact.js +143 -124
- package/dist/hooks/prompt-submit.js +82 -63
- package/dist/hooks/session-end.js +143 -124
- package/dist/index.js +143 -124
- package/dist/lib/exe-daemon.js +151 -130
- package/dist/lib/tasks.js +143 -124
- package/dist/lib/tmux-routing.js +143 -124
- package/dist/mcp/server.js +82 -63
- package/dist/mcp/tools/create-task.js +143 -124
- package/dist/mcp/tools/update-task.js +143 -124
- package/dist/runtime/index.js +143 -124
- package/dist/tui/App.js +143 -124
- package/package.json +4 -4
package/dist/bin/cli.js
CHANGED
|
@@ -9664,6 +9664,110 @@ var init_session_kill_telemetry = __esm({
|
|
|
9664
9664
|
}
|
|
9665
9665
|
});
|
|
9666
9666
|
|
|
9667
|
+
// src/lib/project-name.ts
|
|
9668
|
+
import { execSync as execSync6 } from "child_process";
|
|
9669
|
+
import path23 from "path";
|
|
9670
|
+
function getProjectName(cwd2) {
|
|
9671
|
+
const dir = cwd2 ?? process.cwd();
|
|
9672
|
+
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
9673
|
+
try {
|
|
9674
|
+
let repoRoot;
|
|
9675
|
+
try {
|
|
9676
|
+
const gitCommonDir = execSync6("git rev-parse --path-format=absolute --git-common-dir", {
|
|
9677
|
+
cwd: dir,
|
|
9678
|
+
encoding: "utf8",
|
|
9679
|
+
timeout: 2e3,
|
|
9680
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
9681
|
+
}).trim();
|
|
9682
|
+
repoRoot = path23.dirname(gitCommonDir);
|
|
9683
|
+
} catch {
|
|
9684
|
+
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
9685
|
+
cwd: dir,
|
|
9686
|
+
encoding: "utf8",
|
|
9687
|
+
timeout: 2e3,
|
|
9688
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
9689
|
+
}).trim();
|
|
9690
|
+
}
|
|
9691
|
+
_cached2 = path23.basename(repoRoot);
|
|
9692
|
+
_cachedCwd = dir;
|
|
9693
|
+
return _cached2;
|
|
9694
|
+
} catch {
|
|
9695
|
+
_cached2 = path23.basename(dir);
|
|
9696
|
+
_cachedCwd = dir;
|
|
9697
|
+
return _cached2;
|
|
9698
|
+
}
|
|
9699
|
+
}
|
|
9700
|
+
var _cached2, _cachedCwd;
|
|
9701
|
+
var init_project_name = __esm({
|
|
9702
|
+
"src/lib/project-name.ts"() {
|
|
9703
|
+
"use strict";
|
|
9704
|
+
_cached2 = null;
|
|
9705
|
+
_cachedCwd = null;
|
|
9706
|
+
}
|
|
9707
|
+
});
|
|
9708
|
+
|
|
9709
|
+
// src/lib/session-scope.ts
|
|
9710
|
+
var session_scope_exports = {};
|
|
9711
|
+
__export(session_scope_exports, {
|
|
9712
|
+
assertSessionScope: () => assertSessionScope,
|
|
9713
|
+
findSessionForProject: () => findSessionForProject,
|
|
9714
|
+
getSessionProject: () => getSessionProject
|
|
9715
|
+
});
|
|
9716
|
+
function getSessionProject(sessionName) {
|
|
9717
|
+
const sessions = listSessions();
|
|
9718
|
+
const entry = sessions.find((s) => s.windowName === sessionName);
|
|
9719
|
+
if (!entry) return null;
|
|
9720
|
+
const parts = entry.projectDir.split("/").filter(Boolean);
|
|
9721
|
+
return parts[parts.length - 1] ?? null;
|
|
9722
|
+
}
|
|
9723
|
+
function findSessionForProject(projectName) {
|
|
9724
|
+
const sessions = listSessions();
|
|
9725
|
+
for (const s of sessions) {
|
|
9726
|
+
const proj = s.projectDir.split("/").filter(Boolean).pop();
|
|
9727
|
+
if (proj === projectName && isCoordinatorName(s.agentId)) return s;
|
|
9728
|
+
}
|
|
9729
|
+
return null;
|
|
9730
|
+
}
|
|
9731
|
+
function assertSessionScope(actionType, targetProject) {
|
|
9732
|
+
try {
|
|
9733
|
+
const currentProject = getProjectName();
|
|
9734
|
+
const exeSession = resolveExeSession();
|
|
9735
|
+
if (!exeSession) {
|
|
9736
|
+
return { allowed: true, reason: "no_session" };
|
|
9737
|
+
}
|
|
9738
|
+
if (currentProject === targetProject) {
|
|
9739
|
+
return {
|
|
9740
|
+
allowed: true,
|
|
9741
|
+
reason: "same_session",
|
|
9742
|
+
currentProject,
|
|
9743
|
+
targetProject
|
|
9744
|
+
};
|
|
9745
|
+
}
|
|
9746
|
+
process.stderr.write(
|
|
9747
|
+
`[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
|
|
9748
|
+
`
|
|
9749
|
+
);
|
|
9750
|
+
return {
|
|
9751
|
+
allowed: false,
|
|
9752
|
+
reason: "cross_session_denied",
|
|
9753
|
+
currentProject,
|
|
9754
|
+
targetProject,
|
|
9755
|
+
targetSession: findSessionForProject(targetProject)?.windowName
|
|
9756
|
+
};
|
|
9757
|
+
} catch {
|
|
9758
|
+
return { allowed: true, reason: "no_session" };
|
|
9759
|
+
}
|
|
9760
|
+
}
|
|
9761
|
+
var init_session_scope = __esm({
|
|
9762
|
+
"src/lib/session-scope.ts"() {
|
|
9763
|
+
"use strict";
|
|
9764
|
+
init_session_registry();
|
|
9765
|
+
init_project_name();
|
|
9766
|
+
init_tmux_routing();
|
|
9767
|
+
init_employees();
|
|
9768
|
+
}
|
|
9769
|
+
});
|
|
9770
|
+
|
|
9667
9771
|
// src/lib/tasks-crud.ts
|
|
9668
9772
|
var tasks_crud_exports = {};
|
|
9669
9773
|
__export(tasks_crud_exports, {
|
|
@@ -9682,9 +9786,9 @@ __export(tasks_crud_exports, {
|
|
|
9682
9786
|
writeCheckpoint: () => writeCheckpoint
|
|
9683
9787
|
});
|
|
9684
9788
|
import crypto8 from "crypto";
|
|
9685
|
-
import
|
|
9789
|
+
import path24 from "path";
|
|
9686
9790
|
import os14 from "os";
|
|
9687
|
-
import { execSync as
|
|
9791
|
+
import { execSync as execSync7 } from "child_process";
|
|
9688
9792
|
import { mkdir as mkdir5, writeFile as writeFile5, appendFile } from "fs/promises";
|
|
9689
9793
|
import { existsSync as existsSync21, readFileSync as readFileSync18 } from "fs";
|
|
9690
9794
|
async function writeCheckpoint(input) {
|
|
@@ -9806,9 +9910,24 @@ async function createTaskCore(input) {
|
|
|
9806
9910
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9807
9911
|
const slug = slugify(input.title);
|
|
9808
9912
|
let earlySessionScope = null;
|
|
9913
|
+
let scopeMismatchWarning;
|
|
9809
9914
|
try {
|
|
9810
9915
|
const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
|
|
9811
|
-
|
|
9916
|
+
const resolved = resolveExeSession2();
|
|
9917
|
+
if (resolved && input.projectName) {
|
|
9918
|
+
const { getSessionProject: getSessionProject2 } = await Promise.resolve().then(() => (init_session_scope(), session_scope_exports));
|
|
9919
|
+
const sessionProject = getSessionProject2(resolved);
|
|
9920
|
+
if (sessionProject && sessionProject !== input.projectName) {
|
|
9921
|
+
scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
|
|
9922
|
+
process.stderr.write(`[create_task] ${scopeMismatchWarning}
|
|
9923
|
+
`);
|
|
9924
|
+
earlySessionScope = null;
|
|
9925
|
+
} else {
|
|
9926
|
+
earlySessionScope = resolved;
|
|
9927
|
+
}
|
|
9928
|
+
} else {
|
|
9929
|
+
earlySessionScope = resolved;
|
|
9930
|
+
}
|
|
9812
9931
|
} catch {
|
|
9813
9932
|
}
|
|
9814
9933
|
const scope = earlySessionScope ?? "default";
|
|
@@ -9859,10 +9978,14 @@ async function createTaskCore(input) {
|
|
|
9859
9978
|
${laneWarning}` : laneWarning;
|
|
9860
9979
|
}
|
|
9861
9980
|
}
|
|
9981
|
+
if (scopeMismatchWarning) {
|
|
9982
|
+
warning = warning ? `${warning}
|
|
9983
|
+
${scopeMismatchWarning}` : scopeMismatchWarning;
|
|
9984
|
+
}
|
|
9862
9985
|
if (input.baseDir) {
|
|
9863
9986
|
try {
|
|
9864
|
-
await mkdir5(
|
|
9865
|
-
await mkdir5(
|
|
9987
|
+
await mkdir5(path24.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
9988
|
+
await mkdir5(path24.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
9866
9989
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
9867
9990
|
await ensureGitignoreExe(input.baseDir);
|
|
9868
9991
|
} catch {
|
|
@@ -9898,9 +10021,9 @@ ${laneWarning}` : laneWarning;
|
|
|
9898
10021
|
});
|
|
9899
10022
|
if (input.baseDir) {
|
|
9900
10023
|
try {
|
|
9901
|
-
const EXE_OS_DIR =
|
|
9902
|
-
const mdPath =
|
|
9903
|
-
const mdDir =
|
|
10024
|
+
const EXE_OS_DIR = path24.join(os14.homedir(), ".exe-os");
|
|
10025
|
+
const mdPath = path24.join(EXE_OS_DIR, taskFile);
|
|
10026
|
+
const mdDir = path24.dirname(mdPath);
|
|
9904
10027
|
if (!existsSync21(mdDir)) await mkdir5(mdDir, { recursive: true });
|
|
9905
10028
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
9906
10029
|
const mdContent = `# ${input.title}
|
|
@@ -10005,14 +10128,14 @@ function isTmuxSessionAlive(identifier) {
|
|
|
10005
10128
|
if (!identifier || identifier === "unknown") return true;
|
|
10006
10129
|
try {
|
|
10007
10130
|
if (identifier.startsWith("%")) {
|
|
10008
|
-
const output =
|
|
10131
|
+
const output = execSync7("tmux list-panes -a -F '#{pane_id}'", {
|
|
10009
10132
|
timeout: 2e3,
|
|
10010
10133
|
encoding: "utf8",
|
|
10011
10134
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10012
10135
|
});
|
|
10013
10136
|
return output.split("\n").some((l) => l.trim() === identifier);
|
|
10014
10137
|
} else {
|
|
10015
|
-
|
|
10138
|
+
execSync7(`tmux has-session -t ${JSON.stringify(identifier)}`, {
|
|
10016
10139
|
timeout: 2e3,
|
|
10017
10140
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10018
10141
|
});
|
|
@@ -10021,7 +10144,7 @@ function isTmuxSessionAlive(identifier) {
|
|
|
10021
10144
|
} catch {
|
|
10022
10145
|
if (identifier.startsWith("%")) return true;
|
|
10023
10146
|
try {
|
|
10024
|
-
|
|
10147
|
+
execSync7("tmux list-sessions", {
|
|
10025
10148
|
timeout: 2e3,
|
|
10026
10149
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10027
10150
|
});
|
|
@@ -10036,12 +10159,12 @@ function checkStaleCompletion(taskContext, taskCreatedAt) {
|
|
|
10036
10159
|
if (!DELEGATION_KEYWORDS.test(taskContext)) return null;
|
|
10037
10160
|
try {
|
|
10038
10161
|
const since = new Date(taskCreatedAt).toISOString();
|
|
10039
|
-
const branch =
|
|
10162
|
+
const branch = execSync7(
|
|
10040
10163
|
"git rev-parse --abbrev-ref HEAD 2>/dev/null",
|
|
10041
10164
|
{ encoding: "utf8", timeout: 3e3 }
|
|
10042
10165
|
).trim();
|
|
10043
10166
|
const branchArg = branch && branch !== "HEAD" ? branch : "";
|
|
10044
|
-
const commitCount =
|
|
10167
|
+
const commitCount = execSync7(
|
|
10045
10168
|
`git log --oneline --since="${since}" ${branchArg} 2>/dev/null | wc -l`,
|
|
10046
10169
|
{ encoding: "utf8", timeout: 5e3 }
|
|
10047
10170
|
).trim();
|
|
@@ -10201,7 +10324,7 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
10201
10324
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
10202
10325
|
}
|
|
10203
10326
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
10204
|
-
const archPath =
|
|
10327
|
+
const archPath = path24.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
10205
10328
|
try {
|
|
10206
10329
|
if (existsSync21(archPath)) return;
|
|
10207
10330
|
const template = [
|
|
@@ -10236,7 +10359,7 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
10236
10359
|
}
|
|
10237
10360
|
}
|
|
10238
10361
|
async function ensureGitignoreExe(baseDir) {
|
|
10239
|
-
const gitignorePath =
|
|
10362
|
+
const gitignorePath = path24.join(baseDir, ".gitignore");
|
|
10240
10363
|
try {
|
|
10241
10364
|
if (existsSync21(gitignorePath)) {
|
|
10242
10365
|
const content = readFileSync18(gitignorePath, "utf-8");
|
|
@@ -10270,7 +10393,7 @@ var init_tasks_crud = __esm({
|
|
|
10270
10393
|
});
|
|
10271
10394
|
|
|
10272
10395
|
// src/lib/tasks-review.ts
|
|
10273
|
-
import
|
|
10396
|
+
import path25 from "path";
|
|
10274
10397
|
import { existsSync as existsSync22, readdirSync as readdirSync5, unlinkSync as unlinkSync6 } from "fs";
|
|
10275
10398
|
async function countPendingReviews(sessionScope) {
|
|
10276
10399
|
const client = getClient();
|
|
@@ -10436,11 +10559,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
10436
10559
|
);
|
|
10437
10560
|
}
|
|
10438
10561
|
try {
|
|
10439
|
-
const cacheDir =
|
|
10562
|
+
const cacheDir = path25.join(EXE_AI_DIR, "session-cache");
|
|
10440
10563
|
if (existsSync22(cacheDir)) {
|
|
10441
10564
|
for (const f of readdirSync5(cacheDir)) {
|
|
10442
10565
|
if (f.startsWith("review-notified-")) {
|
|
10443
|
-
unlinkSync6(
|
|
10566
|
+
unlinkSync6(path25.join(cacheDir, f));
|
|
10444
10567
|
}
|
|
10445
10568
|
}
|
|
10446
10569
|
}
|
|
@@ -10462,7 +10585,7 @@ var init_tasks_review = __esm({
|
|
|
10462
10585
|
});
|
|
10463
10586
|
|
|
10464
10587
|
// src/lib/tasks-chain.ts
|
|
10465
|
-
import
|
|
10588
|
+
import path26 from "path";
|
|
10466
10589
|
import { readFile as readFile5, writeFile as writeFile6 } from "fs/promises";
|
|
10467
10590
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
10468
10591
|
const client = getClient();
|
|
@@ -10479,7 +10602,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
10479
10602
|
});
|
|
10480
10603
|
for (const ur of unblockedRows.rows) {
|
|
10481
10604
|
try {
|
|
10482
|
-
const ubFile =
|
|
10605
|
+
const ubFile = path26.join(baseDir, String(ur.task_file));
|
|
10483
10606
|
let ubContent = await readFile5(ubFile, "utf-8");
|
|
10484
10607
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
10485
10608
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -10546,110 +10669,6 @@ var init_tasks_chain = __esm({
|
|
|
10546
10669
|
}
|
|
10547
10670
|
});
|
|
10548
10671
|
|
|
10549
|
-
// src/lib/project-name.ts
|
|
10550
|
-
import { execSync as execSync7 } from "child_process";
|
|
10551
|
-
import path26 from "path";
|
|
10552
|
-
function getProjectName(cwd2) {
|
|
10553
|
-
const dir = cwd2 ?? process.cwd();
|
|
10554
|
-
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
10555
|
-
try {
|
|
10556
|
-
let repoRoot;
|
|
10557
|
-
try {
|
|
10558
|
-
const gitCommonDir = execSync7("git rev-parse --path-format=absolute --git-common-dir", {
|
|
10559
|
-
cwd: dir,
|
|
10560
|
-
encoding: "utf8",
|
|
10561
|
-
timeout: 2e3,
|
|
10562
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
10563
|
-
}).trim();
|
|
10564
|
-
repoRoot = path26.dirname(gitCommonDir);
|
|
10565
|
-
} catch {
|
|
10566
|
-
repoRoot = execSync7("git rev-parse --show-toplevel", {
|
|
10567
|
-
cwd: dir,
|
|
10568
|
-
encoding: "utf8",
|
|
10569
|
-
timeout: 2e3,
|
|
10570
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
10571
|
-
}).trim();
|
|
10572
|
-
}
|
|
10573
|
-
_cached2 = path26.basename(repoRoot);
|
|
10574
|
-
_cachedCwd = dir;
|
|
10575
|
-
return _cached2;
|
|
10576
|
-
} catch {
|
|
10577
|
-
_cached2 = path26.basename(dir);
|
|
10578
|
-
_cachedCwd = dir;
|
|
10579
|
-
return _cached2;
|
|
10580
|
-
}
|
|
10581
|
-
}
|
|
10582
|
-
var _cached2, _cachedCwd;
|
|
10583
|
-
var init_project_name = __esm({
|
|
10584
|
-
"src/lib/project-name.ts"() {
|
|
10585
|
-
"use strict";
|
|
10586
|
-
_cached2 = null;
|
|
10587
|
-
_cachedCwd = null;
|
|
10588
|
-
}
|
|
10589
|
-
});
|
|
10590
|
-
|
|
10591
|
-
// src/lib/session-scope.ts
|
|
10592
|
-
var session_scope_exports = {};
|
|
10593
|
-
__export(session_scope_exports, {
|
|
10594
|
-
assertSessionScope: () => assertSessionScope,
|
|
10595
|
-
findSessionForProject: () => findSessionForProject,
|
|
10596
|
-
getSessionProject: () => getSessionProject
|
|
10597
|
-
});
|
|
10598
|
-
function getSessionProject(sessionName) {
|
|
10599
|
-
const sessions = listSessions();
|
|
10600
|
-
const entry = sessions.find((s) => s.windowName === sessionName);
|
|
10601
|
-
if (!entry) return null;
|
|
10602
|
-
const parts = entry.projectDir.split("/").filter(Boolean);
|
|
10603
|
-
return parts[parts.length - 1] ?? null;
|
|
10604
|
-
}
|
|
10605
|
-
function findSessionForProject(projectName) {
|
|
10606
|
-
const sessions = listSessions();
|
|
10607
|
-
for (const s of sessions) {
|
|
10608
|
-
const proj = s.projectDir.split("/").filter(Boolean).pop();
|
|
10609
|
-
if (proj === projectName && isCoordinatorName(s.agentId)) return s;
|
|
10610
|
-
}
|
|
10611
|
-
return null;
|
|
10612
|
-
}
|
|
10613
|
-
function assertSessionScope(actionType, targetProject) {
|
|
10614
|
-
try {
|
|
10615
|
-
const currentProject = getProjectName();
|
|
10616
|
-
const exeSession = resolveExeSession();
|
|
10617
|
-
if (!exeSession) {
|
|
10618
|
-
return { allowed: true, reason: "no_session" };
|
|
10619
|
-
}
|
|
10620
|
-
if (currentProject === targetProject) {
|
|
10621
|
-
return {
|
|
10622
|
-
allowed: true,
|
|
10623
|
-
reason: "same_session",
|
|
10624
|
-
currentProject,
|
|
10625
|
-
targetProject
|
|
10626
|
-
};
|
|
10627
|
-
}
|
|
10628
|
-
process.stderr.write(
|
|
10629
|
-
`[session-scope] BLOCKED cross-project ${actionType}: session project="${currentProject}" \u2260 target project="${targetProject}"
|
|
10630
|
-
`
|
|
10631
|
-
);
|
|
10632
|
-
return {
|
|
10633
|
-
allowed: false,
|
|
10634
|
-
reason: "cross_session_denied",
|
|
10635
|
-
currentProject,
|
|
10636
|
-
targetProject,
|
|
10637
|
-
targetSession: findSessionForProject(targetProject)?.windowName
|
|
10638
|
-
};
|
|
10639
|
-
} catch {
|
|
10640
|
-
return { allowed: true, reason: "no_session" };
|
|
10641
|
-
}
|
|
10642
|
-
}
|
|
10643
|
-
var init_session_scope = __esm({
|
|
10644
|
-
"src/lib/session-scope.ts"() {
|
|
10645
|
-
"use strict";
|
|
10646
|
-
init_session_registry();
|
|
10647
|
-
init_project_name();
|
|
10648
|
-
init_tmux_routing();
|
|
10649
|
-
init_employees();
|
|
10650
|
-
}
|
|
10651
|
-
});
|
|
10652
|
-
|
|
10653
10672
|
// src/lib/tasks-notify.ts
|
|
10654
10673
|
async function dispatchTaskToEmployee(input) {
|
|
10655
10674
|
if (isCoordinatorName(input.assignedTo)) return { dispatched: "skipped" };
|