@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 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 path23 from "path";
9789
+ import path24 from "path";
9686
9790
  import os14 from "os";
9687
- import { execSync as execSync6 } from "child_process";
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
- earlySessionScope = resolveExeSession2();
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(path23.join(input.baseDir, "exe", "output"), { recursive: true });
9865
- await mkdir5(path23.join(input.baseDir, "exe", "research"), { recursive: true });
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 = path23.join(os14.homedir(), ".exe-os");
9902
- const mdPath = path23.join(EXE_OS_DIR, taskFile);
9903
- const mdDir = path23.dirname(mdPath);
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 = execSync6("tmux list-panes -a -F '#{pane_id}'", {
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
- execSync6(`tmux has-session -t ${JSON.stringify(identifier)}`, {
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
- execSync6("tmux list-sessions", {
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 = execSync6(
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 = execSync6(
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 = path23.join(baseDir, "exe", "ARCHITECTURE.md");
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 = path23.join(baseDir, ".gitignore");
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 path24 from "path";
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 = path24.join(EXE_AI_DIR, "session-cache");
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(path24.join(cacheDir, f));
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 path25 from "path";
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 = path25.join(baseDir, String(ur.task_file));
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" };