zam-core 0.10.8 → 0.10.9
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 +1318 -1090
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/commands/mcp.js +509 -281
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/copilot-extension/host.bundle.js +1 -1
- package/dist/copilot-extension/manifest.json +1 -1
- package/dist/copilot-extension/mcp-client.bundle.mjs +1 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/ui/recall-panel.html +1 -1
- package/dist/ui/studio-panel.html +1 -1
- package/dist/vscode-extension/{ZAM_Companion_0.10.8.vsix → ZAM_Companion_0.10.9.vsix} +0 -0
- package/dist/vscode-extension/manifest.json +2 -2
- package/package.json +1 -1
package/dist/cli/app.js
CHANGED
|
@@ -6122,6 +6122,14 @@ function setAgentConnectAutoDone(done, path = defaultConfigPath()) {
|
|
|
6122
6122
|
}
|
|
6123
6123
|
saveInstallConfig(config, path);
|
|
6124
6124
|
}
|
|
6125
|
+
function getLastRepairedVersion(path = defaultConfigPath()) {
|
|
6126
|
+
return loadInstallConfig(path).lastRepairedVersion;
|
|
6127
|
+
}
|
|
6128
|
+
function setLastRepairedVersion(version, path = defaultConfigPath()) {
|
|
6129
|
+
const config = loadInstallConfig(path);
|
|
6130
|
+
config.lastRepairedVersion = version;
|
|
6131
|
+
saveInstallConfig(config, path);
|
|
6132
|
+
}
|
|
6125
6133
|
function getConfiguredWorkspaces(path = defaultConfigPath()) {
|
|
6126
6134
|
return loadInstallConfig(path).workspaces ?? [];
|
|
6127
6135
|
}
|
|
@@ -6844,6 +6852,7 @@ __export(kernel_exports, {
|
|
|
6844
6852
|
getInstallMode: () => getInstallMode,
|
|
6845
6853
|
getKnowledgeContextById: () => getKnowledgeContextById,
|
|
6846
6854
|
getKnowledgeContextByName: () => getKnowledgeContextByName,
|
|
6855
|
+
getLastRepairedVersion: () => getLastRepairedVersion,
|
|
6847
6856
|
getMachineAiConfig: () => getMachineAiConfig,
|
|
6848
6857
|
getMachineAiModels: () => getMachineAiModels,
|
|
6849
6858
|
getMonitorDir: () => getMonitorDir,
|
|
@@ -6939,6 +6948,7 @@ __export(kernel_exports, {
|
|
|
6939
6948
|
setAgentConnectAutoDone: () => setAgentConnectAutoDone,
|
|
6940
6949
|
setInstallChannel: () => setInstallChannel,
|
|
6941
6950
|
setInstallMode: () => setInstallMode,
|
|
6951
|
+
setLastRepairedVersion: () => setLastRepairedVersion,
|
|
6942
6952
|
setProviderApiKey: () => setProviderApiKey,
|
|
6943
6953
|
setSetting: () => setSetting,
|
|
6944
6954
|
setTursoCredentials: () => setTursoCredentials,
|
|
@@ -7015,8 +7025,8 @@ var init_kernel = __esm({
|
|
|
7015
7025
|
});
|
|
7016
7026
|
|
|
7017
7027
|
// src/cli/app.ts
|
|
7018
|
-
import { readFileSync as
|
|
7019
|
-
import { dirname as dirname14, join as
|
|
7028
|
+
import { readFileSync as readFileSync21 } from "fs";
|
|
7029
|
+
import { dirname as dirname14, join as join30 } from "path";
|
|
7020
7030
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
7021
7031
|
import { Command as Command27 } from "commander";
|
|
7022
7032
|
|
|
@@ -7435,6 +7445,7 @@ function detectInstalledConnectHarnesses(options = {}) {
|
|
|
7435
7445
|
return detected;
|
|
7436
7446
|
}
|
|
7437
7447
|
function parseMcpJsonConfig(path, content) {
|
|
7448
|
+
if (content.trim() === "") return {};
|
|
7438
7449
|
let parsed;
|
|
7439
7450
|
try {
|
|
7440
7451
|
parsed = JSON.parse(content);
|
|
@@ -8308,11 +8319,11 @@ var agentCommand = new Command("agent").description("Provision and inspect the a
|
|
|
8308
8319
|
|
|
8309
8320
|
// src/cli/commands/bridge.ts
|
|
8310
8321
|
init_kernel();
|
|
8311
|
-
import { execFileSync as
|
|
8322
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
8312
8323
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
8313
|
-
import { existsSync as
|
|
8314
|
-
import { homedir as
|
|
8315
|
-
import { join as
|
|
8324
|
+
import { existsSync as existsSync21, readdirSync as readdirSync2, readFileSync as readFileSync17, rmSync as rmSync3 } from "fs";
|
|
8325
|
+
import { homedir as homedir14, tmpdir as tmpdir3 } from "os";
|
|
8326
|
+
import { join as join23, resolve as resolve6 } from "path";
|
|
8316
8327
|
import { Command as Command2 } from "commander";
|
|
8317
8328
|
import { ulid as ulid10 } from "ulid";
|
|
8318
8329
|
|
|
@@ -9399,7 +9410,7 @@ async function prepareRecallChain(db, opts) {
|
|
|
9399
9410
|
} else {
|
|
9400
9411
|
spawnLocalRunner(endpoint.url, endpoint.model, endpoint.runner);
|
|
9401
9412
|
while (Date.now() < deadline) {
|
|
9402
|
-
await new Promise((
|
|
9413
|
+
await new Promise((resolve12) => setTimeout(resolve12, 1e3));
|
|
9403
9414
|
if (await isLlmOnline(endpoint.url)) {
|
|
9404
9415
|
online = true;
|
|
9405
9416
|
break;
|
|
@@ -9692,7 +9703,7 @@ async function startLocalRunner(url, model, locale, hint) {
|
|
|
9692
9703
|
let attempts = 0;
|
|
9693
9704
|
const dotsPerLine = 30;
|
|
9694
9705
|
while (true) {
|
|
9695
|
-
await new Promise((
|
|
9706
|
+
await new Promise((resolve12) => setTimeout(resolve12, 500));
|
|
9696
9707
|
if (await isLlmOnline(url)) {
|
|
9697
9708
|
if (attempts > 0) process.stdout.write("\n");
|
|
9698
9709
|
return true;
|
|
@@ -9781,8 +9792,8 @@ async function fetchWithInteractiveTimeout(url, options = {}) {
|
|
|
9781
9792
|
const dotsPerLine = 30;
|
|
9782
9793
|
while (true) {
|
|
9783
9794
|
let timeoutId;
|
|
9784
|
-
const timeoutPromise = new Promise((
|
|
9785
|
-
timeoutId = setTimeout(() =>
|
|
9795
|
+
const timeoutPromise = new Promise((resolve12) => {
|
|
9796
|
+
timeoutId = setTimeout(() => resolve12("timeout"), timeoutMs);
|
|
9786
9797
|
});
|
|
9787
9798
|
const dotsInterval = setInterval(() => {
|
|
9788
9799
|
process.stdout.write(".");
|
|
@@ -10049,7 +10060,7 @@ async function readWebLink(url) {
|
|
|
10049
10060
|
redirect: "manual",
|
|
10050
10061
|
signal: controller.signal,
|
|
10051
10062
|
headers: {
|
|
10052
|
-
"User-Agent": "ZAM-Content-Studio/0.10.
|
|
10063
|
+
"User-Agent": "ZAM-Content-Studio/0.10.9"
|
|
10053
10064
|
}
|
|
10054
10065
|
});
|
|
10055
10066
|
if (res.status >= 300 && res.status < 400) {
|
|
@@ -11312,6 +11323,127 @@ async function updateCheck(params) {
|
|
|
11312
11323
|
});
|
|
11313
11324
|
}
|
|
11314
11325
|
|
|
11326
|
+
// src/cli/cli-install.ts
|
|
11327
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
11328
|
+
import {
|
|
11329
|
+
chmodSync as chmodSync2,
|
|
11330
|
+
existsSync as existsSync18,
|
|
11331
|
+
mkdirSync as mkdirSync13,
|
|
11332
|
+
readFileSync as readFileSync14,
|
|
11333
|
+
writeFileSync as writeFileSync11
|
|
11334
|
+
} from "fs";
|
|
11335
|
+
import { homedir as homedir13 } from "os";
|
|
11336
|
+
import { delimiter as delimiter2, join as join19, resolve as resolve5 } from "path";
|
|
11337
|
+
var BUILT_CLI_PATTERN = /[\\/]dist[\\/]cli[\\/]index\.js$/;
|
|
11338
|
+
function normalizeForCompare(path, platform) {
|
|
11339
|
+
const resolved = resolve5(path);
|
|
11340
|
+
return platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
11341
|
+
}
|
|
11342
|
+
function windowsShimContent(nodePath, cliPath) {
|
|
11343
|
+
return `@echo off\r
|
|
11344
|
+
"${nodePath}" "${cliPath}" %*\r
|
|
11345
|
+
`;
|
|
11346
|
+
}
|
|
11347
|
+
function unixShimContent(nodePath, cliPath) {
|
|
11348
|
+
return `#!/bin/sh
|
|
11349
|
+
exec "${nodePath}" "${cliPath}" "$@"
|
|
11350
|
+
`;
|
|
11351
|
+
}
|
|
11352
|
+
function psQuote(value) {
|
|
11353
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
11354
|
+
}
|
|
11355
|
+
function ensureWindowsUserPath(binDir) {
|
|
11356
|
+
const script = [
|
|
11357
|
+
"$ErrorActionPreference = 'Stop'",
|
|
11358
|
+
"$key = [Microsoft.Win32.Registry]::CurrentUser.CreateSubKey('Environment', $true)",
|
|
11359
|
+
"$old = [string]$key.GetValue('Path', '', [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)",
|
|
11360
|
+
`$target = ${psQuote(binDir)}`,
|
|
11361
|
+
"$expanded = ($old -split ';') | Where-Object { $_ -ne '' } | ForEach-Object { [Environment]::ExpandEnvironmentVariables($_).TrimEnd('\\') }",
|
|
11362
|
+
"if ($expanded -contains $target.TrimEnd('\\')) { 'present'; exit 0 }",
|
|
11363
|
+
"$new = if ($old -and -not $old.EndsWith(';')) { $old + ';' + $target } else { $old + $target }",
|
|
11364
|
+
"$key.SetValue('Path', $new, [Microsoft.Win32.RegistryValueKind]::ExpandString)",
|
|
11365
|
+
`$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);'`,
|
|
11366
|
+
"$type = Add-Type -MemberDefinition $sig -Name 'ZamEnvBroadcast' -Namespace 'ZamInstall' -PassThru",
|
|
11367
|
+
"[UIntPtr]$result = [UIntPtr]::Zero",
|
|
11368
|
+
"$null = $type::SendMessageTimeout([IntPtr]0xffff, 0x001A, [UIntPtr]::Zero, 'Environment', 2, 5000, [ref]$result)",
|
|
11369
|
+
"'updated'"
|
|
11370
|
+
].join("; ");
|
|
11371
|
+
const output = execFileSync4(
|
|
11372
|
+
"powershell.exe",
|
|
11373
|
+
["-NoProfile", "-NonInteractive", "-Command", script],
|
|
11374
|
+
{ stdio: ["ignore", "pipe", "pipe"], windowsHide: true }
|
|
11375
|
+
).toString().trim();
|
|
11376
|
+
return output.endsWith("updated");
|
|
11377
|
+
}
|
|
11378
|
+
function ensureUnixUserPath(home, platform) {
|
|
11379
|
+
const profile = join19(home, platform === "darwin" ? ".zprofile" : ".profile");
|
|
11380
|
+
const existing = existsSync18(profile) ? readFileSync14(profile, "utf8") : "";
|
|
11381
|
+
if (existing.includes(".zam/bin")) return false;
|
|
11382
|
+
const block = `
|
|
11383
|
+
# Added by ZAM: keep the zam CLI on PATH
|
|
11384
|
+
export PATH="$HOME/.zam/bin:$PATH"
|
|
11385
|
+
`;
|
|
11386
|
+
writeFileSync11(profile, existing + block, "utf8");
|
|
11387
|
+
return true;
|
|
11388
|
+
}
|
|
11389
|
+
function installCliShim(options = {}) {
|
|
11390
|
+
const home = options.home ?? homedir13();
|
|
11391
|
+
const platform = options.platform ?? process.platform;
|
|
11392
|
+
const env = options.env ?? process.env;
|
|
11393
|
+
const find = options.find ?? findExecutable;
|
|
11394
|
+
const nodePath = resolve5(options.nodePath ?? process.execPath);
|
|
11395
|
+
const cliPath = resolve5(options.cliPath ?? process.argv[1] ?? "");
|
|
11396
|
+
const binDir = join19(home, ".zam", "bin");
|
|
11397
|
+
const shimPath = join19(binDir, platform === "win32" ? "zam.cmd" : "zam");
|
|
11398
|
+
const report = {
|
|
11399
|
+
status: "ok",
|
|
11400
|
+
binDir,
|
|
11401
|
+
shimPath,
|
|
11402
|
+
nodePath,
|
|
11403
|
+
cliPath,
|
|
11404
|
+
onPath: false,
|
|
11405
|
+
pathUpdated: false,
|
|
11406
|
+
needsNewTerminal: false
|
|
11407
|
+
};
|
|
11408
|
+
if (!BUILT_CLI_PATTERN.test(cliPath) || !existsSync18(cliPath)) {
|
|
11409
|
+
report.status = "skipped";
|
|
11410
|
+
report.detail = `Not running from a built CLI entry (${cliPath}); nothing to link.`;
|
|
11411
|
+
return report;
|
|
11412
|
+
}
|
|
11413
|
+
try {
|
|
11414
|
+
const existing = find("zam");
|
|
11415
|
+
if (existing && normalizeForCompare(existing, platform) !== normalizeForCompare(shimPath, platform)) {
|
|
11416
|
+
report.status = "external";
|
|
11417
|
+
report.onPath = true;
|
|
11418
|
+
report.detail = existing;
|
|
11419
|
+
return report;
|
|
11420
|
+
}
|
|
11421
|
+
const content = platform === "win32" ? windowsShimContent(nodePath, cliPath) : unixShimContent(nodePath, cliPath);
|
|
11422
|
+
const existed = existsSync18(shimPath);
|
|
11423
|
+
const changed = !existed || readFileSync14(shimPath, "utf8") !== content;
|
|
11424
|
+
if (changed) {
|
|
11425
|
+
mkdirSync13(binDir, { recursive: true });
|
|
11426
|
+
writeFileSync11(shimPath, content, "utf8");
|
|
11427
|
+
if (platform !== "win32") chmodSync2(shimPath, 493);
|
|
11428
|
+
}
|
|
11429
|
+
report.status = !existed ? "installed" : changed ? "refreshed" : "ok";
|
|
11430
|
+
const processPathHasBin = (env.PATH ?? "").split(delimiter2).map((entry) => normalizeForCompare(entry.trim() || ".", platform)).includes(normalizeForCompare(binDir, platform));
|
|
11431
|
+
if (processPathHasBin) {
|
|
11432
|
+
report.onPath = true;
|
|
11433
|
+
return report;
|
|
11434
|
+
}
|
|
11435
|
+
const ensureUserPath = options.ensureUserPath ?? (platform === "win32" ? ensureWindowsUserPath : () => ensureUnixUserPath(home, platform));
|
|
11436
|
+
report.pathUpdated = ensureUserPath(binDir);
|
|
11437
|
+
report.onPath = true;
|
|
11438
|
+
report.needsNewTerminal = true;
|
|
11439
|
+
return report;
|
|
11440
|
+
} catch (error) {
|
|
11441
|
+
report.status = "error";
|
|
11442
|
+
report.detail = error instanceof Error ? error.message : String(error);
|
|
11443
|
+
return report;
|
|
11444
|
+
}
|
|
11445
|
+
}
|
|
11446
|
+
|
|
11315
11447
|
// src/cli/curriculum/breadcrumb.ts
|
|
11316
11448
|
init_kernel();
|
|
11317
11449
|
var LAST_SELECTION_KEY = "curriculum.lastSelection";
|
|
@@ -27998,1054 +28130,1132 @@ function getCurriculumProvider(id) {
|
|
|
27998
28130
|
return CURRICULUM_PROVIDERS.find((provider) => provider.id === id);
|
|
27999
28131
|
}
|
|
28000
28132
|
|
|
28001
|
-
// src/cli/
|
|
28133
|
+
// src/cli/install-repair.ts
|
|
28002
28134
|
init_kernel();
|
|
28003
|
-
|
|
28004
|
-
|
|
28005
|
-
|
|
28006
|
-
|
|
28007
|
-
|
|
28008
|
-
|
|
28009
|
-
|
|
28010
|
-
|
|
28011
|
-
|
|
28012
|
-
|
|
28013
|
-
|
|
28014
|
-
|
|
28015
|
-
|
|
28016
|
-
|
|
28017
|
-
|
|
28018
|
-
|
|
28019
|
-
"
|
|
28020
|
-
"
|
|
28021
|
-
|
|
28022
|
-
|
|
28023
|
-
|
|
28024
|
-
|
|
28025
|
-
|
|
28026
|
-
|
|
28027
|
-
|
|
28028
|
-
|
|
28135
|
+
import { existsSync as existsSync20 } from "fs";
|
|
28136
|
+
|
|
28137
|
+
// src/cli/provisioning/index.ts
|
|
28138
|
+
import {
|
|
28139
|
+
existsSync as existsSync19,
|
|
28140
|
+
lstatSync as lstatSync2,
|
|
28141
|
+
mkdirSync as mkdirSync14,
|
|
28142
|
+
readFileSync as readFileSync15,
|
|
28143
|
+
realpathSync,
|
|
28144
|
+
rmSync as rmSync2,
|
|
28145
|
+
symlinkSync,
|
|
28146
|
+
writeFileSync as writeFileSync12
|
|
28147
|
+
} from "fs";
|
|
28148
|
+
import { basename as basename4, dirname as dirname9, join as join20 } from "path";
|
|
28149
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
28150
|
+
var packageRoot3 = [
|
|
28151
|
+
fileURLToPath5(new URL("../..", import.meta.url)),
|
|
28152
|
+
fileURLToPath5(new URL("../../..", import.meta.url))
|
|
28153
|
+
].find((candidate) => existsSync19(join20(candidate, "package.json"))) ?? fileURLToPath5(new URL("../..", import.meta.url));
|
|
28154
|
+
var ALL_SETUP_AGENTS = ["claude", "copilot", "codex", "agent"];
|
|
28155
|
+
var SKILL_PAIRS = [
|
|
28156
|
+
{
|
|
28157
|
+
from: join20(packageRoot3, ".claude", "skills", "zam", "SKILL.md"),
|
|
28158
|
+
to: join20(".claude", "skills", "zam", "SKILL.md"),
|
|
28159
|
+
agents: ["claude", "copilot"]
|
|
28160
|
+
},
|
|
28161
|
+
{
|
|
28162
|
+
from: join20(packageRoot3, ".agent", "skills", "zam", "SKILL.md"),
|
|
28163
|
+
to: join20(".agent", "skills", "zam", "SKILL.md"),
|
|
28164
|
+
agents: ["agent"]
|
|
28165
|
+
},
|
|
28166
|
+
{
|
|
28167
|
+
from: join20(packageRoot3, ".agents", "skills", "zam", "SKILL.md"),
|
|
28168
|
+
to: join20(".agents", "skills", "zam", "SKILL.md"),
|
|
28169
|
+
agents: ["codex"]
|
|
28170
|
+
}
|
|
28029
28171
|
];
|
|
28030
|
-
function
|
|
28031
|
-
|
|
28032
|
-
|
|
28172
|
+
function parseSetupAgents(value) {
|
|
28173
|
+
if (!value || value.trim().toLowerCase() === "all") {
|
|
28174
|
+
return new Set(ALL_SETUP_AGENTS);
|
|
28175
|
+
}
|
|
28176
|
+
const aliases = {
|
|
28177
|
+
claude: ["claude"],
|
|
28178
|
+
copilot: ["copilot"],
|
|
28179
|
+
codex: ["codex"],
|
|
28180
|
+
agent: ["agent"],
|
|
28181
|
+
opencode: ["agent"]
|
|
28182
|
+
};
|
|
28183
|
+
const selected = /* @__PURE__ */ new Set();
|
|
28184
|
+
for (const raw of value.split(",")) {
|
|
28185
|
+
const key = raw.trim().toLowerCase();
|
|
28186
|
+
const mapped = aliases[key];
|
|
28187
|
+
if (!mapped) {
|
|
28188
|
+
throw new Error(
|
|
28189
|
+
`Unknown agent "${raw}". Use: all, claude, copilot, codex, agent.`
|
|
28190
|
+
);
|
|
28191
|
+
}
|
|
28192
|
+
for (const item of mapped) selected.add(item);
|
|
28193
|
+
}
|
|
28194
|
+
return selected;
|
|
28033
28195
|
}
|
|
28034
|
-
function
|
|
28035
|
-
|
|
28036
|
-
|
|
28196
|
+
function pathExists(path) {
|
|
28197
|
+
try {
|
|
28198
|
+
lstatSync2(path);
|
|
28199
|
+
return true;
|
|
28200
|
+
} catch {
|
|
28201
|
+
return false;
|
|
28202
|
+
}
|
|
28037
28203
|
}
|
|
28038
|
-
function
|
|
28039
|
-
|
|
28040
|
-
|
|
28041
|
-
|
|
28042
|
-
|
|
28043
|
-
return detected;
|
|
28204
|
+
function isSymbolicLink(path) {
|
|
28205
|
+
try {
|
|
28206
|
+
return lstatSync2(path).isSymbolicLink();
|
|
28207
|
+
} catch {
|
|
28208
|
+
return false;
|
|
28044
28209
|
}
|
|
28045
|
-
const looksEmbedding = matchesAny(entry.model, EMBEDDING_MODEL_HINTS);
|
|
28046
|
-
const looksVision = matchesAny(entry.model, VISION_MODEL_HINTS);
|
|
28047
|
-
const inCatalog = catalogHasModel(catalog, entry.model);
|
|
28048
|
-
detected.embedding = looksEmbedding || dimProbeEmbedding;
|
|
28049
|
-
detected.image = looksVision;
|
|
28050
|
-
detected.text = !detected.embedding && (inCatalog || !catalogKnown);
|
|
28051
|
-
return detected;
|
|
28052
28210
|
}
|
|
28053
|
-
function
|
|
28054
|
-
|
|
28055
|
-
return
|
|
28211
|
+
function comparableRealPath(path) {
|
|
28212
|
+
const real = realpathSync(path);
|
|
28213
|
+
return process.platform === "win32" ? real.toLowerCase() : real;
|
|
28056
28214
|
}
|
|
28057
|
-
|
|
28058
|
-
|
|
28059
|
-
|
|
28060
|
-
|
|
28061
|
-
return
|
|
28062
|
-
reachable: online2,
|
|
28063
|
-
catalog: [],
|
|
28064
|
-
detected: classifyCapabilities(entry, [], false)
|
|
28065
|
-
};
|
|
28066
|
-
}
|
|
28067
|
-
const online = await isLlmOnline(entry.url);
|
|
28068
|
-
if (!online) {
|
|
28069
|
-
return { reachable: false, catalog: [], detected: emptyCapabilityFlags() };
|
|
28070
|
-
}
|
|
28071
|
-
const catalog = await getAvailableModels(entry.url, apiKey);
|
|
28072
|
-
const catalogKnown = catalog.length > 0;
|
|
28073
|
-
let dimProbeEmbedding = false;
|
|
28074
|
-
const looksEmbedding = matchesAny(entry.model, EMBEDDING_MODEL_HINTS);
|
|
28075
|
-
if (opts.embeddingDimProbe && !catalogKnown && !looksEmbedding) {
|
|
28076
|
-
try {
|
|
28077
|
-
const [vector] = await embedTexts(
|
|
28078
|
-
{ url: entry.url, model: entry.model, apiKey },
|
|
28079
|
-
["capability probe"]
|
|
28080
|
-
);
|
|
28081
|
-
dimProbeEmbedding = Array.isArray(vector) && vector.length > 0;
|
|
28082
|
-
} catch {
|
|
28083
|
-
dimProbeEmbedding = false;
|
|
28084
|
-
}
|
|
28215
|
+
function pathsResolveToSameDirectory(sourceDir, destinationDir) {
|
|
28216
|
+
try {
|
|
28217
|
+
return comparableRealPath(destinationDir) === comparableRealPath(sourceDir);
|
|
28218
|
+
} catch {
|
|
28219
|
+
return false;
|
|
28085
28220
|
}
|
|
28086
|
-
return {
|
|
28087
|
-
reachable: true,
|
|
28088
|
-
catalog,
|
|
28089
|
-
detected: classifyCapabilities(
|
|
28090
|
-
entry,
|
|
28091
|
-
catalog,
|
|
28092
|
-
catalogKnown,
|
|
28093
|
-
dimProbeEmbedding
|
|
28094
|
-
)
|
|
28095
|
-
};
|
|
28096
28221
|
}
|
|
28097
|
-
function
|
|
28098
|
-
|
|
28099
|
-
|
|
28100
|
-
|
|
28222
|
+
function linkPointsTo(sourceDir, destinationDir) {
|
|
28223
|
+
return isSymbolicLink(destinationDir) && pathsResolveToSameDirectory(sourceDir, destinationDir);
|
|
28224
|
+
}
|
|
28225
|
+
function isZamSkillCopy(destinationDir) {
|
|
28226
|
+
try {
|
|
28227
|
+
if (!lstatSync2(destinationDir).isDirectory()) return false;
|
|
28228
|
+
const skillFile = join20(destinationDir, "SKILL.md");
|
|
28229
|
+
if (!existsSync19(skillFile)) return false;
|
|
28230
|
+
return /^name:\s*zam\s*$/m.test(readFileSync15(skillFile, "utf8"));
|
|
28231
|
+
} catch {
|
|
28232
|
+
return false;
|
|
28101
28233
|
}
|
|
28102
|
-
return result;
|
|
28103
28234
|
}
|
|
28104
|
-
function
|
|
28105
|
-
if (!
|
|
28106
|
-
|
|
28107
|
-
|
|
28108
|
-
error: "Endpoint is unreachable \u2014 cannot verify capabilities. Bring it online and retry."
|
|
28109
|
-
};
|
|
28235
|
+
function classifySkillDestination(sourceDir, destinationDir) {
|
|
28236
|
+
if (!existsSync19(sourceDir)) return "source-missing";
|
|
28237
|
+
if (!isSymbolicLink(destinationDir) && pathsResolveToSameDirectory(sourceDir, destinationDir)) {
|
|
28238
|
+
return "source-directory";
|
|
28110
28239
|
}
|
|
28111
|
-
return
|
|
28112
|
-
|
|
28113
|
-
|
|
28114
|
-
|
|
28115
|
-
|
|
28116
|
-
detectedCapabilities: probe.detected,
|
|
28117
|
-
probedAt: now()
|
|
28118
|
-
}
|
|
28119
|
-
};
|
|
28240
|
+
if (linkPointsTo(sourceDir, destinationDir)) return "linked";
|
|
28241
|
+
if (!pathExists(destinationDir)) return "missing";
|
|
28242
|
+
if (isSymbolicLink(destinationDir)) return "broken";
|
|
28243
|
+
if (isZamSkillCopy(destinationDir)) return "stale-copy";
|
|
28244
|
+
return "unmanaged";
|
|
28120
28245
|
}
|
|
28121
|
-
|
|
28122
|
-
|
|
28123
|
-
|
|
28124
|
-
|
|
28125
|
-
|
|
28126
|
-
|
|
28127
|
-
|
|
28128
|
-
|
|
28129
|
-
|
|
28130
|
-
|
|
28131
|
-
|
|
28132
|
-
|
|
28133
|
-
|
|
28134
|
-
|
|
28135
|
-
ja: "Japanese"
|
|
28136
|
-
};
|
|
28137
|
-
var OBSERVATION_KINDS2 = /* @__PURE__ */ new Set([
|
|
28138
|
-
"progress",
|
|
28139
|
-
"step-completed",
|
|
28140
|
-
"error",
|
|
28141
|
-
"help-seeking",
|
|
28142
|
-
"uncertain",
|
|
28143
|
-
"privacy-pause",
|
|
28144
|
-
"heartbeat"
|
|
28145
|
-
]);
|
|
28146
|
-
var ACTION_TYPES2 = /* @__PURE__ */ new Set([
|
|
28147
|
-
"click",
|
|
28148
|
-
"shortcut",
|
|
28149
|
-
"typing",
|
|
28150
|
-
"scroll",
|
|
28151
|
-
"window-change"
|
|
28152
|
-
]);
|
|
28153
|
-
async function observeUiSnapshotViaLLM(db, input8) {
|
|
28154
|
-
const cfg = await getProviderForRole(db, "vision");
|
|
28155
|
-
if (!cfg.enabled) {
|
|
28156
|
-
throw new Error(
|
|
28157
|
-
"Vision observation is disabled in settings (llm.vision.enabled)"
|
|
28158
|
-
);
|
|
28159
|
-
}
|
|
28160
|
-
const imageUrls = [];
|
|
28161
|
-
const isVideo = /\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(input8.imagePath);
|
|
28162
|
-
if (isVideo) {
|
|
28163
|
-
const { mkdirSync: mkdirSync18, readdirSync: readdirSync3, rmSync: rmSync4 } = await import("fs");
|
|
28164
|
-
const { execSync: execSync6 } = await import("child_process");
|
|
28165
|
-
const tempDir = join19(
|
|
28166
|
-
tmpdir2(),
|
|
28167
|
-
`zam-frames-${randomBytes(4).toString("hex")}`
|
|
28168
|
-
);
|
|
28169
|
-
mkdirSync18(tempDir, { recursive: true });
|
|
28170
|
-
try {
|
|
28171
|
-
execSync6(
|
|
28172
|
-
`ffmpeg -i "${input8.imagePath}" -vf "fps=1/3,scale=1280:-1" -vsync vfr "${tempDir}/frame_%03d.png"`,
|
|
28173
|
-
{ stdio: "ignore" }
|
|
28174
|
-
);
|
|
28175
|
-
let files = readdirSync3(tempDir).filter((f) => f.endsWith(".png")).sort();
|
|
28176
|
-
if (files.length === 0) {
|
|
28177
|
-
execSync6(
|
|
28178
|
-
`ffmpeg -i "${input8.imagePath}" -vframes 1 "${tempDir}/frame_001.png"`,
|
|
28179
|
-
{ stdio: "ignore" }
|
|
28180
|
-
);
|
|
28181
|
-
files = readdirSync3(tempDir).filter((f) => f.endsWith(".png")).sort();
|
|
28182
|
-
}
|
|
28183
|
-
const maxFrames = cfg.maxFrames ?? 100;
|
|
28184
|
-
let sampledFiles = files;
|
|
28185
|
-
if (files.length > maxFrames) {
|
|
28186
|
-
if (maxFrames <= 1) {
|
|
28187
|
-
sampledFiles = [files[0]];
|
|
28188
|
-
} else {
|
|
28189
|
-
const step = (files.length - 1) / (maxFrames - 1);
|
|
28190
|
-
sampledFiles = [];
|
|
28191
|
-
for (let i = 0; i < maxFrames; i++) {
|
|
28192
|
-
const index = Math.round(i * step);
|
|
28193
|
-
sampledFiles.push(files[index]);
|
|
28194
|
-
}
|
|
28195
|
-
}
|
|
28196
|
-
}
|
|
28197
|
-
for (const file of sampledFiles) {
|
|
28198
|
-
const bytes = readFileSync14(join19(tempDir, file));
|
|
28199
|
-
imageUrls.push(`data:image/png;base64,${bytes.toString("base64")}`);
|
|
28200
|
-
}
|
|
28201
|
-
} finally {
|
|
28202
|
-
try {
|
|
28203
|
-
rmSync4(tempDir, { recursive: true, force: true });
|
|
28204
|
-
} catch {
|
|
28246
|
+
function wireSkills(cwd = process.cwd(), agents = parseSetupAgents(), opts = {}) {
|
|
28247
|
+
const results = [];
|
|
28248
|
+
const log = (message) => {
|
|
28249
|
+
if (!opts.quiet) console.log(message);
|
|
28250
|
+
};
|
|
28251
|
+
for (const { from, to, agents: pairAgents } of SKILL_PAIRS) {
|
|
28252
|
+
if (!pairAgents.some((agent) => agents.has(agent))) continue;
|
|
28253
|
+
const sourceDir = dirname9(from);
|
|
28254
|
+
const destinationDir = dirname9(join20(cwd, to));
|
|
28255
|
+
const label = dirname9(to);
|
|
28256
|
+
const state = classifySkillDestination(sourceDir, destinationDir);
|
|
28257
|
+
if (state === "source-missing") {
|
|
28258
|
+
if (!opts.quiet) {
|
|
28259
|
+
console.warn(` warn source not found, skipping: ${sourceDir}`);
|
|
28205
28260
|
}
|
|
28261
|
+
results.push({
|
|
28262
|
+
source: sourceDir,
|
|
28263
|
+
destination: destinationDir,
|
|
28264
|
+
action: "skipped",
|
|
28265
|
+
reason: "source-not-found"
|
|
28266
|
+
});
|
|
28267
|
+
continue;
|
|
28206
28268
|
}
|
|
28207
|
-
|
|
28208
|
-
|
|
28209
|
-
|
|
28210
|
-
|
|
28211
|
-
|
|
28212
|
-
|
|
28213
|
-
|
|
28214
|
-
|
|
28215
|
-
|
|
28216
|
-
const endpoints = [
|
|
28217
|
-
{
|
|
28218
|
-
url: cfg.url,
|
|
28219
|
-
apiKey: cfg.apiKey || DEFAULT_LLM_API_KEY,
|
|
28220
|
-
model: input8.model ?? cfg.model,
|
|
28221
|
-
apiFlavor: cfg.apiFlavor
|
|
28269
|
+
if (state === "source-directory") {
|
|
28270
|
+
log(` skip ${label} (package source)`);
|
|
28271
|
+
results.push({
|
|
28272
|
+
source: sourceDir,
|
|
28273
|
+
destination: destinationDir,
|
|
28274
|
+
action: "skipped",
|
|
28275
|
+
reason: "source-directory"
|
|
28276
|
+
});
|
|
28277
|
+
continue;
|
|
28222
28278
|
}
|
|
28223
|
-
|
|
28224
|
-
|
|
28225
|
-
|
|
28226
|
-
|
|
28227
|
-
|
|
28228
|
-
|
|
28229
|
-
|
|
28230
|
-
});
|
|
28231
|
-
}
|
|
28232
|
-
let lastRequestError;
|
|
28233
|
-
let sawUnparseableDraft = false;
|
|
28234
|
-
let sawInvalidDraft = false;
|
|
28235
|
-
for (const endpoint of endpoints) {
|
|
28236
|
-
let content;
|
|
28237
|
-
try {
|
|
28238
|
-
content = await requestVisionDraft({
|
|
28239
|
-
...endpoint,
|
|
28240
|
-
locale: cfg.locale,
|
|
28241
|
-
imageUrls,
|
|
28242
|
-
input: input8
|
|
28279
|
+
if (state === "linked") {
|
|
28280
|
+
log(` skip ${label} (already linked)`);
|
|
28281
|
+
results.push({
|
|
28282
|
+
source: sourceDir,
|
|
28283
|
+
destination: destinationDir,
|
|
28284
|
+
action: "skipped",
|
|
28285
|
+
reason: "already-linked"
|
|
28243
28286
|
});
|
|
28244
|
-
} catch (err) {
|
|
28245
|
-
lastRequestError = err;
|
|
28246
28287
|
continue;
|
|
28247
28288
|
}
|
|
28248
|
-
|
|
28249
|
-
|
|
28250
|
-
|
|
28251
|
-
|
|
28252
|
-
|
|
28289
|
+
const destinationExists = state !== "missing";
|
|
28290
|
+
const replaceExisting = destinationExists && (Boolean(opts.force) || state === "broken" || state === "stale-copy");
|
|
28291
|
+
if (destinationExists && !replaceExisting) {
|
|
28292
|
+
if (!opts.quiet) {
|
|
28293
|
+
console.warn(
|
|
28294
|
+
` warn ${label} already exists and is not managed by ZAM; use --force to replace it`
|
|
28295
|
+
);
|
|
28296
|
+
}
|
|
28297
|
+
results.push({
|
|
28298
|
+
source: sourceDir,
|
|
28299
|
+
destination: destinationDir,
|
|
28300
|
+
action: "skipped",
|
|
28301
|
+
reason: "unmanaged-destination"
|
|
28302
|
+
});
|
|
28253
28303
|
continue;
|
|
28254
28304
|
}
|
|
28255
|
-
const
|
|
28256
|
-
if (
|
|
28257
|
-
|
|
28305
|
+
const action = destinationExists ? "relinked" : "linked";
|
|
28306
|
+
if (opts.dryRun) {
|
|
28307
|
+
log(` would ${action === "linked" ? "link" : "relink"} ${label}`);
|
|
28308
|
+
} else {
|
|
28309
|
+
if (destinationExists) {
|
|
28310
|
+
rmSync2(destinationDir, { recursive: true, force: true });
|
|
28311
|
+
}
|
|
28312
|
+
mkdirSync14(dirname9(destinationDir), { recursive: true });
|
|
28313
|
+
symlinkSync(
|
|
28314
|
+
sourceDir,
|
|
28315
|
+
destinationDir,
|
|
28316
|
+
process.platform === "win32" ? "junction" : "dir"
|
|
28317
|
+
);
|
|
28318
|
+
log(` ${action === "linked" ? "link" : "relink"} ${label}`);
|
|
28258
28319
|
}
|
|
28259
|
-
|
|
28260
|
-
}
|
|
28261
|
-
if (lastRequestError && !sawUnparseableDraft && !sawInvalidDraft) {
|
|
28262
|
-
throw lastRequestError;
|
|
28320
|
+
results.push({ source: sourceDir, destination: destinationDir, action });
|
|
28263
28321
|
}
|
|
28264
|
-
|
|
28265
|
-
|
|
28266
|
-
|
|
28267
|
-
|
|
28268
|
-
|
|
28322
|
+
return results;
|
|
28323
|
+
}
|
|
28324
|
+
function inspectSkillLinks(cwd = process.cwd(), agents = parseSetupAgents()) {
|
|
28325
|
+
const results = [];
|
|
28326
|
+
for (const { from, to, agents: pairAgents } of SKILL_PAIRS) {
|
|
28327
|
+
if (!pairAgents.some((agent) => agents.has(agent))) continue;
|
|
28328
|
+
const sourceDir = dirname9(from);
|
|
28329
|
+
const destinationDir = dirname9(join20(cwd, to));
|
|
28330
|
+
results.push({
|
|
28331
|
+
agents: pairAgents,
|
|
28332
|
+
source: sourceDir,
|
|
28333
|
+
destination: destinationDir,
|
|
28334
|
+
state: classifySkillDestination(sourceDir, destinationDir)
|
|
28335
|
+
});
|
|
28269
28336
|
}
|
|
28270
|
-
return
|
|
28271
|
-
input8,
|
|
28272
|
-
"Vision model returned output that could not be parsed as JSON."
|
|
28273
|
-
);
|
|
28337
|
+
return results;
|
|
28274
28338
|
}
|
|
28275
|
-
|
|
28276
|
-
|
|
28277
|
-
|
|
28278
|
-
|
|
28279
|
-
"
|
|
28280
|
-
|
|
28281
|
-
|
|
28282
|
-
|
|
28283
|
-
|
|
28339
|
+
function summarizeSkillLinkHealth(inspections) {
|
|
28340
|
+
if (inspections.some((item) => item.state === "unmanaged")) {
|
|
28341
|
+
return "unmanaged";
|
|
28342
|
+
}
|
|
28343
|
+
const repairable = ["missing", "broken", "stale-copy"];
|
|
28344
|
+
if (inspections.some((item) => repairable.includes(item.state))) {
|
|
28345
|
+
return "needs-repair";
|
|
28346
|
+
}
|
|
28347
|
+
return "healthy";
|
|
28284
28348
|
}
|
|
28285
|
-
|
|
28286
|
-
|
|
28287
|
-
|
|
28288
|
-
|
|
28289
|
-
|
|
28349
|
+
var ZAM_BLOCK_START = "<!-- ZAM:START -->";
|
|
28350
|
+
var ZAM_BLOCK_END = "<!-- ZAM:END -->";
|
|
28351
|
+
function upsertMarkedBlock(dest, blockBody, dryRun) {
|
|
28352
|
+
const block = `${ZAM_BLOCK_START}
|
|
28353
|
+
${blockBody.trim()}
|
|
28354
|
+
${ZAM_BLOCK_END}`;
|
|
28355
|
+
if (!existsSync19(dest)) {
|
|
28356
|
+
if (!dryRun) {
|
|
28357
|
+
mkdirSync14(dirname9(dest), { recursive: true });
|
|
28358
|
+
writeFileSync12(dest, `${block}
|
|
28359
|
+
`, "utf8");
|
|
28360
|
+
}
|
|
28361
|
+
return "write";
|
|
28362
|
+
}
|
|
28363
|
+
const existing = readFileSync15(dest, "utf8");
|
|
28364
|
+
if (existing.includes(block)) return "skip";
|
|
28365
|
+
const start = existing.indexOf(ZAM_BLOCK_START);
|
|
28366
|
+
const end = existing.indexOf(ZAM_BLOCK_END);
|
|
28367
|
+
const next = start >= 0 && end > start ? `${existing.slice(0, start)}${block}${existing.slice(end + ZAM_BLOCK_END.length)}` : `${existing.trimEnd()}
|
|
28290
28368
|
|
|
28291
|
-
|
|
28292
|
-
|
|
28369
|
+
${block}
|
|
28370
|
+
`;
|
|
28371
|
+
if (!dryRun) writeFileSync12(dest, next, "utf8");
|
|
28372
|
+
return start >= 0 && end > start ? "update" : "write";
|
|
28293
28373
|
}
|
|
28294
|
-
|
|
28295
|
-
if (
|
|
28296
|
-
|
|
28374
|
+
function logInstructionAction(action, label, dryRun) {
|
|
28375
|
+
if (action === "skip") {
|
|
28376
|
+
console.log(` skip ${label} (ZAM block already present)`);
|
|
28377
|
+
} else {
|
|
28378
|
+
console.log(` ${dryRun ? "would " : ""}${action} ${label}`);
|
|
28297
28379
|
}
|
|
28298
|
-
return requestChatCompletionsVisionDraft(args);
|
|
28299
28380
|
}
|
|
28300
|
-
|
|
28301
|
-
|
|
28302
|
-
const
|
|
28303
|
-
|
|
28304
|
-
{
|
|
28305
|
-
|
|
28306
|
-
|
|
28307
|
-
"Content-Type": "application/json",
|
|
28308
|
-
Authorization: `Bearer ${args.apiKey}`
|
|
28309
|
-
},
|
|
28310
|
-
body: JSON.stringify({
|
|
28311
|
-
model: args.model,
|
|
28312
|
-
messages: [
|
|
28313
|
-
{ role: "system", content: VISION_SYSTEM_PROMPT },
|
|
28314
|
-
{
|
|
28315
|
-
role: "user",
|
|
28316
|
-
content: [
|
|
28317
|
-
{ type: "text", text: visionUserText(args, language) },
|
|
28318
|
-
...args.imageUrls.map((url) => ({
|
|
28319
|
-
type: "image_url",
|
|
28320
|
-
image_url: { url }
|
|
28321
|
-
}))
|
|
28322
|
-
]
|
|
28323
|
-
}
|
|
28324
|
-
],
|
|
28325
|
-
temperature: 0,
|
|
28326
|
-
max_tokens: args.input.maxTokens ?? DEFAULT_LLM_MAX_TOKENS,
|
|
28327
|
-
...args.url.includes("11434") || args.url.includes("localhost") || args.url.includes("127.0.0.1") ? { options: { num_ctx: 32768 } } : {}
|
|
28328
|
-
}),
|
|
28329
|
-
locale: args.locale,
|
|
28330
|
-
hardTimeoutMs: args.input.hardTimeoutMs ?? 18e4
|
|
28331
|
-
}
|
|
28332
|
-
);
|
|
28333
|
-
if (!res.ok) {
|
|
28334
|
-
const errorText = await res.text().catch(() => "");
|
|
28335
|
-
if (errorText.includes("image") && (errorText.includes("not support") || errorText.includes("unsupported"))) {
|
|
28336
|
-
throw new Error(
|
|
28337
|
-
`Vision model "${args.model}" does not support image input. Set a multimodal model: zam settings set llm.vision.model <model>`
|
|
28338
|
-
);
|
|
28381
|
+
function writeClaudeMd(skipClaudeMd, cwd = process.cwd(), opts = {}) {
|
|
28382
|
+
if (skipClaudeMd) return;
|
|
28383
|
+
const dest = join20(cwd, "CLAUDE.md");
|
|
28384
|
+
if (existsSync19(dest)) {
|
|
28385
|
+
if (!opts.updateExisting) {
|
|
28386
|
+
console.log(` skip CLAUDE.md (already present)`);
|
|
28387
|
+
return;
|
|
28339
28388
|
}
|
|
28340
|
-
|
|
28341
|
-
|
|
28389
|
+
const action = upsertMarkedBlock(
|
|
28390
|
+
dest,
|
|
28391
|
+
`## ZAM learning sessions
|
|
28392
|
+
|
|
28393
|
+
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.
|
|
28394
|
+
|
|
28395
|
+
- Skill files live under \`.claude/skills/zam/\`.
|
|
28396
|
+
- Fast-changing review data lives in \`~/.zam/\`, not in this repository.
|
|
28397
|
+
- The skill directory is linked to this ZAM installation and updates with it.`,
|
|
28398
|
+
Boolean(opts.dryRun)
|
|
28342
28399
|
);
|
|
28400
|
+
logInstructionAction(action, "CLAUDE.md", Boolean(opts.dryRun));
|
|
28401
|
+
return;
|
|
28343
28402
|
}
|
|
28344
|
-
const
|
|
28345
|
-
|
|
28346
|
-
|
|
28347
|
-
|
|
28348
|
-
|
|
28349
|
-
|
|
28350
|
-
|
|
28403
|
+
const name = basename4(cwd);
|
|
28404
|
+
const content = `# ZAM Personal Kernel \u2014 ${name}
|
|
28405
|
+
|
|
28406
|
+
This is a ZAM personal instance. ZAM builds lasting skills through spaced
|
|
28407
|
+
repetition during real work \u2014 not separate study sessions.
|
|
28408
|
+
|
|
28409
|
+
## First time here?
|
|
28410
|
+
Run \`/setup\` in Claude Code or Gemini CLI to complete first-time setup.
|
|
28411
|
+
|
|
28412
|
+
## Regular use
|
|
28413
|
+
Run \`/zam\` to start a learning session on whatever you are working on.
|
|
28414
|
+
|
|
28415
|
+
## What lives here
|
|
28416
|
+
- \`beliefs/\` \u2014 your worldview, approved by git commit
|
|
28417
|
+
- \`goals/\` \u2014 your objectives, decomposed into tasks and learning tokens
|
|
28418
|
+
|
|
28419
|
+
## Fast-changing data
|
|
28420
|
+
Learning tokens, cards, and review history live in local SQLite by default.
|
|
28421
|
+
Use \`zam connector setup turso\` to store cloud credentials in
|
|
28422
|
+
\`~/.zam/credentials.json\` and use a Turso database across machines.
|
|
28423
|
+
`;
|
|
28424
|
+
if (opts.dryRun) {
|
|
28425
|
+
console.log(` would write CLAUDE.md`);
|
|
28426
|
+
} else {
|
|
28427
|
+
writeFileSync12(dest, content, "utf8");
|
|
28428
|
+
console.log(` write CLAUDE.md`);
|
|
28429
|
+
}
|
|
28430
|
+
}
|
|
28431
|
+
function writeAgentsMd(skipAgentsMd, cwd = process.cwd(), opts = {}) {
|
|
28432
|
+
if (skipAgentsMd) return;
|
|
28433
|
+
const dest = join20(cwd, "AGENTS.md");
|
|
28434
|
+
if (existsSync19(dest)) {
|
|
28435
|
+
if (!opts.updateExisting) {
|
|
28436
|
+
console.log(` skip AGENTS.md (already present)`);
|
|
28437
|
+
return;
|
|
28351
28438
|
}
|
|
28352
|
-
|
|
28439
|
+
const action = upsertMarkedBlock(
|
|
28440
|
+
dest,
|
|
28441
|
+
`## ZAM learning sessions
|
|
28442
|
+
|
|
28443
|
+
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.
|
|
28444
|
+
|
|
28445
|
+
- Skill files live under \`.agents/skills/zam/\`.
|
|
28446
|
+
- Fast-changing review data lives in \`~/.zam/\`, not in this repository.
|
|
28447
|
+
- The skill directory is linked to this ZAM installation and updates with it.`,
|
|
28448
|
+
Boolean(opts.dryRun)
|
|
28449
|
+
);
|
|
28450
|
+
logInstructionAction(action, "AGENTS.md", Boolean(opts.dryRun));
|
|
28451
|
+
return;
|
|
28353
28452
|
}
|
|
28354
|
-
const
|
|
28355
|
-
|
|
28356
|
-
|
|
28453
|
+
const name = basename4(cwd);
|
|
28454
|
+
const content = `# ZAM Personal Kernel - ${name}
|
|
28455
|
+
|
|
28456
|
+
This is a ZAM personal instance. ZAM builds lasting skills through spaced
|
|
28457
|
+
repetition during real work, not separate study sessions.
|
|
28458
|
+
|
|
28459
|
+
## First time here?
|
|
28460
|
+
Run \`zam setup\` from the shell. When this repository includes
|
|
28461
|
+
\`.agents/skills/setup/\`, you can instead select \`setup\` through \`/skills\`
|
|
28462
|
+
or invoke \`$setup\`.
|
|
28463
|
+
|
|
28464
|
+
## Regular use
|
|
28465
|
+
Select the \`zam\` skill through \`/skills\` or invoke \`$zam\` to start a
|
|
28466
|
+
learning session on whatever you are working on.
|
|
28467
|
+
|
|
28468
|
+
## What lives here
|
|
28469
|
+
- \`beliefs/\` - your worldview, approved by git commit
|
|
28470
|
+
- \`goals/\` - your objectives, decomposed into tasks and learning tokens
|
|
28471
|
+
|
|
28472
|
+
## Fast-changing data
|
|
28473
|
+
Learning tokens, cards, and review history live in local SQLite by default.
|
|
28474
|
+
Use \`zam connector setup turso\` to store cloud credentials in
|
|
28475
|
+
\`~/.zam/credentials.json\` and use a Turso database across machines.
|
|
28476
|
+
|
|
28477
|
+
## Codex skills
|
|
28478
|
+
Codex discovers repository skills under \`.agents/skills/\`. Run
|
|
28479
|
+
\`zam setup --force\` after upgrading \`zam-core\` to refresh them.
|
|
28480
|
+
`;
|
|
28481
|
+
if (opts.dryRun) {
|
|
28482
|
+
console.log(` would write AGENTS.md`);
|
|
28483
|
+
} else {
|
|
28484
|
+
writeFileSync12(dest, content, "utf8");
|
|
28485
|
+
console.log(` write AGENTS.md`);
|
|
28357
28486
|
}
|
|
28358
|
-
return content.trim();
|
|
28359
28487
|
}
|
|
28360
|
-
function
|
|
28361
|
-
const
|
|
28362
|
-
|
|
28363
|
-
|
|
28488
|
+
function writeCopilotInstructions(cwd = process.cwd(), opts = {}) {
|
|
28489
|
+
const dest = join20(cwd, ".github", "copilot-instructions.md");
|
|
28490
|
+
const action = upsertMarkedBlock(
|
|
28491
|
+
dest,
|
|
28492
|
+
`## ZAM learning sessions
|
|
28493
|
+
|
|
28494
|
+
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.
|
|
28495
|
+
|
|
28496
|
+
- Fast-changing review data lives in \`~/.zam/\`, not in this repository.
|
|
28497
|
+
- The skill directory is linked to this ZAM installation and updates with it.`,
|
|
28498
|
+
Boolean(opts.dryRun)
|
|
28499
|
+
);
|
|
28500
|
+
logInstructionAction(
|
|
28501
|
+
action,
|
|
28502
|
+
".github/copilot-instructions.md",
|
|
28503
|
+
Boolean(opts.dryRun)
|
|
28504
|
+
);
|
|
28505
|
+
}
|
|
28506
|
+
|
|
28507
|
+
// src/cli/install-repair.ts
|
|
28508
|
+
function defaultRepairWorkspaces() {
|
|
28509
|
+
const summary = {
|
|
28510
|
+
provisioned: 0,
|
|
28511
|
+
missing: 0,
|
|
28512
|
+
relinked: 0
|
|
28513
|
+
};
|
|
28514
|
+
try {
|
|
28515
|
+
const agents = parseSetupAgents();
|
|
28516
|
+
for (const workspace of getConfiguredWorkspaces()) {
|
|
28517
|
+
if (!existsSync20(workspace.path)) {
|
|
28518
|
+
summary.missing += 1;
|
|
28519
|
+
continue;
|
|
28520
|
+
}
|
|
28521
|
+
const results = wireSkills(workspace.path, agents, { quiet: true });
|
|
28522
|
+
summary.provisioned += 1;
|
|
28523
|
+
summary.relinked += results.filter(
|
|
28524
|
+
(result) => result.action === "linked" || result.action === "relinked"
|
|
28525
|
+
).length;
|
|
28526
|
+
}
|
|
28527
|
+
} catch (error) {
|
|
28528
|
+
summary.error = error instanceof Error ? error.message : String(error);
|
|
28364
28529
|
}
|
|
28530
|
+
return summary;
|
|
28531
|
+
}
|
|
28532
|
+
function defaultConnectAgents(deps) {
|
|
28533
|
+
const report = performAgentConnect({}, deps);
|
|
28534
|
+
const companion = report.results.find((result) => result.extension?.kind === "vscode")?.extension?.action ?? null;
|
|
28365
28535
|
return {
|
|
28366
|
-
|
|
28367
|
-
|
|
28536
|
+
success: report.success,
|
|
28537
|
+
detected: report.detected,
|
|
28538
|
+
connected: report.results.filter(
|
|
28539
|
+
(result) => !result.error && (result.alreadyConfigured || result.wrote)
|
|
28540
|
+
).length,
|
|
28541
|
+
companion,
|
|
28542
|
+
errors: report.results.flatMap(
|
|
28543
|
+
(result) => result.error ? [`${result.label}: ${result.error}`] : []
|
|
28544
|
+
)
|
|
28368
28545
|
};
|
|
28369
28546
|
}
|
|
28370
|
-
|
|
28371
|
-
const
|
|
28372
|
-
const
|
|
28373
|
-
|
|
28374
|
-
|
|
28375
|
-
|
|
28376
|
-
|
|
28377
|
-
|
|
28378
|
-
|
|
28379
|
-
|
|
28380
|
-
|
|
28381
|
-
model: args.model,
|
|
28382
|
-
max_tokens: args.input.maxTokens ?? DEFAULT_LLM_MAX_TOKENS,
|
|
28383
|
-
system: VISION_SYSTEM_PROMPT,
|
|
28384
|
-
messages: [
|
|
28385
|
-
{
|
|
28386
|
-
role: "user",
|
|
28387
|
-
content: [
|
|
28388
|
-
{ type: "text", text: visionUserText(args, language) },
|
|
28389
|
-
...args.imageUrls.map(dataUrlToAnthropicImage)
|
|
28390
|
-
]
|
|
28391
|
-
}
|
|
28392
|
-
]
|
|
28393
|
-
}),
|
|
28394
|
-
locale: args.locale,
|
|
28395
|
-
hardTimeoutMs: args.input.hardTimeoutMs ?? 18e4
|
|
28396
|
-
});
|
|
28397
|
-
if (!res.ok) {
|
|
28398
|
-
const errorText = await res.text().catch(() => "");
|
|
28399
|
-
throw new Error(
|
|
28400
|
-
`Vision LLM request failed: ${res.statusText} (${res.status}) - ${errorText}`
|
|
28401
|
-
);
|
|
28402
|
-
}
|
|
28403
|
-
const data = await res.json();
|
|
28404
|
-
if (data.error !== void 0) {
|
|
28405
|
-
throw new Error(`Vision model failed: ${formatModelError(data.error)}`);
|
|
28406
|
-
}
|
|
28407
|
-
if (data.stop_reason === "refusal") {
|
|
28408
|
-
throw new Error("Vision model refused the request (safety classifier).");
|
|
28547
|
+
function performInstallRepair(opts = {}, deps = {}) {
|
|
28548
|
+
const version = (deps.version ?? currentVersion)();
|
|
28549
|
+
const getLastRepaired = deps.getLastRepaired ?? getLastRepairedVersion;
|
|
28550
|
+
if (opts.ifVersionChanged && getLastRepaired() === version) {
|
|
28551
|
+
return {
|
|
28552
|
+
version,
|
|
28553
|
+
skipped: true,
|
|
28554
|
+
cli: null,
|
|
28555
|
+
workspaces: null,
|
|
28556
|
+
agents: null
|
|
28557
|
+
};
|
|
28409
28558
|
}
|
|
28410
|
-
const
|
|
28411
|
-
|
|
28412
|
-
|
|
28413
|
-
if (
|
|
28414
|
-
|
|
28559
|
+
const cli = (deps.installCli ?? installCliShim)();
|
|
28560
|
+
const workspaces = (deps.repairWorkspaces ?? defaultRepairWorkspaces)();
|
|
28561
|
+
const agentDeps = {};
|
|
28562
|
+
if (cli.status === "installed" || cli.status === "refreshed" || cli.status === "ok") {
|
|
28563
|
+
agentDeps.findZam = () => cli.shimPath;
|
|
28415
28564
|
}
|
|
28416
|
-
|
|
28417
|
-
}
|
|
28418
|
-
function extractDraft(content) {
|
|
28419
|
-
const fenced = content.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
28420
|
-
const candidate = (fenced?.[1] ?? content).trim();
|
|
28565
|
+
let agents;
|
|
28421
28566
|
try {
|
|
28422
|
-
|
|
28423
|
-
} catch {
|
|
28424
|
-
|
|
28425
|
-
|
|
28426
|
-
|
|
28427
|
-
|
|
28428
|
-
|
|
28429
|
-
|
|
28567
|
+
agents = (deps.connectAgents ?? defaultConnectAgents)(agentDeps);
|
|
28568
|
+
} catch (error) {
|
|
28569
|
+
agents = {
|
|
28570
|
+
success: false,
|
|
28571
|
+
detected: [],
|
|
28572
|
+
connected: 0,
|
|
28573
|
+
companion: null,
|
|
28574
|
+
errors: [error instanceof Error ? error.message : String(error)]
|
|
28575
|
+
};
|
|
28430
28576
|
}
|
|
28577
|
+
(deps.setLastRepaired ?? setLastRepairedVersion)(version);
|
|
28578
|
+
return { version, skipped: false, cli, workspaces, agents };
|
|
28431
28579
|
}
|
|
28432
|
-
|
|
28433
|
-
|
|
28434
|
-
|
|
28435
|
-
|
|
28436
|
-
|
|
28437
|
-
|
|
28438
|
-
|
|
28439
|
-
|
|
28440
|
-
|
|
28441
|
-
|
|
28442
|
-
|
|
28443
|
-
|
|
28444
|
-
|
|
28445
|
-
|
|
28446
|
-
|
|
28447
|
-
|
|
28448
|
-
|
|
28449
|
-
|
|
28450
|
-
|
|
28451
|
-
|
|
28452
|
-
|
|
28453
|
-
|
|
28454
|
-
|
|
28455
|
-
|
|
28456
|
-
|
|
28457
|
-
|
|
28458
|
-
|
|
28459
|
-
|
|
28460
|
-
|
|
28461
|
-
|
|
28462
|
-
|
|
28463
|
-
|
|
28464
|
-
|
|
28465
|
-
evidence: [
|
|
28466
|
-
{
|
|
28467
|
-
type: "keyframe",
|
|
28468
|
-
ref: input8.evidenceRef ?? basename4(input8.imagePath),
|
|
28469
|
-
redacted: input8.redacted ?? false
|
|
28470
|
-
}
|
|
28471
|
-
],
|
|
28472
|
-
candidateTokens: [],
|
|
28473
|
-
confidence: 0.1
|
|
28474
|
-
};
|
|
28580
|
+
|
|
28581
|
+
// src/cli/llm/capability-probe.ts
|
|
28582
|
+
init_kernel();
|
|
28583
|
+
var EMBEDDING_MODEL_HINTS = [
|
|
28584
|
+
"embed",
|
|
28585
|
+
"text-embedding",
|
|
28586
|
+
"bge-",
|
|
28587
|
+
"gte-",
|
|
28588
|
+
"nomic",
|
|
28589
|
+
"mxbai"
|
|
28590
|
+
];
|
|
28591
|
+
var VISION_MODEL_HINTS = [
|
|
28592
|
+
"vision",
|
|
28593
|
+
"-vl",
|
|
28594
|
+
"vl-",
|
|
28595
|
+
"vlm",
|
|
28596
|
+
"llava",
|
|
28597
|
+
"gpt-4o",
|
|
28598
|
+
"gpt-4.1",
|
|
28599
|
+
"gpt-5",
|
|
28600
|
+
"gemini",
|
|
28601
|
+
"pixtral",
|
|
28602
|
+
"minicpm-v",
|
|
28603
|
+
"internvl",
|
|
28604
|
+
"moondream",
|
|
28605
|
+
"llama-3.2",
|
|
28606
|
+
"llama3.2",
|
|
28607
|
+
// Xiaomi MiMo(-VL) is multimodal; the plain "mimo-v*" tag carries no "-vl".
|
|
28608
|
+
"mimo"
|
|
28609
|
+
];
|
|
28610
|
+
function matchesAny(id, hints) {
|
|
28611
|
+
const lower = id.toLowerCase();
|
|
28612
|
+
return hints.some((hint) => lower.includes(hint));
|
|
28475
28613
|
}
|
|
28476
|
-
function
|
|
28477
|
-
|
|
28478
|
-
|
|
28479
|
-
}
|
|
28480
|
-
return "uncertain";
|
|
28614
|
+
function catalogHasModel(catalog, model) {
|
|
28615
|
+
const lower = model.toLowerCase();
|
|
28616
|
+
return catalog.some((id) => id.toLowerCase() === lower);
|
|
28481
28617
|
}
|
|
28482
|
-
function
|
|
28483
|
-
|
|
28484
|
-
|
|
28618
|
+
function classifyCapabilities(entry, catalog, catalogKnown, dimProbeEmbedding = false) {
|
|
28619
|
+
const detected = emptyCapabilityFlags();
|
|
28620
|
+
if (entry.apiFlavor === "anthropic-messages") {
|
|
28621
|
+
detected.text = true;
|
|
28622
|
+
detected.image = true;
|
|
28623
|
+
return detected;
|
|
28485
28624
|
}
|
|
28486
|
-
|
|
28625
|
+
const looksEmbedding = matchesAny(entry.model, EMBEDDING_MODEL_HINTS);
|
|
28626
|
+
const looksVision = matchesAny(entry.model, VISION_MODEL_HINTS);
|
|
28627
|
+
const inCatalog = catalogHasModel(catalog, entry.model);
|
|
28628
|
+
detected.embedding = looksEmbedding || dimProbeEmbedding;
|
|
28629
|
+
detected.image = looksVision;
|
|
28630
|
+
detected.text = !detected.embedding && (inCatalog || !catalogKnown);
|
|
28631
|
+
return detected;
|
|
28487
28632
|
}
|
|
28488
|
-
function
|
|
28489
|
-
if (!
|
|
28490
|
-
return
|
|
28491
|
-
if (!isRecord2(item) || typeof item.type !== "string") return [];
|
|
28492
|
-
if (!ACTION_TYPES2.has(item.type)) return [];
|
|
28493
|
-
const action = { type: item.type };
|
|
28494
|
-
if (typeof item.target === "string" && item.target.trim()) {
|
|
28495
|
-
action.target = item.target.trim();
|
|
28496
|
-
}
|
|
28497
|
-
if (typeof item.result === "string" && item.result.trim()) {
|
|
28498
|
-
action.result = item.result.trim();
|
|
28499
|
-
}
|
|
28500
|
-
return [action];
|
|
28501
|
-
});
|
|
28633
|
+
function resolveApiKey(apiKeyRef) {
|
|
28634
|
+
if (!apiKeyRef) return DEFAULT_LLM_API_KEY;
|
|
28635
|
+
return getProviderApiKey(apiKeyRef) ?? DEFAULT_LLM_API_KEY;
|
|
28502
28636
|
}
|
|
28503
|
-
function
|
|
28504
|
-
|
|
28505
|
-
|
|
28506
|
-
|
|
28507
|
-
|
|
28508
|
-
|
|
28509
|
-
|
|
28510
|
-
|
|
28511
|
-
|
|
28637
|
+
async function probeModelCapabilities(entry, opts = {}) {
|
|
28638
|
+
const apiKey = resolveApiKey(entry.apiKeyRef);
|
|
28639
|
+
if (entry.apiFlavor === "anthropic-messages") {
|
|
28640
|
+
const online2 = await isLlmOnline(entry.url);
|
|
28641
|
+
return {
|
|
28642
|
+
reachable: online2,
|
|
28643
|
+
catalog: [],
|
|
28644
|
+
detected: classifyCapabilities(entry, [], false)
|
|
28645
|
+
};
|
|
28646
|
+
}
|
|
28647
|
+
const online = await isLlmOnline(entry.url);
|
|
28648
|
+
if (!online) {
|
|
28649
|
+
return { reachable: false, catalog: [], detected: emptyCapabilityFlags() };
|
|
28650
|
+
}
|
|
28651
|
+
const catalog = await getAvailableModels(entry.url, apiKey);
|
|
28652
|
+
const catalogKnown = catalog.length > 0;
|
|
28653
|
+
let dimProbeEmbedding = false;
|
|
28654
|
+
const looksEmbedding = matchesAny(entry.model, EMBEDDING_MODEL_HINTS);
|
|
28655
|
+
if (opts.embeddingDimProbe && !catalogKnown && !looksEmbedding) {
|
|
28656
|
+
try {
|
|
28657
|
+
const [vector] = await embedTexts(
|
|
28658
|
+
{ url: entry.url, model: entry.model, apiKey },
|
|
28659
|
+
["capability probe"]
|
|
28660
|
+
);
|
|
28661
|
+
dimProbeEmbedding = Array.isArray(vector) && vector.length > 0;
|
|
28662
|
+
} catch {
|
|
28663
|
+
dimProbeEmbedding = false;
|
|
28512
28664
|
}
|
|
28513
|
-
|
|
28514
|
-
|
|
28515
|
-
|
|
28516
|
-
|
|
28517
|
-
|
|
28518
|
-
|
|
28519
|
-
|
|
28520
|
-
|
|
28521
|
-
|
|
28522
|
-
|
|
28523
|
-
|
|
28524
|
-
return Math.max(0, Math.min(1, value));
|
|
28665
|
+
}
|
|
28666
|
+
return {
|
|
28667
|
+
reachable: true,
|
|
28668
|
+
catalog,
|
|
28669
|
+
detected: classifyCapabilities(
|
|
28670
|
+
entry,
|
|
28671
|
+
catalog,
|
|
28672
|
+
catalogKnown,
|
|
28673
|
+
dimProbeEmbedding
|
|
28674
|
+
)
|
|
28675
|
+
};
|
|
28525
28676
|
}
|
|
28526
|
-
function
|
|
28527
|
-
|
|
28528
|
-
|
|
28529
|
-
|
|
28677
|
+
function reconcileCapabilities(userSelected, detected) {
|
|
28678
|
+
const result = emptyCapabilityFlags();
|
|
28679
|
+
for (const key of Object.keys(result)) {
|
|
28680
|
+
result[key] = userSelected[key] && detected[key];
|
|
28530
28681
|
}
|
|
28531
|
-
return
|
|
28682
|
+
return result;
|
|
28532
28683
|
}
|
|
28533
|
-
function
|
|
28534
|
-
|
|
28684
|
+
function validateModelSave(entry, probe, now = () => (/* @__PURE__ */ new Date()).toISOString()) {
|
|
28685
|
+
if (!probe.reachable) {
|
|
28686
|
+
return {
|
|
28687
|
+
ok: false,
|
|
28688
|
+
error: "Endpoint is unreachable \u2014 cannot verify capabilities. Bring it online and retry."
|
|
28689
|
+
};
|
|
28690
|
+
}
|
|
28691
|
+
return {
|
|
28692
|
+
ok: true,
|
|
28693
|
+
entry: {
|
|
28694
|
+
...entry,
|
|
28695
|
+
capabilities: reconcileCapabilities(entry.capabilities, probe.detected),
|
|
28696
|
+
detectedCapabilities: probe.detected,
|
|
28697
|
+
probedAt: now()
|
|
28698
|
+
}
|
|
28699
|
+
};
|
|
28535
28700
|
}
|
|
28536
28701
|
|
|
28537
|
-
// src/cli/
|
|
28538
|
-
init_kernel();
|
|
28539
|
-
|
|
28540
|
-
// src/cli/commands/shared/db.ts
|
|
28702
|
+
// src/cli/llm/vision.ts
|
|
28541
28703
|
init_kernel();
|
|
28542
|
-
|
|
28543
|
-
|
|
28544
|
-
|
|
28545
|
-
}
|
|
28546
|
-
|
|
28547
|
-
|
|
28548
|
-
|
|
28549
|
-
|
|
28550
|
-
|
|
28551
|
-
|
|
28552
|
-
|
|
28553
|
-
|
|
28554
|
-
|
|
28704
|
+
import { randomBytes } from "crypto";
|
|
28705
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
28706
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
28707
|
+
import { basename as basename5, join as join21 } from "path";
|
|
28708
|
+
var LANGUAGE_NAMES2 = {
|
|
28709
|
+
en: "English",
|
|
28710
|
+
de: "German",
|
|
28711
|
+
es: "Spanish",
|
|
28712
|
+
fr: "French",
|
|
28713
|
+
pt: "Portuguese",
|
|
28714
|
+
zh: "Chinese",
|
|
28715
|
+
ja: "Japanese"
|
|
28716
|
+
};
|
|
28717
|
+
var OBSERVATION_KINDS2 = /* @__PURE__ */ new Set([
|
|
28718
|
+
"progress",
|
|
28719
|
+
"step-completed",
|
|
28720
|
+
"error",
|
|
28721
|
+
"help-seeking",
|
|
28722
|
+
"uncertain",
|
|
28723
|
+
"privacy-pause",
|
|
28724
|
+
"heartbeat"
|
|
28725
|
+
]);
|
|
28726
|
+
var ACTION_TYPES2 = /* @__PURE__ */ new Set([
|
|
28727
|
+
"click",
|
|
28728
|
+
"shortcut",
|
|
28729
|
+
"typing",
|
|
28730
|
+
"scroll",
|
|
28731
|
+
"window-change"
|
|
28732
|
+
]);
|
|
28733
|
+
async function observeUiSnapshotViaLLM(db, input8) {
|
|
28734
|
+
const cfg = await getProviderForRole(db, "vision");
|
|
28735
|
+
if (!cfg.enabled) {
|
|
28736
|
+
throw new Error(
|
|
28737
|
+
"Vision observation is disabled in settings (llm.vision.enabled)"
|
|
28738
|
+
);
|
|
28555
28739
|
}
|
|
28556
|
-
|
|
28557
|
-
|
|
28558
|
-
|
|
28559
|
-
}
|
|
28560
|
-
|
|
28561
|
-
|
|
28562
|
-
|
|
28563
|
-
|
|
28564
|
-
|
|
28565
|
-
|
|
28566
|
-
|
|
28567
|
-
|
|
28568
|
-
|
|
28569
|
-
|
|
28570
|
-
|
|
28571
|
-
|
|
28572
|
-
|
|
28573
|
-
|
|
28574
|
-
|
|
28575
|
-
|
|
28576
|
-
|
|
28577
|
-
|
|
28578
|
-
|
|
28579
|
-
|
|
28580
|
-
|
|
28581
|
-
|
|
28582
|
-
|
|
28583
|
-
|
|
28584
|
-
|
|
28585
|
-
|
|
28586
|
-
|
|
28587
|
-
|
|
28588
|
-
|
|
28589
|
-
|
|
28590
|
-
|
|
28591
|
-
|
|
28592
|
-
|
|
28593
|
-
|
|
28594
|
-
|
|
28595
|
-
|
|
28596
|
-
|
|
28597
|
-
|
|
28598
|
-
|
|
28599
|
-
|
|
28600
|
-
}
|
|
28601
|
-
|
|
28602
|
-
|
|
28603
|
-
}
|
|
28604
|
-
|
|
28605
|
-
|
|
28606
|
-
const
|
|
28607
|
-
|
|
28608
|
-
|
|
28609
|
-
|
|
28610
|
-
|
|
28611
|
-
|
|
28612
|
-
|
|
28613
|
-
|
|
28614
|
-
|
|
28615
|
-
|
|
28616
|
-
|
|
28617
|
-
|
|
28618
|
-
|
|
28619
|
-
|
|
28620
|
-
|
|
28621
|
-
|
|
28622
|
-
|
|
28623
|
-
|
|
28624
|
-
|
|
28625
|
-
|
|
28626
|
-
|
|
28627
|
-
|
|
28628
|
-
|
|
28629
|
-
|
|
28630
|
-
|
|
28631
|
-
const
|
|
28632
|
-
|
|
28633
|
-
|
|
28634
|
-
|
|
28635
|
-
|
|
28636
|
-
|
|
28740
|
+
const imageUrls = [];
|
|
28741
|
+
const isVideo = /\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(input8.imagePath);
|
|
28742
|
+
if (isVideo) {
|
|
28743
|
+
const { mkdirSync: mkdirSync19, readdirSync: readdirSync3, rmSync: rmSync4 } = await import("fs");
|
|
28744
|
+
const { execSync: execSync6 } = await import("child_process");
|
|
28745
|
+
const tempDir = join21(
|
|
28746
|
+
tmpdir2(),
|
|
28747
|
+
`zam-frames-${randomBytes(4).toString("hex")}`
|
|
28748
|
+
);
|
|
28749
|
+
mkdirSync19(tempDir, { recursive: true });
|
|
28750
|
+
try {
|
|
28751
|
+
execSync6(
|
|
28752
|
+
`ffmpeg -i "${input8.imagePath}" -vf "fps=1/3,scale=1280:-1" -vsync vfr "${tempDir}/frame_%03d.png"`,
|
|
28753
|
+
{ stdio: "ignore" }
|
|
28754
|
+
);
|
|
28755
|
+
let files = readdirSync3(tempDir).filter((f) => f.endsWith(".png")).sort();
|
|
28756
|
+
if (files.length === 0) {
|
|
28757
|
+
execSync6(
|
|
28758
|
+
`ffmpeg -i "${input8.imagePath}" -vframes 1 "${tempDir}/frame_001.png"`,
|
|
28759
|
+
{ stdio: "ignore" }
|
|
28760
|
+
);
|
|
28761
|
+
files = readdirSync3(tempDir).filter((f) => f.endsWith(".png")).sort();
|
|
28762
|
+
}
|
|
28763
|
+
const maxFrames = cfg.maxFrames ?? 100;
|
|
28764
|
+
let sampledFiles = files;
|
|
28765
|
+
if (files.length > maxFrames) {
|
|
28766
|
+
if (maxFrames <= 1) {
|
|
28767
|
+
sampledFiles = [files[0]];
|
|
28768
|
+
} else {
|
|
28769
|
+
const step = (files.length - 1) / (maxFrames - 1);
|
|
28770
|
+
sampledFiles = [];
|
|
28771
|
+
for (let i = 0; i < maxFrames; i++) {
|
|
28772
|
+
const index = Math.round(i * step);
|
|
28773
|
+
sampledFiles.push(files[index]);
|
|
28774
|
+
}
|
|
28775
|
+
}
|
|
28776
|
+
}
|
|
28777
|
+
for (const file of sampledFiles) {
|
|
28778
|
+
const bytes = readFileSync16(join21(tempDir, file));
|
|
28779
|
+
imageUrls.push(`data:image/png;base64,${bytes.toString("base64")}`);
|
|
28780
|
+
}
|
|
28781
|
+
} finally {
|
|
28782
|
+
try {
|
|
28783
|
+
rmSync4(tempDir, { recursive: true, force: true });
|
|
28784
|
+
} catch {
|
|
28785
|
+
}
|
|
28786
|
+
}
|
|
28787
|
+
} else {
|
|
28788
|
+
const imageBytes = readFileSync16(input8.imagePath);
|
|
28789
|
+
const ext = input8.imagePath.split(".").pop()?.toLowerCase();
|
|
28790
|
+
const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : "image/png";
|
|
28791
|
+
imageUrls.push(`data:${mime};base64,${imageBytes.toString("base64")}`);
|
|
28792
|
+
}
|
|
28793
|
+
if (imageUrls.length === 0) {
|
|
28794
|
+
throw new Error("No image data available for vision analysis");
|
|
28795
|
+
}
|
|
28796
|
+
const endpoints = [
|
|
28797
|
+
{
|
|
28798
|
+
url: cfg.url,
|
|
28799
|
+
apiKey: cfg.apiKey || DEFAULT_LLM_API_KEY,
|
|
28800
|
+
model: input8.model ?? cfg.model,
|
|
28801
|
+
apiFlavor: cfg.apiFlavor
|
|
28802
|
+
}
|
|
28803
|
+
];
|
|
28804
|
+
if (cfg.fallback) {
|
|
28805
|
+
endpoints.push({
|
|
28806
|
+
url: cfg.fallback.url,
|
|
28807
|
+
apiKey: cfg.fallback.apiKey || DEFAULT_LLM_API_KEY,
|
|
28808
|
+
model: cfg.fallback.model,
|
|
28809
|
+
apiFlavor: cfg.fallback.apiFlavor
|
|
28810
|
+
});
|
|
28811
|
+
}
|
|
28812
|
+
let lastRequestError;
|
|
28813
|
+
let sawUnparseableDraft = false;
|
|
28814
|
+
let sawInvalidDraft = false;
|
|
28815
|
+
for (const endpoint of endpoints) {
|
|
28816
|
+
let content;
|
|
28817
|
+
try {
|
|
28818
|
+
content = await requestVisionDraft({
|
|
28819
|
+
...endpoint,
|
|
28820
|
+
locale: cfg.locale,
|
|
28821
|
+
imageUrls,
|
|
28822
|
+
input: input8
|
|
28823
|
+
});
|
|
28824
|
+
} catch (err) {
|
|
28825
|
+
lastRequestError = err;
|
|
28826
|
+
continue;
|
|
28827
|
+
}
|
|
28828
|
+
let draft;
|
|
28829
|
+
try {
|
|
28830
|
+
draft = extractDraft(content);
|
|
28831
|
+
} catch {
|
|
28832
|
+
sawUnparseableDraft = true;
|
|
28833
|
+
continue;
|
|
28834
|
+
}
|
|
28835
|
+
const report = buildReport(input8, draft);
|
|
28836
|
+
if (isUiObservationReport(report)) {
|
|
28837
|
+
return report;
|
|
28838
|
+
}
|
|
28839
|
+
sawInvalidDraft = true;
|
|
28637
28840
|
}
|
|
28638
|
-
|
|
28639
|
-
|
|
28640
|
-
var readRoles = (db) => readJson(db, "llm.roles", {});
|
|
28641
|
-
async function readScopedProviders(db, machine) {
|
|
28642
|
-
if (machine) return getMachineAiConfig().providers ?? {};
|
|
28643
|
-
if (!db)
|
|
28644
|
-
throw new Error("Database is required for shared provider settings.");
|
|
28645
|
-
return readProviders(db);
|
|
28646
|
-
}
|
|
28647
|
-
async function readScopedRoles(db, machine) {
|
|
28648
|
-
if (machine) return getMachineAiConfig().roles ?? {};
|
|
28649
|
-
if (!db)
|
|
28650
|
-
throw new Error("Database is required for shared provider settings.");
|
|
28651
|
-
return readRoles(db);
|
|
28652
|
-
}
|
|
28653
|
-
async function writeScopedProviders(db, machine, p) {
|
|
28654
|
-
if (machine) {
|
|
28655
|
-
const config = getMachineAiConfig();
|
|
28656
|
-
saveMachineAiConfig({ ...config, providers: p });
|
|
28657
|
-
return;
|
|
28841
|
+
if (lastRequestError && !sawUnparseableDraft && !sawInvalidDraft) {
|
|
28842
|
+
throw lastRequestError;
|
|
28658
28843
|
}
|
|
28659
|
-
if (
|
|
28660
|
-
|
|
28661
|
-
|
|
28662
|
-
|
|
28663
|
-
|
|
28664
|
-
if (machine) {
|
|
28665
|
-
const config = getMachineAiConfig();
|
|
28666
|
-
saveMachineAiConfig({ ...config, roles: r });
|
|
28667
|
-
return;
|
|
28844
|
+
if (sawInvalidDraft) {
|
|
28845
|
+
return uncertainReport(
|
|
28846
|
+
input8,
|
|
28847
|
+
"Vision model returned an invalid UI report draft."
|
|
28848
|
+
);
|
|
28668
28849
|
}
|
|
28669
|
-
|
|
28670
|
-
|
|
28671
|
-
|
|
28850
|
+
return uncertainReport(
|
|
28851
|
+
input8,
|
|
28852
|
+
"Vision model returned output that could not be parsed as JSON."
|
|
28853
|
+
);
|
|
28672
28854
|
}
|
|
28673
|
-
|
|
28674
|
-
|
|
28675
|
-
|
|
28676
|
-
|
|
28677
|
-
}
|
|
28678
|
-
|
|
28855
|
+
var VISION_SYSTEM_PROMPT = "You are ZAM's UI observer. Return only strict JSON. Do not include markdown, prose, or fields outside the requested schema.";
|
|
28856
|
+
function visionSchema(language) {
|
|
28857
|
+
return `{
|
|
28858
|
+
"kind": "progress | step-completed | error | help-seeking | uncertain",
|
|
28859
|
+
"summary": "short factual UI summary in ${language}",
|
|
28860
|
+
"actions": [{"type": "click | shortcut | typing | scroll | window-change", "target": "optional UI target", "result": "optional visible result"}],
|
|
28861
|
+
"candidateTokens": [{"slug": "optional-kebab-case-skill-token", "confidence": 0.0, "rationale": "why this token may matter"}],
|
|
28862
|
+
"confidence": 0.0
|
|
28863
|
+
}`;
|
|
28679
28864
|
}
|
|
28865
|
+
function visionUserText(args, language) {
|
|
28866
|
+
const intro = args.imageUrls.length > 1 ? "Observe this sequence of Windows/macOS application snapshots showing a task performed over time." : "Observe this Windows/macOS application snapshot for a learning session.";
|
|
28867
|
+
return `${intro}
|
|
28868
|
+
Application process: ${args.input.application.processName}
|
|
28869
|
+
Window title: ${args.input.application.windowTitle ?? "(unknown)"}
|
|
28680
28870
|
|
|
28681
|
-
|
|
28682
|
-
|
|
28683
|
-
|
|
28684
|
-
|
|
28685
|
-
|
|
28686
|
-
|
|
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"]
|
|
28871
|
+
Return this JSON draft only:
|
|
28872
|
+
${visionSchema(language)}`;
|
|
28873
|
+
}
|
|
28874
|
+
async function requestVisionDraft(args) {
|
|
28875
|
+
if (args.apiFlavor === "anthropic-messages") {
|
|
28876
|
+
return requestAnthropicVisionDraft(args);
|
|
28714
28877
|
}
|
|
28715
|
-
|
|
28716
|
-
|
|
28717
|
-
|
|
28718
|
-
|
|
28878
|
+
return requestChatCompletionsVisionDraft(args);
|
|
28879
|
+
}
|
|
28880
|
+
async function requestChatCompletionsVisionDraft(args) {
|
|
28881
|
+
const language = LANGUAGE_NAMES2[args.locale] ?? "English";
|
|
28882
|
+
const res = await fetchWithInteractiveTimeout(
|
|
28883
|
+
`${args.url}/chat/completions`,
|
|
28884
|
+
{
|
|
28885
|
+
method: "POST",
|
|
28886
|
+
headers: {
|
|
28887
|
+
"Content-Type": "application/json",
|
|
28888
|
+
Authorization: `Bearer ${args.apiKey}`
|
|
28889
|
+
},
|
|
28890
|
+
body: JSON.stringify({
|
|
28891
|
+
model: args.model,
|
|
28892
|
+
messages: [
|
|
28893
|
+
{ role: "system", content: VISION_SYSTEM_PROMPT },
|
|
28894
|
+
{
|
|
28895
|
+
role: "user",
|
|
28896
|
+
content: [
|
|
28897
|
+
{ type: "text", text: visionUserText(args, language) },
|
|
28898
|
+
...args.imageUrls.map((url) => ({
|
|
28899
|
+
type: "image_url",
|
|
28900
|
+
image_url: { url }
|
|
28901
|
+
}))
|
|
28902
|
+
]
|
|
28903
|
+
}
|
|
28904
|
+
],
|
|
28905
|
+
temperature: 0,
|
|
28906
|
+
max_tokens: args.input.maxTokens ?? DEFAULT_LLM_MAX_TOKENS,
|
|
28907
|
+
...args.url.includes("11434") || args.url.includes("localhost") || args.url.includes("127.0.0.1") ? { options: { num_ctx: 32768 } } : {}
|
|
28908
|
+
}),
|
|
28909
|
+
locale: args.locale,
|
|
28910
|
+
hardTimeoutMs: args.input.hardTimeoutMs ?? 18e4
|
|
28911
|
+
}
|
|
28912
|
+
);
|
|
28913
|
+
if (!res.ok) {
|
|
28914
|
+
const errorText = await res.text().catch(() => "");
|
|
28915
|
+
if (errorText.includes("image") && (errorText.includes("not support") || errorText.includes("unsupported"))) {
|
|
28916
|
+
throw new Error(
|
|
28917
|
+
`Vision model "${args.model}" does not support image input. Set a multimodal model: zam settings set llm.vision.model <model>`
|
|
28918
|
+
);
|
|
28919
|
+
}
|
|
28920
|
+
throw new Error(
|
|
28921
|
+
`Vision LLM request failed: ${res.statusText} (${res.status}) - ${errorText}`
|
|
28922
|
+
);
|
|
28719
28923
|
}
|
|
28720
|
-
const
|
|
28721
|
-
|
|
28722
|
-
|
|
28723
|
-
|
|
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) {
|
|
28924
|
+
const data = await res.json();
|
|
28925
|
+
if (data.error !== void 0) {
|
|
28926
|
+
const errorMsg = formatModelError(data.error);
|
|
28927
|
+
if (errorMsg.includes("image") && (errorMsg.includes("not support") || errorMsg.includes("unsupported"))) {
|
|
28732
28928
|
throw new Error(
|
|
28733
|
-
`
|
|
28929
|
+
`Vision model "${args.model}" does not support image input. Set a multimodal model: zam settings set llm.vision.model <model>`
|
|
28734
28930
|
);
|
|
28735
28931
|
}
|
|
28736
|
-
|
|
28932
|
+
throw new Error(`Vision model failed: ${errorMsg}`);
|
|
28737
28933
|
}
|
|
28738
|
-
|
|
28934
|
+
const content = data.choices?.[0]?.message?.content;
|
|
28935
|
+
if (!content || typeof content !== "string") {
|
|
28936
|
+
throw new Error("Empty response from vision model");
|
|
28937
|
+
}
|
|
28938
|
+
return content.trim();
|
|
28739
28939
|
}
|
|
28740
|
-
function
|
|
28741
|
-
|
|
28742
|
-
|
|
28743
|
-
|
|
28744
|
-
}
|
|
28745
|
-
|
|
28940
|
+
function dataUrlToAnthropicImage(dataUrl) {
|
|
28941
|
+
const match = dataUrl.match(/^data:(image\/[a-zA-Z]+);base64,(.+)$/);
|
|
28942
|
+
if (!match) {
|
|
28943
|
+
throw new Error("Unsupported image data for Anthropic vision request");
|
|
28944
|
+
}
|
|
28945
|
+
return {
|
|
28946
|
+
type: "image",
|
|
28947
|
+
source: { type: "base64", media_type: match[1], data: match[2] }
|
|
28948
|
+
};
|
|
28949
|
+
}
|
|
28950
|
+
async function requestAnthropicVisionDraft(args) {
|
|
28951
|
+
const language = LANGUAGE_NAMES2[args.locale] ?? "English";
|
|
28952
|
+
const base = args.url.replace(/\/+$/, "").replace(/\/v1$/, "");
|
|
28953
|
+
const res = await fetchWithInteractiveTimeout(`${base}/v1/messages`, {
|
|
28954
|
+
method: "POST",
|
|
28955
|
+
headers: {
|
|
28956
|
+
"Content-Type": "application/json",
|
|
28957
|
+
"x-api-key": args.apiKey,
|
|
28958
|
+
"anthropic-version": "2023-06-01"
|
|
28959
|
+
},
|
|
28960
|
+
body: JSON.stringify({
|
|
28961
|
+
model: args.model,
|
|
28962
|
+
max_tokens: args.input.maxTokens ?? DEFAULT_LLM_MAX_TOKENS,
|
|
28963
|
+
system: VISION_SYSTEM_PROMPT,
|
|
28964
|
+
messages: [
|
|
28965
|
+
{
|
|
28966
|
+
role: "user",
|
|
28967
|
+
content: [
|
|
28968
|
+
{ type: "text", text: visionUserText(args, language) },
|
|
28969
|
+
...args.imageUrls.map(dataUrlToAnthropicImage)
|
|
28970
|
+
]
|
|
28971
|
+
}
|
|
28972
|
+
]
|
|
28973
|
+
}),
|
|
28974
|
+
locale: args.locale,
|
|
28975
|
+
hardTimeoutMs: args.input.hardTimeoutMs ?? 18e4
|
|
28976
|
+
});
|
|
28977
|
+
if (!res.ok) {
|
|
28978
|
+
const errorText = await res.text().catch(() => "");
|
|
28979
|
+
throw new Error(
|
|
28980
|
+
`Vision LLM request failed: ${res.statusText} (${res.status}) - ${errorText}`
|
|
28981
|
+
);
|
|
28982
|
+
}
|
|
28983
|
+
const data = await res.json();
|
|
28984
|
+
if (data.error !== void 0) {
|
|
28985
|
+
throw new Error(`Vision model failed: ${formatModelError(data.error)}`);
|
|
28986
|
+
}
|
|
28987
|
+
if (data.stop_reason === "refusal") {
|
|
28988
|
+
throw new Error("Vision model refused the request (safety classifier).");
|
|
28989
|
+
}
|
|
28990
|
+
const text = data.content?.find(
|
|
28991
|
+
(b) => b.type === "text" && typeof b.text === "string"
|
|
28992
|
+
)?.text;
|
|
28993
|
+
if (!text) {
|
|
28994
|
+
throw new Error("Empty response from vision model");
|
|
28746
28995
|
}
|
|
28996
|
+
return text.trim();
|
|
28747
28997
|
}
|
|
28748
|
-
function
|
|
28998
|
+
function extractDraft(content) {
|
|
28999
|
+
const fenced = content.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
29000
|
+
const candidate = (fenced?.[1] ?? content).trim();
|
|
28749
29001
|
try {
|
|
28750
|
-
return
|
|
29002
|
+
return JSON.parse(candidate);
|
|
28751
29003
|
} catch {
|
|
28752
|
-
|
|
29004
|
+
const start = candidate.indexOf("{");
|
|
29005
|
+
const end = candidate.lastIndexOf("}");
|
|
29006
|
+
if (start === -1 || end <= start) throw new Error("no JSON object found");
|
|
29007
|
+
return JSON.parse(
|
|
29008
|
+
candidate.slice(start, end + 1)
|
|
29009
|
+
);
|
|
28753
29010
|
}
|
|
28754
29011
|
}
|
|
28755
|
-
function
|
|
28756
|
-
|
|
28757
|
-
|
|
28758
|
-
|
|
28759
|
-
|
|
28760
|
-
|
|
28761
|
-
|
|
28762
|
-
|
|
28763
|
-
|
|
28764
|
-
|
|
29012
|
+
function buildReport(input8, draft) {
|
|
29013
|
+
return {
|
|
29014
|
+
version: UI_OBSERVATION_PROTOCOL_VERSION,
|
|
29015
|
+
sessionId: input8.sessionId,
|
|
29016
|
+
sequence: input8.sequence,
|
|
29017
|
+
observedFrom: input8.observedFrom,
|
|
29018
|
+
observedTo: input8.observedTo,
|
|
29019
|
+
kind: parseKind(draft.kind),
|
|
29020
|
+
application: input8.application,
|
|
29021
|
+
summary: parseSummary(draft.summary),
|
|
29022
|
+
actions: parseActions(draft.actions),
|
|
29023
|
+
evidence: [
|
|
29024
|
+
{
|
|
29025
|
+
type: "keyframe",
|
|
29026
|
+
ref: input8.evidenceRef ?? basename5(input8.imagePath),
|
|
29027
|
+
redacted: input8.redacted ?? false
|
|
29028
|
+
}
|
|
29029
|
+
],
|
|
29030
|
+
candidateTokens: parseCandidateTokens(draft.candidateTokens),
|
|
29031
|
+
confidence: parseConfidence(draft.confidence, 0.35)
|
|
29032
|
+
};
|
|
28765
29033
|
}
|
|
28766
|
-
function
|
|
28767
|
-
return
|
|
29034
|
+
function uncertainReport(input8, summary) {
|
|
29035
|
+
return {
|
|
29036
|
+
version: UI_OBSERVATION_PROTOCOL_VERSION,
|
|
29037
|
+
sessionId: input8.sessionId,
|
|
29038
|
+
sequence: input8.sequence,
|
|
29039
|
+
observedFrom: input8.observedFrom,
|
|
29040
|
+
observedTo: input8.observedTo,
|
|
29041
|
+
kind: "uncertain",
|
|
29042
|
+
application: input8.application,
|
|
29043
|
+
summary,
|
|
29044
|
+
actions: [],
|
|
29045
|
+
evidence: [
|
|
29046
|
+
{
|
|
29047
|
+
type: "keyframe",
|
|
29048
|
+
ref: input8.evidenceRef ?? basename5(input8.imagePath),
|
|
29049
|
+
redacted: input8.redacted ?? false
|
|
29050
|
+
}
|
|
29051
|
+
],
|
|
29052
|
+
candidateTokens: [],
|
|
29053
|
+
confidence: 0.1
|
|
29054
|
+
};
|
|
28768
29055
|
}
|
|
28769
|
-
function
|
|
28770
|
-
|
|
28771
|
-
|
|
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;
|
|
29056
|
+
function parseKind(value) {
|
|
29057
|
+
if (typeof value === "string" && OBSERVATION_KINDS2.has(value)) {
|
|
29058
|
+
return value;
|
|
28777
29059
|
}
|
|
29060
|
+
return "uncertain";
|
|
28778
29061
|
}
|
|
28779
|
-
function
|
|
28780
|
-
if (
|
|
28781
|
-
|
|
28782
|
-
return "source-directory";
|
|
29062
|
+
function parseSummary(value) {
|
|
29063
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
29064
|
+
return value.trim();
|
|
28783
29065
|
}
|
|
28784
|
-
|
|
28785
|
-
if (!pathExists(destinationDir)) return "missing";
|
|
28786
|
-
if (isSymbolicLink(destinationDir)) return "broken";
|
|
28787
|
-
if (isZamSkillCopy(destinationDir)) return "stale-copy";
|
|
28788
|
-
return "unmanaged";
|
|
29066
|
+
return "Vision model did not provide a factual UI summary.";
|
|
28789
29067
|
}
|
|
28790
|
-
function
|
|
28791
|
-
|
|
28792
|
-
|
|
28793
|
-
if (!
|
|
28794
|
-
|
|
28795
|
-
|
|
28796
|
-
if (
|
|
28797
|
-
|
|
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;
|
|
29068
|
+
function parseActions(value) {
|
|
29069
|
+
if (!Array.isArray(value)) return [];
|
|
29070
|
+
return value.flatMap((item) => {
|
|
29071
|
+
if (!isRecord2(item) || typeof item.type !== "string") return [];
|
|
29072
|
+
if (!ACTION_TYPES2.has(item.type)) return [];
|
|
29073
|
+
const action = { type: item.type };
|
|
29074
|
+
if (typeof item.target === "string" && item.target.trim()) {
|
|
29075
|
+
action.target = item.target.trim();
|
|
28812
29076
|
}
|
|
28813
|
-
if (
|
|
28814
|
-
|
|
28815
|
-
results.push({
|
|
28816
|
-
source: sourceDir,
|
|
28817
|
-
destination: destinationDir,
|
|
28818
|
-
action: "skipped",
|
|
28819
|
-
reason: "source-directory"
|
|
28820
|
-
});
|
|
28821
|
-
continue;
|
|
29077
|
+
if (typeof item.result === "string" && item.result.trim()) {
|
|
29078
|
+
action.result = item.result.trim();
|
|
28822
29079
|
}
|
|
28823
|
-
|
|
28824
|
-
|
|
28825
|
-
|
|
28826
|
-
|
|
28827
|
-
|
|
28828
|
-
|
|
28829
|
-
|
|
28830
|
-
|
|
28831
|
-
|
|
29080
|
+
return [action];
|
|
29081
|
+
});
|
|
29082
|
+
}
|
|
29083
|
+
function parseCandidateTokens(value) {
|
|
29084
|
+
if (!Array.isArray(value)) return [];
|
|
29085
|
+
return value.flatMap((item) => {
|
|
29086
|
+
if (!isRecord2(item)) return [];
|
|
29087
|
+
if (typeof item.slug !== "string" || !/^[A-Za-z0-9._-]+$/.test(item.slug)) {
|
|
29088
|
+
return [];
|
|
28832
29089
|
}
|
|
28833
|
-
|
|
28834
|
-
|
|
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;
|
|
29090
|
+
if (typeof item.rationale !== "string" || !item.rationale.trim()) {
|
|
29091
|
+
return [];
|
|
28848
29092
|
}
|
|
28849
|
-
|
|
28850
|
-
|
|
28851
|
-
|
|
28852
|
-
|
|
28853
|
-
|
|
28854
|
-
rmSync2(destinationDir, { recursive: true, force: true });
|
|
29093
|
+
return [
|
|
29094
|
+
{
|
|
29095
|
+
slug: item.slug,
|
|
29096
|
+
confidence: parseConfidence(item.confidence, 0.2),
|
|
29097
|
+
rationale: item.rationale.trim()
|
|
28855
29098
|
}
|
|
28856
|
-
|
|
28857
|
-
|
|
28858
|
-
|
|
28859
|
-
|
|
28860
|
-
|
|
28861
|
-
|
|
28862
|
-
|
|
28863
|
-
|
|
28864
|
-
|
|
29099
|
+
];
|
|
29100
|
+
});
|
|
29101
|
+
}
|
|
29102
|
+
function parseConfidence(value, fallback) {
|
|
29103
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
29104
|
+
return Math.max(0, Math.min(1, value));
|
|
29105
|
+
}
|
|
29106
|
+
function formatModelError(error) {
|
|
29107
|
+
if (typeof error === "string") return error;
|
|
29108
|
+
if (isRecord2(error) && typeof error.message === "string") {
|
|
29109
|
+
return error.message;
|
|
28865
29110
|
}
|
|
28866
|
-
return
|
|
29111
|
+
return JSON.stringify(error);
|
|
29112
|
+
}
|
|
29113
|
+
function isRecord2(value) {
|
|
29114
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
29115
|
+
}
|
|
29116
|
+
|
|
29117
|
+
// src/cli/providers/config.ts
|
|
29118
|
+
init_kernel();
|
|
29119
|
+
|
|
29120
|
+
// src/cli/commands/shared/db.ts
|
|
29121
|
+
init_kernel();
|
|
29122
|
+
function defaultErrorHandler(message) {
|
|
29123
|
+
console.error("Error:", message);
|
|
29124
|
+
process.exit(1);
|
|
29125
|
+
}
|
|
29126
|
+
async function withDb(fn, onError = defaultErrorHandler) {
|
|
29127
|
+
let db;
|
|
29128
|
+
try {
|
|
29129
|
+
db = await openDatabase();
|
|
29130
|
+
await fn(db);
|
|
29131
|
+
} catch (err) {
|
|
29132
|
+
onError(err.message);
|
|
29133
|
+
} finally {
|
|
29134
|
+
await db?.close();
|
|
29135
|
+
}
|
|
29136
|
+
}
|
|
29137
|
+
function jsonOut(data) {
|
|
29138
|
+
console.log(JSON.stringify(data, null, 2));
|
|
29139
|
+
}
|
|
29140
|
+
|
|
29141
|
+
// src/cli/providers/config.ts
|
|
29142
|
+
var VALID_API_FLAVORS = [
|
|
29143
|
+
"chat-completions",
|
|
29144
|
+
"anthropic-messages"
|
|
29145
|
+
];
|
|
29146
|
+
var VALID_ROLES = ["vision", "recall", "text", "embedding"];
|
|
29147
|
+
function upsertProviderRecord(providers, name, patch) {
|
|
29148
|
+
const merged = { ...providers[name] ?? {} };
|
|
29149
|
+
if (patch.url !== void 0) merged.url = patch.url;
|
|
29150
|
+
if (patch.model !== void 0) merged.model = patch.model;
|
|
29151
|
+
if (patch.apiFlavor !== void 0) merged.apiFlavor = patch.apiFlavor;
|
|
29152
|
+
if (patch.apiKeyRef !== void 0) merged.apiKeyRef = patch.apiKeyRef;
|
|
29153
|
+
if (patch.label !== void 0) merged.label = patch.label;
|
|
29154
|
+
if (patch.local !== void 0) merged.local = patch.local;
|
|
29155
|
+
if (patch.runner !== void 0) merged.runner = patch.runner;
|
|
29156
|
+
return { ...providers, [name]: merged };
|
|
29157
|
+
}
|
|
29158
|
+
function removeProviderRecord(providers, name) {
|
|
29159
|
+
if (!(name in providers)) return { providers, removed: false };
|
|
29160
|
+
const next = { ...providers };
|
|
29161
|
+
delete next[name];
|
|
29162
|
+
return { providers: next, removed: true };
|
|
29163
|
+
}
|
|
29164
|
+
function rolesReferencing(roles, name) {
|
|
29165
|
+
return VALID_ROLES.filter((role) => {
|
|
29166
|
+
const binding = roles[role];
|
|
29167
|
+
return binding?.primary === name || binding?.fallback === name;
|
|
29168
|
+
});
|
|
29169
|
+
}
|
|
29170
|
+
function bindRoleProviders(roles, role, primary, fallback) {
|
|
29171
|
+
const binding = { primary };
|
|
29172
|
+
if (fallback) binding.fallback = fallback;
|
|
29173
|
+
return { ...roles, [role]: binding };
|
|
28867
29174
|
}
|
|
28868
|
-
function
|
|
28869
|
-
|
|
28870
|
-
|
|
28871
|
-
|
|
28872
|
-
|
|
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;
|
|
29175
|
+
function unbindRole(roles, role) {
|
|
29176
|
+
if (!(role in roles)) return roles;
|
|
29177
|
+
const next = { ...roles };
|
|
29178
|
+
delete next[role];
|
|
29179
|
+
return next;
|
|
28882
29180
|
}
|
|
28883
|
-
function
|
|
28884
|
-
|
|
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";
|
|
29181
|
+
function maskSecret(key) {
|
|
29182
|
+
return key.length <= 4 ? "\u2022\u2022\u2022\u2022" : `\u2026${key.slice(-4)}`;
|
|
28892
29183
|
}
|
|
28893
|
-
|
|
28894
|
-
|
|
28895
|
-
|
|
28896
|
-
|
|
28897
|
-
|
|
28898
|
-
|
|
28899
|
-
|
|
28900
|
-
|
|
28901
|
-
|
|
28902
|
-
|
|
28903
|
-
|
|
28904
|
-
|
|
28905
|
-
|
|
28906
|
-
|
|
28907
|
-
|
|
28908
|
-
|
|
28909
|
-
|
|
28910
|
-
|
|
28911
|
-
|
|
28912
|
-
|
|
28913
|
-
${block}
|
|
28914
|
-
`;
|
|
28915
|
-
if (!dryRun) writeFileSync11(dest, next, "utf8");
|
|
28916
|
-
return start >= 0 && end > start ? "update" : "write";
|
|
29184
|
+
function buildProviderListing(providers, hasKey) {
|
|
29185
|
+
return Object.entries(providers).map(([name, rec]) => {
|
|
29186
|
+
const apiFlavor = rec.apiFlavor ?? (rec.url ? inferApiFlavor(rec.url) : "chat-completions");
|
|
29187
|
+
let keyState;
|
|
29188
|
+
if (!rec.apiKeyRef) keyState = "none";
|
|
29189
|
+
else keyState = hasKey(rec.apiKeyRef) ? "set" : "missing";
|
|
29190
|
+
const row = {
|
|
29191
|
+
name,
|
|
29192
|
+
url: rec.url,
|
|
29193
|
+
model: rec.model,
|
|
29194
|
+
apiFlavor,
|
|
29195
|
+
apiKeyRef: rec.apiKeyRef,
|
|
29196
|
+
keyState
|
|
29197
|
+
};
|
|
29198
|
+
if (rec.label !== void 0) row.label = rec.label;
|
|
29199
|
+
if (rec.local !== void 0) row.local = rec.local;
|
|
29200
|
+
if (rec.runner !== void 0) row.runner = rec.runner;
|
|
29201
|
+
return row;
|
|
29202
|
+
});
|
|
28917
29203
|
}
|
|
28918
|
-
function
|
|
28919
|
-
|
|
28920
|
-
|
|
28921
|
-
|
|
28922
|
-
|
|
29204
|
+
function findOrphanKeyRefs(storedRefs, providers) {
|
|
29205
|
+
const used = new Set(
|
|
29206
|
+
Object.values(providers).map((rec) => rec.apiKeyRef).filter((ref) => !!ref)
|
|
29207
|
+
);
|
|
29208
|
+
return storedRefs.filter((ref) => !used.has(ref));
|
|
29209
|
+
}
|
|
29210
|
+
async function readJson(db, key, fallback) {
|
|
29211
|
+
const raw = await getSetting(db, key);
|
|
29212
|
+
if (!raw) return fallback;
|
|
29213
|
+
try {
|
|
29214
|
+
return JSON.parse(raw);
|
|
29215
|
+
} catch {
|
|
29216
|
+
return fallback;
|
|
28923
29217
|
}
|
|
28924
29218
|
}
|
|
28925
|
-
|
|
28926
|
-
|
|
28927
|
-
|
|
28928
|
-
if (
|
|
28929
|
-
|
|
28930
|
-
|
|
28931
|
-
|
|
28932
|
-
|
|
28933
|
-
|
|
28934
|
-
|
|
28935
|
-
|
|
28936
|
-
|
|
28937
|
-
|
|
28938
|
-
|
|
28939
|
-
|
|
28940
|
-
|
|
28941
|
-
|
|
28942
|
-
|
|
28943
|
-
);
|
|
28944
|
-
logInstructionAction(action, "CLAUDE.md", Boolean(opts.dryRun));
|
|
29219
|
+
var readProviders = (db) => readJson(db, "llm.providers", {});
|
|
29220
|
+
var readRoles = (db) => readJson(db, "llm.roles", {});
|
|
29221
|
+
async function readScopedProviders(db, machine) {
|
|
29222
|
+
if (machine) return getMachineAiConfig().providers ?? {};
|
|
29223
|
+
if (!db)
|
|
29224
|
+
throw new Error("Database is required for shared provider settings.");
|
|
29225
|
+
return readProviders(db);
|
|
29226
|
+
}
|
|
29227
|
+
async function readScopedRoles(db, machine) {
|
|
29228
|
+
if (machine) return getMachineAiConfig().roles ?? {};
|
|
29229
|
+
if (!db)
|
|
29230
|
+
throw new Error("Database is required for shared provider settings.");
|
|
29231
|
+
return readRoles(db);
|
|
29232
|
+
}
|
|
29233
|
+
async function writeScopedProviders(db, machine, p) {
|
|
29234
|
+
if (machine) {
|
|
29235
|
+
const config = getMachineAiConfig();
|
|
29236
|
+
saveMachineAiConfig({ ...config, providers: p });
|
|
28945
29237
|
return;
|
|
28946
29238
|
}
|
|
28947
|
-
|
|
28948
|
-
|
|
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
|
-
}
|
|
29239
|
+
if (!db)
|
|
29240
|
+
throw new Error("Database is required for shared provider settings.");
|
|
29241
|
+
await setSetting(db, "llm.providers", JSON.stringify(p));
|
|
28974
29242
|
}
|
|
28975
|
-
function
|
|
28976
|
-
if (
|
|
28977
|
-
|
|
28978
|
-
|
|
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));
|
|
29243
|
+
async function writeScopedRoles(db, machine, r) {
|
|
29244
|
+
if (machine) {
|
|
29245
|
+
const config = getMachineAiConfig();
|
|
29246
|
+
saveMachineAiConfig({ ...config, roles: r });
|
|
28995
29247
|
return;
|
|
28996
29248
|
}
|
|
28997
|
-
|
|
28998
|
-
|
|
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
|
-
}
|
|
29249
|
+
if (!db)
|
|
29250
|
+
throw new Error("Database is required for shared provider settings.");
|
|
29251
|
+
await setSetting(db, "llm.roles", JSON.stringify(r));
|
|
29031
29252
|
}
|
|
29032
|
-
function
|
|
29033
|
-
|
|
29034
|
-
|
|
29035
|
-
|
|
29036
|
-
|
|
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
|
-
);
|
|
29253
|
+
async function withProviderScope(machine, action) {
|
|
29254
|
+
if (machine) {
|
|
29255
|
+
await action(void 0);
|
|
29256
|
+
return;
|
|
29257
|
+
}
|
|
29258
|
+
await withDb(action);
|
|
29049
29259
|
}
|
|
29050
29260
|
|
|
29051
29261
|
// src/cli/users/identity.ts
|
|
@@ -29071,13 +29281,13 @@ async function resolveUser(opts, db, resolveOpts) {
|
|
|
29071
29281
|
}
|
|
29072
29282
|
|
|
29073
29283
|
// src/cli/workspaces/backup.ts
|
|
29074
|
-
import { mkdirSync as
|
|
29075
|
-
import { join as
|
|
29284
|
+
import { mkdirSync as mkdirSync15 } from "fs";
|
|
29285
|
+
import { join as join22 } from "path";
|
|
29076
29286
|
async function backupDatabaseTo(db, targetDir) {
|
|
29077
|
-
const backupDir =
|
|
29078
|
-
|
|
29287
|
+
const backupDir = join22(targetDir, "zam-backups");
|
|
29288
|
+
mkdirSync15(backupDir, { recursive: true });
|
|
29079
29289
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
29080
|
-
const dest =
|
|
29290
|
+
const dest = join22(backupDir, `zam-${stamp}.db`);
|
|
29081
29291
|
await db.exec(`VACUUM INTO '${dest.replace(/'/g, "''")}'`);
|
|
29082
29292
|
return dest;
|
|
29083
29293
|
}
|
|
@@ -29208,20 +29418,20 @@ bridgeCommand.command("workspace-info").description("Report the workspace dir, i
|
|
|
29208
29418
|
activeWorkspace,
|
|
29209
29419
|
workspaceDir: activeWorkspace.path,
|
|
29210
29420
|
defaultWorkspaceDir: defaultWorkspaceDir(),
|
|
29211
|
-
dataDir:
|
|
29421
|
+
dataDir: join23(homedir14(), ".zam")
|
|
29212
29422
|
});
|
|
29213
29423
|
});
|
|
29214
29424
|
});
|
|
29215
29425
|
function provisionConfiguredWorkspaces() {
|
|
29216
29426
|
return getConfiguredWorkspaces().flatMap(
|
|
29217
|
-
(workspace) =>
|
|
29427
|
+
(workspace) => existsSync21(workspace.path) ? wireSkills(workspace.path, parseSetupAgents(), { quiet: true }) : []
|
|
29218
29428
|
);
|
|
29219
29429
|
}
|
|
29220
29430
|
function buildWorkspaceLinkHealth(workspaces) {
|
|
29221
29431
|
const agents = parseSetupAgents();
|
|
29222
29432
|
const map = {};
|
|
29223
29433
|
for (const workspace of workspaces) {
|
|
29224
|
-
if (!
|
|
29434
|
+
if (!existsSync21(workspace.path)) continue;
|
|
29225
29435
|
const links = inspectSkillLinks(workspace.path, agents);
|
|
29226
29436
|
map[workspace.id] = {
|
|
29227
29437
|
health: summarizeSkillLinkHealth(links),
|
|
@@ -29251,7 +29461,7 @@ bridgeCommand.command("workspace-list").description("List configured ZAM workspa
|
|
|
29251
29461
|
activeWorkspace,
|
|
29252
29462
|
workspaceDir: activeWorkspace.path,
|
|
29253
29463
|
defaultWorkspaceDir: defaultWorkspaceDir(),
|
|
29254
|
-
dataDir:
|
|
29464
|
+
dataDir: join23(homedir14(), ".zam"),
|
|
29255
29465
|
linkHealth: buildWorkspaceLinkHealth(workspaces)
|
|
29256
29466
|
});
|
|
29257
29467
|
});
|
|
@@ -29266,7 +29476,7 @@ bridgeCommand.command("workspace-repair-links").description(
|
|
|
29266
29476
|
if (!id) jsonError("A non-empty --id is required");
|
|
29267
29477
|
const workspace = getConfiguredWorkspaces().find((item) => item.id === id);
|
|
29268
29478
|
if (!workspace) jsonError(`Workspace "${id}" is not configured`);
|
|
29269
|
-
if (!
|
|
29479
|
+
if (!existsSync21(workspace.path)) {
|
|
29270
29480
|
jsonError(`Workspace path does not exist: ${workspace.path}`);
|
|
29271
29481
|
}
|
|
29272
29482
|
const agents = parseSetupAgents(opts.agents);
|
|
@@ -29297,8 +29507,8 @@ function parseBridgeWorkspaceKind(value) {
|
|
|
29297
29507
|
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
29508
|
const raw = String(opts.path ?? "").trim();
|
|
29299
29509
|
if (!raw) jsonError("A non-empty --path is required");
|
|
29300
|
-
const path =
|
|
29301
|
-
if (!
|
|
29510
|
+
const path = resolve6(raw);
|
|
29511
|
+
if (!existsSync21(path)) jsonError(`Workspace path does not exist: ${path}`);
|
|
29302
29512
|
const id = opts.id ? String(opts.id).trim() : void 0;
|
|
29303
29513
|
if (opts.id && !id) jsonError("A non-empty --id is required");
|
|
29304
29514
|
const kind = parseBridgeWorkspaceKind(opts.kind);
|
|
@@ -29342,8 +29552,8 @@ bridgeCommand.command("workspace-remove").description("Unregister a ZAM workspac
|
|
|
29342
29552
|
bridgeCommand.command("set-workspace-dir").description("Set the personal workspace directory (JSON)").requiredOption("--dir <path>", "Path to the workspace directory").action(async (opts) => {
|
|
29343
29553
|
const raw = String(opts.dir ?? "").trim();
|
|
29344
29554
|
if (!raw) jsonError("A non-empty --dir is required");
|
|
29345
|
-
const dir =
|
|
29346
|
-
if (!
|
|
29555
|
+
const dir = resolve6(raw);
|
|
29556
|
+
if (!existsSync21(dir)) jsonError(`Workspace path does not exist: ${dir}`);
|
|
29347
29557
|
const skillLinks = wireSkills(dir, parseSetupAgents(), { quiet: true });
|
|
29348
29558
|
await withDb2(async (db) => {
|
|
29349
29559
|
const workspace = await activateWorkspacePath(db, dir);
|
|
@@ -29406,7 +29616,7 @@ bridgeCommand.command("agent-open").description("Launch an agent harness in the
|
|
|
29406
29616
|
jsonError(`Workspace is not configured: ${opts.workspace}`);
|
|
29407
29617
|
}
|
|
29408
29618
|
const activeWorkspace = await ensureActiveWorkspace(db);
|
|
29409
|
-
const workspace = opts.dir ?
|
|
29619
|
+
const workspace = opts.dir ? existsSync21(opts.dir) ? opts.dir : homedir14() : existingWorkspaceDirOrHome(configuredWorkspace ?? activeWorkspace);
|
|
29410
29620
|
launchHarness(harness, {
|
|
29411
29621
|
executable,
|
|
29412
29622
|
workspace,
|
|
@@ -29778,7 +29988,7 @@ bridgeCommand.command("discover-skills").description(
|
|
|
29778
29988
|
"20"
|
|
29779
29989
|
).action(async (opts) => {
|
|
29780
29990
|
try {
|
|
29781
|
-
const monitorDir =
|
|
29991
|
+
const monitorDir = join23(homedir14(), ".zam", "monitor");
|
|
29782
29992
|
let files;
|
|
29783
29993
|
try {
|
|
29784
29994
|
files = readdirSync2(monitorDir).filter((f) => f.endsWith(".jsonl"));
|
|
@@ -29791,7 +30001,7 @@ bridgeCommand.command("discover-skills").description(
|
|
|
29791
30001
|
return;
|
|
29792
30002
|
}
|
|
29793
30003
|
const limit = Number(opts.limit);
|
|
29794
|
-
const sorted = files.map((f) => ({ name: f, path:
|
|
30004
|
+
const sorted = files.map((f) => ({ name: f, path: join23(monitorDir, f) })).sort((a, b) => b.name.localeCompare(a.name)).slice(0, limit);
|
|
29795
30005
|
const sessionCommands = /* @__PURE__ */ new Map();
|
|
29796
30006
|
for (const file of sorted) {
|
|
29797
30007
|
const sessionId = file.name.replace(".jsonl", "");
|
|
@@ -29903,7 +30113,7 @@ bridgeCommand.command("observe-ui-snapshot").description(
|
|
|
29903
30113
|
});
|
|
29904
30114
|
function resolveWindowsPowerShell() {
|
|
29905
30115
|
try {
|
|
29906
|
-
|
|
30116
|
+
execFileSync5("where.exe", ["pwsh.exe"], { stdio: "ignore" });
|
|
29907
30117
|
return "pwsh";
|
|
29908
30118
|
} catch {
|
|
29909
30119
|
return "powershell";
|
|
@@ -29918,7 +30128,7 @@ function captureScreenshot(outputPath, hwnd, processName) {
|
|
|
29918
30128
|
throw new Error(`Invalid process name format: ${processName}`);
|
|
29919
30129
|
}
|
|
29920
30130
|
if (platform === "win32") {
|
|
29921
|
-
const stdout =
|
|
30131
|
+
const stdout = execFileSync5(
|
|
29922
30132
|
resolveWindowsPowerShell(),
|
|
29923
30133
|
[
|
|
29924
30134
|
"-NoProfile",
|
|
@@ -30231,13 +30441,13 @@ Write-CaptureResult "fullscreen" $hwndVal $matchedBy
|
|
|
30231
30441
|
} else if (platform === "darwin") {
|
|
30232
30442
|
if (hwnd) {
|
|
30233
30443
|
const parsedHwnd = hwnd.startsWith("0x") ? parseInt(hwnd, 16) : parseInt(hwnd, 10);
|
|
30234
|
-
|
|
30444
|
+
execFileSync5("screencapture", ["-l", String(parsedHwnd), outputPath], {
|
|
30235
30445
|
stdio: "pipe"
|
|
30236
30446
|
});
|
|
30237
30447
|
return { method: "screencapture-window", target: null };
|
|
30238
30448
|
} else if (processName) {
|
|
30239
30449
|
try {
|
|
30240
|
-
const windowId =
|
|
30450
|
+
const windowId = execFileSync5(
|
|
30241
30451
|
"osascript",
|
|
30242
30452
|
[
|
|
30243
30453
|
"-e",
|
|
@@ -30246,17 +30456,17 @@ Write-CaptureResult "fullscreen" $hwndVal $matchedBy
|
|
|
30246
30456
|
{ encoding: "utf8", stdio: ["pipe", "pipe", "ignore"] }
|
|
30247
30457
|
).trim();
|
|
30248
30458
|
if (windowId && /^\d+$/.test(windowId)) {
|
|
30249
|
-
|
|
30459
|
+
execFileSync5("screencapture", ["-l", windowId, outputPath], {
|
|
30250
30460
|
stdio: "pipe"
|
|
30251
30461
|
});
|
|
30252
30462
|
return { method: "screencapture-window", target: null };
|
|
30253
30463
|
}
|
|
30254
30464
|
} catch {
|
|
30255
30465
|
}
|
|
30256
|
-
|
|
30466
|
+
execFileSync5("screencapture", ["-x", outputPath], { stdio: "pipe" });
|
|
30257
30467
|
return { method: "screencapture-full", target: null };
|
|
30258
30468
|
} else {
|
|
30259
|
-
|
|
30469
|
+
execFileSync5("screencapture", ["-x", outputPath], { stdio: "pipe" });
|
|
30260
30470
|
return { method: "screencapture-full", target: null };
|
|
30261
30471
|
}
|
|
30262
30472
|
} else {
|
|
@@ -30293,7 +30503,7 @@ bridgeCommand.command("capture-ui").description("Capture a screenshot for agent-
|
|
|
30293
30503
|
return;
|
|
30294
30504
|
}
|
|
30295
30505
|
}
|
|
30296
|
-
const outputPath = opts.image ?? opts.output ??
|
|
30506
|
+
const outputPath = opts.image ?? opts.output ?? join23(tmpdir3(), `zam-capture-${randomBytes2(4).toString("hex")}.png`);
|
|
30297
30507
|
const captureResult = isProvided ? { method: "provided", target: null } : captureScreenshot(outputPath, opts.hwnd, opts.processName);
|
|
30298
30508
|
if (!isProvided) {
|
|
30299
30509
|
const post = decidePostCapture(policy, {
|
|
@@ -30321,7 +30531,7 @@ bridgeCommand.command("capture-ui").description("Capture a screenshot for agent-
|
|
|
30321
30531
|
return;
|
|
30322
30532
|
}
|
|
30323
30533
|
}
|
|
30324
|
-
const imageBytes =
|
|
30534
|
+
const imageBytes = readFileSync17(outputPath);
|
|
30325
30535
|
const base64 = imageBytes.toString("base64");
|
|
30326
30536
|
jsonOut2({
|
|
30327
30537
|
sessionId: opts.session ?? null,
|
|
@@ -30348,11 +30558,11 @@ bridgeCommand.command("start-recording").description("Start screen recording in
|
|
|
30348
30558
|
return;
|
|
30349
30559
|
}
|
|
30350
30560
|
const sessionId = opts.session;
|
|
30351
|
-
const statePath =
|
|
30561
|
+
const statePath = join23(tmpdir3(), `zam-recording-${sessionId}.json`);
|
|
30352
30562
|
const defaultExt = platform === "win32" ? ".mkv" : ".mov";
|
|
30353
|
-
const outputPath = opts.output ??
|
|
30354
|
-
const { existsSync:
|
|
30355
|
-
if (
|
|
30563
|
+
const outputPath = opts.output ?? join23(tmpdir3(), `zam-recording-${sessionId}${defaultExt}`);
|
|
30564
|
+
const { existsSync: existsSync30, writeFileSync: writeFileSync17, openSync, closeSync } = await import("fs");
|
|
30565
|
+
if (existsSync30(statePath)) {
|
|
30356
30566
|
jsonOut2({
|
|
30357
30567
|
sessionId,
|
|
30358
30568
|
started: false,
|
|
@@ -30360,7 +30570,7 @@ bridgeCommand.command("start-recording").description("Start screen recording in
|
|
|
30360
30570
|
});
|
|
30361
30571
|
return;
|
|
30362
30572
|
}
|
|
30363
|
-
const logPath =
|
|
30573
|
+
const logPath = join23(tmpdir3(), `zam-recording-${sessionId}.log`);
|
|
30364
30574
|
let logFd;
|
|
30365
30575
|
try {
|
|
30366
30576
|
logFd = openSync(logPath, "w");
|
|
@@ -30406,7 +30616,7 @@ bridgeCommand.command("start-recording").description("Start screen recording in
|
|
|
30406
30616
|
}
|
|
30407
30617
|
child.unref();
|
|
30408
30618
|
if (child.pid) {
|
|
30409
|
-
|
|
30619
|
+
writeFileSync17(
|
|
30410
30620
|
statePath,
|
|
30411
30621
|
JSON.stringify({
|
|
30412
30622
|
pid: child.pid,
|
|
@@ -30442,9 +30652,9 @@ bridgeCommand.command("stop-recording").description(
|
|
|
30442
30652
|
return;
|
|
30443
30653
|
}
|
|
30444
30654
|
const sessionId = opts.session;
|
|
30445
|
-
const statePath =
|
|
30446
|
-
const { existsSync:
|
|
30447
|
-
if (!
|
|
30655
|
+
const statePath = join23(tmpdir3(), `zam-recording-${sessionId}.json`);
|
|
30656
|
+
const { existsSync: existsSync30, readFileSync: readFileSync22, rmSync: rmSync4 } = await import("fs");
|
|
30657
|
+
if (!existsSync30(statePath)) {
|
|
30448
30658
|
jsonOut2({
|
|
30449
30659
|
sessionId,
|
|
30450
30660
|
stopped: false,
|
|
@@ -30452,7 +30662,7 @@ bridgeCommand.command("stop-recording").description(
|
|
|
30452
30662
|
});
|
|
30453
30663
|
return;
|
|
30454
30664
|
}
|
|
30455
|
-
const state = JSON.parse(
|
|
30665
|
+
const state = JSON.parse(readFileSync22(statePath, "utf8"));
|
|
30456
30666
|
const { pid, outputPath } = state;
|
|
30457
30667
|
try {
|
|
30458
30668
|
process.kill(pid, "SIGINT");
|
|
@@ -30468,7 +30678,7 @@ bridgeCommand.command("stop-recording").description(
|
|
|
30468
30678
|
};
|
|
30469
30679
|
let attempts = 0;
|
|
30470
30680
|
while (isProcessRunning(pid) && attempts < 20) {
|
|
30471
|
-
await new Promise((
|
|
30681
|
+
await new Promise((resolve12) => setTimeout(resolve12, 250));
|
|
30472
30682
|
attempts++;
|
|
30473
30683
|
}
|
|
30474
30684
|
if (isProcessRunning(pid)) {
|
|
@@ -30481,7 +30691,7 @@ bridgeCommand.command("stop-recording").description(
|
|
|
30481
30691
|
rmSync4(statePath, { force: true });
|
|
30482
30692
|
} catch {
|
|
30483
30693
|
}
|
|
30484
|
-
if (!
|
|
30694
|
+
if (!existsSync30(outputPath)) {
|
|
30485
30695
|
jsonOut2({
|
|
30486
30696
|
sessionId,
|
|
30487
30697
|
stopped: false,
|
|
@@ -30911,7 +31121,7 @@ bridgeCommand.command("cloud-model-hint").description("Suggest a cloud model for
|
|
|
30911
31121
|
});
|
|
30912
31122
|
bridgeCommand.command("local-llm-hints").description("Detect installed local LLM servers and suggest defaults (JSON)").action(() => {
|
|
30913
31123
|
const profile = getSystemProfile();
|
|
30914
|
-
const flmInstalled = hasCommand("flm") ||
|
|
31124
|
+
const flmInstalled = hasCommand("flm") || existsSync21("C:\\Program Files\\flm\\flm.exe");
|
|
30915
31125
|
const ollamaInstalled = isOllamaInstalled();
|
|
30916
31126
|
const runners = [
|
|
30917
31127
|
{ id: "flm", label: "FastFlowLM", installed: flmInstalled },
|
|
@@ -31143,13 +31353,15 @@ bridgeCommand.command("desktop-bootstrap").description("Initialize first-run des
|
|
|
31143
31353
|
const userId = await ensureDefaultUser(db, opts.user);
|
|
31144
31354
|
const { enabled, url, model, locale } = await getLlmConfig(db);
|
|
31145
31355
|
const { workspaceDir, activeWorkspaceId, skillLinks } = await ensureDesktopWorkspace(db);
|
|
31356
|
+
const cli = installCliShim();
|
|
31146
31357
|
jsonOut2({
|
|
31147
31358
|
userId,
|
|
31148
31359
|
locale,
|
|
31149
31360
|
llm: { enabled, url, model },
|
|
31150
31361
|
activeWorkspaceId,
|
|
31151
31362
|
workspaceDir,
|
|
31152
|
-
skillLinks
|
|
31363
|
+
skillLinks,
|
|
31364
|
+
cli
|
|
31153
31365
|
});
|
|
31154
31366
|
});
|
|
31155
31367
|
});
|
|
@@ -31166,6 +31378,22 @@ bridgeCommand.command("get-settings").description("Get active ZAM settings (JSON
|
|
|
31166
31378
|
});
|
|
31167
31379
|
});
|
|
31168
31380
|
});
|
|
31381
|
+
bridgeCommand.command("install-repair").description(
|
|
31382
|
+
"Verify and repair this machine's ZAM installation: CLI shim + PATH, workspace skill links, agent configs and companion extensions (JSON)"
|
|
31383
|
+
).option(
|
|
31384
|
+
"--if-version-changed",
|
|
31385
|
+
"Only run when the app version differs from the last repaired one; reports skipped:true otherwise"
|
|
31386
|
+
).action((opts) => {
|
|
31387
|
+
try {
|
|
31388
|
+
jsonOut2(
|
|
31389
|
+
performInstallRepair({
|
|
31390
|
+
ifVersionChanged: Boolean(opts.ifVersionChanged)
|
|
31391
|
+
})
|
|
31392
|
+
);
|
|
31393
|
+
} catch (err) {
|
|
31394
|
+
jsonError(err.message);
|
|
31395
|
+
}
|
|
31396
|
+
});
|
|
31169
31397
|
bridgeCommand.command("agent-harness-status").description(
|
|
31170
31398
|
"Detect installed agent harnesses and their ZAM MCP configuration state (JSON)"
|
|
31171
31399
|
).action(() => {
|
|
@@ -34098,28 +34326,28 @@ var doctorCommand = new Command5("doctor").description(
|
|
|
34098
34326
|
// src/cli/commands/git-sync.ts
|
|
34099
34327
|
init_kernel();
|
|
34100
34328
|
import { execSync as execSync5 } from "child_process";
|
|
34101
|
-
import { chmodSync as
|
|
34102
|
-
import { join as
|
|
34329
|
+
import { chmodSync as chmodSync3, existsSync as existsSync22, writeFileSync as writeFileSync13 } from "fs";
|
|
34330
|
+
import { join as join24 } from "path";
|
|
34103
34331
|
import { Command as Command6 } from "commander";
|
|
34104
34332
|
function installHook2() {
|
|
34105
|
-
const gitDir =
|
|
34106
|
-
if (!
|
|
34333
|
+
const gitDir = join24(process.cwd(), ".git");
|
|
34334
|
+
if (!existsSync22(gitDir)) {
|
|
34107
34335
|
console.error(
|
|
34108
34336
|
"Error: Current directory is not the root of a Git repository."
|
|
34109
34337
|
);
|
|
34110
34338
|
process.exit(1);
|
|
34111
34339
|
}
|
|
34112
|
-
const hooksDir =
|
|
34113
|
-
const hookPath =
|
|
34340
|
+
const hooksDir = join24(gitDir, "hooks");
|
|
34341
|
+
const hookPath = join24(hooksDir, "post-commit");
|
|
34114
34342
|
const hookContent = `#!/bin/sh
|
|
34115
34343
|
# ZAM Spaced Repetition Auto-Stale Hook
|
|
34116
34344
|
# Triggered automatically on git commits to decay modified concept cards.
|
|
34117
34345
|
zam git-sync --commit HEAD --quiet
|
|
34118
34346
|
`;
|
|
34119
34347
|
try {
|
|
34120
|
-
|
|
34348
|
+
writeFileSync13(hookPath, hookContent, { encoding: "utf-8", flag: "w" });
|
|
34121
34349
|
try {
|
|
34122
|
-
|
|
34350
|
+
chmodSync3(hookPath, "755");
|
|
34123
34351
|
} catch (_e) {
|
|
34124
34352
|
}
|
|
34125
34353
|
console.log(
|
|
@@ -34220,8 +34448,8 @@ ZAM Auto-Stale Complete: Scanned ${changedFiles.length} file(s).`
|
|
|
34220
34448
|
|
|
34221
34449
|
// src/cli/commands/goal.ts
|
|
34222
34450
|
init_kernel();
|
|
34223
|
-
import { existsSync as
|
|
34224
|
-
import { resolve as
|
|
34451
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync16 } from "fs";
|
|
34452
|
+
import { resolve as resolve7 } from "path";
|
|
34225
34453
|
import { input as input2 } from "@inquirer/prompts";
|
|
34226
34454
|
import { Command as Command7 } from "commander";
|
|
34227
34455
|
async function resolveGoalsDir() {
|
|
@@ -34234,7 +34462,7 @@ async function resolveGoalsDir() {
|
|
|
34234
34462
|
} finally {
|
|
34235
34463
|
await db?.close();
|
|
34236
34464
|
}
|
|
34237
|
-
return goalsDir ?
|
|
34465
|
+
return goalsDir ? resolve7(goalsDir) : resolve7("goals");
|
|
34238
34466
|
}
|
|
34239
34467
|
var goalCommand = new Command7("goal").description(
|
|
34240
34468
|
"Manage learning goals (markdown files)"
|
|
@@ -34244,7 +34472,7 @@ goalCommand.command("list").description("List all goals").option(
|
|
|
34244
34472
|
"Filter by status (active, completed, paused, abandoned)"
|
|
34245
34473
|
).option("--tree", "Show goals as a tree with parent/child relationships").option("--json", "Output as JSON").action(async (opts) => {
|
|
34246
34474
|
const goalsDir = await resolveGoalsDir();
|
|
34247
|
-
if (!
|
|
34475
|
+
if (!existsSync23(goalsDir)) {
|
|
34248
34476
|
console.error(`Goals directory not found: ${goalsDir}`);
|
|
34249
34477
|
console.error(
|
|
34250
34478
|
"Set it with: zam settings set personal.goals_dir /path/to/goals"
|
|
@@ -34345,8 +34573,8 @@ ${"\u2500".repeat(50)}`);
|
|
|
34345
34573
|
});
|
|
34346
34574
|
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
34575
|
const goalsDir = await resolveGoalsDir();
|
|
34348
|
-
if (!
|
|
34349
|
-
|
|
34576
|
+
if (!existsSync23(goalsDir)) {
|
|
34577
|
+
mkdirSync16(goalsDir, { recursive: true });
|
|
34350
34578
|
}
|
|
34351
34579
|
let slug = opts.slug;
|
|
34352
34580
|
let title = opts.title;
|
|
@@ -34406,22 +34634,22 @@ goalCommand.command("status <slug> <status>").description("Update a goal's statu
|
|
|
34406
34634
|
|
|
34407
34635
|
// src/cli/commands/init.ts
|
|
34408
34636
|
init_kernel();
|
|
34409
|
-
import { existsSync as
|
|
34410
|
-
import { homedir as
|
|
34411
|
-
import { join as
|
|
34637
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync17, writeFileSync as writeFileSync14 } from "fs";
|
|
34638
|
+
import { homedir as homedir15 } from "os";
|
|
34639
|
+
import { join as join25, resolve as resolve8 } from "path";
|
|
34412
34640
|
import { confirm, input as input3 } from "@inquirer/prompts";
|
|
34413
34641
|
import { Command as Command8 } from "commander";
|
|
34414
|
-
var HOME2 =
|
|
34642
|
+
var HOME2 = homedir15();
|
|
34415
34643
|
function printLine(char = "\u2550", len = 60, color = "\x1B[36m") {
|
|
34416
34644
|
console.log(`${color}${char.repeat(len)}\x1B[0m`);
|
|
34417
34645
|
}
|
|
34418
34646
|
function bootstrapSandboxWorkspace(workspaceDir) {
|
|
34419
|
-
|
|
34420
|
-
|
|
34421
|
-
|
|
34422
|
-
const worldviewFile =
|
|
34423
|
-
if (!
|
|
34424
|
-
|
|
34647
|
+
mkdirSync17(join25(workspaceDir, "beliefs"), { recursive: true });
|
|
34648
|
+
mkdirSync17(join25(workspaceDir, "goals"), { recursive: true });
|
|
34649
|
+
mkdirSync17(join25(workspaceDir, "skills"), { recursive: true });
|
|
34650
|
+
const worldviewFile = join25(workspaceDir, "beliefs", "worldview.md");
|
|
34651
|
+
if (!existsSync24(worldviewFile)) {
|
|
34652
|
+
writeFileSync14(
|
|
34425
34653
|
worldviewFile,
|
|
34426
34654
|
`# Personal Worldview
|
|
34427
34655
|
|
|
@@ -34433,9 +34661,9 @@ Here, I declare the core concepts and principles I want to master.
|
|
|
34433
34661
|
"utf8"
|
|
34434
34662
|
);
|
|
34435
34663
|
}
|
|
34436
|
-
const goalsFile =
|
|
34437
|
-
if (!
|
|
34438
|
-
|
|
34664
|
+
const goalsFile = join25(workspaceDir, "goals", "goals.md");
|
|
34665
|
+
if (!existsSync24(goalsFile)) {
|
|
34666
|
+
writeFileSync14(
|
|
34439
34667
|
goalsFile,
|
|
34440
34668
|
`# Personal Goals
|
|
34441
34669
|
|
|
@@ -34457,8 +34685,8 @@ var initCommand = new Command8("init").description("Launch the guided interactiv
|
|
|
34457
34685
|
);
|
|
34458
34686
|
printLine();
|
|
34459
34687
|
console.log("\n\x1B[1m[1/5] Setting up Local Workspace Sandbox\x1B[0m");
|
|
34460
|
-
const defaultWorkspace =
|
|
34461
|
-
const workspacePath =
|
|
34688
|
+
const defaultWorkspace = join25(HOME2, "Documents", "zam");
|
|
34689
|
+
const workspacePath = resolve8(
|
|
34462
34690
|
await input3({
|
|
34463
34691
|
message: "Choose your ZAM workspace directory:",
|
|
34464
34692
|
default: defaultWorkspace
|
|
@@ -35659,7 +35887,7 @@ observerCommand.command("revoke <process>").description("Remove a process from o
|
|
|
35659
35887
|
|
|
35660
35888
|
// src/cli/commands/profile.ts
|
|
35661
35889
|
init_kernel();
|
|
35662
|
-
import { dirname as dirname10, resolve as
|
|
35890
|
+
import { dirname as dirname10, resolve as resolve9 } from "path";
|
|
35663
35891
|
import { Command as Command13 } from "commander";
|
|
35664
35892
|
var C2 = {
|
|
35665
35893
|
reset: "\x1B[0m",
|
|
@@ -35687,7 +35915,7 @@ var profileCommand = new Command13("profile").description("Show or change this m
|
|
|
35687
35915
|
if (opts.mode) setInstallMode(opts.mode);
|
|
35688
35916
|
db = await openDatabaseWithSync({ initialize: true });
|
|
35689
35917
|
if (opts.dir) {
|
|
35690
|
-
await activateWorkspacePath(db,
|
|
35918
|
+
await activateWorkspacePath(db, resolve9(opts.dir), {
|
|
35691
35919
|
kind: "personal",
|
|
35692
35920
|
label: "Personal"
|
|
35693
35921
|
});
|
|
@@ -36089,7 +36317,7 @@ ${"\u2550".repeat(50)}`);
|
|
|
36089
36317
|
|
|
36090
36318
|
// src/cli/commands/session.ts
|
|
36091
36319
|
init_kernel();
|
|
36092
|
-
import { readFileSync as
|
|
36320
|
+
import { readFileSync as readFileSync18 } from "fs";
|
|
36093
36321
|
import { input as input6, select as select2 } from "@inquirer/prompts";
|
|
36094
36322
|
import { Command as Command16 } from "commander";
|
|
36095
36323
|
var sessionCommand = new Command16("session").description(
|
|
@@ -36280,7 +36508,7 @@ function loadPatternFile(path) {
|
|
|
36280
36508
|
if (!path) return [];
|
|
36281
36509
|
let parsed;
|
|
36282
36510
|
try {
|
|
36283
|
-
parsed = JSON.parse(
|
|
36511
|
+
parsed = JSON.parse(readFileSync18(path, "utf-8"));
|
|
36284
36512
|
} catch (err) {
|
|
36285
36513
|
throw new Error(
|
|
36286
36514
|
`Cannot read synthesis patterns from ${path}: ${err.message}`
|
|
@@ -36471,7 +36699,7 @@ sessionCommand.command("end").description("End a session and show summary").requ
|
|
|
36471
36699
|
|
|
36472
36700
|
// src/cli/commands/settings.ts
|
|
36473
36701
|
init_kernel();
|
|
36474
|
-
import { existsSync as
|
|
36702
|
+
import { existsSync as existsSync25 } from "fs";
|
|
36475
36703
|
import { Command as Command17 } from "commander";
|
|
36476
36704
|
var settingsCommand = new Command17("settings").description(
|
|
36477
36705
|
"Manage user settings"
|
|
@@ -36636,7 +36864,7 @@ settingsCommand.command("repos").description("Show or set Personal, Team, and Or
|
|
|
36636
36864
|
console.log("\nValidation:");
|
|
36637
36865
|
for (const [name, path] of Object.entries(paths)) {
|
|
36638
36866
|
if (path) {
|
|
36639
|
-
const exists =
|
|
36867
|
+
const exists = existsSync25(path);
|
|
36640
36868
|
console.log(
|
|
36641
36869
|
` ${name.padEnd(9)}: ${exists ? "\x1B[32m\u2713 Valid folder\x1B[0m" : "\x1B[31m\u2717 Directory does not exist\x1B[0m"}`
|
|
36642
36870
|
);
|
|
@@ -36649,7 +36877,7 @@ settingsCommand.command("repos").description("Show or set Personal, Team, and Or
|
|
|
36649
36877
|
|
|
36650
36878
|
// src/cli/commands/setup.ts
|
|
36651
36879
|
init_kernel();
|
|
36652
|
-
import { resolve as
|
|
36880
|
+
import { resolve as resolve10 } from "path";
|
|
36653
36881
|
import { Command as Command18 } from "commander";
|
|
36654
36882
|
function formatDatabaseInitTarget(target) {
|
|
36655
36883
|
switch (target.kind) {
|
|
@@ -36749,7 +36977,7 @@ var setupCommand = new Command18("setup").description(
|
|
|
36749
36977
|
console.error(`Error: ${err.message}`);
|
|
36750
36978
|
process.exit(1);
|
|
36751
36979
|
}
|
|
36752
|
-
const target =
|
|
36980
|
+
const target = resolve10(opts.target ?? process.cwd());
|
|
36753
36981
|
const updateExistingInstructions = Boolean(opts.target) || opts.force;
|
|
36754
36982
|
console.log(
|
|
36755
36983
|
`Setting up ZAM in ${target}${opts.dryRun ? " (dry run)" : ""}
|
|
@@ -36874,8 +37102,8 @@ skillCommand.command("add").description("Register a new agent skill").requiredOp
|
|
|
36874
37102
|
|
|
36875
37103
|
// src/cli/commands/snapshot.ts
|
|
36876
37104
|
init_kernel();
|
|
36877
|
-
import { existsSync as
|
|
36878
|
-
import { dirname as dirname11, join as
|
|
37105
|
+
import { existsSync as existsSync26, mkdirSync as mkdirSync18, readFileSync as readFileSync19, writeFileSync as writeFileSync15 } from "fs";
|
|
37106
|
+
import { dirname as dirname11, join as join26 } from "path";
|
|
36879
37107
|
import { Command as Command20 } from "commander";
|
|
36880
37108
|
function defaultOutName() {
|
|
36881
37109
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+Z$/, "").replace(/:/g, "-");
|
|
@@ -36904,12 +37132,12 @@ var exportCmd = new Command20("export").description("Write a portable SQL-text s
|
|
|
36904
37132
|
process.stdout.write(snapshot);
|
|
36905
37133
|
return;
|
|
36906
37134
|
}
|
|
36907
|
-
const out = opts.out ??
|
|
37135
|
+
const out = opts.out ?? join26(personalDir, "snapshots", defaultOutName());
|
|
36908
37136
|
const dir = dirname11(out);
|
|
36909
|
-
if (dir && dir !== "." && !
|
|
36910
|
-
|
|
37137
|
+
if (dir && dir !== "." && !existsSync26(dir)) {
|
|
37138
|
+
mkdirSync18(dir, { recursive: true });
|
|
36911
37139
|
}
|
|
36912
|
-
|
|
37140
|
+
writeFileSync15(out, snapshot, "utf-8");
|
|
36913
37141
|
console.log(`Snapshot written: ${out}`);
|
|
36914
37142
|
console.log(
|
|
36915
37143
|
` ${total} row(s)${nonEmpty.length ? ` \u2014 ${nonEmpty.join(", ")}` : ""}`
|
|
@@ -36923,11 +37151,11 @@ var exportCmd = new Command20("export").description("Write a portable SQL-text s
|
|
|
36923
37151
|
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
37152
|
let db;
|
|
36925
37153
|
try {
|
|
36926
|
-
if (!
|
|
37154
|
+
if (!existsSync26(file)) {
|
|
36927
37155
|
console.error(`Error: Snapshot file not found: ${file}`);
|
|
36928
37156
|
process.exit(1);
|
|
36929
37157
|
}
|
|
36930
|
-
const snapshot =
|
|
37158
|
+
const snapshot = readFileSync19(file, "utf-8");
|
|
36931
37159
|
db = await openDatabaseWithSync({ initialize: true });
|
|
36932
37160
|
const result = await importSnapshot(db, snapshot, { force: opts.force });
|
|
36933
37161
|
await db.close();
|
|
@@ -36945,11 +37173,11 @@ var importCmd = new Command20("import").description("Restore a snapshot into the
|
|
|
36945
37173
|
});
|
|
36946
37174
|
var verifyCmd = new Command20("verify").description("Check a snapshot's manifest and checksum without importing").argument("<file>", "Snapshot file to verify").action((file) => {
|
|
36947
37175
|
try {
|
|
36948
|
-
if (!
|
|
37176
|
+
if (!existsSync26(file)) {
|
|
36949
37177
|
console.error(`Error: Snapshot file not found: ${file}`);
|
|
36950
37178
|
process.exit(1);
|
|
36951
37179
|
}
|
|
36952
|
-
const manifest = verifySnapshot(
|
|
37180
|
+
const manifest = verifySnapshot(readFileSync19(file, "utf-8"));
|
|
36953
37181
|
const { total, nonEmpty } = summarize(manifest.tables);
|
|
36954
37182
|
console.log(`Valid snapshot (format v${manifest.version})`);
|
|
36955
37183
|
console.log(` created: ${manifest.createdAt}`);
|
|
@@ -37498,9 +37726,9 @@ tokenCommand.command("reembed").description("Backfill or refresh semantic-search
|
|
|
37498
37726
|
// src/cli/commands/ui.ts
|
|
37499
37727
|
init_kernel();
|
|
37500
37728
|
import { spawn as spawn3, spawnSync } from "child_process";
|
|
37501
|
-
import { existsSync as
|
|
37502
|
-
import { homedir as
|
|
37503
|
-
import { dirname as dirname12, join as
|
|
37729
|
+
import { existsSync as existsSync27 } from "fs";
|
|
37730
|
+
import { homedir as homedir16 } from "os";
|
|
37731
|
+
import { dirname as dirname12, join as join27 } from "path";
|
|
37504
37732
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
37505
37733
|
import { Command as Command23 } from "commander";
|
|
37506
37734
|
var C3 = {
|
|
@@ -37516,8 +37744,8 @@ function findDesktopDir() {
|
|
|
37516
37744
|
for (const start of starts) {
|
|
37517
37745
|
let dir = start;
|
|
37518
37746
|
for (let i = 0; i < 10; i++) {
|
|
37519
|
-
if (
|
|
37520
|
-
return
|
|
37747
|
+
if (existsSync27(join27(dir, "desktop", "src-tauri", "tauri.conf.json"))) {
|
|
37748
|
+
return join27(dir, "desktop");
|
|
37521
37749
|
}
|
|
37522
37750
|
const parent = dirname12(dir);
|
|
37523
37751
|
if (parent === dir) break;
|
|
@@ -37527,30 +37755,30 @@ function findDesktopDir() {
|
|
|
37527
37755
|
return null;
|
|
37528
37756
|
}
|
|
37529
37757
|
function findBuiltApp(desktopDir) {
|
|
37530
|
-
const releaseDir =
|
|
37758
|
+
const releaseDir = join27(desktopDir, "src-tauri", "target", "release");
|
|
37531
37759
|
if (process.platform === "win32") {
|
|
37532
37760
|
for (const name of ["ZAM.exe", "zam.exe", "zam-desktop.exe"]) {
|
|
37533
|
-
const p =
|
|
37534
|
-
if (
|
|
37761
|
+
const p = join27(releaseDir, name);
|
|
37762
|
+
if (existsSync27(p)) return p;
|
|
37535
37763
|
}
|
|
37536
37764
|
} else if (process.platform === "darwin") {
|
|
37537
|
-
const app =
|
|
37538
|
-
if (
|
|
37765
|
+
const app = join27(releaseDir, "bundle", "macos", "ZAM.app");
|
|
37766
|
+
if (existsSync27(app)) return app;
|
|
37539
37767
|
} else {
|
|
37540
37768
|
for (const name of ["zam", "ZAM", "zam-desktop"]) {
|
|
37541
|
-
const p =
|
|
37542
|
-
if (
|
|
37769
|
+
const p = join27(releaseDir, name);
|
|
37770
|
+
if (existsSync27(p)) return p;
|
|
37543
37771
|
}
|
|
37544
37772
|
}
|
|
37545
37773
|
return null;
|
|
37546
37774
|
}
|
|
37547
37775
|
function findInstalledApp() {
|
|
37548
37776
|
const candidates = process.platform === "win32" ? [
|
|
37549
|
-
process.env.LOCALAPPDATA &&
|
|
37550
|
-
process.env.ProgramFiles &&
|
|
37551
|
-
process.env["ProgramFiles(x86)"] &&
|
|
37552
|
-
] : process.platform === "darwin" ? ["/Applications/ZAM.app",
|
|
37553
|
-
return candidates.find((candidate) => candidate &&
|
|
37777
|
+
process.env.LOCALAPPDATA && join27(process.env.LOCALAPPDATA, "Programs", "ZAM", "ZAM.exe"),
|
|
37778
|
+
process.env.ProgramFiles && join27(process.env.ProgramFiles, "ZAM", "ZAM.exe"),
|
|
37779
|
+
process.env["ProgramFiles(x86)"] && join27(process.env["ProgramFiles(x86)"], "ZAM", "ZAM.exe")
|
|
37780
|
+
] : process.platform === "darwin" ? ["/Applications/ZAM.app", join27(homedir16(), "Applications", "ZAM.app")] : ["/opt/ZAM/zam", "/usr/bin/zam-desktop"];
|
|
37781
|
+
return candidates.find((candidate) => candidate && existsSync27(candidate)) || null;
|
|
37554
37782
|
}
|
|
37555
37783
|
function runNpm(args, opts) {
|
|
37556
37784
|
const res = spawnSync("npm", args, {
|
|
@@ -37561,7 +37789,7 @@ function runNpm(args, opts) {
|
|
|
37561
37789
|
return res.status ?? 1;
|
|
37562
37790
|
}
|
|
37563
37791
|
function ensureDesktopDeps(desktopDir) {
|
|
37564
|
-
if (
|
|
37792
|
+
if (existsSync27(join27(desktopDir, "node_modules"))) return true;
|
|
37565
37793
|
console.log(
|
|
37566
37794
|
`${C3.cyan}Installing desktop dependencies (one-time)...${C3.reset}`
|
|
37567
37795
|
);
|
|
@@ -37587,13 +37815,13 @@ function requireRust() {
|
|
|
37587
37815
|
function hasMsvcBuildTools() {
|
|
37588
37816
|
if (process.platform !== "win32") return true;
|
|
37589
37817
|
const pf86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
|
37590
|
-
const vswhere =
|
|
37818
|
+
const vswhere = join27(
|
|
37591
37819
|
pf86,
|
|
37592
37820
|
"Microsoft Visual Studio",
|
|
37593
37821
|
"Installer",
|
|
37594
37822
|
"vswhere.exe"
|
|
37595
37823
|
);
|
|
37596
|
-
if (!
|
|
37824
|
+
if (!existsSync27(vswhere)) return false;
|
|
37597
37825
|
const res = spawnSync(
|
|
37598
37826
|
vswhere,
|
|
37599
37827
|
[
|
|
@@ -37625,7 +37853,7 @@ function requireMsvcOnWindows() {
|
|
|
37625
37853
|
return false;
|
|
37626
37854
|
}
|
|
37627
37855
|
function warnIfCliMissing(repoRoot) {
|
|
37628
|
-
if (!
|
|
37856
|
+
if (!existsSync27(join27(repoRoot, "dist", "cli", "index.js"))) {
|
|
37629
37857
|
console.warn(
|
|
37630
37858
|
`${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
37859
|
);
|
|
@@ -37688,7 +37916,7 @@ var uiCommand = new Command23("ui").description("Launch the ZAM Desktop GUI (Act
|
|
|
37688
37916
|
).option("--shortcut", "Create Desktop + Start-menu shortcuts to the GUI").action((opts) => {
|
|
37689
37917
|
const installedApp = findInstalledApp();
|
|
37690
37918
|
if (!opts.dev && !opts.build && !opts.shortcut && installedApp) {
|
|
37691
|
-
launchApp(installedApp,
|
|
37919
|
+
launchApp(installedApp, homedir16());
|
|
37692
37920
|
return;
|
|
37693
37921
|
}
|
|
37694
37922
|
const desktopDir = findDesktopDir();
|
|
@@ -37709,7 +37937,7 @@ var uiCommand = new Command23("ui").description("Launch the ZAM Desktop GUI (Act
|
|
|
37709
37937
|
);
|
|
37710
37938
|
const code = runNpm(["run", "tauri", "build"], { cwd: desktopDir });
|
|
37711
37939
|
if (code === 0) {
|
|
37712
|
-
const bundle =
|
|
37940
|
+
const bundle = join27(
|
|
37713
37941
|
desktopDir,
|
|
37714
37942
|
"src-tauri",
|
|
37715
37943
|
"target",
|
|
@@ -37787,8 +38015,8 @@ ${C3.dim}After that, 'zam ui' launches it directly.${C3.reset}`
|
|
|
37787
38015
|
// src/cli/commands/update.ts
|
|
37788
38016
|
init_kernel();
|
|
37789
38017
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
37790
|
-
import { existsSync as
|
|
37791
|
-
import { dirname as dirname13, join as
|
|
38018
|
+
import { existsSync as existsSync28, readFileSync as readFileSync20, realpathSync as realpathSync2 } from "fs";
|
|
38019
|
+
import { dirname as dirname13, join as join28 } from "path";
|
|
37792
38020
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
37793
38021
|
import { confirm as confirm3 } from "@inquirer/prompts";
|
|
37794
38022
|
import { Command as Command24 } from "commander";
|
|
@@ -37810,7 +38038,7 @@ var C4 = {
|
|
|
37810
38038
|
function versionAt(dir) {
|
|
37811
38039
|
try {
|
|
37812
38040
|
const pkg2 = JSON.parse(
|
|
37813
|
-
|
|
38041
|
+
readFileSync20(join28(dir, "package.json"), "utf-8")
|
|
37814
38042
|
);
|
|
37815
38043
|
return pkg2.version ?? "unknown";
|
|
37816
38044
|
} catch {
|
|
@@ -37871,11 +38099,11 @@ function findSourceRepo() {
|
|
|
37871
38099
|
let dir = realpathSync2(dirname13(fileURLToPath7(import.meta.url)));
|
|
37872
38100
|
let parent = dirname13(dir);
|
|
37873
38101
|
while (parent !== dir) {
|
|
37874
|
-
if (
|
|
38102
|
+
if (existsSync28(join28(dir, ".git"))) return dir;
|
|
37875
38103
|
dir = parent;
|
|
37876
38104
|
parent = dirname13(dir);
|
|
37877
38105
|
}
|
|
37878
|
-
return
|
|
38106
|
+
return existsSync28(join28(dir, ".git")) ? dir : null;
|
|
37879
38107
|
}
|
|
37880
38108
|
function runGit(cwd, args, capture) {
|
|
37881
38109
|
const res = spawnSync2("git", args, {
|
|
@@ -37896,7 +38124,7 @@ function runNpm2(args, cwd) {
|
|
|
37896
38124
|
function smokeTestBuild(src) {
|
|
37897
38125
|
const res = spawnSync2(
|
|
37898
38126
|
process.execPath,
|
|
37899
|
-
[
|
|
38127
|
+
[join28(src, "dist", "cli", "index.js"), "--version"],
|
|
37900
38128
|
{
|
|
37901
38129
|
cwd: src,
|
|
37902
38130
|
encoding: "utf8",
|
|
@@ -37976,7 +38204,7 @@ Fix it manually in ${src} (try \`npm ci && npm run build\`, and check your Node
|
|
|
37976
38204
|
console.log(`${C4.dim}\u2192 zam setup --force${C4.reset}`);
|
|
37977
38205
|
const setup = spawnSync2(
|
|
37978
38206
|
process.execPath,
|
|
37979
|
-
[
|
|
38207
|
+
[join28(src, "dist", "cli", "index.js"), "setup", "--force"],
|
|
37980
38208
|
{ cwd: process.cwd(), stdio: "inherit" }
|
|
37981
38209
|
);
|
|
37982
38210
|
if (setup.status !== 0) {
|
|
@@ -38088,15 +38316,15 @@ var whoamiCommand = new Command25("whoami").description("Show or set the default
|
|
|
38088
38316
|
|
|
38089
38317
|
// src/cli/commands/workspace.ts
|
|
38090
38318
|
init_kernel();
|
|
38091
|
-
import { execFileSync as
|
|
38092
|
-
import { existsSync as
|
|
38093
|
-
import { homedir as
|
|
38094
|
-
import { join as
|
|
38319
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
38320
|
+
import { existsSync as existsSync29, writeFileSync as writeFileSync16 } from "fs";
|
|
38321
|
+
import { homedir as homedir17 } from "os";
|
|
38322
|
+
import { join as join29, resolve as resolve11 } from "path";
|
|
38095
38323
|
import { confirm as confirm4, input as input7 } from "@inquirer/prompts";
|
|
38096
38324
|
import { Command as Command26 } from "commander";
|
|
38097
38325
|
function runGit2(cwd, args) {
|
|
38098
38326
|
try {
|
|
38099
|
-
return
|
|
38327
|
+
return execFileSync6("git", args, {
|
|
38100
38328
|
cwd,
|
|
38101
38329
|
stdio: "pipe",
|
|
38102
38330
|
encoding: "utf8"
|
|
@@ -38175,7 +38403,7 @@ workspaceCommand.command("list").description("List configured ZAM workspaces").o
|
|
|
38175
38403
|
const workspaces = getConfiguredWorkspaces();
|
|
38176
38404
|
const agents = parseSetupAgents();
|
|
38177
38405
|
const linkHealth = Object.fromEntries(
|
|
38178
|
-
workspaces.filter((workspace) =>
|
|
38406
|
+
workspaces.filter((workspace) => existsSync29(workspace.path)).map((workspace) => [
|
|
38179
38407
|
workspace.id,
|
|
38180
38408
|
summarizeSkillLinkHealth(inspectSkillLinks(workspace.path, agents))
|
|
38181
38409
|
])
|
|
@@ -38217,8 +38445,8 @@ workspaceCommand.command("add <id>").description("Register an existing directory
|
|
|
38217
38445
|
`Source-control provider (${WORKSPACE_SOURCE_CONTROLS.join(" | ")})`
|
|
38218
38446
|
).option("--scopes <list>", "Comma-separated knowledge scopes").option("--default-agent <id>", "Default agent harness for this workspace").action((id, opts) => {
|
|
38219
38447
|
try {
|
|
38220
|
-
const path =
|
|
38221
|
-
if (!
|
|
38448
|
+
const path = resolve11(String(opts.path));
|
|
38449
|
+
if (!existsSync29(path)) {
|
|
38222
38450
|
console.error(`Workspace path does not exist: ${path}`);
|
|
38223
38451
|
process.exit(1);
|
|
38224
38452
|
}
|
|
@@ -38312,7 +38540,7 @@ workspaceCommand.command("publish").description("Publish your local workspace sa
|
|
|
38312
38540
|
);
|
|
38313
38541
|
process.exit(1);
|
|
38314
38542
|
}
|
|
38315
|
-
if (!
|
|
38543
|
+
if (!existsSync29(workspaceDir)) {
|
|
38316
38544
|
console.error(
|
|
38317
38545
|
`\x1B[31m\u2717 Workspace directory does not exist: ${workspaceDir}\x1B[0m`
|
|
38318
38546
|
);
|
|
@@ -38326,15 +38554,15 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
|
|
|
38326
38554
|
);
|
|
38327
38555
|
process.exit(1);
|
|
38328
38556
|
}
|
|
38329
|
-
const gitignorePath =
|
|
38330
|
-
if (!
|
|
38331
|
-
|
|
38557
|
+
const gitignorePath = join29(workspaceDir, ".gitignore");
|
|
38558
|
+
if (!existsSync29(gitignorePath)) {
|
|
38559
|
+
writeFileSync16(
|
|
38332
38560
|
gitignorePath,
|
|
38333
38561
|
"node_modules/\n.agent/\n.agents/\n.claude/\n.gemini/\n.goose/\n*.log\n",
|
|
38334
38562
|
"utf8"
|
|
38335
38563
|
);
|
|
38336
38564
|
}
|
|
38337
|
-
const hasGitRepo =
|
|
38565
|
+
const hasGitRepo = existsSync29(join29(workspaceDir, ".git"));
|
|
38338
38566
|
if (!hasGitRepo) {
|
|
38339
38567
|
console.log("Initializing local Git repository...");
|
|
38340
38568
|
runGit2(workspaceDir, ["init", "-b", "main"]);
|
|
@@ -38366,7 +38594,7 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
|
|
|
38366
38594
|
if (proceedGh) {
|
|
38367
38595
|
try {
|
|
38368
38596
|
console.log(`Creating GitHub repository ${repoName}...`);
|
|
38369
|
-
|
|
38597
|
+
execFileSync6("gh", ghRepoCreateArgs(repoName, repoVisibility), {
|
|
38370
38598
|
cwd: workspaceDir,
|
|
38371
38599
|
stdio: "inherit"
|
|
38372
38600
|
});
|
|
@@ -38421,7 +38649,7 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
|
|
|
38421
38649
|
}
|
|
38422
38650
|
});
|
|
38423
38651
|
workspaceCommand.command("data-dir").description("Print the ZAM data directory (database, credentials, config)").option("--json", "Output as JSON").action((opts) => {
|
|
38424
|
-
const dir =
|
|
38652
|
+
const dir = join29(homedir17(), ".zam");
|
|
38425
38653
|
console.log(opts.json ? JSON.stringify({ dataDir: dir }) : dir);
|
|
38426
38654
|
});
|
|
38427
38655
|
workspaceCommand.command("backup").description("Back up the local ZAM database into your workspace").option(
|
|
@@ -38464,7 +38692,7 @@ workspaceCommand.command("backup").description("Back up the local ZAM database i
|
|
|
38464
38692
|
// src/cli/app.ts
|
|
38465
38693
|
var __dirname = dirname14(fileURLToPath8(import.meta.url));
|
|
38466
38694
|
var pkg = JSON.parse(
|
|
38467
|
-
|
|
38695
|
+
readFileSync21(join30(__dirname, "..", "..", "package.json"), "utf-8")
|
|
38468
38696
|
);
|
|
38469
38697
|
var program = new Command27();
|
|
38470
38698
|
program.name("zam").description(
|