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