zam-core 0.4.1 → 0.4.2

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/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/cli/index.ts
4
4
  import { readFileSync as readFileSync14 } from "fs";
5
- import { dirname as dirname10, join as join22 } from "path";
5
+ import { dirname as dirname10, join as join23 } from "path";
6
6
  import { fileURLToPath as fileURLToPath5 } from "url";
7
7
  import { Command as Command24 } from "commander";
8
8
 
@@ -4932,10 +4932,10 @@ var agentCommand = new Command("agent").description("Provision and inspect the a
4932
4932
 
4933
4933
  // src/cli/commands/bridge.ts
4934
4934
  import { execFileSync as execFileSync2 } from "child_process";
4935
- import { randomBytes } from "crypto";
4935
+ import { randomBytes as randomBytes2 } from "crypto";
4936
4936
  import { readdirSync as readdirSync2, readFileSync as readFileSync10, rmSync as rmSync2 } from "fs";
4937
- import { homedir as homedir8, tmpdir } from "os";
4938
- import { join as join12 } from "path";
4937
+ import { homedir as homedir8, tmpdir as tmpdir2 } from "os";
4938
+ import { join as join13 } from "path";
4939
4939
  import { Command as Command2 } from "commander";
4940
4940
 
4941
4941
  // src/cli/llm/client.ts
@@ -5498,7 +5498,9 @@ async function ensureHighQualityQuestion(db, token) {
5498
5498
 
5499
5499
  // src/cli/llm/vision.ts
5500
5500
  import { readFileSync as readFileSync9 } from "fs";
5501
- import { basename as basename2 } from "path";
5501
+ import { randomBytes } from "crypto";
5502
+ import { tmpdir } from "os";
5503
+ import { basename as basename2, join as join12 } from "path";
5502
5504
  var LANGUAGE_NAMES2 = {
5503
5505
  en: "English",
5504
5506
  de: "German",
@@ -5531,15 +5533,61 @@ async function observeUiSnapshotViaLLM(db, input8) {
5531
5533
  "Vision observation is disabled in settings (llm.vision.enabled)"
5532
5534
  );
5533
5535
  }
5534
- const imageBytes = readFileSync9(input8.imagePath);
5535
- const imageUrl = `data:image/png;base64,${imageBytes.toString("base64")}`;
5536
+ const imageUrls = [];
5537
+ const isVideo = /\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(input8.imagePath);
5538
+ if (isVideo) {
5539
+ const { mkdirSync: mkdirSync12, readdirSync: readdirSync3, rmSync: rmSync3 } = await import("fs");
5540
+ const { execSync: execSync7 } = await import("child_process");
5541
+ const tempDir = join12(tmpdir(), `zam-frames-${randomBytes(4).toString("hex")}`);
5542
+ mkdirSync12(tempDir, { recursive: true });
5543
+ try {
5544
+ execSync7(
5545
+ `ffmpeg -i "${input8.imagePath}" -vf "fps=1/3,scale=1280:-1" -vsync vfr "${tempDir}/frame_%03d.png"`,
5546
+ { stdio: "ignore" }
5547
+ );
5548
+ let files = readdirSync3(tempDir).filter((f) => f.endsWith(".png")).sort();
5549
+ if (files.length === 0) {
5550
+ execSync7(
5551
+ `ffmpeg -i "${input8.imagePath}" -vframes 1 "${tempDir}/frame_001.png"`,
5552
+ { stdio: "ignore" }
5553
+ );
5554
+ files = readdirSync3(tempDir).filter((f) => f.endsWith(".png")).sort();
5555
+ }
5556
+ let sampledFiles = files;
5557
+ if (files.length > 12) {
5558
+ const step = (files.length - 1) / 11;
5559
+ sampledFiles = [];
5560
+ for (let i = 0; i < 12; i++) {
5561
+ const index = Math.round(i * step);
5562
+ sampledFiles.push(files[index]);
5563
+ }
5564
+ }
5565
+ for (const file of sampledFiles) {
5566
+ const bytes = readFileSync9(join12(tempDir, file));
5567
+ imageUrls.push(`data:image/png;base64,${bytes.toString("base64")}`);
5568
+ }
5569
+ } finally {
5570
+ try {
5571
+ rmSync3(tempDir, { recursive: true, force: true });
5572
+ } catch {
5573
+ }
5574
+ }
5575
+ } else {
5576
+ const imageBytes = readFileSync9(input8.imagePath);
5577
+ const ext = input8.imagePath.split(".").pop()?.toLowerCase();
5578
+ const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : "image/png";
5579
+ imageUrls.push(`data:${mime};base64,${imageBytes.toString("base64")}`);
5580
+ }
5581
+ if (imageUrls.length === 0) {
5582
+ throw new Error("No image data available for vision analysis");
5583
+ }
5536
5584
  const model = input8.model ?? cfg.model;
5537
5585
  const content = await requestVisionDraft({
5538
5586
  url: cfg.url,
5539
5587
  apiKey: cfg.apiKey || DEFAULT_LLM_API_KEY,
5540
5588
  model,
5541
5589
  locale: cfg.locale,
5542
- imageUrl,
5590
+ imageUrls,
5543
5591
  input: input8
5544
5592
  });
5545
5593
  let draft;
@@ -5589,17 +5637,22 @@ async function requestVisionDraft(args) {
5589
5637
  content: [
5590
5638
  {
5591
5639
  type: "text",
5592
- text: `Observe this Windows application snapshot for a learning session.
5640
+ text: args.imageUrls.length > 1 ? `Observe this sequence of Windows/macOS application snapshots showing a task performed over time.
5641
+ Application process: ${args.input.application.processName}
5642
+ Window title: ${args.input.application.windowTitle ?? "(unknown)"}
5643
+
5644
+ Return this JSON draft only:
5645
+ ${schema}` : `Observe this Windows/macOS application snapshot for a learning session.
5593
5646
  Application process: ${args.input.application.processName}
5594
5647
  Window title: ${args.input.application.windowTitle ?? "(unknown)"}
5595
5648
 
5596
5649
  Return this JSON draft only:
5597
5650
  ${schema}`
5598
5651
  },
5599
- {
5652
+ ...args.imageUrls.map((url) => ({
5600
5653
  type: "image_url",
5601
- image_url: { url: args.imageUrl }
5602
- }
5654
+ image_url: { url }
5655
+ }))
5603
5656
  ]
5604
5657
  }
5605
5658
  ],
@@ -6239,7 +6292,7 @@ bridgeCommand.command("discover-skills").description(
6239
6292
  "20"
6240
6293
  ).action(async (opts) => {
6241
6294
  try {
6242
- const monitorDir = join12(homedir8(), ".zam", "monitor");
6295
+ const monitorDir = join13(homedir8(), ".zam", "monitor");
6243
6296
  let files;
6244
6297
  try {
6245
6298
  files = readdirSync2(monitorDir).filter((f) => f.endsWith(".jsonl"));
@@ -6252,7 +6305,7 @@ bridgeCommand.command("discover-skills").description(
6252
6305
  return;
6253
6306
  }
6254
6307
  const limit = Number(opts.limit);
6255
- const sorted = files.map((f) => ({ name: f, path: join12(monitorDir, f) })).sort((a, b) => b.name.localeCompare(a.name)).slice(0, limit);
6308
+ const sorted = files.map((f) => ({ name: f, path: join13(monitorDir, f) })).sort((a, b) => b.name.localeCompare(a.name)).slice(0, limit);
6256
6309
  const sessionCommands = /* @__PURE__ */ new Map();
6257
6310
  for (const file of sorted) {
6258
6311
  const sessionId = file.name.replace(".jsonl", "");
@@ -6332,8 +6385,8 @@ bridgeCommand.command("get-observations").description("Read UI observer reports
6332
6385
  }
6333
6386
  });
6334
6387
  bridgeCommand.command("observe-ui-snapshot").description(
6335
- "Analyze a captured UI snapshot with the configured vision LLM (JSON)"
6336
- ).requiredOption("--session <id>", "Observer session ID").requiredOption("--sequence <n>", "Monotonic observation sequence number").requiredOption("--image <path>", "PNG snapshot path").requiredOption("--observed-from <iso>", "Observation window start time").requiredOption("--observed-to <iso>", "Observation window end time").requiredOption("--process-name <name>", "Observed application process name").option("--process-id <n>", "Observed application process ID").option("--window-title <title>", "Observed window title").option("--evidence-ref <ref>", "Evidence reference to put in the report").option("--model <model>", "Override configured LLM model for this request").option("--max-tokens <n>", "Model response token budget").option("--timeout <ms>", "Hard request timeout in milliseconds").option("--redacted", "Mark the snapshot evidence as redacted").option("--write-log", "Append the generated report to the session JSONL").action(async (opts) => {
6388
+ "Analyze a captured UI snapshot or video recording with the configured vision LLM (JSON)"
6389
+ ).requiredOption("--session <id>", "Observer session ID").requiredOption("--sequence <n>", "Monotonic observation sequence number").requiredOption("--image <path>", "PNG snapshot or video recording path").requiredOption("--observed-from <iso>", "Observation window start time").requiredOption("--observed-to <iso>", "Observation window end time").requiredOption("--process-name <name>", "Observed application process name").option("--process-id <n>", "Observed application process ID").option("--window-title <title>", "Observed window title").option("--evidence-ref <ref>", "Evidence reference to put in the report").option("--model <model>", "Override configured LLM model for this request").option("--max-tokens <n>", "Model response token budget").option("--timeout <ms>", "Hard request timeout in milliseconds").option("--redacted", "Mark the snapshot evidence as redacted").option("--write-log", "Append the generated report to the session JSONL").action(async (opts) => {
6337
6390
  const sequence = parseNonNegativeIntegerOption("sequence", opts.sequence);
6338
6391
  const processId = opts.processId === void 0 ? void 0 : parseNonNegativeIntegerOption("process-id", opts.processId);
6339
6392
  const maxTokens = opts.maxTokens === void 0 ? void 0 : parseNonNegativeIntegerOption("max-tokens", opts.maxTokens);
@@ -6754,7 +6807,7 @@ bridgeCommand.command("capture-ui").description("Capture a screenshot for agent-
6754
6807
  return;
6755
6808
  }
6756
6809
  }
6757
- const outputPath = opts.image ?? opts.output ?? join12(tmpdir(), `zam-capture-${randomBytes(4).toString("hex")}.png`);
6810
+ const outputPath = opts.image ?? opts.output ?? join13(tmpdir2(), `zam-capture-${randomBytes2(4).toString("hex")}.png`);
6758
6811
  const captureResult = isProvided ? { method: "provided", target: null } : captureScreenshot(outputPath, opts.hwnd, opts.processName);
6759
6812
  if (!isProvided) {
6760
6813
  const post = decidePostCapture(policy, {
@@ -6798,6 +6851,177 @@ bridgeCommand.command("capture-ui").description("Capture a screenshot for agent-
6798
6851
  });
6799
6852
  });
6800
6853
  });
6854
+ bridgeCommand.command("start-recording").description("Start screen recording in the background (macOS only) (JSON)").requiredOption("--session <id>", "ZAM session ID").option("--output <path>", "Video output path").action(async (opts) => {
6855
+ const platform = process.platform;
6856
+ if (platform !== "darwin") {
6857
+ jsonOut2({
6858
+ sessionId: opts.session,
6859
+ started: false,
6860
+ error: "Screen recording is only supported on macOS (darwin)"
6861
+ });
6862
+ return;
6863
+ }
6864
+ const sessionId = opts.session;
6865
+ const statePath = join13(tmpdir2(), `zam-recording-${sessionId}.json`);
6866
+ const outputPath = opts.output ?? join13(tmpdir2(), `zam-recording-${sessionId}.mov`);
6867
+ const { existsSync: existsSync22, writeFileSync: writeFileSync12, openSync, closeSync } = await import("fs");
6868
+ if (existsSync22(statePath)) {
6869
+ jsonOut2({
6870
+ sessionId,
6871
+ started: false,
6872
+ error: `Recording is already active for session ${sessionId}`
6873
+ });
6874
+ return;
6875
+ }
6876
+ const logPath = join13(tmpdir2(), `zam-recording-${sessionId}.log`);
6877
+ let logFd;
6878
+ try {
6879
+ logFd = openSync(logPath, "w");
6880
+ } catch (e) {
6881
+ jsonOut2({
6882
+ sessionId,
6883
+ started: false,
6884
+ error: `Failed to open log file at ${logPath}: ${e.message}`
6885
+ });
6886
+ return;
6887
+ }
6888
+ const { spawn: spawn3 } = await import("child_process");
6889
+ const child = spawn3(
6890
+ "ffmpeg",
6891
+ [
6892
+ "-y",
6893
+ "-f",
6894
+ "avfoundation",
6895
+ "-r",
6896
+ "5",
6897
+ "-i",
6898
+ "0",
6899
+ "-pix_fmt",
6900
+ "yuv420p",
6901
+ outputPath
6902
+ ],
6903
+ {
6904
+ detached: true,
6905
+ stdio: ["pipe", logFd, logFd]
6906
+ }
6907
+ );
6908
+ try {
6909
+ closeSync(logFd);
6910
+ } catch {
6911
+ }
6912
+ child.unref();
6913
+ if (child.pid) {
6914
+ writeFileSync12(
6915
+ statePath,
6916
+ JSON.stringify({
6917
+ pid: child.pid,
6918
+ outputPath,
6919
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
6920
+ }),
6921
+ "utf8"
6922
+ );
6923
+ jsonOut2({
6924
+ sessionId,
6925
+ started: true,
6926
+ outputPath,
6927
+ pid: child.pid
6928
+ });
6929
+ } else {
6930
+ jsonOut2({
6931
+ sessionId,
6932
+ started: false,
6933
+ error: "Failed to spawn ffmpeg process"
6934
+ });
6935
+ }
6936
+ });
6937
+ bridgeCommand.command("stop-recording").description(
6938
+ "Stop active screen recording and apply idle-frame compression (macOS only) (JSON)"
6939
+ ).requiredOption("--session <id>", "ZAM session ID").action(async (opts) => {
6940
+ const platform = process.platform;
6941
+ if (platform !== "darwin") {
6942
+ jsonOut2({
6943
+ sessionId: opts.session,
6944
+ stopped: false,
6945
+ error: "Screen recording is only supported on macOS (darwin)"
6946
+ });
6947
+ return;
6948
+ }
6949
+ const sessionId = opts.session;
6950
+ const statePath = join13(tmpdir2(), `zam-recording-${sessionId}.json`);
6951
+ const { existsSync: existsSync22, readFileSync: readFileSync15, rmSync: rmSync3 } = await import("fs");
6952
+ if (!existsSync22(statePath)) {
6953
+ jsonOut2({
6954
+ sessionId,
6955
+ stopped: false,
6956
+ error: `No active recording found for session ${sessionId}`
6957
+ });
6958
+ return;
6959
+ }
6960
+ const state = JSON.parse(readFileSync15(statePath, "utf8"));
6961
+ const { pid, outputPath } = state;
6962
+ try {
6963
+ process.kill(pid, "SIGINT");
6964
+ } catch (e) {
6965
+ }
6966
+ const isProcessRunning = (pId) => {
6967
+ try {
6968
+ process.kill(pId, 0);
6969
+ return true;
6970
+ } catch (e) {
6971
+ return false;
6972
+ }
6973
+ };
6974
+ let attempts = 0;
6975
+ while (isProcessRunning(pid) && attempts < 20) {
6976
+ await new Promise((resolve5) => setTimeout(resolve5, 250));
6977
+ attempts++;
6978
+ }
6979
+ if (isProcessRunning(pid)) {
6980
+ try {
6981
+ process.kill(pid, "SIGKILL");
6982
+ } catch (e) {
6983
+ }
6984
+ }
6985
+ try {
6986
+ rmSync3(statePath, { force: true });
6987
+ } catch {
6988
+ }
6989
+ if (!existsSync22(outputPath)) {
6990
+ jsonOut2({
6991
+ sessionId,
6992
+ stopped: false,
6993
+ error: `Recording file not found at ${outputPath}`
6994
+ });
6995
+ return;
6996
+ }
6997
+ const decimatedPath = outputPath.replace(/\.[^.]+$/, "-decimated.mp4");
6998
+ const { execSync: execSync7 } = await import("child_process");
6999
+ try {
7000
+ execSync7(
7001
+ `ffmpeg -y -i "${outputPath}" -vf "mpdecimate,setpts=N/FRAME_RATE/TB" -an -pix_fmt yuv420p "${decimatedPath}"`,
7002
+ { stdio: "ignore" }
7003
+ );
7004
+ } catch (ffmpegErr) {
7005
+ jsonOut2({
7006
+ sessionId,
7007
+ stopped: true,
7008
+ videoPath: outputPath,
7009
+ decimated: false,
7010
+ warning: `mpdecimate post-processing failed: ${ffmpegErr.message}`
7011
+ });
7012
+ return;
7013
+ }
7014
+ try {
7015
+ rmSync3(outputPath, { force: true });
7016
+ } catch {
7017
+ }
7018
+ jsonOut2({
7019
+ sessionId,
7020
+ stopped: true,
7021
+ videoPath: decimatedPath,
7022
+ decimated: true
7023
+ });
7024
+ });
6801
7025
  bridgeCommand.command("get-observer-policy").description(
6802
7026
  "Report the resolved observer policy so an agent can check before capturing (JSON)"
6803
7027
  ).action(async () => {
@@ -7506,18 +7730,18 @@ async function setupTurso(urlArg, tokenArg, mode) {
7506
7730
  // src/cli/commands/git-sync.ts
7507
7731
  import { execSync as execSync4 } from "child_process";
7508
7732
  import { chmodSync as chmodSync2, existsSync as existsSync13, writeFileSync as writeFileSync6 } from "fs";
7509
- import { join as join13 } from "path";
7733
+ import { join as join14 } from "path";
7510
7734
  import { Command as Command5 } from "commander";
7511
7735
  function installHook2() {
7512
- const gitDir = join13(process.cwd(), ".git");
7736
+ const gitDir = join14(process.cwd(), ".git");
7513
7737
  if (!existsSync13(gitDir)) {
7514
7738
  console.error(
7515
7739
  "Error: Current directory is not the root of a Git repository."
7516
7740
  );
7517
7741
  process.exit(1);
7518
7742
  }
7519
- const hooksDir = join13(gitDir, "hooks");
7520
- const hookPath = join13(hooksDir, "post-commit");
7743
+ const hooksDir = join14(gitDir, "hooks");
7744
+ const hookPath = join14(hooksDir, "post-commit");
7521
7745
  const hookContent = `#!/bin/sh
7522
7746
  # ZAM Spaced Repetition Auto-Stale Hook
7523
7747
  # Triggered automatically on git commits to decay modified concept cards.
@@ -7813,7 +8037,7 @@ goalCommand.command("status <slug> <status>").description("Update a goal's statu
7813
8037
  // src/cli/commands/init.ts
7814
8038
  import { existsSync as existsSync15, mkdirSync as mkdirSync9, writeFileSync as writeFileSync7 } from "fs";
7815
8039
  import { homedir as homedir9 } from "os";
7816
- import { join as join14 } from "path";
8040
+ import { join as join15 } from "path";
7817
8041
  import { confirm, input as input3 } from "@inquirer/prompts";
7818
8042
  import { Command as Command7 } from "commander";
7819
8043
  var HOME2 = homedir9();
@@ -7821,10 +8045,10 @@ function printLine(char = "\u2550", len = 60, color = "\x1B[36m") {
7821
8045
  console.log(`${color}${char.repeat(len)}\x1B[0m`);
7822
8046
  }
7823
8047
  function bootstrapSandboxWorkspace(workspaceDir) {
7824
- mkdirSync9(join14(workspaceDir, "beliefs"), { recursive: true });
7825
- mkdirSync9(join14(workspaceDir, "goals"), { recursive: true });
7826
- mkdirSync9(join14(workspaceDir, "skills"), { recursive: true });
7827
- const worldviewFile = join14(workspaceDir, "beliefs", "worldview.md");
8048
+ mkdirSync9(join15(workspaceDir, "beliefs"), { recursive: true });
8049
+ mkdirSync9(join15(workspaceDir, "goals"), { recursive: true });
8050
+ mkdirSync9(join15(workspaceDir, "skills"), { recursive: true });
8051
+ const worldviewFile = join15(workspaceDir, "beliefs", "worldview.md");
7828
8052
  if (!existsSync15(worldviewFile)) {
7829
8053
  writeFileSync7(
7830
8054
  worldviewFile,
@@ -7838,7 +8062,7 @@ Here, I declare the core concepts and principles I want to master.
7838
8062
  "utf8"
7839
8063
  );
7840
8064
  }
7841
- const goalsFile = join14(workspaceDir, "goals", "goals.md");
8065
+ const goalsFile = join15(workspaceDir, "goals", "goals.md");
7842
8066
  if (!existsSync15(goalsFile)) {
7843
8067
  writeFileSync7(
7844
8068
  goalsFile,
@@ -7862,7 +8086,7 @@ var initCommand = new Command7("init").description("Launch the guided interactiv
7862
8086
  );
7863
8087
  printLine();
7864
8088
  console.log("\n\x1B[1m[1/5] Setting up Local Workspace Sandbox\x1B[0m");
7865
- const defaultWorkspace = join14(HOME2, "Documents", "zam");
8089
+ const defaultWorkspace = join15(HOME2, "Documents", "zam");
7866
8090
  const workspacePath = await input3({
7867
8091
  message: "Choose your ZAM workspace directory:",
7868
8092
  default: defaultWorkspace
@@ -8561,8 +8785,8 @@ ${"\u2550".repeat(50)}`);
8561
8785
  // src/cli/commands/monitor.ts
8562
8786
  import { execFileSync as execFileSync3, execSync as execSync5 } from "child_process";
8563
8787
  import { unlinkSync, writeFileSync as writeFileSync8 } from "fs";
8564
- import { tmpdir as tmpdir2 } from "os";
8565
- import { basename as basename3, join as join15 } from "path";
8788
+ import { tmpdir as tmpdir3 } from "os";
8789
+ import { basename as basename3, join as join16 } from "path";
8566
8790
  import { Command as Command9 } from "commander";
8567
8791
  function isPowerShellShell(shell) {
8568
8792
  return shell === "pwsh" || shell === "powershell";
@@ -8739,8 +8963,8 @@ function resolveZamInvocation(shell) {
8739
8963
  if (installed) {
8740
8964
  return isPowerShellShell(shell) ? `& ${psSingleQuoted2(installed)}` : installed;
8741
8965
  }
8742
- const projectRoot = join15(import.meta.dirname, "..", "..", "..");
8743
- const cliSource = join15(projectRoot, "src/cli/index.ts");
8966
+ const projectRoot = join16(import.meta.dirname, "..", "..", "..");
8967
+ const cliSource = join16(projectRoot, "src/cli/index.ts");
8744
8968
  if (isPowerShellShell(shell)) {
8745
8969
  return `& npx --prefix ${psSingleQuoted2(projectRoot)} tsx ${psSingleQuoted2(cliSource)}`;
8746
8970
  }
@@ -8830,7 +9054,7 @@ end tell` : `tell application "Terminal"
8830
9054
  activate
8831
9055
  do script "${escaped}"
8832
9056
  end tell`;
8833
- const tmpFile = join15(tmpdir2(), `zam-monitor-${sessionId}.scpt`);
9057
+ const tmpFile = join16(tmpdir3(), `zam-monitor-${sessionId}.scpt`);
8834
9058
  try {
8835
9059
  writeFileSync8(tmpFile, appleScript);
8836
9060
  execSync5(`osascript ${JSON.stringify(tmpFile)}`, { stdio: "ignore" });
@@ -8946,7 +9170,7 @@ observerCommand.command("revoke <process>").description("Remove a process from o
8946
9170
 
8947
9171
  // src/cli/commands/profile.ts
8948
9172
  import { homedir as homedir10 } from "os";
8949
- import { dirname as dirname5, join as join16, resolve as resolve4 } from "path";
9173
+ import { dirname as dirname5, join as join17, resolve as resolve4 } from "path";
8950
9174
  import { Command as Command11 } from "commander";
8951
9175
  var C2 = {
8952
9176
  reset: "\x1B[0m",
@@ -8956,7 +9180,7 @@ var C2 = {
8956
9180
  green: "\x1B[32m"
8957
9181
  };
8958
9182
  function defaultPersonalDir() {
8959
- return join16(homedir10(), "Documents", "zam");
9183
+ return join17(homedir10(), "Documents", "zam");
8960
9184
  }
8961
9185
  function render(profile) {
8962
9186
  const sync = profile.syncProvider ? `${C2.green}${profile.syncProvider}${C2.reset} ${C2.dim}(good for cross-device snapshots)${C2.reset}` : `${C2.dim}local folder (use a synced folder or snapshots to move between machines)${C2.reset}`;
@@ -9677,31 +9901,31 @@ settingsCommand.command("repos").description("Show or set Personal, Team, and Or
9677
9901
 
9678
9902
  // src/cli/commands/setup.ts
9679
9903
  import { copyFileSync as copyFileSync2, existsSync as existsSync17, mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "fs";
9680
- import { basename as basename4, dirname as dirname6, join as join17 } from "path";
9904
+ import { basename as basename4, dirname as dirname6, join as join18 } from "path";
9681
9905
  import { fileURLToPath as fileURLToPath2 } from "url";
9682
9906
  import { Command as Command15 } from "commander";
9683
9907
  var packageRoot = [
9684
9908
  fileURLToPath2(new URL("../..", import.meta.url)),
9685
9909
  fileURLToPath2(new URL("../../..", import.meta.url))
9686
- ].find((candidate) => existsSync17(join17(candidate, "package.json"))) ?? fileURLToPath2(new URL("../..", import.meta.url));
9910
+ ].find((candidate) => existsSync17(join18(candidate, "package.json"))) ?? fileURLToPath2(new URL("../..", import.meta.url));
9687
9911
  var SKILL_PAIRS = [
9688
9912
  {
9689
- from: join17(packageRoot, ".claude", "skills", "zam", "SKILL.md"),
9690
- to: join17(".claude", "skills", "zam", "SKILL.md")
9913
+ from: join18(packageRoot, ".claude", "skills", "zam", "SKILL.md"),
9914
+ to: join18(".claude", "skills", "zam", "SKILL.md")
9691
9915
  },
9692
9916
  {
9693
- from: join17(packageRoot, ".agent", "skills", "zam", "SKILL.md"),
9694
- to: join17(".agent", "skills", "zam", "SKILL.md")
9917
+ from: join18(packageRoot, ".agent", "skills", "zam", "SKILL.md"),
9918
+ to: join18(".agent", "skills", "zam", "SKILL.md")
9695
9919
  },
9696
9920
  {
9697
- from: join17(packageRoot, ".agents", "skills", "zam", "SKILL.md"),
9698
- to: join17(".agents", "skills", "zam", "SKILL.md")
9921
+ from: join18(packageRoot, ".agents", "skills", "zam", "SKILL.md"),
9922
+ to: join18(".agents", "skills", "zam", "SKILL.md")
9699
9923
  }
9700
9924
  ];
9701
9925
  function copySkills(force, cwd = process.cwd()) {
9702
9926
  let anyAction = false;
9703
9927
  for (const { from, to } of SKILL_PAIRS) {
9704
- const dest = join17(cwd, to);
9928
+ const dest = join18(cwd, to);
9705
9929
  if (!existsSync17(from)) {
9706
9930
  console.warn(` warn source not found, skipping: ${from}`);
9707
9931
  continue;
@@ -9751,7 +9975,7 @@ async function initDatabase(skipInit) {
9751
9975
  }
9752
9976
  function writeClaudeMd(skipClaudeMd, cwd = process.cwd()) {
9753
9977
  if (skipClaudeMd) return;
9754
- const dest = join17(cwd, "CLAUDE.md");
9978
+ const dest = join18(cwd, "CLAUDE.md");
9755
9979
  if (existsSync17(dest)) {
9756
9980
  console.log(` skip CLAUDE.md (already present)`);
9757
9981
  return;
@@ -9785,7 +10009,7 @@ Use \`zam connector setup turso\` to store cloud credentials in
9785
10009
  }
9786
10010
  function writeAgentsMd(skipAgentsMd, cwd = process.cwd()) {
9787
10011
  if (skipAgentsMd) return;
9788
- const dest = join17(cwd, "AGENTS.md");
10012
+ const dest = join18(cwd, "AGENTS.md");
9789
10013
  if (existsSync17(dest)) {
9790
10014
  console.log(` skip AGENTS.md (already present)`);
9791
10015
  return;
@@ -9933,7 +10157,7 @@ skillCommand.command("add").description("Register a new agent skill").requiredOp
9933
10157
  // src/cli/commands/snapshot.ts
9934
10158
  import { existsSync as existsSync18, mkdirSync as mkdirSync11, readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "fs";
9935
10159
  import { homedir as homedir11 } from "os";
9936
- import { dirname as dirname7, join as join18 } from "path";
10160
+ import { dirname as dirname7, join as join19 } from "path";
9937
10161
  import { Command as Command17 } from "commander";
9938
10162
  function defaultOutName() {
9939
10163
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+Z$/, "").replace(/:/g, "-");
@@ -9953,7 +10177,7 @@ var exportCmd = new Command17("export").description("Write a portable SQL-text s
9953
10177
  try {
9954
10178
  db = await openDatabaseWithSync({ initialize: true });
9955
10179
  const snapshot = await exportSnapshot(db);
9956
- const personalDir = await getSetting(db, "personal.workspace_dir") || join18(homedir11(), "Documents", "zam");
10180
+ const personalDir = await getSetting(db, "personal.workspace_dir") || join19(homedir11(), "Documents", "zam");
9957
10181
  await db.close();
9958
10182
  db = void 0;
9959
10183
  const manifest = verifySnapshot(snapshot);
@@ -9962,7 +10186,7 @@ var exportCmd = new Command17("export").description("Write a portable SQL-text s
9962
10186
  process.stdout.write(snapshot);
9963
10187
  return;
9964
10188
  }
9965
- const out = opts.out ?? join18(personalDir, "snapshots", defaultOutName());
10189
+ const out = opts.out ?? join19(personalDir, "snapshots", defaultOutName());
9966
10190
  const dir = dirname7(out);
9967
10191
  if (dir && dir !== "." && !existsSync18(dir)) {
9968
10192
  mkdirSync11(dir, { recursive: true });
@@ -10349,7 +10573,7 @@ tokenCommand.command("status").description("Show full status of a token for a us
10349
10573
  import { spawn as spawn2, spawnSync } from "child_process";
10350
10574
  import { existsSync as existsSync19 } from "fs";
10351
10575
  import { homedir as homedir12 } from "os";
10352
- import { dirname as dirname8, join as join19 } from "path";
10576
+ import { dirname as dirname8, join as join20 } from "path";
10353
10577
  import { fileURLToPath as fileURLToPath3 } from "url";
10354
10578
  import { Command as Command20 } from "commander";
10355
10579
  var C3 = {
@@ -10365,8 +10589,8 @@ function findDesktopDir() {
10365
10589
  for (const start of starts) {
10366
10590
  let dir = start;
10367
10591
  for (let i = 0; i < 10; i++) {
10368
- if (existsSync19(join19(dir, "desktop", "src-tauri", "tauri.conf.json"))) {
10369
- return join19(dir, "desktop");
10592
+ if (existsSync19(join20(dir, "desktop", "src-tauri", "tauri.conf.json"))) {
10593
+ return join20(dir, "desktop");
10370
10594
  }
10371
10595
  const parent = dirname8(dir);
10372
10596
  if (parent === dir) break;
@@ -10376,18 +10600,18 @@ function findDesktopDir() {
10376
10600
  return null;
10377
10601
  }
10378
10602
  function findBuiltApp(desktopDir) {
10379
- const releaseDir = join19(desktopDir, "src-tauri", "target", "release");
10603
+ const releaseDir = join20(desktopDir, "src-tauri", "target", "release");
10380
10604
  if (process.platform === "win32") {
10381
10605
  for (const name of ["ZAM.exe", "zam.exe", "zam-desktop.exe"]) {
10382
- const p = join19(releaseDir, name);
10606
+ const p = join20(releaseDir, name);
10383
10607
  if (existsSync19(p)) return p;
10384
10608
  }
10385
10609
  } else if (process.platform === "darwin") {
10386
- const app = join19(releaseDir, "bundle", "macos", "ZAM.app");
10610
+ const app = join20(releaseDir, "bundle", "macos", "ZAM.app");
10387
10611
  if (existsSync19(app)) return app;
10388
10612
  } else {
10389
10613
  for (const name of ["zam", "ZAM", "zam-desktop"]) {
10390
- const p = join19(releaseDir, name);
10614
+ const p = join20(releaseDir, name);
10391
10615
  if (existsSync19(p)) return p;
10392
10616
  }
10393
10617
  }
@@ -10395,10 +10619,10 @@ function findBuiltApp(desktopDir) {
10395
10619
  }
10396
10620
  function findInstalledApp() {
10397
10621
  const candidates = process.platform === "win32" ? [
10398
- process.env.LOCALAPPDATA && join19(process.env.LOCALAPPDATA, "Programs", "ZAM", "ZAM.exe"),
10399
- process.env.ProgramFiles && join19(process.env.ProgramFiles, "ZAM", "ZAM.exe"),
10400
- process.env["ProgramFiles(x86)"] && join19(process.env["ProgramFiles(x86)"], "ZAM", "ZAM.exe")
10401
- ] : process.platform === "darwin" ? ["/Applications/ZAM.app", join19(homedir12(), "Applications", "ZAM.app")] : ["/opt/ZAM/zam", "/usr/bin/zam-desktop"];
10622
+ process.env.LOCALAPPDATA && join20(process.env.LOCALAPPDATA, "Programs", "ZAM", "ZAM.exe"),
10623
+ process.env.ProgramFiles && join20(process.env.ProgramFiles, "ZAM", "ZAM.exe"),
10624
+ process.env["ProgramFiles(x86)"] && join20(process.env["ProgramFiles(x86)"], "ZAM", "ZAM.exe")
10625
+ ] : process.platform === "darwin" ? ["/Applications/ZAM.app", join20(homedir12(), "Applications", "ZAM.app")] : ["/opt/ZAM/zam", "/usr/bin/zam-desktop"];
10402
10626
  return candidates.find((candidate) => candidate && existsSync19(candidate)) || null;
10403
10627
  }
10404
10628
  function runNpm(args, opts) {
@@ -10410,7 +10634,7 @@ function runNpm(args, opts) {
10410
10634
  return res.status ?? 1;
10411
10635
  }
10412
10636
  function ensureDesktopDeps(desktopDir) {
10413
- if (existsSync19(join19(desktopDir, "node_modules"))) return true;
10637
+ if (existsSync19(join20(desktopDir, "node_modules"))) return true;
10414
10638
  console.log(
10415
10639
  `${C3.cyan}Installing desktop dependencies (one-time)...${C3.reset}`
10416
10640
  );
@@ -10436,7 +10660,7 @@ function requireRust() {
10436
10660
  function hasMsvcBuildTools() {
10437
10661
  if (process.platform !== "win32") return true;
10438
10662
  const pf86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
10439
- const vswhere = join19(
10663
+ const vswhere = join20(
10440
10664
  pf86,
10441
10665
  "Microsoft Visual Studio",
10442
10666
  "Installer",
@@ -10474,7 +10698,7 @@ function requireMsvcOnWindows() {
10474
10698
  return false;
10475
10699
  }
10476
10700
  function warnIfCliMissing(repoRoot) {
10477
- if (!existsSync19(join19(repoRoot, "dist", "cli", "index.js"))) {
10701
+ if (!existsSync19(join20(repoRoot, "dist", "cli", "index.js"))) {
10478
10702
  console.warn(
10479
10703
  `${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}`
10480
10704
  );
@@ -10558,7 +10782,7 @@ var uiCommand = new Command20("ui").description("Launch the ZAM Desktop GUI (Act
10558
10782
  );
10559
10783
  const code = runNpm(["run", "tauri", "build"], { cwd: desktopDir });
10560
10784
  if (code === 0) {
10561
- const bundle = join19(
10785
+ const bundle = join20(
10562
10786
  desktopDir,
10563
10787
  "src-tauri",
10564
10788
  "target",
@@ -10636,7 +10860,7 @@ ${C3.dim}After that, 'zam ui' launches it directly.${C3.reset}`
10636
10860
  // src/cli/commands/update.ts
10637
10861
  import { spawnSync as spawnSync2 } from "child_process";
10638
10862
  import { existsSync as existsSync20, readFileSync as readFileSync13, realpathSync } from "fs";
10639
- import { dirname as dirname9, join as join20 } from "path";
10863
+ import { dirname as dirname9, join as join21 } from "path";
10640
10864
  import { fileURLToPath as fileURLToPath4 } from "url";
10641
10865
  import { confirm as confirm3 } from "@inquirer/prompts";
10642
10866
  import { Command as Command21 } from "commander";
@@ -10661,7 +10885,7 @@ function currentVersion() {
10661
10885
  for (const up of ["..", "../..", "../../.."]) {
10662
10886
  try {
10663
10887
  const pkg2 = JSON.parse(
10664
- readFileSync13(join20(here, up, "package.json"), "utf-8")
10888
+ readFileSync13(join21(here, up, "package.json"), "utf-8")
10665
10889
  );
10666
10890
  if (pkg2.version) return pkg2.version;
10667
10891
  } catch {
@@ -10672,7 +10896,7 @@ function currentVersion() {
10672
10896
  function versionAt(dir) {
10673
10897
  try {
10674
10898
  const pkg2 = JSON.parse(
10675
- readFileSync13(join20(dir, "package.json"), "utf-8")
10899
+ readFileSync13(join21(dir, "package.json"), "utf-8")
10676
10900
  );
10677
10901
  return pkg2.version ?? "unknown";
10678
10902
  } catch {
@@ -10752,11 +10976,11 @@ function findSourceRepo() {
10752
10976
  let dir = realpathSync(dirname9(fileURLToPath4(import.meta.url)));
10753
10977
  let parent = dirname9(dir);
10754
10978
  while (parent !== dir) {
10755
- if (existsSync20(join20(dir, ".git"))) return dir;
10979
+ if (existsSync20(join21(dir, ".git"))) return dir;
10756
10980
  dir = parent;
10757
10981
  parent = dirname9(dir);
10758
10982
  }
10759
- return existsSync20(join20(dir, ".git")) ? dir : null;
10983
+ return existsSync20(join21(dir, ".git")) ? dir : null;
10760
10984
  }
10761
10985
  function runGit(cwd, args, capture) {
10762
10986
  const res = spawnSync2("git", args, {
@@ -10821,7 +11045,7 @@ Commit or stash them, or re-run with ${C4.cyan}--force${C4.reset}.`
10821
11045
  console.log(`${C4.dim}\u2192 zam setup --force${C4.reset}`);
10822
11046
  const setup = spawnSync2(
10823
11047
  process.execPath,
10824
- [join20(src, "dist", "cli", "index.js"), "setup", "--force"],
11048
+ [join21(src, "dist", "cli", "index.js"), "setup", "--force"],
10825
11049
  { cwd: process.cwd(), stdio: "inherit" }
10826
11050
  );
10827
11051
  if (setup.status !== 0) {
@@ -10933,7 +11157,7 @@ var whoamiCommand = new Command22("whoami").description("Show or set the default
10933
11157
  // src/cli/commands/workspace.ts
10934
11158
  import { execSync as execSync6 } from "child_process";
10935
11159
  import { existsSync as existsSync21, writeFileSync as writeFileSync11 } from "fs";
10936
- import { join as join21 } from "path";
11160
+ import { join as join22 } from "path";
10937
11161
  import { confirm as confirm4, input as input7 } from "@inquirer/prompts";
10938
11162
  import { Command as Command23 } from "commander";
10939
11163
  function runGit2(cwd, args) {
@@ -10980,7 +11204,7 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
10980
11204
  );
10981
11205
  process.exit(1);
10982
11206
  }
10983
- const gitignorePath = join21(workspaceDir, ".gitignore");
11207
+ const gitignorePath = join22(workspaceDir, ".gitignore");
10984
11208
  if (!existsSync21(gitignorePath)) {
10985
11209
  writeFileSync11(
10986
11210
  gitignorePath,
@@ -10988,7 +11212,7 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
10988
11212
  "utf8"
10989
11213
  );
10990
11214
  }
10991
- const hasGitRepo = existsSync21(join21(workspaceDir, ".git"));
11215
+ const hasGitRepo = existsSync21(join22(workspaceDir, ".git"));
10992
11216
  if (!hasGitRepo) {
10993
11217
  console.log("Initializing local Git repository...");
10994
11218
  runGit2(workspaceDir, "init -b main");
@@ -11084,7 +11308,7 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
11084
11308
  // src/cli/index.ts
11085
11309
  var __dirname = dirname10(fileURLToPath5(import.meta.url));
11086
11310
  var pkg = JSON.parse(
11087
- readFileSync14(join22(__dirname, "..", "..", "package.json"), "utf-8")
11311
+ readFileSync14(join23(__dirname, "..", "..", "package.json"), "utf-8")
11088
11312
  );
11089
11313
  var program = new Command24();
11090
11314
  program.name("zam").description(