@askexenow/exe-os 0.9.84 → 0.9.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/cli.js CHANGED
@@ -1241,6 +1241,7 @@ async function registerMcpServer(packageRoot, homeDir = os6.homedir()) {
1241
1241
  `
1242
1242
  );
1243
1243
  }
1244
+ await registerExeRustMcpConfig(packageRoot, homeDir);
1244
1245
  return false;
1245
1246
  }
1246
1247
  claudeJson.mcpServers[MCP_PRIMARY_KEY] = newEntry;
@@ -1254,8 +1255,39 @@ async function registerMcpServer(packageRoot, homeDir = os6.homedir()) {
1254
1255
  `
1255
1256
  );
1256
1257
  }
1258
+ await registerExeRustMcpConfig(packageRoot, homeDir);
1257
1259
  return true;
1258
1260
  }
1261
+ async function registerExeRustMcpConfig(packageRoot, homeDir) {
1262
+ const exeDir = path7.join(homeDir, ".exe");
1263
+ const exeMcpPath = path7.join(exeDir, "mcp.json");
1264
+ await mkdir3(exeDir, { recursive: true });
1265
+ let config = { servers: {} };
1266
+ if (existsSync8(exeMcpPath)) {
1267
+ try {
1268
+ config = JSON.parse(await readFile3(exeMcpPath, "utf-8"));
1269
+ if (!config.servers) config.servers = {};
1270
+ } catch {
1271
+ config = { servers: {} };
1272
+ }
1273
+ }
1274
+ const stdioEntry = {
1275
+ command: "node",
1276
+ args: [path7.join(packageRoot, "dist", "mcp", "server.js")],
1277
+ env: {},
1278
+ shared: true
1279
+ };
1280
+ const existing = config.servers[MCP_PRIMARY_KEY];
1281
+ if (existing && JSON.stringify(existing) === JSON.stringify(stdioEntry)) {
1282
+ return;
1283
+ }
1284
+ if (config.servers[MCP_LEGACY_KEY]) {
1285
+ delete config.servers[MCP_LEGACY_KEY];
1286
+ }
1287
+ config.servers[MCP_PRIMARY_KEY] = stdioEntry;
1288
+ await writeFile3(exeMcpPath, JSON.stringify(config, null, 2) + "\n");
1289
+ process.stderr.write("exe-os: registered MCP server in ~/.exe/mcp.json (Rust runtime)\n");
1290
+ }
1259
1291
  async function cleanSettingsJsonMcp(settingsPath) {
1260
1292
  if (!existsSync8(settingsPath)) return;
1261
1293
  try {
@@ -20113,12 +20145,25 @@ function assertHostReadyForApply(report) {
20113
20145
  if (blockers.length > 0) throw new Error(`Stack host is not ready:
20114
20146
  - ${blockers.join("\n- ")}`);
20115
20147
  }
20148
+ function areStackContainersRunning(composeFile, envFile) {
20149
+ try {
20150
+ const result = spawnSync("docker", ["compose", "--file", composeFile, "--env-file", envFile, "ps", "-q"], {
20151
+ stdio: ["pipe", "pipe", "pipe"],
20152
+ timeout: 15e3
20153
+ });
20154
+ return result.status === 0 && (result.stdout?.toString().trim().length ?? 0) > 0;
20155
+ } catch {
20156
+ return false;
20157
+ }
20158
+ }
20116
20159
  async function runStackUpdate(options) {
20117
20160
  const exec2 = options.exec ?? defaultExec;
20118
20161
  const now2 = options.now ?? (() => /* @__PURE__ */ new Date());
20119
20162
  if (options.rollback) return rollbackStackUpdate(options);
20120
- const report = bootstrapStackHost({ ...options, installDocker: options.installDocker ?? !!options.yes });
20121
- if (!options.dryRun) assertHostReadyForApply(report);
20163
+ if (options.bootstrap !== false) {
20164
+ const report = bootstrapStackHost({ ...options, installDocker: options.installDocker ?? !!options.yes });
20165
+ if (!options.dryRun) assertHostReadyForApply(report);
20166
+ }
20122
20167
  const manifest = await loadStackManifest(options.manifestRef, options.fetchText, options.manifestPublicKey, options.manifestAuthToken);
20123
20168
  const envRaw = readFileSync29(options.envFile, "utf8");
20124
20169
  const plan = createStackUpdatePlan(manifest, envRaw, options.targetVersion);
@@ -20134,7 +20179,8 @@ async function runStackUpdate(options) {
20134
20179
  });
20135
20180
  const lockFile = options.lockFile ?? path42.join(path42.dirname(options.envFile), ".exe-stack-lock.json");
20136
20181
  const previousVersion = readCurrentStackVersion(lockFile);
20137
- if (options.dryRun || plan.changes.length === 0) {
20182
+ const containersRunning = plan.changes.length === 0 ? areStackContainersRunning(options.composeFile, options.envFile) : true;
20183
+ if (options.dryRun || plan.changes.length === 0 && containersRunning) {
20138
20184
  return { status: "planned", targetVersion: plan.targetVersion, changes: plan.changes, lockFile };
20139
20185
  }
20140
20186
  await postDeployAudit(options, "started", plan.targetVersion, previousVersion);
@@ -20322,6 +20368,7 @@ __export(stack_update_exports, {
20322
20368
  runStackUpdateCli: () => main7
20323
20369
  });
20324
20370
  import { readFileSync as readFileSync30 } from "fs";
20371
+ import { spawnSync as spawnSync2 } from "child_process";
20325
20372
  function parseArgs4(args2) {
20326
20373
  const defaults = defaultStackPaths();
20327
20374
  const opts = {
@@ -20431,9 +20478,14 @@ Options:
20431
20478
  -y, --yes Non-interactive confirmation
20432
20479
  `);
20433
20480
  }
20434
- function printChanges(changes) {
20481
+ function printChanges(changes, composeFile, envFile) {
20435
20482
  if (changes.length === 0) {
20436
- console.log("\u2705 Stack already matches target manifest.");
20483
+ const running = areCliContainersRunning(composeFile, envFile);
20484
+ if (running) {
20485
+ console.log("\u2705 Stack already matches target manifest.");
20486
+ } else {
20487
+ console.log("\u26A0\uFE0F Stack .env matches target manifest but containers are not running. Will start them.");
20488
+ }
20437
20489
  return;
20438
20490
  }
20439
20491
  console.log("Planned image tag changes:");
@@ -20442,6 +20494,17 @@ function printChanges(changes) {
20442
20494
  console.log(` ${c.before ?? "<unset>"} \u2192 ${c.after}`);
20443
20495
  }
20444
20496
  }
20497
+ function areCliContainersRunning(composeFile, envFile) {
20498
+ try {
20499
+ const result = spawnSync2("docker", ["compose", "--file", composeFile, "--env-file", envFile, "ps", "-q"], {
20500
+ stdio: ["pipe", "pipe", "pipe"],
20501
+ timeout: 15e3
20502
+ });
20503
+ return result.status === 0 && (result.stdout?.toString().trim().length ?? 0) > 0;
20504
+ } catch {
20505
+ return false;
20506
+ }
20507
+ }
20445
20508
  function printBreaking(changes) {
20446
20509
  if (changes.length === 0) return;
20447
20510
  console.log("\nBreaking-change notices:");
@@ -20496,7 +20559,7 @@ async function main7(args2 = process.argv.slice(2)) {
20496
20559
  console.log(`Compose: ${opts.composeFile}`);
20497
20560
  console.log(`Env: ${opts.envFile}
20498
20561
  `);
20499
- printChanges(plan.changes);
20562
+ printChanges(plan.changes, opts.composeFile, opts.envFile);
20500
20563
  printBreaking(plan.breakingChanges);
20501
20564
  if (opts.check || opts.dryRun) return;
20502
20565
  if (!opts.yes) {
@@ -35061,7 +35124,7 @@ __export(code_context_index_exports, {
35061
35124
  import crypto14 from "crypto";
35062
35125
  import path51 from "path";
35063
35126
  import { existsSync as existsSync36, mkdirSync as mkdirSync25, readFileSync as readFileSync32, readdirSync as readdirSync11, statSync as statSync7, writeFileSync as writeFileSync26 } from "fs";
35064
- import { spawnSync as spawnSync2 } from "child_process";
35127
+ import { spawnSync as spawnSync3 } from "child_process";
35065
35128
  function normalizeProjectRoot(projectRoot) {
35066
35129
  return path51.resolve(projectRoot || process.cwd());
35067
35130
  }
@@ -35079,7 +35142,7 @@ function getCodeContextIndexPath(projectRoot) {
35079
35142
  return path51.join(indexDir(), `${rootHash}.json`);
35080
35143
  }
35081
35144
  function currentBranch(projectRoot) {
35082
- const result = spawnSync2("git", ["branch", "--show-current"], { cwd: projectRoot, encoding: "utf8", timeout: 2e3 });
35145
+ const result = spawnSync3("git", ["branch", "--show-current"], { cwd: projectRoot, encoding: "utf8", timeout: 2e3 });
35083
35146
  const branch = result.status === 0 ? result.stdout.trim() : "";
35084
35147
  return branch || "detached-or-unknown";
35085
35148
  }
@@ -35098,12 +35161,12 @@ function listRecursive(projectRoot, dir = projectRoot, out = []) {
35098
35161
  return out;
35099
35162
  }
35100
35163
  function listCodeFiles(projectRoot, maxFiles) {
35101
- const git = spawnSync2("git", ["ls-files"], { cwd: projectRoot, encoding: "utf8", timeout: 5e3, maxBuffer: 1024 * 1024 * 16 });
35164
+ const git = spawnSync3("git", ["ls-files"], { cwd: projectRoot, encoding: "utf8", timeout: 5e3, maxBuffer: 1024 * 1024 * 16 });
35102
35165
  let files = [];
35103
35166
  if (git.status === 0 && git.stdout.trim()) {
35104
35167
  files = git.stdout.split("\n").map((s) => s.trim()).filter(Boolean);
35105
35168
  } else {
35106
- const rg = spawnSync2("rg", ["--files"], { cwd: projectRoot, encoding: "utf8", timeout: 5e3, maxBuffer: 1024 * 1024 * 16 });
35169
+ const rg = spawnSync3("rg", ["--files"], { cwd: projectRoot, encoding: "utf8", timeout: 5e3, maxBuffer: 1024 * 1024 * 16 });
35107
35170
  files = rg.status === 0 && rg.stdout.trim() ? rg.stdout.split("\n").map((s) => s.trim()).filter(Boolean) : listRecursive(projectRoot);
35108
35171
  }
35109
35172
  return files.map((file) => file.replaceAll(path51.sep, "/")).filter((file) => isChunkable(file) && !shouldIgnore(file)).slice(0, maxFiles).sort();
@@ -3332,8 +3332,8 @@ function deriveMachineKey() {
3332
3332
  }
3333
3333
  function readMachineId() {
3334
3334
  try {
3335
- const { readFileSync: readFileSync7 } = __require("fs");
3336
- return readFileSync7("/etc/machine-id", "utf-8").trim();
3335
+ const { readFileSync: readFileSync8 } = __require("fs");
3336
+ return readFileSync8("/etc/machine-id", "utf-8").trim();
3337
3337
  } catch {
3338
3338
  return "";
3339
3339
  }
@@ -5473,6 +5473,68 @@ var init_store = __esm({
5473
5473
  }
5474
5474
  });
5475
5475
 
5476
+ // src/lib/runtime-table.ts
5477
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
5478
+ var init_runtime_table = __esm({
5479
+ "src/lib/runtime-table.ts"() {
5480
+ "use strict";
5481
+ RUNTIME_TABLE = {
5482
+ codex: {
5483
+ binary: "codex",
5484
+ launchMode: "interactive",
5485
+ autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
5486
+ inlineFlag: "--no-alt-screen",
5487
+ apiKeyEnv: "OPENAI_API_KEY",
5488
+ defaultModel: "gpt-5.5"
5489
+ },
5490
+ opencode: {
5491
+ binary: "opencode",
5492
+ launchMode: "exec",
5493
+ autoApproveFlag: "--dangerously-skip-permissions",
5494
+ inlineFlag: "",
5495
+ apiKeyEnv: "ANTHROPIC_API_KEY",
5496
+ defaultModel: "anthropic/claude-sonnet-4-6"
5497
+ }
5498
+ };
5499
+ DEFAULT_RUNTIME = "claude";
5500
+ }
5501
+ });
5502
+
5503
+ // src/lib/agent-config.ts
5504
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync9 } from "fs";
5505
+ import path9 from "path";
5506
+ function loadAgentConfig() {
5507
+ if (!existsSync9(AGENT_CONFIG_PATH)) return {};
5508
+ try {
5509
+ return JSON.parse(readFileSync5(AGENT_CONFIG_PATH, "utf-8"));
5510
+ } catch {
5511
+ return {};
5512
+ }
5513
+ }
5514
+ function getAgentRuntime(agentId) {
5515
+ const config = loadAgentConfig();
5516
+ const entry = config[agentId];
5517
+ if (entry) return entry;
5518
+ const orgDefault = config["default"];
5519
+ if (orgDefault) return orgDefault;
5520
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
5521
+ }
5522
+ var AGENT_CONFIG_PATH, DEFAULT_MODELS;
5523
+ var init_agent_config = __esm({
5524
+ "src/lib/agent-config.ts"() {
5525
+ "use strict";
5526
+ init_config();
5527
+ init_runtime_table();
5528
+ init_secure_files();
5529
+ AGENT_CONFIG_PATH = path9.join(EXE_AI_DIR, "agent-config.json");
5530
+ DEFAULT_MODELS = {
5531
+ claude: "claude-opus-4.6",
5532
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
5533
+ opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
5534
+ };
5535
+ }
5536
+ });
5537
+
5476
5538
  // src/lib/session-key.ts
5477
5539
  import { execSync as execSync4 } from "child_process";
5478
5540
  function normalizeCommand(command) {
@@ -5564,9 +5626,9 @@ __export(active_agent_exports, {
5564
5626
  resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
5565
5627
  writeActiveAgent: () => writeActiveAgent
5566
5628
  });
5567
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, unlinkSync as unlinkSync4, readdirSync as readdirSync3 } from "fs";
5629
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, unlinkSync as unlinkSync4, readdirSync as readdirSync3 } from "fs";
5568
5630
  import { execSync as execSync5 } from "child_process";
5569
- import path9 from "path";
5631
+ import path10 from "path";
5570
5632
  function isNameWithOptionalInstance(candidate, baseName) {
5571
5633
  if (candidate === baseName) return true;
5572
5634
  if (!candidate.startsWith(baseName)) return false;
@@ -5610,12 +5672,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
5610
5672
  return null;
5611
5673
  }
5612
5674
  function getMarkerPath() {
5613
- return path9.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
5675
+ return path10.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
5614
5676
  }
5615
5677
  function writeActiveAgent(agentId, agentRole) {
5616
5678
  try {
5617
5679
  mkdirSync4(CACHE_DIR, { recursive: true });
5618
- writeFileSync4(
5680
+ writeFileSync5(
5619
5681
  getMarkerPath(),
5620
5682
  JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
5621
5683
  );
@@ -5633,7 +5695,7 @@ function getActiveAgent() {
5633
5695
  if (httpCtx) return httpCtx;
5634
5696
  try {
5635
5697
  const markerPath = getMarkerPath();
5636
- const raw = readFileSync5(markerPath, "utf8");
5698
+ const raw = readFileSync6(markerPath, "utf8");
5637
5699
  const data = JSON.parse(raw);
5638
5700
  if (data.agentId) {
5639
5701
  if (data.startedAt) {
@@ -5681,14 +5743,14 @@ function getAllActiveAgents() {
5681
5743
  const key = file.slice("active-agent-".length, -".json".length);
5682
5744
  if (key === "undefined") continue;
5683
5745
  try {
5684
- const raw = readFileSync5(path9.join(CACHE_DIR, file), "utf8");
5746
+ const raw = readFileSync6(path10.join(CACHE_DIR, file), "utf8");
5685
5747
  const data = JSON.parse(raw);
5686
5748
  if (!data.agentId) continue;
5687
5749
  if (data.startedAt) {
5688
5750
  const age = Date.now() - new Date(data.startedAt).getTime();
5689
5751
  if (age > STALE_MS) {
5690
5752
  try {
5691
- unlinkSync4(path9.join(CACHE_DIR, file));
5753
+ unlinkSync4(path10.join(CACHE_DIR, file));
5692
5754
  } catch {
5693
5755
  }
5694
5756
  continue;
@@ -5711,11 +5773,11 @@ function getAllActiveAgents() {
5711
5773
  function cleanupSessionMarkers() {
5712
5774
  const key = getSessionKey();
5713
5775
  try {
5714
- unlinkSync4(path9.join(CACHE_DIR, `active-agent-${key}.json`));
5776
+ unlinkSync4(path10.join(CACHE_DIR, `active-agent-${key}.json`));
5715
5777
  } catch {
5716
5778
  }
5717
5779
  try {
5718
- unlinkSync4(path9.join(CACHE_DIR, "active-agent-undefined.json"));
5780
+ unlinkSync4(path10.join(CACHE_DIR, "active-agent-undefined.json"));
5719
5781
  } catch {
5720
5782
  }
5721
5783
  }
@@ -5727,7 +5789,7 @@ var init_active_agent = __esm({
5727
5789
  init_session_key();
5728
5790
  init_agent_context();
5729
5791
  init_employees();
5730
- CACHE_DIR = path9.join(EXE_AI_DIR, "session-cache");
5792
+ CACHE_DIR = path10.join(EXE_AI_DIR, "session-cache");
5731
5793
  STALE_MS = 24 * 60 * 60 * 1e3;
5732
5794
  }
5733
5795
  });
@@ -5735,8 +5797,8 @@ var init_active_agent = __esm({
5735
5797
  // src/bin/exe-launch-agent.ts
5736
5798
  init_store();
5737
5799
  import os7 from "os";
5738
- import path10 from "path";
5739
- import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, readdirSync as readdirSync4 } from "fs";
5800
+ import path11 from "path";
5801
+ import { existsSync as existsSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, readdirSync as readdirSync4 } from "fs";
5740
5802
  import { spawnSync } from "child_process";
5741
5803
 
5742
5804
  // src/bin/fast-db-init.ts
@@ -6108,6 +6170,7 @@ var PROVIDER_TABLE = {
6108
6170
  var DEFAULT_PROVIDER = "default";
6109
6171
 
6110
6172
  // src/bin/exe-launch-agent.ts
6173
+ init_agent_config();
6111
6174
  function getKnownAgents() {
6112
6175
  try {
6113
6176
  return loadEmployeesSync().map((e) => e.name);
@@ -6125,7 +6188,7 @@ function parseBasename(basename) {
6125
6188
  return { agent, provider };
6126
6189
  }
6127
6190
  function resolveAgent(argv) {
6128
- const invokedAs = path10.basename(argv[1] ?? "");
6191
+ const invokedAs = path11.basename(argv[1] ?? "");
6129
6192
  if (invokedAs && invokedAs !== "exe-launch-agent" && !invokedAs.endsWith(".js")) {
6130
6193
  const { agent: agent2, provider } = parseBasename(invokedAs.toLowerCase());
6131
6194
  return { agent: agent2, provider, passthrough: argv.slice(2) };
@@ -6154,13 +6217,13 @@ async function isKnownAgent(agent) {
6154
6217
  }
6155
6218
  }
6156
6219
  function identityPathFor(agent) {
6157
- const dir = path10.join(os7.homedir(), ".exe-os", "identity");
6158
- const exactPath = path10.join(dir, `${agent}.md`);
6159
- if (existsSync9(exactPath)) return exactPath;
6220
+ const dir = path11.join(os7.homedir(), ".exe-os", "identity");
6221
+ const exactPath = path11.join(dir, `${agent}.md`);
6222
+ if (existsSync10(exactPath)) return exactPath;
6160
6223
  try {
6161
6224
  const files = readdirSync4(dir);
6162
6225
  const match = files.find((f) => f.toLowerCase() === `${agent.toLowerCase()}.md`);
6163
- if (match) return path10.join(dir, match);
6226
+ if (match) return path11.join(dir, match);
6164
6227
  } catch {
6165
6228
  }
6166
6229
  return exactPath;
@@ -6182,13 +6245,13 @@ function leanMcpEnabled() {
6182
6245
  function collectAllMcpServers() {
6183
6246
  const servers = {};
6184
6247
  const sources = [
6185
- path10.join(os7.homedir(), ".claude.json"),
6186
- path10.join(os7.homedir(), ".claude", "settings.json")
6248
+ path11.join(os7.homedir(), ".claude.json"),
6249
+ path11.join(os7.homedir(), ".claude", "settings.json")
6187
6250
  ];
6188
6251
  for (const src of sources) {
6189
6252
  try {
6190
- if (!existsSync9(src)) continue;
6191
- const data = JSON.parse(readFileSync6(src, "utf-8"));
6253
+ if (!existsSync10(src)) continue;
6254
+ const data = JSON.parse(readFileSync7(src, "utf-8"));
6192
6255
  const block = data.mcpServers;
6193
6256
  if (!block) continue;
6194
6257
  for (const [name, cfg] of Object.entries(block)) {
@@ -6214,18 +6277,18 @@ function generateLeanMcpConfig(agent, role) {
6214
6277
  }
6215
6278
  if (Object.keys(leanServers).length >= Object.keys(allServers).length) return null;
6216
6279
  if (!leanServers["exe-mem"]) {
6217
- const packageRoot = path10.resolve(path10.dirname(new URL(import.meta.url).pathname), "..", "..");
6280
+ const packageRoot = path11.resolve(path11.dirname(new URL(import.meta.url).pathname), "..", "..");
6218
6281
  leanServers["exe-mem"] = {
6219
6282
  type: "stdio",
6220
6283
  command: "node",
6221
- args: [path10.join(packageRoot, "dist", "mcp", "server.js")],
6284
+ args: [path11.join(packageRoot, "dist", "mcp", "server.js")],
6222
6285
  env: {}
6223
6286
  };
6224
6287
  }
6225
- const configDir = path10.join(os7.homedir(), ".exe-os", "mcp-configs");
6288
+ const configDir = path11.join(os7.homedir(), ".exe-os", "mcp-configs");
6226
6289
  mkdirSync5(configDir, { recursive: true });
6227
- const configPath = path10.join(configDir, `${agent}-lean.json`);
6228
- writeFileSync5(configPath, JSON.stringify({ mcpServers: leanServers }, null, 2), "utf-8");
6290
+ const configPath = path11.join(configDir, `${agent}-lean.json`);
6291
+ writeFileSync6(configPath, JSON.stringify({ mcpServers: leanServers }, null, 2), "utf-8");
6229
6292
  const saved = Object.keys(allServers).length - Object.keys(leanServers).length;
6230
6293
  if (saved > 0) {
6231
6294
  process.stderr.write(
@@ -6244,8 +6307,8 @@ function generateLeanMcpConfig(agent, role) {
6244
6307
  }
6245
6308
  function leanMcpConfigFor(agent) {
6246
6309
  if (!leanMcpEnabled()) return null;
6247
- const p = path10.join(os7.homedir(), ".exe-os", "mcp-configs", `${agent}-lean.json`);
6248
- return existsSync9(p) ? p : null;
6310
+ const p = path11.join(os7.homedir(), ".exe-os", "mcp-configs", `${agent}-lean.json`);
6311
+ return existsSync10(p) ? p : null;
6249
6312
  }
6250
6313
  var _ccHelpOutput = null;
6251
6314
  function getCcHelpOutput() {
@@ -6268,39 +6331,39 @@ function _resetCcHelpCache() {
6268
6331
  function buildLaunchPlan(agent, behaviorsPath, passthrough, _hasAgentFlag, _provider) {
6269
6332
  const args = ["--dangerously-skip-permissions"];
6270
6333
  const idPath = identityPathFor(agent);
6271
- const ccAgentPath = path10.join(os7.homedir(), ".claude", "agents", `${agent}.md`);
6334
+ const ccAgentPath = path11.join(os7.homedir(), ".claude", "agents", `${agent}.md`);
6272
6335
  let effectiveCcPath = null;
6273
- if (existsSync9(ccAgentPath)) {
6336
+ if (existsSync10(ccAgentPath)) {
6274
6337
  effectiveCcPath = ccAgentPath;
6275
6338
  } else {
6276
6339
  try {
6277
- const ccAgentDir = path10.join(os7.homedir(), ".claude", "agents");
6340
+ const ccAgentDir = path11.join(os7.homedir(), ".claude", "agents");
6278
6341
  const ccFiles = readdirSync4(ccAgentDir);
6279
6342
  const ccMatch = ccFiles.find((f) => f.toLowerCase() === `${agent.toLowerCase()}.md`);
6280
- if (ccMatch) effectiveCcPath = path10.join(ccAgentDir, ccMatch);
6343
+ if (ccMatch) effectiveCcPath = path11.join(ccAgentDir, ccMatch);
6281
6344
  } catch {
6282
6345
  }
6283
6346
  }
6284
- const effectiveIdPath = existsSync9(idPath) ? idPath : effectiveCcPath;
6347
+ const effectiveIdPath = existsSync10(idPath) ? idPath : effectiveCcPath;
6285
6348
  let identityContent = null;
6286
- if (effectiveIdPath && existsSync9(effectiveIdPath)) {
6349
+ if (effectiveIdPath && existsSync10(effectiveIdPath)) {
6287
6350
  try {
6288
- const content = readFileSync6(effectiveIdPath, "utf-8");
6351
+ const content = readFileSync7(effectiveIdPath, "utf-8");
6289
6352
  if (content.trim().length > 0) identityContent = content;
6290
6353
  } catch {
6291
6354
  }
6292
6355
  }
6293
6356
  if (!identityContent) {
6294
6357
  try {
6295
- const rosterPath = path10.join(os7.homedir(), ".exe-os", "exe-employees.json");
6296
- if (existsSync9(rosterPath)) {
6297
- const roster = JSON.parse(readFileSync6(rosterPath, "utf8"));
6358
+ const rosterPath = path11.join(os7.homedir(), ".exe-os", "exe-employees.json");
6359
+ if (existsSync10(rosterPath)) {
6360
+ const roster = JSON.parse(readFileSync7(rosterPath, "utf8"));
6298
6361
  const emp = roster.find((e) => e.name.toLowerCase() === agent.toLowerCase());
6299
6362
  if (emp?.systemPrompt && emp.systemPrompt.trim().length > 20) {
6300
6363
  identityContent = emp.systemPrompt;
6301
6364
  try {
6302
- const dir = path10.dirname(idPath);
6303
- if (!existsSync9(dir)) mkdirSync5(dir, { recursive: true });
6365
+ const dir = path11.dirname(idPath);
6366
+ if (!existsSync10(dir)) mkdirSync5(dir, { recursive: true });
6304
6367
  const hasFrontmatter = identityContent.trimStart().startsWith("---");
6305
6368
  const fileContent = hasFrontmatter ? identityContent : `---
6306
6369
  role: ${(emp.role ?? "employee").toLowerCase()}
@@ -6312,7 +6375,7 @@ updated_at: ${(/* @__PURE__ */ new Date()).toISOString()}
6312
6375
  ---
6313
6376
 
6314
6377
  ${identityContent}`;
6315
- writeFileSync5(idPath, fileContent, "utf-8");
6378
+ writeFileSync6(idPath, fileContent, "utf-8");
6316
6379
  identityContent = fileContent;
6317
6380
  process.stderr.write(`[exe-launch-agent] self-healed missing identity file: ${idPath}
6318
6381
  `);
@@ -6340,15 +6403,15 @@ ${identityContent}`;
6340
6403
  args.push("--system-prompt", getSessionPrompt(identityContent));
6341
6404
  } else {
6342
6405
  try {
6343
- const tmpPath = path10.join(os7.homedir(), ".exe-os", "session-cache", `${agent}-identity.md`);
6344
- mkdirSync5(path10.dirname(tmpPath), { recursive: true });
6345
- writeFileSync5(tmpPath, identityContent, "utf-8");
6406
+ const tmpPath = path11.join(os7.homedir(), ".exe-os", "session-cache", `${agent}-identity.md`);
6407
+ mkdirSync5(path11.dirname(tmpPath), { recursive: true });
6408
+ writeFileSync6(tmpPath, identityContent, "utf-8");
6346
6409
  args.push("--append-system-prompt-file", tmpPath);
6347
6410
  } catch {
6348
6411
  }
6349
6412
  }
6350
6413
  }
6351
- if (behaviorsPath && existsSync9(behaviorsPath)) {
6414
+ if (behaviorsPath && existsSync10(behaviorsPath)) {
6352
6415
  args.push("--append-system-prompt-file", behaviorsPath);
6353
6416
  }
6354
6417
  const leanMcp = leanMcpConfigFor(agent);
@@ -6440,28 +6503,28 @@ async function main() {
6440
6503
  _resetCcAgentSupportCache();
6441
6504
  const hasAgentFlag = claudeSupportsAgentFlag();
6442
6505
  if (hasAgentFlag) {
6443
- const ccAgentDir = path10.join(os7.homedir(), ".claude", "agents");
6444
- const ccAgentFile = path10.join(ccAgentDir, `${agent}.md`);
6445
- if (!existsSync9(ccAgentFile)) {
6506
+ const ccAgentDir = path11.join(os7.homedir(), ".claude", "agents");
6507
+ const ccAgentFile = path11.join(ccAgentDir, `${agent}.md`);
6508
+ if (!existsSync10(ccAgentFile)) {
6446
6509
  const exeIdentity = identityPathFor(agent);
6447
6510
  let sourceFile = null;
6448
- if (existsSync9(exeIdentity)) {
6511
+ if (existsSync10(exeIdentity)) {
6449
6512
  sourceFile = exeIdentity;
6450
6513
  } else {
6451
6514
  try {
6452
- const identityDir = path10.dirname(exeIdentity);
6515
+ const identityDir = path11.dirname(exeIdentity);
6453
6516
  const files = readdirSync4(identityDir);
6454
6517
  const match = files.find((f) => f.toLowerCase() === `${agent.toLowerCase()}.md`);
6455
- if (match) sourceFile = path10.join(identityDir, match);
6518
+ if (match) sourceFile = path11.join(identityDir, match);
6456
6519
  } catch {
6457
6520
  }
6458
6521
  }
6459
6522
  if (sourceFile) {
6460
6523
  try {
6461
6524
  mkdirSync5(ccAgentDir, { recursive: true });
6462
- let content = readFileSync6(sourceFile, "utf-8");
6525
+ let content = readFileSync7(sourceFile, "utf-8");
6463
6526
  content = content.replace(/\$\{agent_id\}/g, baseAgentName(agent));
6464
- writeFileSync5(ccAgentFile, content, "utf-8");
6527
+ writeFileSync6(ccAgentFile, content, "utf-8");
6465
6528
  process.stderr.write(
6466
6529
  `[exe-launch-agent] auto-provisioned ${ccAgentFile} from ${sourceFile}
6467
6530
  `
@@ -6474,8 +6537,8 @@ async function main() {
6474
6537
  const memoryAgent = baseAgentName(agent);
6475
6538
  const empRole = (() => {
6476
6539
  try {
6477
- const emps = readFileSync6(
6478
- path10.join(os7.homedir(), ".exe-os", "exe-employees.json"),
6540
+ const emps = readFileSync7(
6541
+ path11.join(os7.homedir(), ".exe-os", "exe-employees.json"),
6479
6542
  "utf-8"
6480
6543
  );
6481
6544
  const found = JSON.parse(emps).find(
@@ -6504,6 +6567,16 @@ async function main() {
6504
6567
  if (!process.env.CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING) {
6505
6568
  process.env.CLAUDE_CODE_DISABLE_ADAPTIVE_THINKING = "1";
6506
6569
  }
6570
+ if (provider === DEFAULT_PROVIDER) {
6571
+ const rtConfig = getAgentRuntime(memoryAgent);
6572
+ if (rtConfig.runtime === "claude" && rtConfig.model) {
6573
+ let ccModel = rtConfig.model.replace(/(\d+)\.(\d+)/g, "$1-$2");
6574
+ if (/claude-(opus|sonnet)-4-[6-9]/.test(ccModel) && !ccModel.includes("[1m]")) {
6575
+ ccModel += "[1m]";
6576
+ }
6577
+ process.env.ANTHROPIC_MODEL = ccModel;
6578
+ }
6579
+ }
6507
6580
  const child = spawnSync(plan.command, plan.args, { stdio: "inherit" });
6508
6581
  if (child.error) {
6509
6582
  const err = child.error;
@@ -1662,6 +1662,7 @@ async function registerMcpServer(packageRoot, homeDir = os8.homedir()) {
1662
1662
  `
1663
1663
  );
1664
1664
  }
1665
+ await registerExeRustMcpConfig(packageRoot, homeDir);
1665
1666
  return false;
1666
1667
  }
1667
1668
  claudeJson.mcpServers[MCP_PRIMARY_KEY] = newEntry;
@@ -1675,8 +1676,39 @@ async function registerMcpServer(packageRoot, homeDir = os8.homedir()) {
1675
1676
  `
1676
1677
  );
1677
1678
  }
1679
+ await registerExeRustMcpConfig(packageRoot, homeDir);
1678
1680
  return true;
1679
1681
  }
1682
+ async function registerExeRustMcpConfig(packageRoot, homeDir) {
1683
+ const exeDir = path12.join(homeDir, ".exe");
1684
+ const exeMcpPath = path12.join(exeDir, "mcp.json");
1685
+ await mkdir3(exeDir, { recursive: true });
1686
+ let config = { servers: {} };
1687
+ if (existsSync12(exeMcpPath)) {
1688
+ try {
1689
+ config = JSON.parse(await readFile3(exeMcpPath, "utf-8"));
1690
+ if (!config.servers) config.servers = {};
1691
+ } catch {
1692
+ config = { servers: {} };
1693
+ }
1694
+ }
1695
+ const stdioEntry = {
1696
+ command: "node",
1697
+ args: [path12.join(packageRoot, "dist", "mcp", "server.js")],
1698
+ env: {},
1699
+ shared: true
1700
+ };
1701
+ const existing = config.servers[MCP_PRIMARY_KEY];
1702
+ if (existing && JSON.stringify(existing) === JSON.stringify(stdioEntry)) {
1703
+ return;
1704
+ }
1705
+ if (config.servers[MCP_LEGACY_KEY]) {
1706
+ delete config.servers[MCP_LEGACY_KEY];
1707
+ }
1708
+ config.servers[MCP_PRIMARY_KEY] = stdioEntry;
1709
+ await writeFile3(exeMcpPath, JSON.stringify(config, null, 2) + "\n");
1710
+ process.stderr.write("exe-os: registered MCP server in ~/.exe/mcp.json (Rust runtime)\n");
1711
+ }
1680
1712
  async function cleanSettingsJsonMcp(settingsPath) {
1681
1713
  if (!existsSync12(settingsPath)) return;
1682
1714
  try {
@@ -991,6 +991,7 @@ async function registerMcpServer(packageRoot, homeDir = os6.homedir()) {
991
991
  `
992
992
  );
993
993
  }
994
+ await registerExeRustMcpConfig(packageRoot, homeDir);
994
995
  return false;
995
996
  }
996
997
  claudeJson.mcpServers[MCP_PRIMARY_KEY] = newEntry;
@@ -1004,8 +1005,39 @@ async function registerMcpServer(packageRoot, homeDir = os6.homedir()) {
1004
1005
  `
1005
1006
  );
1006
1007
  }
1008
+ await registerExeRustMcpConfig(packageRoot, homeDir);
1007
1009
  return true;
1008
1010
  }
1011
+ async function registerExeRustMcpConfig(packageRoot, homeDir) {
1012
+ const exeDir = path7.join(homeDir, ".exe");
1013
+ const exeMcpPath = path7.join(exeDir, "mcp.json");
1014
+ await mkdir3(exeDir, { recursive: true });
1015
+ let config = { servers: {} };
1016
+ if (existsSync8(exeMcpPath)) {
1017
+ try {
1018
+ config = JSON.parse(await readFile3(exeMcpPath, "utf-8"));
1019
+ if (!config.servers) config.servers = {};
1020
+ } catch {
1021
+ config = { servers: {} };
1022
+ }
1023
+ }
1024
+ const stdioEntry = {
1025
+ command: "node",
1026
+ args: [path7.join(packageRoot, "dist", "mcp", "server.js")],
1027
+ env: {},
1028
+ shared: true
1029
+ };
1030
+ const existing = config.servers[MCP_PRIMARY_KEY];
1031
+ if (existing && JSON.stringify(existing) === JSON.stringify(stdioEntry)) {
1032
+ return;
1033
+ }
1034
+ if (config.servers[MCP_LEGACY_KEY]) {
1035
+ delete config.servers[MCP_LEGACY_KEY];
1036
+ }
1037
+ config.servers[MCP_PRIMARY_KEY] = stdioEntry;
1038
+ await writeFile3(exeMcpPath, JSON.stringify(config, null, 2) + "\n");
1039
+ process.stderr.write("exe-os: registered MCP server in ~/.exe/mcp.json (Rust runtime)\n");
1040
+ }
1009
1041
  async function cleanSettingsJsonMcp(settingsPath) {
1010
1042
  if (!existsSync8(settingsPath)) return;
1011
1043
  try {
@@ -2,6 +2,7 @@
2
2
 
3
3
  // src/bin/stack-update.ts
4
4
  import { readFileSync as readFileSync4 } from "fs";
5
+ import { spawnSync as spawnSync2 } from "child_process";
5
6
 
6
7
  // src/lib/is-main.ts
7
8
  import { realpathSync } from "fs";
@@ -510,12 +511,25 @@ function assertHostReadyForApply(report) {
510
511
  if (blockers.length > 0) throw new Error(`Stack host is not ready:
511
512
  - ${blockers.join("\n- ")}`);
512
513
  }
514
+ function areStackContainersRunning(composeFile, envFile) {
515
+ try {
516
+ const result = spawnSync("docker", ["compose", "--file", composeFile, "--env-file", envFile, "ps", "-q"], {
517
+ stdio: ["pipe", "pipe", "pipe"],
518
+ timeout: 15e3
519
+ });
520
+ return result.status === 0 && (result.stdout?.toString().trim().length ?? 0) > 0;
521
+ } catch {
522
+ return false;
523
+ }
524
+ }
513
525
  async function runStackUpdate(options) {
514
526
  const exec = options.exec ?? defaultExec;
515
527
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
516
528
  if (options.rollback) return rollbackStackUpdate(options);
517
- const report = bootstrapStackHost({ ...options, installDocker: options.installDocker ?? !!options.yes });
518
- if (!options.dryRun) assertHostReadyForApply(report);
529
+ if (options.bootstrap !== false) {
530
+ const report = bootstrapStackHost({ ...options, installDocker: options.installDocker ?? !!options.yes });
531
+ if (!options.dryRun) assertHostReadyForApply(report);
532
+ }
519
533
  const manifest = await loadStackManifest(options.manifestRef, options.fetchText, options.manifestPublicKey, options.manifestAuthToken);
520
534
  const envRaw = readFileSync3(options.envFile, "utf8");
521
535
  const plan = createStackUpdatePlan(manifest, envRaw, options.targetVersion);
@@ -531,7 +545,8 @@ async function runStackUpdate(options) {
531
545
  });
532
546
  const lockFile = options.lockFile ?? path3.join(path3.dirname(options.envFile), ".exe-stack-lock.json");
533
547
  const previousVersion = readCurrentStackVersion(lockFile);
534
- if (options.dryRun || plan.changes.length === 0) {
548
+ const containersRunning = plan.changes.length === 0 ? areStackContainersRunning(options.composeFile, options.envFile) : true;
549
+ if (options.dryRun || plan.changes.length === 0 && containersRunning) {
535
550
  return { status: "planned", targetVersion: plan.targetVersion, changes: plan.changes, lockFile };
536
551
  }
537
552
  await postDeployAudit(options, "started", plan.targetVersion, previousVersion);
@@ -815,9 +830,14 @@ Options:
815
830
  -y, --yes Non-interactive confirmation
816
831
  `);
817
832
  }
818
- function printChanges(changes) {
833
+ function printChanges(changes, composeFile, envFile) {
819
834
  if (changes.length === 0) {
820
- console.log("\u2705 Stack already matches target manifest.");
835
+ const running = areCliContainersRunning(composeFile, envFile);
836
+ if (running) {
837
+ console.log("\u2705 Stack already matches target manifest.");
838
+ } else {
839
+ console.log("\u26A0\uFE0F Stack .env matches target manifest but containers are not running. Will start them.");
840
+ }
821
841
  return;
822
842
  }
823
843
  console.log("Planned image tag changes:");
@@ -826,6 +846,17 @@ function printChanges(changes) {
826
846
  console.log(` ${c.before ?? "<unset>"} \u2192 ${c.after}`);
827
847
  }
828
848
  }
849
+ function areCliContainersRunning(composeFile, envFile) {
850
+ try {
851
+ const result = spawnSync2("docker", ["compose", "--file", composeFile, "--env-file", envFile, "ps", "-q"], {
852
+ stdio: ["pipe", "pipe", "pipe"],
853
+ timeout: 15e3
854
+ });
855
+ return result.status === 0 && (result.stdout?.toString().trim().length ?? 0) > 0;
856
+ } catch {
857
+ return false;
858
+ }
859
+ }
829
860
  function printBreaking(changes) {
830
861
  if (changes.length === 0) return;
831
862
  console.log("\nBreaking-change notices:");
@@ -880,7 +911,7 @@ async function main(args = process.argv.slice(2)) {
880
911
  console.log(`Compose: ${opts.composeFile}`);
881
912
  console.log(`Env: ${opts.envFile}
882
913
  `);
883
- printChanges(plan.changes);
914
+ printChanges(plan.changes, opts.composeFile, opts.envFile);
884
915
  printBreaking(plan.breakingChanges);
885
916
  if (opts.check || opts.dryRun) return;
886
917
  if (!opts.yes) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askexenow/exe-os",
3
- "version": "0.9.84",
3
+ "version": "0.9.85",
4
4
  "description": "AI employee operating system — persistent memory, task management, and multi-agent coordination for Claude Code.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",