zam-core 0.10.8 → 0.10.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/cli/app.js CHANGED
@@ -892,9 +892,26 @@ function loadLibsql() {
892
892
  );
893
893
  }
894
894
  }
895
+ function isTransientRemoteDatabaseError(err) {
896
+ const message = err instanceof Error ? err.message : "";
897
+ return TRANSIENT_REMOTE_ERROR_PATTERNS.some(
898
+ (pattern) => pattern.test(message)
899
+ );
900
+ }
895
901
  async function openDatabase(options = {}) {
896
902
  const { dbPath, provider, isRemote, isEmbeddedReplica, configuredCloud } = resolveDatabaseTarget(options);
897
903
  const shouldInitialize = options.initialize === true || !isRemote && !isEmbeddedReplica && !existsSync2(dbPath);
904
+ const openViaHttpProvider = async (url) => {
905
+ const db = openRemoteDatabase({
906
+ url,
907
+ authToken: configuredCloud?.token ?? options.authToken
908
+ });
909
+ if (options.initialize) {
910
+ await db.exec(SCHEMA);
911
+ }
912
+ await runMigrations(db);
913
+ return db;
914
+ };
898
915
  if (provider === "remote") {
899
916
  const url = isRemote ? dbPath : options.syncUrl;
900
917
  if (!url) {
@@ -902,15 +919,7 @@ async function openDatabase(options = {}) {
902
919
  "The remote database provider is selected but no Turso URL is configured. Run: zam connector setup turso"
903
920
  );
904
921
  }
905
- const db2 = openRemoteDatabase({
906
- url,
907
- authToken: configuredCloud?.token ?? options.authToken
908
- });
909
- if (options.initialize) {
910
- await db2.exec(SCHEMA);
911
- }
912
- await runMigrations(db2);
913
- return db2;
922
+ return openViaHttpProvider(url);
914
923
  }
915
924
  if (shouldInitialize && !isRemote) {
916
925
  const dir = dirname2(dbPath);
@@ -956,39 +965,43 @@ async function openDatabase(options = {}) {
956
965
  }
957
966
  }
958
967
  } catch (nativeErr) {
959
- const fallbackUrl = isRemote ? dbPath : options.syncUrl;
960
- if (isRemote && !isEmbeddedReplica && fallbackUrl) {
961
- const db2 = openRemoteDatabase({
962
- url: fallbackUrl,
963
- authToken: configuredCloud?.token ?? options.authToken
964
- });
965
- if (options.initialize) {
966
- await db2.exec(SCHEMA);
967
- }
968
- await runMigrations(db2);
969
- return db2;
968
+ if (isRemote && !isEmbeddedReplica) {
969
+ return openViaHttpProvider(dbPath);
970
970
  }
971
971
  throw nativeErr;
972
972
  }
973
973
  } else {
974
974
  driver = openLocalSqlite(dbPath);
975
975
  }
976
- if (!isRemote && !isEmbeddedReplica) {
977
- driver.pragma("journal_mode = WAL");
978
- }
979
- driver.pragma("foreign_keys = ON");
980
- if (!isRemote) {
981
- driver.pragma("busy_timeout = 5000");
982
- }
983
- const db = wrapSyncDatabase(driver);
984
- if (isEmbeddedReplica) {
985
- await db.sync?.();
986
- }
987
- if (shouldInitialize) {
988
- await db.exec(SCHEMA);
976
+ const finishOpen = async () => {
977
+ if (!isRemote && !isEmbeddedReplica) {
978
+ driver.pragma("journal_mode = WAL");
979
+ }
980
+ driver.pragma("foreign_keys = ON");
981
+ if (!isRemote) {
982
+ driver.pragma("busy_timeout = 5000");
983
+ }
984
+ const db = wrapSyncDatabase(driver);
985
+ if (isEmbeddedReplica) {
986
+ await db.sync?.();
987
+ }
988
+ if (shouldInitialize) {
989
+ await db.exec(SCHEMA);
990
+ }
991
+ await runMigrations(db);
992
+ return db;
993
+ };
994
+ if (isRemote && !isEmbeddedReplica) {
995
+ try {
996
+ return await finishOpen();
997
+ } catch (err) {
998
+ if (isTransientRemoteDatabaseError(err)) {
999
+ return openViaHttpProvider(dbPath);
1000
+ }
1001
+ throw err;
1002
+ }
989
1003
  }
990
- await runMigrations(db);
991
- return db;
1004
+ return finishOpen();
992
1005
  }
993
1006
  function resolveProvider(options, credentialsMode, isRemote) {
994
1007
  if (options.provider) return options.provider;
@@ -1120,7 +1133,7 @@ async function runMigrations(db) {
1120
1133
  );
1121
1134
  }
1122
1135
  }
1123
- var DEFAULT_DB_DIR, DEFAULT_DB_PATH, require2;
1136
+ var DEFAULT_DB_DIR, DEFAULT_DB_PATH, require2, TRANSIENT_REMOTE_ERROR_PATTERNS;
1124
1137
  var init_connection = __esm({
1125
1138
  "src/kernel/db/connection.ts"() {
1126
1139
  "use strict";
@@ -1131,6 +1144,14 @@ var init_connection = __esm({
1131
1144
  DEFAULT_DB_DIR = join2(homedir2(), ".zam");
1132
1145
  DEFAULT_DB_PATH = join2(DEFAULT_DB_DIR, "zam.db");
1133
1146
  require2 = createRequire(import.meta.url);
1147
+ TRANSIENT_REMOTE_ERROR_PATTERNS = [
1148
+ /status=5\d\d/i,
1149
+ /websocket/i,
1150
+ /stream closed/i,
1151
+ /connection (?:closed|reset|refused)/i,
1152
+ /fetch failed/i,
1153
+ /\b(?:ETIMEDOUT|ENOTFOUND|ECONNREFUSED|ECONNRESET|EAI_AGAIN|EPIPE)\b/
1154
+ ];
1134
1155
  }
1135
1156
  });
1136
1157
 
@@ -6122,6 +6143,14 @@ function setAgentConnectAutoDone(done, path = defaultConfigPath()) {
6122
6143
  }
6123
6144
  saveInstallConfig(config, path);
6124
6145
  }
6146
+ function getLastRepairedVersion(path = defaultConfigPath()) {
6147
+ return loadInstallConfig(path).lastRepairedVersion;
6148
+ }
6149
+ function setLastRepairedVersion(version, path = defaultConfigPath()) {
6150
+ const config = loadInstallConfig(path);
6151
+ config.lastRepairedVersion = version;
6152
+ saveInstallConfig(config, path);
6153
+ }
6125
6154
  function getConfiguredWorkspaces(path = defaultConfigPath()) {
6126
6155
  return loadInstallConfig(path).workspaces ?? [];
6127
6156
  }
@@ -6844,6 +6873,7 @@ __export(kernel_exports, {
6844
6873
  getInstallMode: () => getInstallMode,
6845
6874
  getKnowledgeContextById: () => getKnowledgeContextById,
6846
6875
  getKnowledgeContextByName: () => getKnowledgeContextByName,
6876
+ getLastRepairedVersion: () => getLastRepairedVersion,
6847
6877
  getMachineAiConfig: () => getMachineAiConfig,
6848
6878
  getMachineAiModels: () => getMachineAiModels,
6849
6879
  getMonitorDir: () => getMonitorDir,
@@ -6939,6 +6969,7 @@ __export(kernel_exports, {
6939
6969
  setAgentConnectAutoDone: () => setAgentConnectAutoDone,
6940
6970
  setInstallChannel: () => setInstallChannel,
6941
6971
  setInstallMode: () => setInstallMode,
6972
+ setLastRepairedVersion: () => setLastRepairedVersion,
6942
6973
  setProviderApiKey: () => setProviderApiKey,
6943
6974
  setSetting: () => setSetting,
6944
6975
  setTursoCredentials: () => setTursoCredentials,
@@ -7015,8 +7046,8 @@ var init_kernel = __esm({
7015
7046
  });
7016
7047
 
7017
7048
  // src/cli/app.ts
7018
- import { readFileSync as readFileSync20 } from "fs";
7019
- import { dirname as dirname14, join as join29 } from "path";
7049
+ import { readFileSync as readFileSync21 } from "fs";
7050
+ import { dirname as dirname14, join as join30 } from "path";
7020
7051
  import { fileURLToPath as fileURLToPath8 } from "url";
7021
7052
  import { Command as Command27 } from "commander";
7022
7053
 
@@ -7435,6 +7466,7 @@ function detectInstalledConnectHarnesses(options = {}) {
7435
7466
  return detected;
7436
7467
  }
7437
7468
  function parseMcpJsonConfig(path, content) {
7469
+ if (content.trim() === "") return {};
7438
7470
  let parsed;
7439
7471
  try {
7440
7472
  parsed = JSON.parse(content);
@@ -7844,6 +7876,12 @@ function resolveVscodeExecutable(options = {}) {
7844
7876
  ] : ["/usr/bin/code", "/usr/local/bin/code", "/snap/bin/code"];
7845
7877
  return candidates.find((candidate) => exists(candidate)) ?? null;
7846
7878
  }
7879
+ function buildVscodeCliInvocation(command, args, platform = process.platform) {
7880
+ const needsShell = platform === "win32" && /\.(cmd|bat)$/i.test(command);
7881
+ if (!needsShell) return { command, args, shell: false };
7882
+ const quote = (value) => `"${value}"`;
7883
+ return { command: quote(command), args: args.map(quote), shell: true };
7884
+ }
7847
7885
  function packageVersion() {
7848
7886
  const parsed = JSON.parse(
7849
7887
  readFileSync11(join14(packageRoot2, "package.json"), "utf8")
@@ -7899,7 +7937,11 @@ function installVscodeExtension(options) {
7899
7937
  const existed = existsSync14(plan.launchConfigPath);
7900
7938
  const changed = !existed || readFileSync11(plan.launchConfigPath, "utf8") !== launchContent;
7901
7939
  const run = options.run ?? ((command, args) => {
7902
- execFileSync3(command, args, { stdio: "pipe" });
7940
+ const invocation = buildVscodeCliInvocation(command, args);
7941
+ execFileSync3(invocation.command, invocation.args, {
7942
+ stdio: "pipe",
7943
+ shell: invocation.shell
7944
+ });
7903
7945
  });
7904
7946
  run(plan.codePath, ["--install-extension", plan.vsixPath, "--force"]);
7905
7947
  if (changed) {
@@ -8308,11 +8350,11 @@ var agentCommand = new Command("agent").description("Provision and inspect the a
8308
8350
 
8309
8351
  // src/cli/commands/bridge.ts
8310
8352
  init_kernel();
8311
- import { execFileSync as execFileSync4 } from "child_process";
8353
+ import { execFileSync as execFileSync5 } from "child_process";
8312
8354
  import { randomBytes as randomBytes2 } from "crypto";
8313
- import { existsSync as existsSync19, readdirSync as readdirSync2, readFileSync as readFileSync16, rmSync as rmSync3 } from "fs";
8314
- import { homedir as homedir13, tmpdir as tmpdir3 } from "os";
8315
- import { join as join22, resolve as resolve5 } from "path";
8355
+ import { existsSync as existsSync21, readdirSync as readdirSync2, readFileSync as readFileSync17, rmSync as rmSync3 } from "fs";
8356
+ import { homedir as homedir14, tmpdir as tmpdir3 } from "os";
8357
+ import { join as join23, resolve as resolve6 } from "path";
8316
8358
  import { Command as Command2 } from "commander";
8317
8359
  import { ulid as ulid10 } from "ulid";
8318
8360
 
@@ -9399,7 +9441,7 @@ async function prepareRecallChain(db, opts) {
9399
9441
  } else {
9400
9442
  spawnLocalRunner(endpoint.url, endpoint.model, endpoint.runner);
9401
9443
  while (Date.now() < deadline) {
9402
- await new Promise((resolve11) => setTimeout(resolve11, 1e3));
9444
+ await new Promise((resolve12) => setTimeout(resolve12, 1e3));
9403
9445
  if (await isLlmOnline(endpoint.url)) {
9404
9446
  online = true;
9405
9447
  break;
@@ -9692,7 +9734,7 @@ async function startLocalRunner(url, model, locale, hint) {
9692
9734
  let attempts = 0;
9693
9735
  const dotsPerLine = 30;
9694
9736
  while (true) {
9695
- await new Promise((resolve11) => setTimeout(resolve11, 500));
9737
+ await new Promise((resolve12) => setTimeout(resolve12, 500));
9696
9738
  if (await isLlmOnline(url)) {
9697
9739
  if (attempts > 0) process.stdout.write("\n");
9698
9740
  return true;
@@ -9781,8 +9823,8 @@ async function fetchWithInteractiveTimeout(url, options = {}) {
9781
9823
  const dotsPerLine = 30;
9782
9824
  while (true) {
9783
9825
  let timeoutId;
9784
- const timeoutPromise = new Promise((resolve11) => {
9785
- timeoutId = setTimeout(() => resolve11("timeout"), timeoutMs);
9826
+ const timeoutPromise = new Promise((resolve12) => {
9827
+ timeoutId = setTimeout(() => resolve12("timeout"), timeoutMs);
9786
9828
  });
9787
9829
  const dotsInterval = setInterval(() => {
9788
9830
  process.stdout.write(".");
@@ -10049,7 +10091,7 @@ async function readWebLink(url) {
10049
10091
  redirect: "manual",
10050
10092
  signal: controller.signal,
10051
10093
  headers: {
10052
- "User-Agent": "ZAM-Content-Studio/0.10.8"
10094
+ "User-Agent": "ZAM-Content-Studio/0.10.10"
10053
10095
  }
10054
10096
  });
10055
10097
  if (res.status >= 300 && res.status < 400) {
@@ -10434,9 +10476,11 @@ function findWorkspaceByPath(dir) {
10434
10476
  );
10435
10477
  }
10436
10478
  async function clearLegacyWorkspaceDir(db) {
10479
+ if (!db) return;
10437
10480
  await deleteSetting(db, LEGACY_WORKSPACE_DIR_KEY);
10438
10481
  }
10439
10482
  async function migrateLegacyWorkspaceDir(db) {
10483
+ if (!db) return void 0;
10440
10484
  const legacyDir = await getSetting(db, LEGACY_WORKSPACE_DIR_KEY);
10441
10485
  if (!legacyDir) return void 0;
10442
10486
  const path = resolve4(legacyDir);
@@ -11312,6 +11356,127 @@ async function updateCheck(params) {
11312
11356
  });
11313
11357
  }
11314
11358
 
11359
+ // src/cli/cli-install.ts
11360
+ import { execFileSync as execFileSync4 } from "child_process";
11361
+ import {
11362
+ chmodSync as chmodSync2,
11363
+ existsSync as existsSync18,
11364
+ mkdirSync as mkdirSync13,
11365
+ readFileSync as readFileSync14,
11366
+ writeFileSync as writeFileSync11
11367
+ } from "fs";
11368
+ import { homedir as homedir13 } from "os";
11369
+ import { delimiter as delimiter2, join as join19, resolve as resolve5 } from "path";
11370
+ var BUILT_CLI_PATTERN = /[\\/]dist[\\/]cli[\\/]index\.js$/;
11371
+ function normalizeForCompare(path, platform) {
11372
+ const resolved = resolve5(path);
11373
+ return platform === "win32" ? resolved.toLowerCase() : resolved;
11374
+ }
11375
+ function windowsShimContent(nodePath, cliPath) {
11376
+ return `@echo off\r
11377
+ "${nodePath}" "${cliPath}" %*\r
11378
+ `;
11379
+ }
11380
+ function unixShimContent(nodePath, cliPath) {
11381
+ return `#!/bin/sh
11382
+ exec "${nodePath}" "${cliPath}" "$@"
11383
+ `;
11384
+ }
11385
+ function psQuote(value) {
11386
+ return `'${value.replace(/'/g, "''")}'`;
11387
+ }
11388
+ function ensureWindowsUserPath(binDir) {
11389
+ const script = [
11390
+ "$ErrorActionPreference = 'Stop'",
11391
+ "$key = [Microsoft.Win32.Registry]::CurrentUser.CreateSubKey('Environment', $true)",
11392
+ "$old = [string]$key.GetValue('Path', '', [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)",
11393
+ `$target = ${psQuote(binDir)}`,
11394
+ "$expanded = ($old -split ';') | Where-Object { $_ -ne '' } | ForEach-Object { [Environment]::ExpandEnvironmentVariables($_).TrimEnd('\\') }",
11395
+ "if ($expanded -contains $target.TrimEnd('\\')) { 'present'; exit 0 }",
11396
+ "$new = if ($old -and -not $old.EndsWith(';')) { $old + ';' + $target } else { $old + $target }",
11397
+ "$key.SetValue('Path', $new, [Microsoft.Win32.RegistryValueKind]::ExpandString)",
11398
+ `$sig = '[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam, uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);'`,
11399
+ "$type = Add-Type -MemberDefinition $sig -Name 'ZamEnvBroadcast' -Namespace 'ZamInstall' -PassThru",
11400
+ "[UIntPtr]$result = [UIntPtr]::Zero",
11401
+ "$null = $type::SendMessageTimeout([IntPtr]0xffff, 0x001A, [UIntPtr]::Zero, 'Environment', 2, 5000, [ref]$result)",
11402
+ "'updated'"
11403
+ ].join("; ");
11404
+ const output = execFileSync4(
11405
+ "powershell.exe",
11406
+ ["-NoProfile", "-NonInteractive", "-Command", script],
11407
+ { stdio: ["ignore", "pipe", "pipe"], windowsHide: true }
11408
+ ).toString().trim();
11409
+ return output.endsWith("updated");
11410
+ }
11411
+ function ensureUnixUserPath(home, platform) {
11412
+ const profile = join19(home, platform === "darwin" ? ".zprofile" : ".profile");
11413
+ const existing = existsSync18(profile) ? readFileSync14(profile, "utf8") : "";
11414
+ if (existing.includes(".zam/bin")) return false;
11415
+ const block = `
11416
+ # Added by ZAM: keep the zam CLI on PATH
11417
+ export PATH="$HOME/.zam/bin:$PATH"
11418
+ `;
11419
+ writeFileSync11(profile, existing + block, "utf8");
11420
+ return true;
11421
+ }
11422
+ function installCliShim(options = {}) {
11423
+ const home = options.home ?? homedir13();
11424
+ const platform = options.platform ?? process.platform;
11425
+ const env = options.env ?? process.env;
11426
+ const find = options.find ?? findExecutable;
11427
+ const nodePath = resolve5(options.nodePath ?? process.execPath);
11428
+ const cliPath = resolve5(options.cliPath ?? process.argv[1] ?? "");
11429
+ const binDir = join19(home, ".zam", "bin");
11430
+ const shimPath = join19(binDir, platform === "win32" ? "zam.cmd" : "zam");
11431
+ const report = {
11432
+ status: "ok",
11433
+ binDir,
11434
+ shimPath,
11435
+ nodePath,
11436
+ cliPath,
11437
+ onPath: false,
11438
+ pathUpdated: false,
11439
+ needsNewTerminal: false
11440
+ };
11441
+ if (!BUILT_CLI_PATTERN.test(cliPath) || !existsSync18(cliPath)) {
11442
+ report.status = "skipped";
11443
+ report.detail = `Not running from a built CLI entry (${cliPath}); nothing to link.`;
11444
+ return report;
11445
+ }
11446
+ try {
11447
+ const existing = find("zam");
11448
+ if (existing && normalizeForCompare(existing, platform) !== normalizeForCompare(shimPath, platform)) {
11449
+ report.status = "external";
11450
+ report.onPath = true;
11451
+ report.detail = existing;
11452
+ return report;
11453
+ }
11454
+ const content = platform === "win32" ? windowsShimContent(nodePath, cliPath) : unixShimContent(nodePath, cliPath);
11455
+ const existed = existsSync18(shimPath);
11456
+ const changed = !existed || readFileSync14(shimPath, "utf8") !== content;
11457
+ if (changed) {
11458
+ mkdirSync13(binDir, { recursive: true });
11459
+ writeFileSync11(shimPath, content, "utf8");
11460
+ if (platform !== "win32") chmodSync2(shimPath, 493);
11461
+ }
11462
+ report.status = !existed ? "installed" : changed ? "refreshed" : "ok";
11463
+ const processPathHasBin = (env.PATH ?? "").split(delimiter2).map((entry) => normalizeForCompare(entry.trim() || ".", platform)).includes(normalizeForCompare(binDir, platform));
11464
+ if (processPathHasBin) {
11465
+ report.onPath = true;
11466
+ return report;
11467
+ }
11468
+ const ensureUserPath = options.ensureUserPath ?? (platform === "win32" ? ensureWindowsUserPath : () => ensureUnixUserPath(home, platform));
11469
+ report.pathUpdated = ensureUserPath(binDir);
11470
+ report.onPath = true;
11471
+ report.needsNewTerminal = true;
11472
+ return report;
11473
+ } catch (error) {
11474
+ report.status = "error";
11475
+ report.detail = error instanceof Error ? error.message : String(error);
11476
+ return report;
11477
+ }
11478
+ }
11479
+
11315
11480
  // src/cli/curriculum/breadcrumb.ts
11316
11481
  init_kernel();
11317
11482
  var LAST_SELECTION_KEY = "curriculum.lastSelection";
@@ -27998,6 +28163,454 @@ function getCurriculumProvider(id) {
27998
28163
  return CURRICULUM_PROVIDERS.find((provider) => provider.id === id);
27999
28164
  }
28000
28165
 
28166
+ // src/cli/install-repair.ts
28167
+ init_kernel();
28168
+ import { existsSync as existsSync20 } from "fs";
28169
+
28170
+ // src/cli/provisioning/index.ts
28171
+ import {
28172
+ existsSync as existsSync19,
28173
+ lstatSync as lstatSync2,
28174
+ mkdirSync as mkdirSync14,
28175
+ readFileSync as readFileSync15,
28176
+ realpathSync,
28177
+ rmSync as rmSync2,
28178
+ symlinkSync,
28179
+ writeFileSync as writeFileSync12
28180
+ } from "fs";
28181
+ import { basename as basename4, dirname as dirname9, join as join20 } from "path";
28182
+ import { fileURLToPath as fileURLToPath5 } from "url";
28183
+ var packageRoot3 = [
28184
+ fileURLToPath5(new URL("../..", import.meta.url)),
28185
+ fileURLToPath5(new URL("../../..", import.meta.url))
28186
+ ].find((candidate) => existsSync19(join20(candidate, "package.json"))) ?? fileURLToPath5(new URL("../..", import.meta.url));
28187
+ var ALL_SETUP_AGENTS = ["claude", "copilot", "codex", "agent"];
28188
+ var SKILL_PAIRS = [
28189
+ {
28190
+ from: join20(packageRoot3, ".claude", "skills", "zam", "SKILL.md"),
28191
+ to: join20(".claude", "skills", "zam", "SKILL.md"),
28192
+ agents: ["claude", "copilot"]
28193
+ },
28194
+ {
28195
+ from: join20(packageRoot3, ".agent", "skills", "zam", "SKILL.md"),
28196
+ to: join20(".agent", "skills", "zam", "SKILL.md"),
28197
+ agents: ["agent"]
28198
+ },
28199
+ {
28200
+ from: join20(packageRoot3, ".agents", "skills", "zam", "SKILL.md"),
28201
+ to: join20(".agents", "skills", "zam", "SKILL.md"),
28202
+ agents: ["codex"]
28203
+ }
28204
+ ];
28205
+ function parseSetupAgents(value) {
28206
+ if (!value || value.trim().toLowerCase() === "all") {
28207
+ return new Set(ALL_SETUP_AGENTS);
28208
+ }
28209
+ const aliases = {
28210
+ claude: ["claude"],
28211
+ copilot: ["copilot"],
28212
+ codex: ["codex"],
28213
+ agent: ["agent"],
28214
+ opencode: ["agent"]
28215
+ };
28216
+ const selected = /* @__PURE__ */ new Set();
28217
+ for (const raw of value.split(",")) {
28218
+ const key = raw.trim().toLowerCase();
28219
+ const mapped = aliases[key];
28220
+ if (!mapped) {
28221
+ throw new Error(
28222
+ `Unknown agent "${raw}". Use: all, claude, copilot, codex, agent.`
28223
+ );
28224
+ }
28225
+ for (const item of mapped) selected.add(item);
28226
+ }
28227
+ return selected;
28228
+ }
28229
+ function pathExists(path) {
28230
+ try {
28231
+ lstatSync2(path);
28232
+ return true;
28233
+ } catch {
28234
+ return false;
28235
+ }
28236
+ }
28237
+ function isSymbolicLink(path) {
28238
+ try {
28239
+ return lstatSync2(path).isSymbolicLink();
28240
+ } catch {
28241
+ return false;
28242
+ }
28243
+ }
28244
+ function comparableRealPath(path) {
28245
+ const real = realpathSync(path);
28246
+ return process.platform === "win32" ? real.toLowerCase() : real;
28247
+ }
28248
+ function pathsResolveToSameDirectory(sourceDir, destinationDir) {
28249
+ try {
28250
+ return comparableRealPath(destinationDir) === comparableRealPath(sourceDir);
28251
+ } catch {
28252
+ return false;
28253
+ }
28254
+ }
28255
+ function linkPointsTo(sourceDir, destinationDir) {
28256
+ return isSymbolicLink(destinationDir) && pathsResolveToSameDirectory(sourceDir, destinationDir);
28257
+ }
28258
+ function isZamSkillCopy(destinationDir) {
28259
+ try {
28260
+ if (!lstatSync2(destinationDir).isDirectory()) return false;
28261
+ const skillFile = join20(destinationDir, "SKILL.md");
28262
+ if (!existsSync19(skillFile)) return false;
28263
+ return /^name:\s*zam\s*$/m.test(readFileSync15(skillFile, "utf8"));
28264
+ } catch {
28265
+ return false;
28266
+ }
28267
+ }
28268
+ function classifySkillDestination(sourceDir, destinationDir) {
28269
+ if (!existsSync19(sourceDir)) return "source-missing";
28270
+ if (!isSymbolicLink(destinationDir) && pathsResolveToSameDirectory(sourceDir, destinationDir)) {
28271
+ return "source-directory";
28272
+ }
28273
+ if (linkPointsTo(sourceDir, destinationDir)) return "linked";
28274
+ if (!pathExists(destinationDir)) return "missing";
28275
+ if (isSymbolicLink(destinationDir)) return "broken";
28276
+ if (isZamSkillCopy(destinationDir)) return "stale-copy";
28277
+ return "unmanaged";
28278
+ }
28279
+ function wireSkills(cwd = process.cwd(), agents = parseSetupAgents(), opts = {}) {
28280
+ const results = [];
28281
+ const log = (message) => {
28282
+ if (!opts.quiet) console.log(message);
28283
+ };
28284
+ for (const { from, to, agents: pairAgents } of SKILL_PAIRS) {
28285
+ if (!pairAgents.some((agent) => agents.has(agent))) continue;
28286
+ const sourceDir = dirname9(from);
28287
+ const destinationDir = dirname9(join20(cwd, to));
28288
+ const label = dirname9(to);
28289
+ const state = classifySkillDestination(sourceDir, destinationDir);
28290
+ if (state === "source-missing") {
28291
+ if (!opts.quiet) {
28292
+ console.warn(` warn source not found, skipping: ${sourceDir}`);
28293
+ }
28294
+ results.push({
28295
+ source: sourceDir,
28296
+ destination: destinationDir,
28297
+ action: "skipped",
28298
+ reason: "source-not-found"
28299
+ });
28300
+ continue;
28301
+ }
28302
+ if (state === "source-directory") {
28303
+ log(` skip ${label} (package source)`);
28304
+ results.push({
28305
+ source: sourceDir,
28306
+ destination: destinationDir,
28307
+ action: "skipped",
28308
+ reason: "source-directory"
28309
+ });
28310
+ continue;
28311
+ }
28312
+ if (state === "linked") {
28313
+ log(` skip ${label} (already linked)`);
28314
+ results.push({
28315
+ source: sourceDir,
28316
+ destination: destinationDir,
28317
+ action: "skipped",
28318
+ reason: "already-linked"
28319
+ });
28320
+ continue;
28321
+ }
28322
+ const destinationExists = state !== "missing";
28323
+ const replaceExisting = destinationExists && (Boolean(opts.force) || state === "broken" || state === "stale-copy");
28324
+ if (destinationExists && !replaceExisting) {
28325
+ if (!opts.quiet) {
28326
+ console.warn(
28327
+ ` warn ${label} already exists and is not managed by ZAM; use --force to replace it`
28328
+ );
28329
+ }
28330
+ results.push({
28331
+ source: sourceDir,
28332
+ destination: destinationDir,
28333
+ action: "skipped",
28334
+ reason: "unmanaged-destination"
28335
+ });
28336
+ continue;
28337
+ }
28338
+ const action = destinationExists ? "relinked" : "linked";
28339
+ if (opts.dryRun) {
28340
+ log(` would ${action === "linked" ? "link" : "relink"} ${label}`);
28341
+ } else {
28342
+ if (destinationExists) {
28343
+ rmSync2(destinationDir, { recursive: true, force: true });
28344
+ }
28345
+ mkdirSync14(dirname9(destinationDir), { recursive: true });
28346
+ symlinkSync(
28347
+ sourceDir,
28348
+ destinationDir,
28349
+ process.platform === "win32" ? "junction" : "dir"
28350
+ );
28351
+ log(` ${action === "linked" ? "link" : "relink"} ${label}`);
28352
+ }
28353
+ results.push({ source: sourceDir, destination: destinationDir, action });
28354
+ }
28355
+ return results;
28356
+ }
28357
+ function inspectSkillLinks(cwd = process.cwd(), agents = parseSetupAgents()) {
28358
+ const results = [];
28359
+ for (const { from, to, agents: pairAgents } of SKILL_PAIRS) {
28360
+ if (!pairAgents.some((agent) => agents.has(agent))) continue;
28361
+ const sourceDir = dirname9(from);
28362
+ const destinationDir = dirname9(join20(cwd, to));
28363
+ results.push({
28364
+ agents: pairAgents,
28365
+ source: sourceDir,
28366
+ destination: destinationDir,
28367
+ state: classifySkillDestination(sourceDir, destinationDir)
28368
+ });
28369
+ }
28370
+ return results;
28371
+ }
28372
+ function summarizeSkillLinkHealth(inspections) {
28373
+ if (inspections.some((item) => item.state === "unmanaged")) {
28374
+ return "unmanaged";
28375
+ }
28376
+ const repairable = ["missing", "broken", "stale-copy"];
28377
+ if (inspections.some((item) => repairable.includes(item.state))) {
28378
+ return "needs-repair";
28379
+ }
28380
+ return "healthy";
28381
+ }
28382
+ var ZAM_BLOCK_START = "<!-- ZAM:START -->";
28383
+ var ZAM_BLOCK_END = "<!-- ZAM:END -->";
28384
+ function upsertMarkedBlock(dest, blockBody, dryRun) {
28385
+ const block = `${ZAM_BLOCK_START}
28386
+ ${blockBody.trim()}
28387
+ ${ZAM_BLOCK_END}`;
28388
+ if (!existsSync19(dest)) {
28389
+ if (!dryRun) {
28390
+ mkdirSync14(dirname9(dest), { recursive: true });
28391
+ writeFileSync12(dest, `${block}
28392
+ `, "utf8");
28393
+ }
28394
+ return "write";
28395
+ }
28396
+ const existing = readFileSync15(dest, "utf8");
28397
+ if (existing.includes(block)) return "skip";
28398
+ const start = existing.indexOf(ZAM_BLOCK_START);
28399
+ const end = existing.indexOf(ZAM_BLOCK_END);
28400
+ const next = start >= 0 && end > start ? `${existing.slice(0, start)}${block}${existing.slice(end + ZAM_BLOCK_END.length)}` : `${existing.trimEnd()}
28401
+
28402
+ ${block}
28403
+ `;
28404
+ if (!dryRun) writeFileSync12(dest, next, "utf8");
28405
+ return start >= 0 && end > start ? "update" : "write";
28406
+ }
28407
+ function logInstructionAction(action, label, dryRun) {
28408
+ if (action === "skip") {
28409
+ console.log(` skip ${label} (ZAM block already present)`);
28410
+ } else {
28411
+ console.log(` ${dryRun ? "would " : ""}${action} ${label}`);
28412
+ }
28413
+ }
28414
+ function writeClaudeMd(skipClaudeMd, cwd = process.cwd(), opts = {}) {
28415
+ if (skipClaudeMd) return;
28416
+ const dest = join20(cwd, "CLAUDE.md");
28417
+ if (existsSync19(dest)) {
28418
+ if (!opts.updateExisting) {
28419
+ console.log(` skip CLAUDE.md (already present)`);
28420
+ return;
28421
+ }
28422
+ const action = upsertMarkedBlock(
28423
+ dest,
28424
+ `## ZAM learning sessions
28425
+
28426
+ ZAM is available in this repository. Use the \`zam\` skill in Claude Code to turn real work into an observed learning session with active recall and FSRS scheduling.
28427
+
28428
+ - Skill files live under \`.claude/skills/zam/\`.
28429
+ - Fast-changing review data lives in \`~/.zam/\`, not in this repository.
28430
+ - The skill directory is linked to this ZAM installation and updates with it.`,
28431
+ Boolean(opts.dryRun)
28432
+ );
28433
+ logInstructionAction(action, "CLAUDE.md", Boolean(opts.dryRun));
28434
+ return;
28435
+ }
28436
+ const name = basename4(cwd);
28437
+ const content = `# ZAM Personal Kernel \u2014 ${name}
28438
+
28439
+ This is a ZAM personal instance. ZAM builds lasting skills through spaced
28440
+ repetition during real work \u2014 not separate study sessions.
28441
+
28442
+ ## First time here?
28443
+ Run \`/setup\` in Claude Code or Gemini CLI to complete first-time setup.
28444
+
28445
+ ## Regular use
28446
+ Run \`/zam\` to start a learning session on whatever you are working on.
28447
+
28448
+ ## What lives here
28449
+ - \`beliefs/\` \u2014 your worldview, approved by git commit
28450
+ - \`goals/\` \u2014 your objectives, decomposed into tasks and learning tokens
28451
+
28452
+ ## Fast-changing data
28453
+ Learning tokens, cards, and review history live in local SQLite by default.
28454
+ Use \`zam connector setup turso\` to store cloud credentials in
28455
+ \`~/.zam/credentials.json\` and use a Turso database across machines.
28456
+ `;
28457
+ if (opts.dryRun) {
28458
+ console.log(` would write CLAUDE.md`);
28459
+ } else {
28460
+ writeFileSync12(dest, content, "utf8");
28461
+ console.log(` write CLAUDE.md`);
28462
+ }
28463
+ }
28464
+ function writeAgentsMd(skipAgentsMd, cwd = process.cwd(), opts = {}) {
28465
+ if (skipAgentsMd) return;
28466
+ const dest = join20(cwd, "AGENTS.md");
28467
+ if (existsSync19(dest)) {
28468
+ if (!opts.updateExisting) {
28469
+ console.log(` skip AGENTS.md (already present)`);
28470
+ return;
28471
+ }
28472
+ const action = upsertMarkedBlock(
28473
+ dest,
28474
+ `## ZAM learning sessions
28475
+
28476
+ ZAM is available in this repository. Select the \`zam\` skill through \`/skills\` or invoke \`$zam\` where supported to turn real work into an observed learning session with active recall and FSRS scheduling.
28477
+
28478
+ - Skill files live under \`.agents/skills/zam/\`.
28479
+ - Fast-changing review data lives in \`~/.zam/\`, not in this repository.
28480
+ - The skill directory is linked to this ZAM installation and updates with it.`,
28481
+ Boolean(opts.dryRun)
28482
+ );
28483
+ logInstructionAction(action, "AGENTS.md", Boolean(opts.dryRun));
28484
+ return;
28485
+ }
28486
+ const name = basename4(cwd);
28487
+ const content = `# ZAM Personal Kernel - ${name}
28488
+
28489
+ This is a ZAM personal instance. ZAM builds lasting skills through spaced
28490
+ repetition during real work, not separate study sessions.
28491
+
28492
+ ## First time here?
28493
+ Run \`zam setup\` from the shell. When this repository includes
28494
+ \`.agents/skills/setup/\`, you can instead select \`setup\` through \`/skills\`
28495
+ or invoke \`$setup\`.
28496
+
28497
+ ## Regular use
28498
+ Select the \`zam\` skill through \`/skills\` or invoke \`$zam\` to start a
28499
+ learning session on whatever you are working on.
28500
+
28501
+ ## What lives here
28502
+ - \`beliefs/\` - your worldview, approved by git commit
28503
+ - \`goals/\` - your objectives, decomposed into tasks and learning tokens
28504
+
28505
+ ## Fast-changing data
28506
+ Learning tokens, cards, and review history live in local SQLite by default.
28507
+ Use \`zam connector setup turso\` to store cloud credentials in
28508
+ \`~/.zam/credentials.json\` and use a Turso database across machines.
28509
+
28510
+ ## Codex skills
28511
+ Codex discovers repository skills under \`.agents/skills/\`. Run
28512
+ \`zam setup --force\` after upgrading \`zam-core\` to refresh them.
28513
+ `;
28514
+ if (opts.dryRun) {
28515
+ console.log(` would write AGENTS.md`);
28516
+ } else {
28517
+ writeFileSync12(dest, content, "utf8");
28518
+ console.log(` write AGENTS.md`);
28519
+ }
28520
+ }
28521
+ function writeCopilotInstructions(cwd = process.cwd(), opts = {}) {
28522
+ const dest = join20(cwd, ".github", "copilot-instructions.md");
28523
+ const action = upsertMarkedBlock(
28524
+ dest,
28525
+ `## ZAM learning sessions
28526
+
28527
+ ZAM is available in this repository through \`.claude/skills/zam/\`. Use the \`zam\` project skill from Copilot-compatible skill selection surfaces to turn real work into an observed learning session with active recall and FSRS scheduling.
28528
+
28529
+ - Fast-changing review data lives in \`~/.zam/\`, not in this repository.
28530
+ - The skill directory is linked to this ZAM installation and updates with it.`,
28531
+ Boolean(opts.dryRun)
28532
+ );
28533
+ logInstructionAction(
28534
+ action,
28535
+ ".github/copilot-instructions.md",
28536
+ Boolean(opts.dryRun)
28537
+ );
28538
+ }
28539
+
28540
+ // src/cli/install-repair.ts
28541
+ function defaultRepairWorkspaces() {
28542
+ const summary = {
28543
+ provisioned: 0,
28544
+ missing: 0,
28545
+ relinked: 0
28546
+ };
28547
+ try {
28548
+ const agents = parseSetupAgents();
28549
+ for (const workspace of getConfiguredWorkspaces()) {
28550
+ if (!existsSync20(workspace.path)) {
28551
+ summary.missing += 1;
28552
+ continue;
28553
+ }
28554
+ const results = wireSkills(workspace.path, agents, { quiet: true });
28555
+ summary.provisioned += 1;
28556
+ summary.relinked += results.filter(
28557
+ (result) => result.action === "linked" || result.action === "relinked"
28558
+ ).length;
28559
+ }
28560
+ } catch (error) {
28561
+ summary.error = error instanceof Error ? error.message : String(error);
28562
+ }
28563
+ return summary;
28564
+ }
28565
+ function defaultConnectAgents(deps) {
28566
+ const report = performAgentConnect({}, deps);
28567
+ const companion = report.results.find((result) => result.extension?.kind === "vscode")?.extension?.action ?? null;
28568
+ return {
28569
+ success: report.success,
28570
+ detected: report.detected,
28571
+ connected: report.results.filter(
28572
+ (result) => !result.error && (result.alreadyConfigured || result.wrote)
28573
+ ).length,
28574
+ companion,
28575
+ errors: report.results.flatMap(
28576
+ (result) => result.error ? [`${result.label}: ${result.error}`] : []
28577
+ )
28578
+ };
28579
+ }
28580
+ function performInstallRepair(opts = {}, deps = {}) {
28581
+ const version = (deps.version ?? currentVersion)();
28582
+ const getLastRepaired = deps.getLastRepaired ?? getLastRepairedVersion;
28583
+ if (opts.ifVersionChanged && getLastRepaired() === version) {
28584
+ return {
28585
+ version,
28586
+ skipped: true,
28587
+ cli: null,
28588
+ workspaces: null,
28589
+ agents: null
28590
+ };
28591
+ }
28592
+ const cli = (deps.installCli ?? installCliShim)();
28593
+ const workspaces = (deps.repairWorkspaces ?? defaultRepairWorkspaces)();
28594
+ const agentDeps = {};
28595
+ if (cli.status === "installed" || cli.status === "refreshed" || cli.status === "ok") {
28596
+ agentDeps.findZam = () => cli.shimPath;
28597
+ }
28598
+ let agents;
28599
+ try {
28600
+ agents = (deps.connectAgents ?? defaultConnectAgents)(agentDeps);
28601
+ } catch (error) {
28602
+ agents = {
28603
+ success: false,
28604
+ detected: [],
28605
+ connected: 0,
28606
+ companion: null,
28607
+ errors: [error instanceof Error ? error.message : String(error)]
28608
+ };
28609
+ }
28610
+ (deps.setLastRepaired ?? setLastRepairedVersion)(version);
28611
+ return { version, skipped: false, cli, workspaces, agents };
28612
+ }
28613
+
28001
28614
  // src/cli/llm/capability-probe.ts
28002
28615
  init_kernel();
28003
28616
  var EMBEDDING_MODEL_HINTS = [
@@ -28122,9 +28735,9 @@ function validateModelSave(entry, probe, now = () => (/* @__PURE__ */ new Date()
28122
28735
  // src/cli/llm/vision.ts
28123
28736
  init_kernel();
28124
28737
  import { randomBytes } from "crypto";
28125
- import { readFileSync as readFileSync14 } from "fs";
28738
+ import { readFileSync as readFileSync16 } from "fs";
28126
28739
  import { tmpdir as tmpdir2 } from "os";
28127
- import { basename as basename4, join as join19 } from "path";
28740
+ import { basename as basename5, join as join21 } from "path";
28128
28741
  var LANGUAGE_NAMES2 = {
28129
28742
  en: "English",
28130
28743
  de: "German",
@@ -28160,13 +28773,13 @@ async function observeUiSnapshotViaLLM(db, input8) {
28160
28773
  const imageUrls = [];
28161
28774
  const isVideo = /\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(input8.imagePath);
28162
28775
  if (isVideo) {
28163
- const { mkdirSync: mkdirSync18, readdirSync: readdirSync3, rmSync: rmSync4 } = await import("fs");
28776
+ const { mkdirSync: mkdirSync19, readdirSync: readdirSync3, rmSync: rmSync4 } = await import("fs");
28164
28777
  const { execSync: execSync6 } = await import("child_process");
28165
- const tempDir = join19(
28778
+ const tempDir = join21(
28166
28779
  tmpdir2(),
28167
28780
  `zam-frames-${randomBytes(4).toString("hex")}`
28168
28781
  );
28169
- mkdirSync18(tempDir, { recursive: true });
28782
+ mkdirSync19(tempDir, { recursive: true });
28170
28783
  try {
28171
28784
  execSync6(
28172
28785
  `ffmpeg -i "${input8.imagePath}" -vf "fps=1/3,scale=1280:-1" -vsync vfr "${tempDir}/frame_%03d.png"`,
@@ -28195,7 +28808,7 @@ async function observeUiSnapshotViaLLM(db, input8) {
28195
28808
  }
28196
28809
  }
28197
28810
  for (const file of sampledFiles) {
28198
- const bytes = readFileSync14(join19(tempDir, file));
28811
+ const bytes = readFileSync16(join21(tempDir, file));
28199
28812
  imageUrls.push(`data:image/png;base64,${bytes.toString("base64")}`);
28200
28813
  }
28201
28814
  } finally {
@@ -28205,7 +28818,7 @@ async function observeUiSnapshotViaLLM(db, input8) {
28205
28818
  }
28206
28819
  }
28207
28820
  } else {
28208
- const imageBytes = readFileSync14(input8.imagePath);
28821
+ const imageBytes = readFileSync16(input8.imagePath);
28209
28822
  const ext = input8.imagePath.split(".").pop()?.toLowerCase();
28210
28823
  const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : "image/png";
28211
28824
  imageUrls.push(`data:${mime};base64,${imageBytes.toString("base64")}`);
@@ -28443,7 +29056,7 @@ function buildReport(input8, draft) {
28443
29056
  evidence: [
28444
29057
  {
28445
29058
  type: "keyframe",
28446
- ref: input8.evidenceRef ?? basename4(input8.imagePath),
29059
+ ref: input8.evidenceRef ?? basename5(input8.imagePath),
28447
29060
  redacted: input8.redacted ?? false
28448
29061
  }
28449
29062
  ],
@@ -28465,7 +29078,7 @@ function uncertainReport(input8, summary) {
28465
29078
  evidence: [
28466
29079
  {
28467
29080
  type: "keyframe",
28468
- ref: input8.evidenceRef ?? basename4(input8.imagePath),
29081
+ ref: input8.evidenceRef ?? basename5(input8.imagePath),
28469
29082
  redacted: input8.redacted ?? false
28470
29083
  }
28471
29084
  ],
@@ -28554,6 +29167,21 @@ async function withDb(fn, onError = defaultErrorHandler) {
28554
29167
  await db?.close();
28555
29168
  }
28556
29169
  }
29170
+ async function withOptionalDb(fn, onError = defaultErrorHandler) {
29171
+ let db = null;
29172
+ try {
29173
+ db = await openDatabase();
29174
+ } catch {
29175
+ db = null;
29176
+ }
29177
+ try {
29178
+ await fn(db);
29179
+ } catch (err) {
29180
+ onError(err.message);
29181
+ } finally {
29182
+ await db?.close();
29183
+ }
29184
+ }
28557
29185
  function jsonOut(data) {
28558
29186
  console.log(JSON.stringify(data, null, 2));
28559
29187
  }
@@ -28678,376 +29306,6 @@ async function withProviderScope(machine, action) {
28678
29306
  await withDb(action);
28679
29307
  }
28680
29308
 
28681
- // src/cli/provisioning/index.ts
28682
- import {
28683
- existsSync as existsSync18,
28684
- lstatSync as lstatSync2,
28685
- mkdirSync as mkdirSync13,
28686
- readFileSync as readFileSync15,
28687
- realpathSync,
28688
- rmSync as rmSync2,
28689
- symlinkSync,
28690
- writeFileSync as writeFileSync11
28691
- } from "fs";
28692
- import { basename as basename5, dirname as dirname9, join as join20 } from "path";
28693
- import { fileURLToPath as fileURLToPath5 } from "url";
28694
- var packageRoot3 = [
28695
- fileURLToPath5(new URL("../..", import.meta.url)),
28696
- fileURLToPath5(new URL("../../..", import.meta.url))
28697
- ].find((candidate) => existsSync18(join20(candidate, "package.json"))) ?? fileURLToPath5(new URL("../..", import.meta.url));
28698
- var ALL_SETUP_AGENTS = ["claude", "copilot", "codex", "agent"];
28699
- var SKILL_PAIRS = [
28700
- {
28701
- from: join20(packageRoot3, ".claude", "skills", "zam", "SKILL.md"),
28702
- to: join20(".claude", "skills", "zam", "SKILL.md"),
28703
- agents: ["claude", "copilot"]
28704
- },
28705
- {
28706
- from: join20(packageRoot3, ".agent", "skills", "zam", "SKILL.md"),
28707
- to: join20(".agent", "skills", "zam", "SKILL.md"),
28708
- agents: ["agent"]
28709
- },
28710
- {
28711
- from: join20(packageRoot3, ".agents", "skills", "zam", "SKILL.md"),
28712
- to: join20(".agents", "skills", "zam", "SKILL.md"),
28713
- agents: ["codex"]
28714
- }
28715
- ];
28716
- function parseSetupAgents(value) {
28717
- if (!value || value.trim().toLowerCase() === "all") {
28718
- return new Set(ALL_SETUP_AGENTS);
28719
- }
28720
- const aliases = {
28721
- claude: ["claude"],
28722
- copilot: ["copilot"],
28723
- codex: ["codex"],
28724
- agent: ["agent"],
28725
- opencode: ["agent"]
28726
- };
28727
- const selected = /* @__PURE__ */ new Set();
28728
- for (const raw of value.split(",")) {
28729
- const key = raw.trim().toLowerCase();
28730
- const mapped = aliases[key];
28731
- if (!mapped) {
28732
- throw new Error(
28733
- `Unknown agent "${raw}". Use: all, claude, copilot, codex, agent.`
28734
- );
28735
- }
28736
- for (const item of mapped) selected.add(item);
28737
- }
28738
- return selected;
28739
- }
28740
- function pathExists(path) {
28741
- try {
28742
- lstatSync2(path);
28743
- return true;
28744
- } catch {
28745
- return false;
28746
- }
28747
- }
28748
- function isSymbolicLink(path) {
28749
- try {
28750
- return lstatSync2(path).isSymbolicLink();
28751
- } catch {
28752
- return false;
28753
- }
28754
- }
28755
- function comparableRealPath(path) {
28756
- const real = realpathSync(path);
28757
- return process.platform === "win32" ? real.toLowerCase() : real;
28758
- }
28759
- function pathsResolveToSameDirectory(sourceDir, destinationDir) {
28760
- try {
28761
- return comparableRealPath(destinationDir) === comparableRealPath(sourceDir);
28762
- } catch {
28763
- return false;
28764
- }
28765
- }
28766
- function linkPointsTo(sourceDir, destinationDir) {
28767
- return isSymbolicLink(destinationDir) && pathsResolveToSameDirectory(sourceDir, destinationDir);
28768
- }
28769
- function isZamSkillCopy(destinationDir) {
28770
- try {
28771
- if (!lstatSync2(destinationDir).isDirectory()) return false;
28772
- const skillFile = join20(destinationDir, "SKILL.md");
28773
- if (!existsSync18(skillFile)) return false;
28774
- return /^name:\s*zam\s*$/m.test(readFileSync15(skillFile, "utf8"));
28775
- } catch {
28776
- return false;
28777
- }
28778
- }
28779
- function classifySkillDestination(sourceDir, destinationDir) {
28780
- if (!existsSync18(sourceDir)) return "source-missing";
28781
- if (!isSymbolicLink(destinationDir) && pathsResolveToSameDirectory(sourceDir, destinationDir)) {
28782
- return "source-directory";
28783
- }
28784
- if (linkPointsTo(sourceDir, destinationDir)) return "linked";
28785
- if (!pathExists(destinationDir)) return "missing";
28786
- if (isSymbolicLink(destinationDir)) return "broken";
28787
- if (isZamSkillCopy(destinationDir)) return "stale-copy";
28788
- return "unmanaged";
28789
- }
28790
- function wireSkills(cwd = process.cwd(), agents = parseSetupAgents(), opts = {}) {
28791
- const results = [];
28792
- const log = (message) => {
28793
- if (!opts.quiet) console.log(message);
28794
- };
28795
- for (const { from, to, agents: pairAgents } of SKILL_PAIRS) {
28796
- if (!pairAgents.some((agent) => agents.has(agent))) continue;
28797
- const sourceDir = dirname9(from);
28798
- const destinationDir = dirname9(join20(cwd, to));
28799
- const label = dirname9(to);
28800
- const state = classifySkillDestination(sourceDir, destinationDir);
28801
- if (state === "source-missing") {
28802
- if (!opts.quiet) {
28803
- console.warn(` warn source not found, skipping: ${sourceDir}`);
28804
- }
28805
- results.push({
28806
- source: sourceDir,
28807
- destination: destinationDir,
28808
- action: "skipped",
28809
- reason: "source-not-found"
28810
- });
28811
- continue;
28812
- }
28813
- if (state === "source-directory") {
28814
- log(` skip ${label} (package source)`);
28815
- results.push({
28816
- source: sourceDir,
28817
- destination: destinationDir,
28818
- action: "skipped",
28819
- reason: "source-directory"
28820
- });
28821
- continue;
28822
- }
28823
- if (state === "linked") {
28824
- log(` skip ${label} (already linked)`);
28825
- results.push({
28826
- source: sourceDir,
28827
- destination: destinationDir,
28828
- action: "skipped",
28829
- reason: "already-linked"
28830
- });
28831
- continue;
28832
- }
28833
- const destinationExists = state !== "missing";
28834
- const replaceExisting = destinationExists && (Boolean(opts.force) || state === "broken" || state === "stale-copy");
28835
- if (destinationExists && !replaceExisting) {
28836
- if (!opts.quiet) {
28837
- console.warn(
28838
- ` warn ${label} already exists and is not managed by ZAM; use --force to replace it`
28839
- );
28840
- }
28841
- results.push({
28842
- source: sourceDir,
28843
- destination: destinationDir,
28844
- action: "skipped",
28845
- reason: "unmanaged-destination"
28846
- });
28847
- continue;
28848
- }
28849
- const action = destinationExists ? "relinked" : "linked";
28850
- if (opts.dryRun) {
28851
- log(` would ${action === "linked" ? "link" : "relink"} ${label}`);
28852
- } else {
28853
- if (destinationExists) {
28854
- rmSync2(destinationDir, { recursive: true, force: true });
28855
- }
28856
- mkdirSync13(dirname9(destinationDir), { recursive: true });
28857
- symlinkSync(
28858
- sourceDir,
28859
- destinationDir,
28860
- process.platform === "win32" ? "junction" : "dir"
28861
- );
28862
- log(` ${action === "linked" ? "link" : "relink"} ${label}`);
28863
- }
28864
- results.push({ source: sourceDir, destination: destinationDir, action });
28865
- }
28866
- return results;
28867
- }
28868
- function inspectSkillLinks(cwd = process.cwd(), agents = parseSetupAgents()) {
28869
- const results = [];
28870
- for (const { from, to, agents: pairAgents } of SKILL_PAIRS) {
28871
- if (!pairAgents.some((agent) => agents.has(agent))) continue;
28872
- const sourceDir = dirname9(from);
28873
- const destinationDir = dirname9(join20(cwd, to));
28874
- results.push({
28875
- agents: pairAgents,
28876
- source: sourceDir,
28877
- destination: destinationDir,
28878
- state: classifySkillDestination(sourceDir, destinationDir)
28879
- });
28880
- }
28881
- return results;
28882
- }
28883
- function summarizeSkillLinkHealth(inspections) {
28884
- if (inspections.some((item) => item.state === "unmanaged")) {
28885
- return "unmanaged";
28886
- }
28887
- const repairable = ["missing", "broken", "stale-copy"];
28888
- if (inspections.some((item) => repairable.includes(item.state))) {
28889
- return "needs-repair";
28890
- }
28891
- return "healthy";
28892
- }
28893
- var ZAM_BLOCK_START = "<!-- ZAM:START -->";
28894
- var ZAM_BLOCK_END = "<!-- ZAM:END -->";
28895
- function upsertMarkedBlock(dest, blockBody, dryRun) {
28896
- const block = `${ZAM_BLOCK_START}
28897
- ${blockBody.trim()}
28898
- ${ZAM_BLOCK_END}`;
28899
- if (!existsSync18(dest)) {
28900
- if (!dryRun) {
28901
- mkdirSync13(dirname9(dest), { recursive: true });
28902
- writeFileSync11(dest, `${block}
28903
- `, "utf8");
28904
- }
28905
- return "write";
28906
- }
28907
- const existing = readFileSync15(dest, "utf8");
28908
- if (existing.includes(block)) return "skip";
28909
- const start = existing.indexOf(ZAM_BLOCK_START);
28910
- const end = existing.indexOf(ZAM_BLOCK_END);
28911
- const next = start >= 0 && end > start ? `${existing.slice(0, start)}${block}${existing.slice(end + ZAM_BLOCK_END.length)}` : `${existing.trimEnd()}
28912
-
28913
- ${block}
28914
- `;
28915
- if (!dryRun) writeFileSync11(dest, next, "utf8");
28916
- return start >= 0 && end > start ? "update" : "write";
28917
- }
28918
- function logInstructionAction(action, label, dryRun) {
28919
- if (action === "skip") {
28920
- console.log(` skip ${label} (ZAM block already present)`);
28921
- } else {
28922
- console.log(` ${dryRun ? "would " : ""}${action} ${label}`);
28923
- }
28924
- }
28925
- function writeClaudeMd(skipClaudeMd, cwd = process.cwd(), opts = {}) {
28926
- if (skipClaudeMd) return;
28927
- const dest = join20(cwd, "CLAUDE.md");
28928
- if (existsSync18(dest)) {
28929
- if (!opts.updateExisting) {
28930
- console.log(` skip CLAUDE.md (already present)`);
28931
- return;
28932
- }
28933
- const action = upsertMarkedBlock(
28934
- dest,
28935
- `## ZAM learning sessions
28936
-
28937
- ZAM is available in this repository. Use the \`zam\` skill in Claude Code to turn real work into an observed learning session with active recall and FSRS scheduling.
28938
-
28939
- - Skill files live under \`.claude/skills/zam/\`.
28940
- - Fast-changing review data lives in \`~/.zam/\`, not in this repository.
28941
- - The skill directory is linked to this ZAM installation and updates with it.`,
28942
- Boolean(opts.dryRun)
28943
- );
28944
- logInstructionAction(action, "CLAUDE.md", Boolean(opts.dryRun));
28945
- return;
28946
- }
28947
- const name = basename5(cwd);
28948
- const content = `# ZAM Personal Kernel \u2014 ${name}
28949
-
28950
- This is a ZAM personal instance. ZAM builds lasting skills through spaced
28951
- repetition during real work \u2014 not separate study sessions.
28952
-
28953
- ## First time here?
28954
- Run \`/setup\` in Claude Code or Gemini CLI to complete first-time setup.
28955
-
28956
- ## Regular use
28957
- Run \`/zam\` to start a learning session on whatever you are working on.
28958
-
28959
- ## What lives here
28960
- - \`beliefs/\` \u2014 your worldview, approved by git commit
28961
- - \`goals/\` \u2014 your objectives, decomposed into tasks and learning tokens
28962
-
28963
- ## Fast-changing data
28964
- Learning tokens, cards, and review history live in local SQLite by default.
28965
- Use \`zam connector setup turso\` to store cloud credentials in
28966
- \`~/.zam/credentials.json\` and use a Turso database across machines.
28967
- `;
28968
- if (opts.dryRun) {
28969
- console.log(` would write CLAUDE.md`);
28970
- } else {
28971
- writeFileSync11(dest, content, "utf8");
28972
- console.log(` write CLAUDE.md`);
28973
- }
28974
- }
28975
- function writeAgentsMd(skipAgentsMd, cwd = process.cwd(), opts = {}) {
28976
- if (skipAgentsMd) return;
28977
- const dest = join20(cwd, "AGENTS.md");
28978
- if (existsSync18(dest)) {
28979
- if (!opts.updateExisting) {
28980
- console.log(` skip AGENTS.md (already present)`);
28981
- return;
28982
- }
28983
- const action = upsertMarkedBlock(
28984
- dest,
28985
- `## ZAM learning sessions
28986
-
28987
- ZAM is available in this repository. Select the \`zam\` skill through \`/skills\` or invoke \`$zam\` where supported to turn real work into an observed learning session with active recall and FSRS scheduling.
28988
-
28989
- - Skill files live under \`.agents/skills/zam/\`.
28990
- - Fast-changing review data lives in \`~/.zam/\`, not in this repository.
28991
- - The skill directory is linked to this ZAM installation and updates with it.`,
28992
- Boolean(opts.dryRun)
28993
- );
28994
- logInstructionAction(action, "AGENTS.md", Boolean(opts.dryRun));
28995
- return;
28996
- }
28997
- const name = basename5(cwd);
28998
- const content = `# ZAM Personal Kernel - ${name}
28999
-
29000
- This is a ZAM personal instance. ZAM builds lasting skills through spaced
29001
- repetition during real work, not separate study sessions.
29002
-
29003
- ## First time here?
29004
- Run \`zam setup\` from the shell. When this repository includes
29005
- \`.agents/skills/setup/\`, you can instead select \`setup\` through \`/skills\`
29006
- or invoke \`$setup\`.
29007
-
29008
- ## Regular use
29009
- Select the \`zam\` skill through \`/skills\` or invoke \`$zam\` to start a
29010
- learning session on whatever you are working on.
29011
-
29012
- ## What lives here
29013
- - \`beliefs/\` - your worldview, approved by git commit
29014
- - \`goals/\` - your objectives, decomposed into tasks and learning tokens
29015
-
29016
- ## Fast-changing data
29017
- Learning tokens, cards, and review history live in local SQLite by default.
29018
- Use \`zam connector setup turso\` to store cloud credentials in
29019
- \`~/.zam/credentials.json\` and use a Turso database across machines.
29020
-
29021
- ## Codex skills
29022
- Codex discovers repository skills under \`.agents/skills/\`. Run
29023
- \`zam setup --force\` after upgrading \`zam-core\` to refresh them.
29024
- `;
29025
- if (opts.dryRun) {
29026
- console.log(` would write AGENTS.md`);
29027
- } else {
29028
- writeFileSync11(dest, content, "utf8");
29029
- console.log(` write AGENTS.md`);
29030
- }
29031
- }
29032
- function writeCopilotInstructions(cwd = process.cwd(), opts = {}) {
29033
- const dest = join20(cwd, ".github", "copilot-instructions.md");
29034
- const action = upsertMarkedBlock(
29035
- dest,
29036
- `## ZAM learning sessions
29037
-
29038
- ZAM is available in this repository through \`.claude/skills/zam/\`. Use the \`zam\` project skill from Copilot-compatible skill selection surfaces to turn real work into an observed learning session with active recall and FSRS scheduling.
29039
-
29040
- - Fast-changing review data lives in \`~/.zam/\`, not in this repository.
29041
- - The skill directory is linked to this ZAM installation and updates with it.`,
29042
- Boolean(opts.dryRun)
29043
- );
29044
- logInstructionAction(
29045
- action,
29046
- ".github/copilot-instructions.md",
29047
- Boolean(opts.dryRun)
29048
- );
29049
- }
29050
-
29051
29309
  // src/cli/users/identity.ts
29052
29310
  init_kernel();
29053
29311
  async function ensureDefaultUser(db, preferredUserId) {
@@ -29071,13 +29329,13 @@ async function resolveUser(opts, db, resolveOpts) {
29071
29329
  }
29072
29330
 
29073
29331
  // src/cli/workspaces/backup.ts
29074
- import { mkdirSync as mkdirSync14 } from "fs";
29075
- import { join as join21 } from "path";
29332
+ import { mkdirSync as mkdirSync15 } from "fs";
29333
+ import { join as join22 } from "path";
29076
29334
  async function backupDatabaseTo(db, targetDir) {
29077
- const backupDir = join21(targetDir, "zam-backups");
29078
- mkdirSync14(backupDir, { recursive: true });
29335
+ const backupDir = join22(targetDir, "zam-backups");
29336
+ mkdirSync15(backupDir, { recursive: true });
29079
29337
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
29080
- const dest = join21(backupDir, `zam-${stamp}.db`);
29338
+ const dest = join22(backupDir, `zam-${stamp}.db`);
29081
29339
  await db.exec(`VACUUM INTO '${dest.replace(/'/g, "''")}'`);
29082
29340
  return dest;
29083
29341
  }
@@ -29133,6 +29391,9 @@ function parseProviderScope(scope) {
29133
29391
  async function withDb2(fn) {
29134
29392
  await withDb(fn, jsonError);
29135
29393
  }
29394
+ async function withOptionalDb2(fn) {
29395
+ await withOptionalDb(fn, jsonError);
29396
+ }
29136
29397
  var bridgeCommand = new Command2("bridge").description(
29137
29398
  "Machine-readable JSON protocol for AI integration"
29138
29399
  );
@@ -29201,27 +29462,27 @@ bridgeCommand.command("update-check").description("Check whether a newer ZAM rel
29201
29462
  }
29202
29463
  });
29203
29464
  bridgeCommand.command("workspace-info").description("Report the workspace dir, its default, and the data dir (JSON)").action(async () => {
29204
- await withDb2(async (db) => {
29465
+ await withOptionalDb2(async (db) => {
29205
29466
  const activeWorkspace = await ensureActiveWorkspace(db);
29206
29467
  jsonOut2({
29207
29468
  activeWorkspaceId: activeWorkspace.id,
29208
29469
  activeWorkspace,
29209
29470
  workspaceDir: activeWorkspace.path,
29210
29471
  defaultWorkspaceDir: defaultWorkspaceDir(),
29211
- dataDir: join22(homedir13(), ".zam")
29472
+ dataDir: join23(homedir14(), ".zam")
29212
29473
  });
29213
29474
  });
29214
29475
  });
29215
29476
  function provisionConfiguredWorkspaces() {
29216
29477
  return getConfiguredWorkspaces().flatMap(
29217
- (workspace) => existsSync19(workspace.path) ? wireSkills(workspace.path, parseSetupAgents(), { quiet: true }) : []
29478
+ (workspace) => existsSync21(workspace.path) ? wireSkills(workspace.path, parseSetupAgents(), { quiet: true }) : []
29218
29479
  );
29219
29480
  }
29220
29481
  function buildWorkspaceLinkHealth(workspaces) {
29221
29482
  const agents = parseSetupAgents();
29222
29483
  const map = {};
29223
29484
  for (const workspace of workspaces) {
29224
- if (!existsSync19(workspace.path)) continue;
29485
+ if (!existsSync21(workspace.path)) continue;
29225
29486
  const links = inspectSkillLinks(workspace.path, agents);
29226
29487
  map[workspace.id] = {
29227
29488
  health: summarizeSkillLinkHealth(links),
@@ -29242,7 +29503,7 @@ async function ensureDesktopWorkspace(db) {
29242
29503
  };
29243
29504
  }
29244
29505
  bridgeCommand.command("workspace-list").description("List configured ZAM workspaces (JSON)").action(async () => {
29245
- await withDb2(async (db) => {
29506
+ await withOptionalDb2(async (db) => {
29246
29507
  const activeWorkspace = await ensureActiveWorkspace(db);
29247
29508
  const workspaces = getConfiguredWorkspaces();
29248
29509
  jsonOut2({
@@ -29251,7 +29512,7 @@ bridgeCommand.command("workspace-list").description("List configured ZAM workspa
29251
29512
  activeWorkspace,
29252
29513
  workspaceDir: activeWorkspace.path,
29253
29514
  defaultWorkspaceDir: defaultWorkspaceDir(),
29254
- dataDir: join22(homedir13(), ".zam"),
29515
+ dataDir: join23(homedir14(), ".zam"),
29255
29516
  linkHealth: buildWorkspaceLinkHealth(workspaces)
29256
29517
  });
29257
29518
  });
@@ -29266,7 +29527,7 @@ bridgeCommand.command("workspace-repair-links").description(
29266
29527
  if (!id) jsonError("A non-empty --id is required");
29267
29528
  const workspace = getConfiguredWorkspaces().find((item) => item.id === id);
29268
29529
  if (!workspace) jsonError(`Workspace "${id}" is not configured`);
29269
- if (!existsSync19(workspace.path)) {
29530
+ if (!existsSync21(workspace.path)) {
29270
29531
  jsonError(`Workspace path does not exist: ${workspace.path}`);
29271
29532
  }
29272
29533
  const agents = parseSetupAgents(opts.agents);
@@ -29297,13 +29558,13 @@ function parseBridgeWorkspaceKind(value) {
29297
29558
  bridgeCommand.command("workspace-add").description("Register an existing directory as a ZAM workspace (JSON)").requiredOption("--path <dir>", "Existing workspace/repository directory").option("--id <id>", "Workspace id").option("--label <label>", "Human-readable label").option("--kind <kind>", "Workspace kind", "custom").action(async (opts) => {
29298
29559
  const raw = String(opts.path ?? "").trim();
29299
29560
  if (!raw) jsonError("A non-empty --path is required");
29300
- const path = resolve5(raw);
29301
- if (!existsSync19(path)) jsonError(`Workspace path does not exist: ${path}`);
29561
+ const path = resolve6(raw);
29562
+ if (!existsSync21(path)) jsonError(`Workspace path does not exist: ${path}`);
29302
29563
  const id = opts.id ? String(opts.id).trim() : void 0;
29303
29564
  if (opts.id && !id) jsonError("A non-empty --id is required");
29304
29565
  const kind = parseBridgeWorkspaceKind(opts.kind);
29305
29566
  const skillLinks = wireSkills(path, parseSetupAgents(), { quiet: true });
29306
- await withDb2(async (db) => {
29567
+ await withOptionalDb2(async (db) => {
29307
29568
  const workspace = await activateWorkspacePath(db, path, {
29308
29569
  ...id ? { id } : {},
29309
29570
  ...opts.label ? { label: opts.label } : {},
@@ -29325,7 +29586,7 @@ bridgeCommand.command("workspace-remove").description("Unregister a ZAM workspac
29325
29586
  if (!id) jsonError("A non-empty --id is required");
29326
29587
  const workspace = getConfiguredWorkspaces().find((item) => item.id === id);
29327
29588
  if (!workspace) jsonError(`Workspace "${id}" is not configured`);
29328
- await withDb2(async (db) => {
29589
+ await withOptionalDb2(async (db) => {
29329
29590
  const { activeWorkspace, workspaces } = await removeWorkspaceAndResolveActive(db, id);
29330
29591
  const skillLinks = provisionConfiguredWorkspaces();
29331
29592
  jsonOut2({
@@ -29342,10 +29603,10 @@ bridgeCommand.command("workspace-remove").description("Unregister a ZAM workspac
29342
29603
  bridgeCommand.command("set-workspace-dir").description("Set the personal workspace directory (JSON)").requiredOption("--dir <path>", "Path to the workspace directory").action(async (opts) => {
29343
29604
  const raw = String(opts.dir ?? "").trim();
29344
29605
  if (!raw) jsonError("A non-empty --dir is required");
29345
- const dir = resolve5(raw);
29346
- if (!existsSync19(dir)) jsonError(`Workspace path does not exist: ${dir}`);
29606
+ const dir = resolve6(raw);
29607
+ if (!existsSync21(dir)) jsonError(`Workspace path does not exist: ${dir}`);
29347
29608
  const skillLinks = wireSkills(dir, parseSetupAgents(), { quiet: true });
29348
- await withDb2(async (db) => {
29609
+ await withOptionalDb2(async (db) => {
29349
29610
  const workspace = await activateWorkspacePath(db, dir);
29350
29611
  jsonOut2({
29351
29612
  ok: true,
@@ -29406,7 +29667,7 @@ bridgeCommand.command("agent-open").description("Launch an agent harness in the
29406
29667
  jsonError(`Workspace is not configured: ${opts.workspace}`);
29407
29668
  }
29408
29669
  const activeWorkspace = await ensureActiveWorkspace(db);
29409
- const workspace = opts.dir ? existsSync19(opts.dir) ? opts.dir : homedir13() : existingWorkspaceDirOrHome(configuredWorkspace ?? activeWorkspace);
29670
+ const workspace = opts.dir ? existsSync21(opts.dir) ? opts.dir : homedir14() : existingWorkspaceDirOrHome(configuredWorkspace ?? activeWorkspace);
29410
29671
  launchHarness(harness, {
29411
29672
  executable,
29412
29673
  workspace,
@@ -29778,7 +30039,7 @@ bridgeCommand.command("discover-skills").description(
29778
30039
  "20"
29779
30040
  ).action(async (opts) => {
29780
30041
  try {
29781
- const monitorDir = join22(homedir13(), ".zam", "monitor");
30042
+ const monitorDir = join23(homedir14(), ".zam", "monitor");
29782
30043
  let files;
29783
30044
  try {
29784
30045
  files = readdirSync2(monitorDir).filter((f) => f.endsWith(".jsonl"));
@@ -29791,7 +30052,7 @@ bridgeCommand.command("discover-skills").description(
29791
30052
  return;
29792
30053
  }
29793
30054
  const limit = Number(opts.limit);
29794
- const sorted = files.map((f) => ({ name: f, path: join22(monitorDir, f) })).sort((a, b) => b.name.localeCompare(a.name)).slice(0, limit);
30055
+ const sorted = files.map((f) => ({ name: f, path: join23(monitorDir, f) })).sort((a, b) => b.name.localeCompare(a.name)).slice(0, limit);
29795
30056
  const sessionCommands = /* @__PURE__ */ new Map();
29796
30057
  for (const file of sorted) {
29797
30058
  const sessionId = file.name.replace(".jsonl", "");
@@ -29903,7 +30164,7 @@ bridgeCommand.command("observe-ui-snapshot").description(
29903
30164
  });
29904
30165
  function resolveWindowsPowerShell() {
29905
30166
  try {
29906
- execFileSync4("where.exe", ["pwsh.exe"], { stdio: "ignore" });
30167
+ execFileSync5("where.exe", ["pwsh.exe"], { stdio: "ignore" });
29907
30168
  return "pwsh";
29908
30169
  } catch {
29909
30170
  return "powershell";
@@ -29918,7 +30179,7 @@ function captureScreenshot(outputPath, hwnd, processName) {
29918
30179
  throw new Error(`Invalid process name format: ${processName}`);
29919
30180
  }
29920
30181
  if (platform === "win32") {
29921
- const stdout = execFileSync4(
30182
+ const stdout = execFileSync5(
29922
30183
  resolveWindowsPowerShell(),
29923
30184
  [
29924
30185
  "-NoProfile",
@@ -30231,13 +30492,13 @@ Write-CaptureResult "fullscreen" $hwndVal $matchedBy
30231
30492
  } else if (platform === "darwin") {
30232
30493
  if (hwnd) {
30233
30494
  const parsedHwnd = hwnd.startsWith("0x") ? parseInt(hwnd, 16) : parseInt(hwnd, 10);
30234
- execFileSync4("screencapture", ["-l", String(parsedHwnd), outputPath], {
30495
+ execFileSync5("screencapture", ["-l", String(parsedHwnd), outputPath], {
30235
30496
  stdio: "pipe"
30236
30497
  });
30237
30498
  return { method: "screencapture-window", target: null };
30238
30499
  } else if (processName) {
30239
30500
  try {
30240
- const windowId = execFileSync4(
30501
+ const windowId = execFileSync5(
30241
30502
  "osascript",
30242
30503
  [
30243
30504
  "-e",
@@ -30246,17 +30507,17 @@ Write-CaptureResult "fullscreen" $hwndVal $matchedBy
30246
30507
  { encoding: "utf8", stdio: ["pipe", "pipe", "ignore"] }
30247
30508
  ).trim();
30248
30509
  if (windowId && /^\d+$/.test(windowId)) {
30249
- execFileSync4("screencapture", ["-l", windowId, outputPath], {
30510
+ execFileSync5("screencapture", ["-l", windowId, outputPath], {
30250
30511
  stdio: "pipe"
30251
30512
  });
30252
30513
  return { method: "screencapture-window", target: null };
30253
30514
  }
30254
30515
  } catch {
30255
30516
  }
30256
- execFileSync4("screencapture", ["-x", outputPath], { stdio: "pipe" });
30517
+ execFileSync5("screencapture", ["-x", outputPath], { stdio: "pipe" });
30257
30518
  return { method: "screencapture-full", target: null };
30258
30519
  } else {
30259
- execFileSync4("screencapture", ["-x", outputPath], { stdio: "pipe" });
30520
+ execFileSync5("screencapture", ["-x", outputPath], { stdio: "pipe" });
30260
30521
  return { method: "screencapture-full", target: null };
30261
30522
  }
30262
30523
  } else {
@@ -30293,7 +30554,7 @@ bridgeCommand.command("capture-ui").description("Capture a screenshot for agent-
30293
30554
  return;
30294
30555
  }
30295
30556
  }
30296
- const outputPath = opts.image ?? opts.output ?? join22(tmpdir3(), `zam-capture-${randomBytes2(4).toString("hex")}.png`);
30557
+ const outputPath = opts.image ?? opts.output ?? join23(tmpdir3(), `zam-capture-${randomBytes2(4).toString("hex")}.png`);
30297
30558
  const captureResult = isProvided ? { method: "provided", target: null } : captureScreenshot(outputPath, opts.hwnd, opts.processName);
30298
30559
  if (!isProvided) {
30299
30560
  const post = decidePostCapture(policy, {
@@ -30321,7 +30582,7 @@ bridgeCommand.command("capture-ui").description("Capture a screenshot for agent-
30321
30582
  return;
30322
30583
  }
30323
30584
  }
30324
- const imageBytes = readFileSync16(outputPath);
30585
+ const imageBytes = readFileSync17(outputPath);
30325
30586
  const base64 = imageBytes.toString("base64");
30326
30587
  jsonOut2({
30327
30588
  sessionId: opts.session ?? null,
@@ -30348,11 +30609,11 @@ bridgeCommand.command("start-recording").description("Start screen recording in
30348
30609
  return;
30349
30610
  }
30350
30611
  const sessionId = opts.session;
30351
- const statePath = join22(tmpdir3(), `zam-recording-${sessionId}.json`);
30612
+ const statePath = join23(tmpdir3(), `zam-recording-${sessionId}.json`);
30352
30613
  const defaultExt = platform === "win32" ? ".mkv" : ".mov";
30353
- const outputPath = opts.output ?? join22(tmpdir3(), `zam-recording-${sessionId}${defaultExt}`);
30354
- const { existsSync: existsSync28, writeFileSync: writeFileSync16, openSync, closeSync } = await import("fs");
30355
- if (existsSync28(statePath)) {
30614
+ const outputPath = opts.output ?? join23(tmpdir3(), `zam-recording-${sessionId}${defaultExt}`);
30615
+ const { existsSync: existsSync30, writeFileSync: writeFileSync17, openSync, closeSync } = await import("fs");
30616
+ if (existsSync30(statePath)) {
30356
30617
  jsonOut2({
30357
30618
  sessionId,
30358
30619
  started: false,
@@ -30360,7 +30621,7 @@ bridgeCommand.command("start-recording").description("Start screen recording in
30360
30621
  });
30361
30622
  return;
30362
30623
  }
30363
- const logPath = join22(tmpdir3(), `zam-recording-${sessionId}.log`);
30624
+ const logPath = join23(tmpdir3(), `zam-recording-${sessionId}.log`);
30364
30625
  let logFd;
30365
30626
  try {
30366
30627
  logFd = openSync(logPath, "w");
@@ -30406,7 +30667,7 @@ bridgeCommand.command("start-recording").description("Start screen recording in
30406
30667
  }
30407
30668
  child.unref();
30408
30669
  if (child.pid) {
30409
- writeFileSync16(
30670
+ writeFileSync17(
30410
30671
  statePath,
30411
30672
  JSON.stringify({
30412
30673
  pid: child.pid,
@@ -30442,9 +30703,9 @@ bridgeCommand.command("stop-recording").description(
30442
30703
  return;
30443
30704
  }
30444
30705
  const sessionId = opts.session;
30445
- const statePath = join22(tmpdir3(), `zam-recording-${sessionId}.json`);
30446
- const { existsSync: existsSync28, readFileSync: readFileSync21, rmSync: rmSync4 } = await import("fs");
30447
- if (!existsSync28(statePath)) {
30706
+ const statePath = join23(tmpdir3(), `zam-recording-${sessionId}.json`);
30707
+ const { existsSync: existsSync30, readFileSync: readFileSync22, rmSync: rmSync4 } = await import("fs");
30708
+ if (!existsSync30(statePath)) {
30448
30709
  jsonOut2({
30449
30710
  sessionId,
30450
30711
  stopped: false,
@@ -30452,7 +30713,7 @@ bridgeCommand.command("stop-recording").description(
30452
30713
  });
30453
30714
  return;
30454
30715
  }
30455
- const state = JSON.parse(readFileSync21(statePath, "utf8"));
30716
+ const state = JSON.parse(readFileSync22(statePath, "utf8"));
30456
30717
  const { pid, outputPath } = state;
30457
30718
  try {
30458
30719
  process.kill(pid, "SIGINT");
@@ -30468,7 +30729,7 @@ bridgeCommand.command("stop-recording").description(
30468
30729
  };
30469
30730
  let attempts = 0;
30470
30731
  while (isProcessRunning(pid) && attempts < 20) {
30471
- await new Promise((resolve11) => setTimeout(resolve11, 250));
30732
+ await new Promise((resolve12) => setTimeout(resolve12, 250));
30472
30733
  attempts++;
30473
30734
  }
30474
30735
  if (isProcessRunning(pid)) {
@@ -30481,7 +30742,7 @@ bridgeCommand.command("stop-recording").description(
30481
30742
  rmSync4(statePath, { force: true });
30482
30743
  } catch {
30483
30744
  }
30484
- if (!existsSync28(outputPath)) {
30745
+ if (!existsSync30(outputPath)) {
30485
30746
  jsonOut2({
30486
30747
  sessionId,
30487
30748
  stopped: false,
@@ -30911,7 +31172,7 @@ bridgeCommand.command("cloud-model-hint").description("Suggest a cloud model for
30911
31172
  });
30912
31173
  bridgeCommand.command("local-llm-hints").description("Detect installed local LLM servers and suggest defaults (JSON)").action(() => {
30913
31174
  const profile = getSystemProfile();
30914
- const flmInstalled = hasCommand("flm") || existsSync19("C:\\Program Files\\flm\\flm.exe");
31175
+ const flmInstalled = hasCommand("flm") || existsSync21("C:\\Program Files\\flm\\flm.exe");
30915
31176
  const ollamaInstalled = isOllamaInstalled();
30916
31177
  const runners = [
30917
31178
  { id: "flm", label: "FastFlowLM", installed: flmInstalled },
@@ -31143,13 +31404,15 @@ bridgeCommand.command("desktop-bootstrap").description("Initialize first-run des
31143
31404
  const userId = await ensureDefaultUser(db, opts.user);
31144
31405
  const { enabled, url, model, locale } = await getLlmConfig(db);
31145
31406
  const { workspaceDir, activeWorkspaceId, skillLinks } = await ensureDesktopWorkspace(db);
31407
+ const cli = installCliShim();
31146
31408
  jsonOut2({
31147
31409
  userId,
31148
31410
  locale,
31149
31411
  llm: { enabled, url, model },
31150
31412
  activeWorkspaceId,
31151
31413
  workspaceDir,
31152
- skillLinks
31414
+ skillLinks,
31415
+ cli
31153
31416
  });
31154
31417
  });
31155
31418
  });
@@ -31166,6 +31429,22 @@ bridgeCommand.command("get-settings").description("Get active ZAM settings (JSON
31166
31429
  });
31167
31430
  });
31168
31431
  });
31432
+ bridgeCommand.command("install-repair").description(
31433
+ "Verify and repair this machine's ZAM installation: CLI shim + PATH, workspace skill links, agent configs and companion extensions (JSON)"
31434
+ ).option(
31435
+ "--if-version-changed",
31436
+ "Only run when the app version differs from the last repaired one; reports skipped:true otherwise"
31437
+ ).action((opts) => {
31438
+ try {
31439
+ jsonOut2(
31440
+ performInstallRepair({
31441
+ ifVersionChanged: Boolean(opts.ifVersionChanged)
31442
+ })
31443
+ );
31444
+ } catch (err) {
31445
+ jsonError(err.message);
31446
+ }
31447
+ });
31169
31448
  bridgeCommand.command("agent-harness-status").description(
31170
31449
  "Detect installed agent harnesses and their ZAM MCP configuration state (JSON)"
31171
31450
  ).action(() => {
@@ -32628,8 +32907,17 @@ bridgeCommand.command("unassign-knowledge-context").description("Remove a token
32628
32907
  });
32629
32908
  });
32630
32909
  bridgeCommand.command("get-active-knowledge-context").description("Get the active knowledge context name (JSON)").action(async () => {
32631
- await withDb2(async (db) => {
32910
+ await withOptionalDb2(async (db) => {
32632
32911
  const configured = getActiveWorkspaceContext();
32912
+ if (!db) {
32913
+ jsonOut2({
32914
+ success: true,
32915
+ activeContext: configured ?? null,
32916
+ staleContext: null,
32917
+ validated: false
32918
+ });
32919
+ return;
32920
+ }
32633
32921
  const active = configured ? await getKnowledgeContextByName(db, configured) : void 0;
32634
32922
  jsonOut2({
32635
32923
  success: true,
@@ -32639,7 +32927,7 @@ bridgeCommand.command("get-active-knowledge-context").description("Get the activ
32639
32927
  });
32640
32928
  });
32641
32929
  bridgeCommand.command("set-active-knowledge-context").description("Set the active knowledge context name (JSON)").argument("[name]", "Context name to use (use empty/none to clear)").action(async (name) => {
32642
- await withDb2(async (db) => {
32930
+ await withOptionalDb2(async (db) => {
32643
32931
  if (!name || name === "none" || name === "null" || name === "undefined") {
32644
32932
  if (!setActiveWorkspaceContext(void 0)) {
32645
32933
  jsonError("No active workspace configured");
@@ -32647,6 +32935,13 @@ bridgeCommand.command("set-active-knowledge-context").description("Set the activ
32647
32935
  jsonOut2({ success: true, activeContext: null });
32648
32936
  return;
32649
32937
  }
32938
+ if (!db) {
32939
+ if (!setActiveWorkspaceContext(name)) {
32940
+ jsonError("No active workspace configured");
32941
+ }
32942
+ jsonOut2({ success: true, activeContext: name, validated: false });
32943
+ return;
32944
+ }
32650
32945
  const context = await getKnowledgeContextByName(db, name);
32651
32946
  if (!context) {
32652
32947
  jsonError(`Knowledge context not found: ${name}`);
@@ -34098,28 +34393,28 @@ var doctorCommand = new Command5("doctor").description(
34098
34393
  // src/cli/commands/git-sync.ts
34099
34394
  init_kernel();
34100
34395
  import { execSync as execSync5 } from "child_process";
34101
- import { chmodSync as chmodSync2, existsSync as existsSync20, writeFileSync as writeFileSync12 } from "fs";
34102
- import { join as join23 } from "path";
34396
+ import { chmodSync as chmodSync3, existsSync as existsSync22, writeFileSync as writeFileSync13 } from "fs";
34397
+ import { join as join24 } from "path";
34103
34398
  import { Command as Command6 } from "commander";
34104
34399
  function installHook2() {
34105
- const gitDir = join23(process.cwd(), ".git");
34106
- if (!existsSync20(gitDir)) {
34400
+ const gitDir = join24(process.cwd(), ".git");
34401
+ if (!existsSync22(gitDir)) {
34107
34402
  console.error(
34108
34403
  "Error: Current directory is not the root of a Git repository."
34109
34404
  );
34110
34405
  process.exit(1);
34111
34406
  }
34112
- const hooksDir = join23(gitDir, "hooks");
34113
- const hookPath = join23(hooksDir, "post-commit");
34407
+ const hooksDir = join24(gitDir, "hooks");
34408
+ const hookPath = join24(hooksDir, "post-commit");
34114
34409
  const hookContent = `#!/bin/sh
34115
34410
  # ZAM Spaced Repetition Auto-Stale Hook
34116
34411
  # Triggered automatically on git commits to decay modified concept cards.
34117
34412
  zam git-sync --commit HEAD --quiet
34118
34413
  `;
34119
34414
  try {
34120
- writeFileSync12(hookPath, hookContent, { encoding: "utf-8", flag: "w" });
34415
+ writeFileSync13(hookPath, hookContent, { encoding: "utf-8", flag: "w" });
34121
34416
  try {
34122
- chmodSync2(hookPath, "755");
34417
+ chmodSync3(hookPath, "755");
34123
34418
  } catch (_e) {
34124
34419
  }
34125
34420
  console.log(
@@ -34220,8 +34515,8 @@ ZAM Auto-Stale Complete: Scanned ${changedFiles.length} file(s).`
34220
34515
 
34221
34516
  // src/cli/commands/goal.ts
34222
34517
  init_kernel();
34223
- import { existsSync as existsSync21, mkdirSync as mkdirSync15 } from "fs";
34224
- import { resolve as resolve6 } from "path";
34518
+ import { existsSync as existsSync23, mkdirSync as mkdirSync16 } from "fs";
34519
+ import { resolve as resolve7 } from "path";
34225
34520
  import { input as input2 } from "@inquirer/prompts";
34226
34521
  import { Command as Command7 } from "commander";
34227
34522
  async function resolveGoalsDir() {
@@ -34234,7 +34529,7 @@ async function resolveGoalsDir() {
34234
34529
  } finally {
34235
34530
  await db?.close();
34236
34531
  }
34237
- return goalsDir ? resolve6(goalsDir) : resolve6("goals");
34532
+ return goalsDir ? resolve7(goalsDir) : resolve7("goals");
34238
34533
  }
34239
34534
  var goalCommand = new Command7("goal").description(
34240
34535
  "Manage learning goals (markdown files)"
@@ -34244,7 +34539,7 @@ goalCommand.command("list").description("List all goals").option(
34244
34539
  "Filter by status (active, completed, paused, abandoned)"
34245
34540
  ).option("--tree", "Show goals as a tree with parent/child relationships").option("--json", "Output as JSON").action(async (opts) => {
34246
34541
  const goalsDir = await resolveGoalsDir();
34247
- if (!existsSync21(goalsDir)) {
34542
+ if (!existsSync23(goalsDir)) {
34248
34543
  console.error(`Goals directory not found: ${goalsDir}`);
34249
34544
  console.error(
34250
34545
  "Set it with: zam settings set personal.goals_dir /path/to/goals"
@@ -34345,8 +34640,8 @@ ${"\u2500".repeat(50)}`);
34345
34640
  });
34346
34641
  goalCommand.command("create").description("Create a new goal").option("--slug <slug>", "Goal slug (used as filename)").option("--title <title>", "Goal title").option("--parent <slug>", "Parent goal slug").option("--description <text>", "Goal description").option("--json", "Output as JSON").action(async (opts) => {
34347
34642
  const goalsDir = await resolveGoalsDir();
34348
- if (!existsSync21(goalsDir)) {
34349
- mkdirSync15(goalsDir, { recursive: true });
34643
+ if (!existsSync23(goalsDir)) {
34644
+ mkdirSync16(goalsDir, { recursive: true });
34350
34645
  }
34351
34646
  let slug = opts.slug;
34352
34647
  let title = opts.title;
@@ -34406,22 +34701,22 @@ goalCommand.command("status <slug> <status>").description("Update a goal's statu
34406
34701
 
34407
34702
  // src/cli/commands/init.ts
34408
34703
  init_kernel();
34409
- import { existsSync as existsSync22, mkdirSync as mkdirSync16, writeFileSync as writeFileSync13 } from "fs";
34410
- import { homedir as homedir14 } from "os";
34411
- import { join as join24, resolve as resolve7 } from "path";
34704
+ import { existsSync as existsSync24, mkdirSync as mkdirSync17, writeFileSync as writeFileSync14 } from "fs";
34705
+ import { homedir as homedir15 } from "os";
34706
+ import { join as join25, resolve as resolve8 } from "path";
34412
34707
  import { confirm, input as input3 } from "@inquirer/prompts";
34413
34708
  import { Command as Command8 } from "commander";
34414
- var HOME2 = homedir14();
34709
+ var HOME2 = homedir15();
34415
34710
  function printLine(char = "\u2550", len = 60, color = "\x1B[36m") {
34416
34711
  console.log(`${color}${char.repeat(len)}\x1B[0m`);
34417
34712
  }
34418
34713
  function bootstrapSandboxWorkspace(workspaceDir) {
34419
- mkdirSync16(join24(workspaceDir, "beliefs"), { recursive: true });
34420
- mkdirSync16(join24(workspaceDir, "goals"), { recursive: true });
34421
- mkdirSync16(join24(workspaceDir, "skills"), { recursive: true });
34422
- const worldviewFile = join24(workspaceDir, "beliefs", "worldview.md");
34423
- if (!existsSync22(worldviewFile)) {
34424
- writeFileSync13(
34714
+ mkdirSync17(join25(workspaceDir, "beliefs"), { recursive: true });
34715
+ mkdirSync17(join25(workspaceDir, "goals"), { recursive: true });
34716
+ mkdirSync17(join25(workspaceDir, "skills"), { recursive: true });
34717
+ const worldviewFile = join25(workspaceDir, "beliefs", "worldview.md");
34718
+ if (!existsSync24(worldviewFile)) {
34719
+ writeFileSync14(
34425
34720
  worldviewFile,
34426
34721
  `# Personal Worldview
34427
34722
 
@@ -34433,9 +34728,9 @@ Here, I declare the core concepts and principles I want to master.
34433
34728
  "utf8"
34434
34729
  );
34435
34730
  }
34436
- const goalsFile = join24(workspaceDir, "goals", "goals.md");
34437
- if (!existsSync22(goalsFile)) {
34438
- writeFileSync13(
34731
+ const goalsFile = join25(workspaceDir, "goals", "goals.md");
34732
+ if (!existsSync24(goalsFile)) {
34733
+ writeFileSync14(
34439
34734
  goalsFile,
34440
34735
  `# Personal Goals
34441
34736
 
@@ -34457,8 +34752,8 @@ var initCommand = new Command8("init").description("Launch the guided interactiv
34457
34752
  );
34458
34753
  printLine();
34459
34754
  console.log("\n\x1B[1m[1/5] Setting up Local Workspace Sandbox\x1B[0m");
34460
- const defaultWorkspace = join24(HOME2, "Documents", "zam");
34461
- const workspacePath = resolve7(
34755
+ const defaultWorkspace = join25(HOME2, "Documents", "zam");
34756
+ const workspacePath = resolve8(
34462
34757
  await input3({
34463
34758
  message: "Choose your ZAM workspace directory:",
34464
34759
  default: defaultWorkspace
@@ -35659,7 +35954,7 @@ observerCommand.command("revoke <process>").description("Remove a process from o
35659
35954
 
35660
35955
  // src/cli/commands/profile.ts
35661
35956
  init_kernel();
35662
- import { dirname as dirname10, resolve as resolve8 } from "path";
35957
+ import { dirname as dirname10, resolve as resolve9 } from "path";
35663
35958
  import { Command as Command13 } from "commander";
35664
35959
  var C2 = {
35665
35960
  reset: "\x1B[0m",
@@ -35687,7 +35982,7 @@ var profileCommand = new Command13("profile").description("Show or change this m
35687
35982
  if (opts.mode) setInstallMode(opts.mode);
35688
35983
  db = await openDatabaseWithSync({ initialize: true });
35689
35984
  if (opts.dir) {
35690
- await activateWorkspacePath(db, resolve8(opts.dir), {
35985
+ await activateWorkspacePath(db, resolve9(opts.dir), {
35691
35986
  kind: "personal",
35692
35987
  label: "Personal"
35693
35988
  });
@@ -36089,7 +36384,7 @@ ${"\u2550".repeat(50)}`);
36089
36384
 
36090
36385
  // src/cli/commands/session.ts
36091
36386
  init_kernel();
36092
- import { readFileSync as readFileSync17 } from "fs";
36387
+ import { readFileSync as readFileSync18 } from "fs";
36093
36388
  import { input as input6, select as select2 } from "@inquirer/prompts";
36094
36389
  import { Command as Command16 } from "commander";
36095
36390
  var sessionCommand = new Command16("session").description(
@@ -36280,7 +36575,7 @@ function loadPatternFile(path) {
36280
36575
  if (!path) return [];
36281
36576
  let parsed;
36282
36577
  try {
36283
- parsed = JSON.parse(readFileSync17(path, "utf-8"));
36578
+ parsed = JSON.parse(readFileSync18(path, "utf-8"));
36284
36579
  } catch (err) {
36285
36580
  throw new Error(
36286
36581
  `Cannot read synthesis patterns from ${path}: ${err.message}`
@@ -36471,7 +36766,7 @@ sessionCommand.command("end").description("End a session and show summary").requ
36471
36766
 
36472
36767
  // src/cli/commands/settings.ts
36473
36768
  init_kernel();
36474
- import { existsSync as existsSync23 } from "fs";
36769
+ import { existsSync as existsSync25 } from "fs";
36475
36770
  import { Command as Command17 } from "commander";
36476
36771
  var settingsCommand = new Command17("settings").description(
36477
36772
  "Manage user settings"
@@ -36636,7 +36931,7 @@ settingsCommand.command("repos").description("Show or set Personal, Team, and Or
36636
36931
  console.log("\nValidation:");
36637
36932
  for (const [name, path] of Object.entries(paths)) {
36638
36933
  if (path) {
36639
- const exists = existsSync23(path);
36934
+ const exists = existsSync25(path);
36640
36935
  console.log(
36641
36936
  ` ${name.padEnd(9)}: ${exists ? "\x1B[32m\u2713 Valid folder\x1B[0m" : "\x1B[31m\u2717 Directory does not exist\x1B[0m"}`
36642
36937
  );
@@ -36649,7 +36944,7 @@ settingsCommand.command("repos").description("Show or set Personal, Team, and Or
36649
36944
 
36650
36945
  // src/cli/commands/setup.ts
36651
36946
  init_kernel();
36652
- import { resolve as resolve9 } from "path";
36947
+ import { resolve as resolve10 } from "path";
36653
36948
  import { Command as Command18 } from "commander";
36654
36949
  function formatDatabaseInitTarget(target) {
36655
36950
  switch (target.kind) {
@@ -36749,7 +37044,7 @@ var setupCommand = new Command18("setup").description(
36749
37044
  console.error(`Error: ${err.message}`);
36750
37045
  process.exit(1);
36751
37046
  }
36752
- const target = resolve9(opts.target ?? process.cwd());
37047
+ const target = resolve10(opts.target ?? process.cwd());
36753
37048
  const updateExistingInstructions = Boolean(opts.target) || opts.force;
36754
37049
  console.log(
36755
37050
  `Setting up ZAM in ${target}${opts.dryRun ? " (dry run)" : ""}
@@ -36874,8 +37169,8 @@ skillCommand.command("add").description("Register a new agent skill").requiredOp
36874
37169
 
36875
37170
  // src/cli/commands/snapshot.ts
36876
37171
  init_kernel();
36877
- import { existsSync as existsSync24, mkdirSync as mkdirSync17, readFileSync as readFileSync18, writeFileSync as writeFileSync14 } from "fs";
36878
- import { dirname as dirname11, join as join25 } from "path";
37172
+ import { existsSync as existsSync26, mkdirSync as mkdirSync18, readFileSync as readFileSync19, writeFileSync as writeFileSync15 } from "fs";
37173
+ import { dirname as dirname11, join as join26 } from "path";
36879
37174
  import { Command as Command20 } from "commander";
36880
37175
  function defaultOutName() {
36881
37176
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+Z$/, "").replace(/:/g, "-");
@@ -36904,12 +37199,12 @@ var exportCmd = new Command20("export").description("Write a portable SQL-text s
36904
37199
  process.stdout.write(snapshot);
36905
37200
  return;
36906
37201
  }
36907
- const out = opts.out ?? join25(personalDir, "snapshots", defaultOutName());
37202
+ const out = opts.out ?? join26(personalDir, "snapshots", defaultOutName());
36908
37203
  const dir = dirname11(out);
36909
- if (dir && dir !== "." && !existsSync24(dir)) {
36910
- mkdirSync17(dir, { recursive: true });
37204
+ if (dir && dir !== "." && !existsSync26(dir)) {
37205
+ mkdirSync18(dir, { recursive: true });
36911
37206
  }
36912
- writeFileSync14(out, snapshot, "utf-8");
37207
+ writeFileSync15(out, snapshot, "utf-8");
36913
37208
  console.log(`Snapshot written: ${out}`);
36914
37209
  console.log(
36915
37210
  ` ${total} row(s)${nonEmpty.length ? ` \u2014 ${nonEmpty.join(", ")}` : ""}`
@@ -36923,11 +37218,11 @@ var exportCmd = new Command20("export").description("Write a portable SQL-text s
36923
37218
  var importCmd = new Command20("import").description("Restore a snapshot into the database").argument("<file>", "Snapshot file to restore").option("--force", "Overwrite a non-empty database", false).action(async (file, opts) => {
36924
37219
  let db;
36925
37220
  try {
36926
- if (!existsSync24(file)) {
37221
+ if (!existsSync26(file)) {
36927
37222
  console.error(`Error: Snapshot file not found: ${file}`);
36928
37223
  process.exit(1);
36929
37224
  }
36930
- const snapshot = readFileSync18(file, "utf-8");
37225
+ const snapshot = readFileSync19(file, "utf-8");
36931
37226
  db = await openDatabaseWithSync({ initialize: true });
36932
37227
  const result = await importSnapshot(db, snapshot, { force: opts.force });
36933
37228
  await db.close();
@@ -36945,11 +37240,11 @@ var importCmd = new Command20("import").description("Restore a snapshot into the
36945
37240
  });
36946
37241
  var verifyCmd = new Command20("verify").description("Check a snapshot's manifest and checksum without importing").argument("<file>", "Snapshot file to verify").action((file) => {
36947
37242
  try {
36948
- if (!existsSync24(file)) {
37243
+ if (!existsSync26(file)) {
36949
37244
  console.error(`Error: Snapshot file not found: ${file}`);
36950
37245
  process.exit(1);
36951
37246
  }
36952
- const manifest = verifySnapshot(readFileSync18(file, "utf-8"));
37247
+ const manifest = verifySnapshot(readFileSync19(file, "utf-8"));
36953
37248
  const { total, nonEmpty } = summarize(manifest.tables);
36954
37249
  console.log(`Valid snapshot (format v${manifest.version})`);
36955
37250
  console.log(` created: ${manifest.createdAt}`);
@@ -37498,9 +37793,9 @@ tokenCommand.command("reembed").description("Backfill or refresh semantic-search
37498
37793
  // src/cli/commands/ui.ts
37499
37794
  init_kernel();
37500
37795
  import { spawn as spawn3, spawnSync } from "child_process";
37501
- import { existsSync as existsSync25 } from "fs";
37502
- import { homedir as homedir15 } from "os";
37503
- import { dirname as dirname12, join as join26 } from "path";
37796
+ import { existsSync as existsSync27 } from "fs";
37797
+ import { homedir as homedir16 } from "os";
37798
+ import { dirname as dirname12, join as join27 } from "path";
37504
37799
  import { fileURLToPath as fileURLToPath6 } from "url";
37505
37800
  import { Command as Command23 } from "commander";
37506
37801
  var C3 = {
@@ -37516,8 +37811,8 @@ function findDesktopDir() {
37516
37811
  for (const start of starts) {
37517
37812
  let dir = start;
37518
37813
  for (let i = 0; i < 10; i++) {
37519
- if (existsSync25(join26(dir, "desktop", "src-tauri", "tauri.conf.json"))) {
37520
- return join26(dir, "desktop");
37814
+ if (existsSync27(join27(dir, "desktop", "src-tauri", "tauri.conf.json"))) {
37815
+ return join27(dir, "desktop");
37521
37816
  }
37522
37817
  const parent = dirname12(dir);
37523
37818
  if (parent === dir) break;
@@ -37527,30 +37822,30 @@ function findDesktopDir() {
37527
37822
  return null;
37528
37823
  }
37529
37824
  function findBuiltApp(desktopDir) {
37530
- const releaseDir = join26(desktopDir, "src-tauri", "target", "release");
37825
+ const releaseDir = join27(desktopDir, "src-tauri", "target", "release");
37531
37826
  if (process.platform === "win32") {
37532
37827
  for (const name of ["ZAM.exe", "zam.exe", "zam-desktop.exe"]) {
37533
- const p = join26(releaseDir, name);
37534
- if (existsSync25(p)) return p;
37828
+ const p = join27(releaseDir, name);
37829
+ if (existsSync27(p)) return p;
37535
37830
  }
37536
37831
  } else if (process.platform === "darwin") {
37537
- const app = join26(releaseDir, "bundle", "macos", "ZAM.app");
37538
- if (existsSync25(app)) return app;
37832
+ const app = join27(releaseDir, "bundle", "macos", "ZAM.app");
37833
+ if (existsSync27(app)) return app;
37539
37834
  } else {
37540
37835
  for (const name of ["zam", "ZAM", "zam-desktop"]) {
37541
- const p = join26(releaseDir, name);
37542
- if (existsSync25(p)) return p;
37836
+ const p = join27(releaseDir, name);
37837
+ if (existsSync27(p)) return p;
37543
37838
  }
37544
37839
  }
37545
37840
  return null;
37546
37841
  }
37547
37842
  function findInstalledApp() {
37548
37843
  const candidates = process.platform === "win32" ? [
37549
- process.env.LOCALAPPDATA && join26(process.env.LOCALAPPDATA, "Programs", "ZAM", "ZAM.exe"),
37550
- process.env.ProgramFiles && join26(process.env.ProgramFiles, "ZAM", "ZAM.exe"),
37551
- process.env["ProgramFiles(x86)"] && join26(process.env["ProgramFiles(x86)"], "ZAM", "ZAM.exe")
37552
- ] : process.platform === "darwin" ? ["/Applications/ZAM.app", join26(homedir15(), "Applications", "ZAM.app")] : ["/opt/ZAM/zam", "/usr/bin/zam-desktop"];
37553
- return candidates.find((candidate) => candidate && existsSync25(candidate)) || null;
37844
+ process.env.LOCALAPPDATA && join27(process.env.LOCALAPPDATA, "Programs", "ZAM", "ZAM.exe"),
37845
+ process.env.ProgramFiles && join27(process.env.ProgramFiles, "ZAM", "ZAM.exe"),
37846
+ process.env["ProgramFiles(x86)"] && join27(process.env["ProgramFiles(x86)"], "ZAM", "ZAM.exe")
37847
+ ] : process.platform === "darwin" ? ["/Applications/ZAM.app", join27(homedir16(), "Applications", "ZAM.app")] : ["/opt/ZAM/zam", "/usr/bin/zam-desktop"];
37848
+ return candidates.find((candidate) => candidate && existsSync27(candidate)) || null;
37554
37849
  }
37555
37850
  function runNpm(args, opts) {
37556
37851
  const res = spawnSync("npm", args, {
@@ -37561,7 +37856,7 @@ function runNpm(args, opts) {
37561
37856
  return res.status ?? 1;
37562
37857
  }
37563
37858
  function ensureDesktopDeps(desktopDir) {
37564
- if (existsSync25(join26(desktopDir, "node_modules"))) return true;
37859
+ if (existsSync27(join27(desktopDir, "node_modules"))) return true;
37565
37860
  console.log(
37566
37861
  `${C3.cyan}Installing desktop dependencies (one-time)...${C3.reset}`
37567
37862
  );
@@ -37587,13 +37882,13 @@ function requireRust() {
37587
37882
  function hasMsvcBuildTools() {
37588
37883
  if (process.platform !== "win32") return true;
37589
37884
  const pf86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
37590
- const vswhere = join26(
37885
+ const vswhere = join27(
37591
37886
  pf86,
37592
37887
  "Microsoft Visual Studio",
37593
37888
  "Installer",
37594
37889
  "vswhere.exe"
37595
37890
  );
37596
- if (!existsSync25(vswhere)) return false;
37891
+ if (!existsSync27(vswhere)) return false;
37597
37892
  const res = spawnSync(
37598
37893
  vswhere,
37599
37894
  [
@@ -37625,7 +37920,7 @@ function requireMsvcOnWindows() {
37625
37920
  return false;
37626
37921
  }
37627
37922
  function warnIfCliMissing(repoRoot) {
37628
- if (!existsSync25(join26(repoRoot, "dist", "cli", "index.js"))) {
37923
+ if (!existsSync27(join27(repoRoot, "dist", "cli", "index.js"))) {
37629
37924
  console.warn(
37630
37925
  `${C3.yellow}\u26A0 CLI build not found (dist/cli/index.js). The GUI bridge needs it \u2014 run 'npm run build' at the repo root.${C3.reset}`
37631
37926
  );
@@ -37688,7 +37983,7 @@ var uiCommand = new Command23("ui").description("Launch the ZAM Desktop GUI (Act
37688
37983
  ).option("--shortcut", "Create Desktop + Start-menu shortcuts to the GUI").action((opts) => {
37689
37984
  const installedApp = findInstalledApp();
37690
37985
  if (!opts.dev && !opts.build && !opts.shortcut && installedApp) {
37691
- launchApp(installedApp, homedir15());
37986
+ launchApp(installedApp, homedir16());
37692
37987
  return;
37693
37988
  }
37694
37989
  const desktopDir = findDesktopDir();
@@ -37709,7 +38004,7 @@ var uiCommand = new Command23("ui").description("Launch the ZAM Desktop GUI (Act
37709
38004
  );
37710
38005
  const code = runNpm(["run", "tauri", "build"], { cwd: desktopDir });
37711
38006
  if (code === 0) {
37712
- const bundle = join26(
38007
+ const bundle = join27(
37713
38008
  desktopDir,
37714
38009
  "src-tauri",
37715
38010
  "target",
@@ -37787,8 +38082,8 @@ ${C3.dim}After that, 'zam ui' launches it directly.${C3.reset}`
37787
38082
  // src/cli/commands/update.ts
37788
38083
  init_kernel();
37789
38084
  import { spawnSync as spawnSync2 } from "child_process";
37790
- import { existsSync as existsSync26, readFileSync as readFileSync19, realpathSync as realpathSync2 } from "fs";
37791
- import { dirname as dirname13, join as join27 } from "path";
38085
+ import { existsSync as existsSync28, readFileSync as readFileSync20, realpathSync as realpathSync2 } from "fs";
38086
+ import { dirname as dirname13, join as join28 } from "path";
37792
38087
  import { fileURLToPath as fileURLToPath7 } from "url";
37793
38088
  import { confirm as confirm3 } from "@inquirer/prompts";
37794
38089
  import { Command as Command24 } from "commander";
@@ -37810,7 +38105,7 @@ var C4 = {
37810
38105
  function versionAt(dir) {
37811
38106
  try {
37812
38107
  const pkg2 = JSON.parse(
37813
- readFileSync19(join27(dir, "package.json"), "utf-8")
38108
+ readFileSync20(join28(dir, "package.json"), "utf-8")
37814
38109
  );
37815
38110
  return pkg2.version ?? "unknown";
37816
38111
  } catch {
@@ -37871,11 +38166,11 @@ function findSourceRepo() {
37871
38166
  let dir = realpathSync2(dirname13(fileURLToPath7(import.meta.url)));
37872
38167
  let parent = dirname13(dir);
37873
38168
  while (parent !== dir) {
37874
- if (existsSync26(join27(dir, ".git"))) return dir;
38169
+ if (existsSync28(join28(dir, ".git"))) return dir;
37875
38170
  dir = parent;
37876
38171
  parent = dirname13(dir);
37877
38172
  }
37878
- return existsSync26(join27(dir, ".git")) ? dir : null;
38173
+ return existsSync28(join28(dir, ".git")) ? dir : null;
37879
38174
  }
37880
38175
  function runGit(cwd, args, capture) {
37881
38176
  const res = spawnSync2("git", args, {
@@ -37896,7 +38191,7 @@ function runNpm2(args, cwd) {
37896
38191
  function smokeTestBuild(src) {
37897
38192
  const res = spawnSync2(
37898
38193
  process.execPath,
37899
- [join27(src, "dist", "cli", "index.js"), "--version"],
38194
+ [join28(src, "dist", "cli", "index.js"), "--version"],
37900
38195
  {
37901
38196
  cwd: src,
37902
38197
  encoding: "utf8",
@@ -37976,7 +38271,7 @@ Fix it manually in ${src} (try \`npm ci && npm run build\`, and check your Node
37976
38271
  console.log(`${C4.dim}\u2192 zam setup --force${C4.reset}`);
37977
38272
  const setup = spawnSync2(
37978
38273
  process.execPath,
37979
- [join27(src, "dist", "cli", "index.js"), "setup", "--force"],
38274
+ [join28(src, "dist", "cli", "index.js"), "setup", "--force"],
37980
38275
  { cwd: process.cwd(), stdio: "inherit" }
37981
38276
  );
37982
38277
  if (setup.status !== 0) {
@@ -38088,15 +38383,15 @@ var whoamiCommand = new Command25("whoami").description("Show or set the default
38088
38383
 
38089
38384
  // src/cli/commands/workspace.ts
38090
38385
  init_kernel();
38091
- import { execFileSync as execFileSync5 } from "child_process";
38092
- import { existsSync as existsSync27, writeFileSync as writeFileSync15 } from "fs";
38093
- import { homedir as homedir16 } from "os";
38094
- import { join as join28, resolve as resolve10 } from "path";
38386
+ import { execFileSync as execFileSync6 } from "child_process";
38387
+ import { existsSync as existsSync29, writeFileSync as writeFileSync16 } from "fs";
38388
+ import { homedir as homedir17 } from "os";
38389
+ import { join as join29, resolve as resolve11 } from "path";
38095
38390
  import { confirm as confirm4, input as input7 } from "@inquirer/prompts";
38096
38391
  import { Command as Command26 } from "commander";
38097
38392
  function runGit2(cwd, args) {
38098
38393
  try {
38099
- return execFileSync5("git", args, {
38394
+ return execFileSync6("git", args, {
38100
38395
  cwd,
38101
38396
  stdio: "pipe",
38102
38397
  encoding: "utf8"
@@ -38175,7 +38470,7 @@ workspaceCommand.command("list").description("List configured ZAM workspaces").o
38175
38470
  const workspaces = getConfiguredWorkspaces();
38176
38471
  const agents = parseSetupAgents();
38177
38472
  const linkHealth = Object.fromEntries(
38178
- workspaces.filter((workspace) => existsSync27(workspace.path)).map((workspace) => [
38473
+ workspaces.filter((workspace) => existsSync29(workspace.path)).map((workspace) => [
38179
38474
  workspace.id,
38180
38475
  summarizeSkillLinkHealth(inspectSkillLinks(workspace.path, agents))
38181
38476
  ])
@@ -38217,8 +38512,8 @@ workspaceCommand.command("add <id>").description("Register an existing directory
38217
38512
  `Source-control provider (${WORKSPACE_SOURCE_CONTROLS.join(" | ")})`
38218
38513
  ).option("--scopes <list>", "Comma-separated knowledge scopes").option("--default-agent <id>", "Default agent harness for this workspace").action((id, opts) => {
38219
38514
  try {
38220
- const path = resolve10(String(opts.path));
38221
- if (!existsSync27(path)) {
38515
+ const path = resolve11(String(opts.path));
38516
+ if (!existsSync29(path)) {
38222
38517
  console.error(`Workspace path does not exist: ${path}`);
38223
38518
  process.exit(1);
38224
38519
  }
@@ -38312,7 +38607,7 @@ workspaceCommand.command("publish").description("Publish your local workspace sa
38312
38607
  );
38313
38608
  process.exit(1);
38314
38609
  }
38315
- if (!existsSync27(workspaceDir)) {
38610
+ if (!existsSync29(workspaceDir)) {
38316
38611
  console.error(
38317
38612
  `\x1B[31m\u2717 Workspace directory does not exist: ${workspaceDir}\x1B[0m`
38318
38613
  );
@@ -38326,15 +38621,15 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
38326
38621
  );
38327
38622
  process.exit(1);
38328
38623
  }
38329
- const gitignorePath = join28(workspaceDir, ".gitignore");
38330
- if (!existsSync27(gitignorePath)) {
38331
- writeFileSync15(
38624
+ const gitignorePath = join29(workspaceDir, ".gitignore");
38625
+ if (!existsSync29(gitignorePath)) {
38626
+ writeFileSync16(
38332
38627
  gitignorePath,
38333
38628
  "node_modules/\n.agent/\n.agents/\n.claude/\n.gemini/\n.goose/\n*.log\n",
38334
38629
  "utf8"
38335
38630
  );
38336
38631
  }
38337
- const hasGitRepo = existsSync27(join28(workspaceDir, ".git"));
38632
+ const hasGitRepo = existsSync29(join29(workspaceDir, ".git"));
38338
38633
  if (!hasGitRepo) {
38339
38634
  console.log("Initializing local Git repository...");
38340
38635
  runGit2(workspaceDir, ["init", "-b", "main"]);
@@ -38366,7 +38661,7 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
38366
38661
  if (proceedGh) {
38367
38662
  try {
38368
38663
  console.log(`Creating GitHub repository ${repoName}...`);
38369
- execFileSync5("gh", ghRepoCreateArgs(repoName, repoVisibility), {
38664
+ execFileSync6("gh", ghRepoCreateArgs(repoName, repoVisibility), {
38370
38665
  cwd: workspaceDir,
38371
38666
  stdio: "inherit"
38372
38667
  });
@@ -38421,7 +38716,7 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
38421
38716
  }
38422
38717
  });
38423
38718
  workspaceCommand.command("data-dir").description("Print the ZAM data directory (database, credentials, config)").option("--json", "Output as JSON").action((opts) => {
38424
- const dir = join28(homedir16(), ".zam");
38719
+ const dir = join29(homedir17(), ".zam");
38425
38720
  console.log(opts.json ? JSON.stringify({ dataDir: dir }) : dir);
38426
38721
  });
38427
38722
  workspaceCommand.command("backup").description("Back up the local ZAM database into your workspace").option(
@@ -38464,7 +38759,7 @@ workspaceCommand.command("backup").description("Back up the local ZAM database i
38464
38759
  // src/cli/app.ts
38465
38760
  var __dirname = dirname14(fileURLToPath8(import.meta.url));
38466
38761
  var pkg = JSON.parse(
38467
- readFileSync20(join29(__dirname, "..", "..", "package.json"), "utf-8")
38762
+ readFileSync21(join30(__dirname, "..", "..", "package.json"), "utf-8")
38468
38763
  );
38469
38764
  var program = new Command27();
38470
38765
  program.name("zam").description(