zam-core 0.10.2 → 0.10.4
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/.agent/skills/zam/SKILL.md +31 -5
- package/.agents/skills/zam/SKILL.md +31 -5
- package/.claude/skills/zam/SKILL.md +31 -5
- package/dist/cli/app.js +960 -298
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/commands/mcp.js +1130 -114
- 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 +14 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/ui/recall-panel.html +1 -1
- package/dist/ui/studio-panel.html +1 -1
- package/dist/vscode-extension/ZAM_Companion_0.10.4.vsix +0 -0
- package/dist/vscode-extension/extension.cjs +109 -0
- package/dist/vscode-extension/host.bundle.js +5331 -0
- package/dist/vscode-extension/manifest.json +5 -0
- package/package.json +3 -2
package/dist/cli/app.js
CHANGED
|
@@ -5481,20 +5481,20 @@ import { homedir as homedir5 } from "os";
|
|
|
5481
5481
|
import { join as join8 } from "path";
|
|
5482
5482
|
import { fileURLToPath } from "url";
|
|
5483
5483
|
function getPackageSkillPath(agent = "default") {
|
|
5484
|
-
const
|
|
5484
|
+
const packageRoot4 = [
|
|
5485
5485
|
fileURLToPath(new URL("../../..", import.meta.url)),
|
|
5486
5486
|
fileURLToPath(new URL("../..", import.meta.url)),
|
|
5487
5487
|
fileURLToPath(new URL("..", import.meta.url))
|
|
5488
5488
|
].find((candidate) => existsSync7(join8(candidate, "package.json"))) ?? "";
|
|
5489
|
-
if (!
|
|
5489
|
+
if (!packageRoot4) return "";
|
|
5490
5490
|
if (agent === "codex") {
|
|
5491
|
-
const codexPath = join8(
|
|
5491
|
+
const codexPath = join8(packageRoot4, ".agents", "skills", "zam", "SKILL.md");
|
|
5492
5492
|
if (existsSync7(codexPath)) return codexPath;
|
|
5493
5493
|
return "";
|
|
5494
5494
|
}
|
|
5495
5495
|
if (agent === "claude") {
|
|
5496
5496
|
const claudePath = join8(
|
|
5497
|
-
|
|
5497
|
+
packageRoot4,
|
|
5498
5498
|
".claude",
|
|
5499
5499
|
"skills",
|
|
5500
5500
|
"zam",
|
|
@@ -5503,9 +5503,9 @@ function getPackageSkillPath(agent = "default") {
|
|
|
5503
5503
|
if (existsSync7(claudePath)) return claudePath;
|
|
5504
5504
|
return "";
|
|
5505
5505
|
}
|
|
5506
|
-
let path = join8(
|
|
5506
|
+
let path = join8(packageRoot4, ".agent", "skills", "zam", "SKILL.md");
|
|
5507
5507
|
if (existsSync7(path)) return path;
|
|
5508
|
-
path = join8(
|
|
5508
|
+
path = join8(packageRoot4, ".claude", "skills", "zam", "SKILL.md");
|
|
5509
5509
|
if (existsSync7(path)) return path;
|
|
5510
5510
|
return "";
|
|
5511
5511
|
}
|
|
@@ -5946,6 +5946,18 @@ function saveMachineAiConfig(ai, path = defaultConfigPath()) {
|
|
|
5946
5946
|
config.ai = ai;
|
|
5947
5947
|
saveInstallConfig(config, path);
|
|
5948
5948
|
}
|
|
5949
|
+
function getAgentConnectAutoDone(path = defaultConfigPath()) {
|
|
5950
|
+
return loadInstallConfig(path).agent?.connectAutoDone === true;
|
|
5951
|
+
}
|
|
5952
|
+
function setAgentConnectAutoDone(done, path = defaultConfigPath()) {
|
|
5953
|
+
const config = loadInstallConfig(path);
|
|
5954
|
+
if (done) {
|
|
5955
|
+
config.agent = { ...config.agent ?? {}, connectAutoDone: true };
|
|
5956
|
+
} else if (config.agent) {
|
|
5957
|
+
delete config.agent.connectAutoDone;
|
|
5958
|
+
}
|
|
5959
|
+
saveInstallConfig(config, path);
|
|
5960
|
+
}
|
|
5949
5961
|
function getConfiguredWorkspaces(path = defaultConfigPath()) {
|
|
5950
5962
|
return loadInstallConfig(path).workspaces ?? [];
|
|
5951
5963
|
}
|
|
@@ -6622,6 +6634,7 @@ __export(kernel_exports, {
|
|
|
6622
6634
|
getActiveWorkspace: () => getActiveWorkspace,
|
|
6623
6635
|
getActiveWorkspaceContext: () => getActiveWorkspaceContext,
|
|
6624
6636
|
getActiveWorkspaceId: () => getActiveWorkspaceId,
|
|
6637
|
+
getAgentConnectAutoDone: () => getAgentConnectAutoDone,
|
|
6625
6638
|
getAgentSkill: () => getAgentSkill,
|
|
6626
6639
|
getAllSettings: () => getAllSettings,
|
|
6627
6640
|
getAllSettingsDetailed: () => getAllSettingsDetailed,
|
|
@@ -6731,6 +6744,7 @@ __export(kernel_exports, {
|
|
|
6731
6744
|
setADOCredentials: () => setADOCredentials,
|
|
6732
6745
|
setActiveWorkspaceContext: () => setActiveWorkspaceContext,
|
|
6733
6746
|
setActiveWorkspaceId: () => setActiveWorkspaceId,
|
|
6747
|
+
setAgentConnectAutoDone: () => setAgentConnectAutoDone,
|
|
6734
6748
|
setInstallChannel: () => setInstallChannel,
|
|
6735
6749
|
setInstallMode: () => setInstallMode,
|
|
6736
6750
|
setProviderApiKey: () => setProviderApiKey,
|
|
@@ -6808,18 +6822,23 @@ var init_kernel = __esm({
|
|
|
6808
6822
|
});
|
|
6809
6823
|
|
|
6810
6824
|
// src/cli/app.ts
|
|
6811
|
-
import { readFileSync as
|
|
6812
|
-
import { dirname as
|
|
6813
|
-
import { fileURLToPath as
|
|
6825
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
6826
|
+
import { dirname as dirname14, join as join29 } from "path";
|
|
6827
|
+
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
6814
6828
|
import { Command as Command27 } from "commander";
|
|
6815
6829
|
|
|
6816
6830
|
// src/cli/commands/agent.ts
|
|
6817
6831
|
init_kernel();
|
|
6818
|
-
import { existsSync as
|
|
6819
|
-
import {
|
|
6820
|
-
import { dirname as dirname6, join as join14 } from "path";
|
|
6832
|
+
import { existsSync as existsSync15 } from "fs";
|
|
6833
|
+
import { join as join15 } from "path";
|
|
6821
6834
|
import { Command } from "commander";
|
|
6822
6835
|
|
|
6836
|
+
// src/cli/agent-connect.ts
|
|
6837
|
+
init_kernel();
|
|
6838
|
+
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync9 } from "fs";
|
|
6839
|
+
import { homedir as homedir11 } from "os";
|
|
6840
|
+
import { dirname as dirname7 } from "path";
|
|
6841
|
+
|
|
6823
6842
|
// src/cli/agent-harness.ts
|
|
6824
6843
|
import { spawn } from "child_process";
|
|
6825
6844
|
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
|
|
@@ -7040,6 +7059,18 @@ function openTerminalWindow(opts) {
|
|
|
7040
7059
|
}
|
|
7041
7060
|
|
|
7042
7061
|
// src/cli/agent-harness.ts
|
|
7062
|
+
var ANTIGRAVITY_IDE_CANDIDATE_PATHS = {
|
|
7063
|
+
darwin: [
|
|
7064
|
+
join12(
|
|
7065
|
+
homedir8(),
|
|
7066
|
+
".antigravity-ide",
|
|
7067
|
+
"antigravity-ide",
|
|
7068
|
+
"bin",
|
|
7069
|
+
"antigravity-ide"
|
|
7070
|
+
),
|
|
7071
|
+
"/Applications/Antigravity IDE.app/Contents/Resources/app/bin/antigravity-ide"
|
|
7072
|
+
]
|
|
7073
|
+
};
|
|
7043
7074
|
var AGENT_HARNESSES = [
|
|
7044
7075
|
{ id: "claude-code", label: "Claude Code", kind: "cli", command: "claude" },
|
|
7045
7076
|
{ id: "codex", label: "Codex", kind: "cli", command: "codex" },
|
|
@@ -7061,19 +7092,41 @@ var AGENT_HARNESSES = [
|
|
|
7061
7092
|
id: "antigravity",
|
|
7062
7093
|
label: "Antigravity",
|
|
7063
7094
|
kind: "app",
|
|
7064
|
-
command: "antigravity"
|
|
7095
|
+
command: "antigravity",
|
|
7096
|
+
candidatePaths: {
|
|
7097
|
+
darwin: [
|
|
7098
|
+
...ANTIGRAVITY_IDE_CANDIDATE_PATHS.darwin ?? [],
|
|
7099
|
+
"/Applications/Antigravity.app/Contents/MacOS/Antigravity"
|
|
7100
|
+
]
|
|
7101
|
+
}
|
|
7065
7102
|
},
|
|
7066
7103
|
{ id: "goose", label: "goose", kind: "cli", command: "goose" }
|
|
7067
7104
|
];
|
|
7068
7105
|
function getHarness(id) {
|
|
7069
7106
|
return AGENT_HARNESSES.find((h) => h.id === id);
|
|
7070
7107
|
}
|
|
7108
|
+
function resolveAntigravityIdeExecutable(deps = {}) {
|
|
7109
|
+
const find = deps.find ?? findExecutable;
|
|
7110
|
+
const exists = deps.exists ?? existsSync12;
|
|
7111
|
+
const platform = deps.platform ?? process.platform;
|
|
7112
|
+
const found = find("antigravity-ide");
|
|
7113
|
+
if (found) return found;
|
|
7114
|
+
return ANTIGRAVITY_IDE_CANDIDATE_PATHS[platform]?.find((path) => exists(path)) ?? null;
|
|
7115
|
+
}
|
|
7071
7116
|
function resolveHarnessExecutable(harness, overrideCommand, deps = {}) {
|
|
7072
7117
|
const find = deps.find ?? findExecutable;
|
|
7073
7118
|
const exists = deps.exists ?? existsSync12;
|
|
7074
7119
|
const platform = deps.platform ?? process.platform;
|
|
7075
7120
|
const found = find(overrideCommand || harness.command);
|
|
7076
7121
|
if (found) return found;
|
|
7122
|
+
if (!overrideCommand && harness.id === "antigravity") {
|
|
7123
|
+
const foundIde = resolveAntigravityIdeExecutable({
|
|
7124
|
+
find,
|
|
7125
|
+
exists,
|
|
7126
|
+
platform
|
|
7127
|
+
});
|
|
7128
|
+
if (foundIde) return foundIde;
|
|
7129
|
+
}
|
|
7077
7130
|
if (harness.kind === "app") {
|
|
7078
7131
|
for (const candidate of harness.candidatePaths?.[platform] ?? []) {
|
|
7079
7132
|
if (exists(candidate)) return candidate;
|
|
@@ -7118,6 +7171,76 @@ function launchHarness(harness, opts) {
|
|
|
7118
7171
|
console.log(`Launched ${harness.label} in ${opts.workspace}`);
|
|
7119
7172
|
}
|
|
7120
7173
|
}
|
|
7174
|
+
function detectInstalledConnectHarnesses(options = {}) {
|
|
7175
|
+
const home = options.home ?? homedir8();
|
|
7176
|
+
const platform = options.platform ?? process.platform;
|
|
7177
|
+
const find = options.find ?? findExecutable;
|
|
7178
|
+
const exists = options.exists ?? existsSync12;
|
|
7179
|
+
const detected = [];
|
|
7180
|
+
const hasCommandOrPath = (command, paths = []) => Boolean(find(command)) || paths.some((path) => exists(path));
|
|
7181
|
+
if (hasCommandOrPath("codex", [
|
|
7182
|
+
join12(home, ".codex"),
|
|
7183
|
+
...platform === "darwin" ? ["/Applications/Codex.app"] : platform === "win32" ? [join12(home, "AppData", "Local", "Programs", "Codex")] : []
|
|
7184
|
+
])) {
|
|
7185
|
+
detected.push("codex");
|
|
7186
|
+
}
|
|
7187
|
+
const vscodePaths = platform === "darwin" ? [
|
|
7188
|
+
"/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code",
|
|
7189
|
+
join12(
|
|
7190
|
+
home,
|
|
7191
|
+
"Applications",
|
|
7192
|
+
"Visual Studio Code.app",
|
|
7193
|
+
"Contents",
|
|
7194
|
+
"Resources",
|
|
7195
|
+
"app",
|
|
7196
|
+
"bin",
|
|
7197
|
+
"code"
|
|
7198
|
+
)
|
|
7199
|
+
] : platform === "win32" ? [
|
|
7200
|
+
join12(
|
|
7201
|
+
home,
|
|
7202
|
+
"AppData",
|
|
7203
|
+
"Local",
|
|
7204
|
+
"Programs",
|
|
7205
|
+
"Microsoft VS Code",
|
|
7206
|
+
"bin",
|
|
7207
|
+
"code.cmd"
|
|
7208
|
+
)
|
|
7209
|
+
] : ["/usr/bin/code", "/usr/local/bin/code", "/snap/bin/code"];
|
|
7210
|
+
if (hasCommandOrPath("code", vscodePaths)) detected.push("vscode");
|
|
7211
|
+
const copilotHome = options.copilotHome ?? join12(home, ".copilot");
|
|
7212
|
+
if (hasCommandOrPath("copilot", [
|
|
7213
|
+
copilotHome,
|
|
7214
|
+
...platform === "darwin" ? ["/Applications/GitHub Copilot.app"] : []
|
|
7215
|
+
])) {
|
|
7216
|
+
detected.push("copilot");
|
|
7217
|
+
}
|
|
7218
|
+
if (hasCommandOrPath("opencode", [join12(home, ".config", "opencode")])) {
|
|
7219
|
+
detected.push("opencode");
|
|
7220
|
+
}
|
|
7221
|
+
if (hasCommandOrPath("goose", [join12(home, ".config", "goose")])) {
|
|
7222
|
+
detected.push("goose");
|
|
7223
|
+
}
|
|
7224
|
+
if (hasCommandOrPath("antigravity", [
|
|
7225
|
+
join12(home, ".gemini", "config"),
|
|
7226
|
+
...platform === "darwin" ? [
|
|
7227
|
+
join12(
|
|
7228
|
+
home,
|
|
7229
|
+
".antigravity-ide",
|
|
7230
|
+
"antigravity-ide",
|
|
7231
|
+
"bin",
|
|
7232
|
+
"antigravity-ide"
|
|
7233
|
+
),
|
|
7234
|
+
"/Applications/Antigravity.app",
|
|
7235
|
+
"/Applications/Antigravity IDE.app"
|
|
7236
|
+
] : []
|
|
7237
|
+
]) || find("antigravity-ide")) {
|
|
7238
|
+
detected.push("antigravity");
|
|
7239
|
+
}
|
|
7240
|
+
const claudeDesktopPath = platform === "darwin" ? "/Applications/Claude.app" : platform === "win32" ? join12(home, "AppData", "Local", "AnthropicClaude") : join12(home, ".config", "Claude");
|
|
7241
|
+
if (exists(claudeDesktopPath)) detected.push("claude-desktop");
|
|
7242
|
+
return detected;
|
|
7243
|
+
}
|
|
7121
7244
|
function parseMcpJsonConfig(path, content) {
|
|
7122
7245
|
let parsed;
|
|
7123
7246
|
try {
|
|
@@ -7248,6 +7371,36 @@ approval_mode = "prompt"
|
|
|
7248
7371
|
content = existingStr ? `${existingStr.trimEnd()}
|
|
7249
7372
|
${block}` : block;
|
|
7250
7373
|
}
|
|
7374
|
+
} else if (harnessId === "vscode") {
|
|
7375
|
+
const platform = opts.platform ?? process.platform;
|
|
7376
|
+
targetPath = platform === "win32" ? join12(opts.home, "AppData", "Roaming", "Code", "User", "mcp.json") : platform === "darwin" ? join12(
|
|
7377
|
+
opts.home,
|
|
7378
|
+
"Library",
|
|
7379
|
+
"Application Support",
|
|
7380
|
+
"Code",
|
|
7381
|
+
"User",
|
|
7382
|
+
"mcp.json"
|
|
7383
|
+
) : join12(opts.home, ".config", "Code", "User", "mcp.json");
|
|
7384
|
+
hint = "Reload VS Code after setup. ZAM Companion stays separate from the Codex chat and can be moved to any panel or sidebar.";
|
|
7385
|
+
let existing = {};
|
|
7386
|
+
if (exists(targetPath)) {
|
|
7387
|
+
existing = parseMcpJsonConfig(targetPath, read(targetPath));
|
|
7388
|
+
}
|
|
7389
|
+
if (existing.servers !== void 0 && (typeof existing.servers !== "object" || existing.servers === null || Array.isArray(existing.servers))) {
|
|
7390
|
+
throw new Error(
|
|
7391
|
+
`Cannot update ${targetPath}: servers must be a JSON object`
|
|
7392
|
+
);
|
|
7393
|
+
}
|
|
7394
|
+
if (existing.inputs !== void 0 && !Array.isArray(existing.inputs)) {
|
|
7395
|
+
throw new Error(`Cannot update ${targetPath}: inputs must be an array`);
|
|
7396
|
+
}
|
|
7397
|
+
if (!existing.servers) existing.servers = {};
|
|
7398
|
+
const expectedServer = { command: opts.zamPath, args: ["mcp"] };
|
|
7399
|
+
const currentServer = existing.servers.zam;
|
|
7400
|
+
alreadyConfigured = Array.isArray(existing.inputs) && typeof currentServer === "object" && currentServer !== null && !Array.isArray(currentServer) && JSON.stringify(currentServer) === JSON.stringify(expectedServer);
|
|
7401
|
+
existing.servers.zam = expectedServer;
|
|
7402
|
+
if (!existing.inputs) existing.inputs = [];
|
|
7403
|
+
content = JSON.stringify(existing, null, 2);
|
|
7251
7404
|
} else if (harnessId === "goose") {
|
|
7252
7405
|
targetPath = join12(opts.home, ".config", "goose", "config.yaml");
|
|
7253
7406
|
hint = "goose will load the 'zam' extension on next session start. Run 'goose configure' to manage extensions.";
|
|
@@ -7290,7 +7443,7 @@ ${zamExtension}
|
|
|
7290
7443
|
opts.copilotHome ?? join12(opts.home, ".copilot"),
|
|
7291
7444
|
"mcp-config.json"
|
|
7292
7445
|
);
|
|
7293
|
-
hint = "Restart GitHub Copilot or start a new session to load the 'zam' MCP server and its
|
|
7446
|
+
hint = "Restart GitHub Copilot or start a new session to load the 'zam' MCP server and its focused Recall, Graph, and Settings canvases.";
|
|
7294
7447
|
let existing = {};
|
|
7295
7448
|
if (exists(targetPath)) {
|
|
7296
7449
|
existing = parseMcpJsonConfig(targetPath, read(targetPath));
|
|
@@ -7429,30 +7582,312 @@ function installCopilotExtension(options) {
|
|
|
7429
7582
|
};
|
|
7430
7583
|
}
|
|
7431
7584
|
|
|
7432
|
-
// src/cli/
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
|
|
7440
|
-
|
|
7441
|
-
|
|
7585
|
+
// src/cli/vscode-extension.ts
|
|
7586
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
7587
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync9, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "fs";
|
|
7588
|
+
import { homedir as homedir10 } from "os";
|
|
7589
|
+
import { dirname as dirname6, join as join14 } from "path";
|
|
7590
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
7591
|
+
var packageRoot2 = [
|
|
7592
|
+
fileURLToPath3(new URL("../..", import.meta.url)),
|
|
7593
|
+
fileURLToPath3(new URL("../../..", import.meta.url))
|
|
7594
|
+
].find((candidate) => existsSync14(join14(candidate, "package.json"))) ?? fileURLToPath3(new URL("../..", import.meta.url));
|
|
7595
|
+
function resolveVscodeExecutable(options = {}) {
|
|
7596
|
+
const home = options.home ?? homedir10();
|
|
7597
|
+
const platform = options.platform ?? process.platform;
|
|
7598
|
+
const find = options.find ?? findExecutable;
|
|
7599
|
+
const exists = options.exists ?? existsSync14;
|
|
7600
|
+
const found = find("code");
|
|
7601
|
+
if (found) return found;
|
|
7602
|
+
const candidates = platform === "darwin" ? [
|
|
7603
|
+
"/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code",
|
|
7604
|
+
join14(
|
|
7605
|
+
home,
|
|
7606
|
+
"Applications",
|
|
7607
|
+
"Visual Studio Code.app",
|
|
7608
|
+
"Contents",
|
|
7609
|
+
"Resources",
|
|
7610
|
+
"app",
|
|
7611
|
+
"bin",
|
|
7612
|
+
"code"
|
|
7613
|
+
)
|
|
7614
|
+
] : platform === "win32" ? [
|
|
7615
|
+
join14(
|
|
7616
|
+
home,
|
|
7617
|
+
"AppData",
|
|
7618
|
+
"Local",
|
|
7619
|
+
"Programs",
|
|
7620
|
+
"Microsoft VS Code",
|
|
7621
|
+
"bin",
|
|
7622
|
+
"code.cmd"
|
|
7623
|
+
)
|
|
7624
|
+
] : ["/usr/bin/code", "/usr/local/bin/code", "/snap/bin/code"];
|
|
7625
|
+
return candidates.find((candidate) => exists(candidate)) ?? null;
|
|
7626
|
+
}
|
|
7627
|
+
function packageVersion() {
|
|
7628
|
+
const parsed = JSON.parse(
|
|
7629
|
+
readFileSync11(join14(packageRoot2, "package.json"), "utf8")
|
|
7630
|
+
);
|
|
7631
|
+
if (typeof parsed.version !== "string" || !parsed.version) {
|
|
7632
|
+
throw new Error("Cannot determine the ZAM package version");
|
|
7633
|
+
}
|
|
7634
|
+
return parsed.version;
|
|
7635
|
+
}
|
|
7636
|
+
function planVscodeExtensionInstall(options) {
|
|
7637
|
+
const home = options.home ?? homedir10();
|
|
7638
|
+
const version = options.version ?? packageVersion();
|
|
7639
|
+
const assetsDir = options.assetsDir ?? join14(packageRoot2, "dist", "vscode-extension");
|
|
7640
|
+
const vsixPath = join14(assetsDir, `ZAM_Companion_${version}.vsix`);
|
|
7641
|
+
if (!existsSync14(vsixPath)) {
|
|
7642
|
+
throw new Error(
|
|
7643
|
+
`ZAM Companion VSIX asset is missing: ${vsixPath}. Run \`npm run build\` and retry.`
|
|
7644
|
+
);
|
|
7645
|
+
}
|
|
7646
|
+
const codePath = options.codePath ?? resolveVscodeExecutable({
|
|
7647
|
+
home,
|
|
7648
|
+
platform: options.platform,
|
|
7649
|
+
find: options.find,
|
|
7650
|
+
exists: options.exists
|
|
7651
|
+
});
|
|
7652
|
+
if (!codePath) {
|
|
7653
|
+
throw new Error(
|
|
7654
|
+
"Visual Studio Code was not detected. Install VS Code or add its 'code' command to PATH."
|
|
7655
|
+
);
|
|
7656
|
+
}
|
|
7657
|
+
return {
|
|
7658
|
+
version,
|
|
7659
|
+
vsixPath,
|
|
7660
|
+
codePath,
|
|
7661
|
+
launchConfigPath: join14(home, ".zam", "vscode-launch.json"),
|
|
7662
|
+
launch: { command: options.zamPath, args: ["mcp"] }
|
|
7663
|
+
};
|
|
7664
|
+
}
|
|
7665
|
+
function installVscodeExtension(options) {
|
|
7666
|
+
const plan = planVscodeExtensionInstall(options);
|
|
7667
|
+
if (options.dryRun) return { ...plan, action: "planned" };
|
|
7668
|
+
const launchContent = `${JSON.stringify(
|
|
7669
|
+
{
|
|
7670
|
+
schemaVersion: 1,
|
|
7671
|
+
version: plan.version,
|
|
7672
|
+
command: plan.launch.command,
|
|
7673
|
+
args: plan.launch.args
|
|
7674
|
+
},
|
|
7675
|
+
null,
|
|
7676
|
+
2
|
|
7677
|
+
)}
|
|
7678
|
+
`;
|
|
7679
|
+
const existed = existsSync14(plan.launchConfigPath);
|
|
7680
|
+
const changed = !existed || readFileSync11(plan.launchConfigPath, "utf8") !== launchContent;
|
|
7681
|
+
const run = options.run ?? ((command, args) => {
|
|
7682
|
+
execFileSync3(command, args, { stdio: "pipe" });
|
|
7683
|
+
});
|
|
7684
|
+
run(plan.codePath, ["--install-extension", plan.vsixPath, "--force"]);
|
|
7685
|
+
if (changed) {
|
|
7686
|
+
mkdirSync9(dirname6(plan.launchConfigPath), { recursive: true });
|
|
7687
|
+
writeFileSync8(plan.launchConfigPath, launchContent, "utf8");
|
|
7688
|
+
}
|
|
7689
|
+
return {
|
|
7690
|
+
...plan,
|
|
7691
|
+
action: !existed ? "installed" : changed ? "updated" : "unchanged"
|
|
7692
|
+
};
|
|
7693
|
+
}
|
|
7694
|
+
|
|
7695
|
+
// src/cli/agent-connect.ts
|
|
7442
7696
|
var CONNECT_HARNESSES = [
|
|
7443
7697
|
"claude-code",
|
|
7444
7698
|
"claude-desktop",
|
|
7445
7699
|
"antigravity",
|
|
7446
7700
|
"codex",
|
|
7701
|
+
"vscode",
|
|
7447
7702
|
"opencode",
|
|
7448
7703
|
"goose",
|
|
7449
7704
|
"copilot"
|
|
7450
7705
|
];
|
|
7706
|
+
var USER_SCOPED_CONNECT_HARNESSES = [
|
|
7707
|
+
"claude-desktop",
|
|
7708
|
+
"antigravity",
|
|
7709
|
+
"codex",
|
|
7710
|
+
"vscode",
|
|
7711
|
+
"opencode",
|
|
7712
|
+
"goose",
|
|
7713
|
+
"copilot"
|
|
7714
|
+
];
|
|
7715
|
+
var CONNECT_HARNESS_LABELS = {
|
|
7716
|
+
"claude-code": "Claude Code",
|
|
7717
|
+
"claude-desktop": "Claude Desktop",
|
|
7718
|
+
antigravity: "Antigravity",
|
|
7719
|
+
codex: "Codex",
|
|
7720
|
+
vscode: "VS Code",
|
|
7721
|
+
opencode: "OpenCode",
|
|
7722
|
+
goose: "Goose",
|
|
7723
|
+
copilot: "GitHub Copilot"
|
|
7724
|
+
};
|
|
7451
7725
|
function isConnectHarnessId(value) {
|
|
7452
7726
|
return CONNECT_HARNESSES.includes(value);
|
|
7453
7727
|
}
|
|
7728
|
+
function resolveDeps(deps) {
|
|
7729
|
+
const home = deps.home ?? homedir11();
|
|
7730
|
+
const cwd = deps.cwd ?? process.cwd();
|
|
7731
|
+
const copilotHome = deps.copilotHome ?? process.env.COPILOT_HOME;
|
|
7732
|
+
return {
|
|
7733
|
+
home,
|
|
7734
|
+
cwd,
|
|
7735
|
+
copilotHome,
|
|
7736
|
+
findZam: deps.findZam ?? (() => findExecutable("zam")),
|
|
7737
|
+
detect: deps.detect ?? (() => detectInstalledConnectHarnesses({ home, copilotHome })),
|
|
7738
|
+
connectMcp: deps.connectMcp ?? connectHarnessMcp,
|
|
7739
|
+
writeConfig: deps.writeConfig ?? ((path, content) => {
|
|
7740
|
+
mkdirSync10(dirname7(path), { recursive: true });
|
|
7741
|
+
writeFileSync9(path, content, "utf-8");
|
|
7742
|
+
}),
|
|
7743
|
+
installCopilot: deps.installCopilot ?? installCopilotExtension,
|
|
7744
|
+
installVscode: deps.installVscode ?? installVscodeExtension,
|
|
7745
|
+
resolveAntigravity: deps.resolveAntigravity ?? resolveAntigravityIdeExecutable,
|
|
7746
|
+
refreshSkills: deps.refreshSkills ?? distributeGlobalSkills
|
|
7747
|
+
};
|
|
7748
|
+
}
|
|
7749
|
+
function performAgentConnect(opts = {}, deps = {}) {
|
|
7750
|
+
const d = resolveDeps(deps);
|
|
7751
|
+
const dryRun = Boolean(opts.dryRun);
|
|
7752
|
+
const detected = opts.harness ? [opts.harness] : d.detect();
|
|
7753
|
+
const foundZam = d.findZam();
|
|
7754
|
+
const zamPath = foundZam ?? "zam";
|
|
7755
|
+
const results = [];
|
|
7756
|
+
for (const harness of detected) {
|
|
7757
|
+
const label = CONNECT_HARNESS_LABELS[harness];
|
|
7758
|
+
try {
|
|
7759
|
+
const prepared = d.connectMcp(harness, {
|
|
7760
|
+
zamPath,
|
|
7761
|
+
cwd: d.cwd,
|
|
7762
|
+
home: d.home,
|
|
7763
|
+
copilotHome: d.copilotHome
|
|
7764
|
+
});
|
|
7765
|
+
let extension = null;
|
|
7766
|
+
if (harness === "copilot") {
|
|
7767
|
+
const installed = d.installCopilot({
|
|
7768
|
+
home: d.home,
|
|
7769
|
+
zamPath,
|
|
7770
|
+
dryRun
|
|
7771
|
+
});
|
|
7772
|
+
extension = {
|
|
7773
|
+
kind: "copilot",
|
|
7774
|
+
action: installed.action,
|
|
7775
|
+
location: installed.destinationDir,
|
|
7776
|
+
detail: `${installed.launch.command} ${installed.launch.args.join(" ")}`
|
|
7777
|
+
};
|
|
7778
|
+
} else if (harness === "vscode") {
|
|
7779
|
+
const installed = d.installVscode({ home: d.home, zamPath, dryRun });
|
|
7780
|
+
extension = {
|
|
7781
|
+
kind: "vscode",
|
|
7782
|
+
action: installed.action,
|
|
7783
|
+
location: installed.vsixPath,
|
|
7784
|
+
detail: installed.launchConfigPath
|
|
7785
|
+
};
|
|
7786
|
+
} else if (harness === "antigravity") {
|
|
7787
|
+
const antigravityPath = d.resolveAntigravity();
|
|
7788
|
+
if (antigravityPath) {
|
|
7789
|
+
const installed = d.installVscode({
|
|
7790
|
+
home: d.home,
|
|
7791
|
+
zamPath,
|
|
7792
|
+
codePath: antigravityPath,
|
|
7793
|
+
dryRun
|
|
7794
|
+
});
|
|
7795
|
+
extension = {
|
|
7796
|
+
kind: "vscode",
|
|
7797
|
+
action: installed.action,
|
|
7798
|
+
location: installed.vsixPath,
|
|
7799
|
+
detail: installed.launchConfigPath
|
|
7800
|
+
};
|
|
7801
|
+
}
|
|
7802
|
+
}
|
|
7803
|
+
let wrote = false;
|
|
7804
|
+
if (!prepared.alreadyConfigured && !dryRun) {
|
|
7805
|
+
d.writeConfig(prepared.path, prepared.content);
|
|
7806
|
+
wrote = true;
|
|
7807
|
+
}
|
|
7808
|
+
results.push({
|
|
7809
|
+
harness,
|
|
7810
|
+
label,
|
|
7811
|
+
path: prepared.path,
|
|
7812
|
+
content: prepared.content,
|
|
7813
|
+
alreadyConfigured: prepared.alreadyConfigured,
|
|
7814
|
+
wrote,
|
|
7815
|
+
hint: prepared.hint,
|
|
7816
|
+
extension
|
|
7817
|
+
});
|
|
7818
|
+
} catch (error) {
|
|
7819
|
+
results.push({
|
|
7820
|
+
harness,
|
|
7821
|
+
label,
|
|
7822
|
+
path: "",
|
|
7823
|
+
content: "",
|
|
7824
|
+
alreadyConfigured: false,
|
|
7825
|
+
wrote: false,
|
|
7826
|
+
hint: "",
|
|
7827
|
+
extension: null,
|
|
7828
|
+
error: error instanceof Error ? error.message : String(error)
|
|
7829
|
+
});
|
|
7830
|
+
}
|
|
7831
|
+
}
|
|
7832
|
+
let skills = null;
|
|
7833
|
+
if (!dryRun && detected.length > 0) {
|
|
7834
|
+
const skillResults = d.refreshSkills(d.home);
|
|
7835
|
+
skills = {
|
|
7836
|
+
refreshed: skillResults.filter((result) => result.success).length,
|
|
7837
|
+
total: skillResults.length
|
|
7838
|
+
};
|
|
7839
|
+
}
|
|
7840
|
+
return {
|
|
7841
|
+
success: results.every((result) => !result.error),
|
|
7842
|
+
detected,
|
|
7843
|
+
zamPath,
|
|
7844
|
+
zamOnPath: Boolean(foundZam),
|
|
7845
|
+
results,
|
|
7846
|
+
skills
|
|
7847
|
+
};
|
|
7848
|
+
}
|
|
7849
|
+
function inspectConnectHarnesses(deps = {}) {
|
|
7850
|
+
const d = resolveDeps(deps);
|
|
7851
|
+
const foundZam = d.findZam();
|
|
7852
|
+
const zamPath = foundZam ?? "zam";
|
|
7853
|
+
const installed = new Set(d.detect());
|
|
7854
|
+
const harnesses = USER_SCOPED_CONNECT_HARNESSES.map((harness) => {
|
|
7855
|
+
const status = {
|
|
7856
|
+
harness,
|
|
7857
|
+
label: CONNECT_HARNESS_LABELS[harness],
|
|
7858
|
+
installed: installed.has(harness),
|
|
7859
|
+
configured: false,
|
|
7860
|
+
configPath: ""
|
|
7861
|
+
};
|
|
7862
|
+
try {
|
|
7863
|
+
const probe = d.connectMcp(harness, {
|
|
7864
|
+
zamPath,
|
|
7865
|
+
cwd: d.cwd,
|
|
7866
|
+
home: d.home,
|
|
7867
|
+
copilotHome: d.copilotHome
|
|
7868
|
+
});
|
|
7869
|
+
status.configured = probe.alreadyConfigured;
|
|
7870
|
+
status.configPath = probe.path;
|
|
7871
|
+
} catch (error) {
|
|
7872
|
+
status.note = error instanceof Error ? error.message : String(error);
|
|
7873
|
+
}
|
|
7874
|
+
return status;
|
|
7875
|
+
});
|
|
7876
|
+
return { zamOnPath: Boolean(foundZam), harnesses };
|
|
7877
|
+
}
|
|
7878
|
+
|
|
7879
|
+
// src/cli/commands/agent.ts
|
|
7880
|
+
var C = {
|
|
7881
|
+
reset: "\x1B[0m",
|
|
7882
|
+
bold: "\x1B[1m",
|
|
7883
|
+
cyan: "\x1B[36m",
|
|
7884
|
+
dim: "\x1B[2m",
|
|
7885
|
+
green: "\x1B[32m",
|
|
7886
|
+
yellow: "\x1B[33m"
|
|
7887
|
+
};
|
|
7888
|
+
var SUPPORTED_AGENTS = ["opencode"];
|
|
7454
7889
|
function agentsMdPresent(cwd = process.cwd()) {
|
|
7455
|
-
return
|
|
7890
|
+
return existsSync15(join15(cwd, "AGENTS.md"));
|
|
7456
7891
|
}
|
|
7457
7892
|
function printStatus() {
|
|
7458
7893
|
const installed = hasCommand("opencode");
|
|
@@ -7558,95 +7993,87 @@ var openCmd = new Command("open").description("Open an agent harness in the work
|
|
|
7558
7993
|
const workspace = opts.dir ?? process.cwd();
|
|
7559
7994
|
launchHarness(harness, { executable, workspace, shell });
|
|
7560
7995
|
});
|
|
7561
|
-
var connectCmd = new Command("connect").description(
|
|
7562
|
-
"
|
|
7563
|
-
|
|
7996
|
+
var connectCmd = new Command("connect").description(
|
|
7997
|
+
"Configure ZAM for detected user agent harnesses, or one explicit harness"
|
|
7998
|
+
).argument(
|
|
7999
|
+
"[harness]",
|
|
8000
|
+
"Optional harness: claude-code | claude-desktop | antigravity | codex | vscode | opencode | goose | copilot"
|
|
7564
8001
|
).option(
|
|
7565
8002
|
"--print",
|
|
7566
8003
|
"Print configuration changes instead of writing them to disk"
|
|
7567
8004
|
).action(async (harnessArg, opts) => {
|
|
7568
|
-
|
|
8005
|
+
let explicitHarness;
|
|
8006
|
+
if (harnessArg) {
|
|
8007
|
+
if (!isConnectHarnessId(harnessArg)) {
|
|
8008
|
+
console.error(
|
|
8009
|
+
`Unsupported harness: ${harnessArg}. Supported: ${CONNECT_HARNESSES.join(", ")}.`
|
|
8010
|
+
);
|
|
8011
|
+
process.exit(1);
|
|
8012
|
+
} else {
|
|
8013
|
+
explicitHarness = harnessArg;
|
|
8014
|
+
}
|
|
8015
|
+
}
|
|
8016
|
+
const report = performAgentConnect({
|
|
8017
|
+
harness: explicitHarness,
|
|
8018
|
+
dryRun: Boolean(opts.print)
|
|
8019
|
+
});
|
|
8020
|
+
if (report.detected.length === 0) {
|
|
7569
8021
|
console.error(
|
|
7570
|
-
|
|
8022
|
+
"No supported user-scoped agent harness was detected. Install Codex, VS Code, or another supported host, or pass a harness explicitly."
|
|
7571
8023
|
);
|
|
7572
8024
|
process.exit(1);
|
|
7573
8025
|
}
|
|
7574
|
-
|
|
7575
|
-
if (!zamPath) {
|
|
8026
|
+
if (!report.zamOnPath) {
|
|
7576
8027
|
console.warn(
|
|
7577
8028
|
`${C.yellow}Warning: 'zam' executable was not found on your PATH. Falling back to literal 'zam'.${C.reset}`
|
|
7578
8029
|
);
|
|
7579
|
-
zamPath = "zam";
|
|
7580
8030
|
}
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
cwd: process.cwd(),
|
|
7586
|
-
home: homedir10(),
|
|
7587
|
-
copilotHome: process.env.COPILOT_HOME
|
|
7588
|
-
});
|
|
7589
|
-
} catch (error) {
|
|
7590
|
-
console.error(
|
|
7591
|
-
`Error preparing MCP configuration: ${error instanceof Error ? error.message : String(error)}`
|
|
7592
|
-
);
|
|
7593
|
-
process.exit(1);
|
|
7594
|
-
}
|
|
7595
|
-
let copilotExtension;
|
|
7596
|
-
if (harnessArg === "copilot") {
|
|
7597
|
-
try {
|
|
7598
|
-
copilotExtension = installCopilotExtension({
|
|
7599
|
-
home: homedir10(),
|
|
7600
|
-
zamPath,
|
|
7601
|
-
dryRun: Boolean(opts.print)
|
|
7602
|
-
});
|
|
7603
|
-
} catch (error) {
|
|
7604
|
-
console.error(
|
|
7605
|
-
`Error preparing Copilot MCP Apps extension: ${error instanceof Error ? error.message : String(error)}`
|
|
7606
|
-
);
|
|
7607
|
-
process.exit(1);
|
|
8031
|
+
for (const result of report.results) {
|
|
8032
|
+
if (result.error) {
|
|
8033
|
+
console.error(`Error connecting ${result.harness}: ${result.error}`);
|
|
8034
|
+
continue;
|
|
7608
8035
|
}
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
8036
|
+
if (opts.print) {
|
|
8037
|
+
if (report.results.length > 1)
|
|
8038
|
+
console.log(`${C.bold}${result.harness}${C.reset}`);
|
|
8039
|
+
console.log(`Path: ${result.path}`);
|
|
8040
|
+
console.log(`Content:
|
|
7613
8041
|
${result.content}`);
|
|
7614
|
-
|
|
7615
|
-
|
|
8042
|
+
if (result.extension?.kind === "copilot") {
|
|
8043
|
+
console.log(`Extension: ${result.extension.location}`);
|
|
8044
|
+
console.log(`Launch: ${result.extension.detail}`);
|
|
8045
|
+
} else if (result.extension) {
|
|
8046
|
+
console.log(`Extension: ${result.extension.location}`);
|
|
8047
|
+
console.log(`Launch config: ${result.extension.detail}`);
|
|
8048
|
+
}
|
|
8049
|
+
continue;
|
|
8050
|
+
}
|
|
8051
|
+
if (result.alreadyConfigured) {
|
|
7616
8052
|
console.log(
|
|
7617
|
-
|
|
8053
|
+
`${C.green}\u2713${C.reset} ${result.harness}: MCP server 'zam' already configured in ${result.path}`
|
|
7618
8054
|
);
|
|
7619
|
-
}
|
|
7620
|
-
return;
|
|
7621
|
-
}
|
|
7622
|
-
if (result.alreadyConfigured) {
|
|
7623
|
-
console.log(
|
|
7624
|
-
`${C.green}\u2713${C.reset} MCP server 'zam' already configured in ${result.path}`
|
|
7625
|
-
);
|
|
7626
|
-
if (copilotExtension) {
|
|
8055
|
+
} else {
|
|
7627
8056
|
console.log(
|
|
7628
|
-
`${C.green}\u2713${C.reset}
|
|
8057
|
+
`${C.green}\u2713${C.reset} ${result.harness}: wrote MCP configuration to ${result.path}`
|
|
7629
8058
|
);
|
|
7630
8059
|
}
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
|
|
7635
|
-
|
|
7636
|
-
writeFileSync8(result.path, result.content, "utf-8");
|
|
7637
|
-
console.log(
|
|
7638
|
-
`${C.green}\u2713${C.reset} Wrote MCP configuration to ${result.path}`
|
|
7639
|
-
);
|
|
7640
|
-
if (copilotExtension) {
|
|
8060
|
+
if (result.extension?.kind === "copilot") {
|
|
8061
|
+
console.log(
|
|
8062
|
+
`${C.green}\u2713${C.reset} Copilot MCP Apps extension ${result.extension.action} at ${result.extension.location}`
|
|
8063
|
+
);
|
|
8064
|
+
} else if (result.extension) {
|
|
7641
8065
|
console.log(
|
|
7642
|
-
`${C.green}\u2713${C.reset}
|
|
8066
|
+
`${C.green}\u2713${C.reset} ZAM Companion ${result.extension.action} from ${result.extension.location}`
|
|
7643
8067
|
);
|
|
7644
8068
|
}
|
|
7645
8069
|
console.log(` ${C.dim}${result.hint}${C.reset}`);
|
|
7646
|
-
}
|
|
7647
|
-
|
|
7648
|
-
|
|
8070
|
+
}
|
|
8071
|
+
if (report.skills) {
|
|
8072
|
+
console.log(
|
|
8073
|
+
`${C.green}\u2713${C.reset} Refreshed ${report.skills.refreshed}/${report.skills.total} global ZAM skill installations`
|
|
7649
8074
|
);
|
|
8075
|
+
}
|
|
8076
|
+
if (!report.success) {
|
|
7650
8077
|
process.exit(1);
|
|
7651
8078
|
}
|
|
7652
8079
|
});
|
|
@@ -7654,11 +8081,11 @@ var agentCommand = new Command("agent").description("Provision and inspect the a
|
|
|
7654
8081
|
|
|
7655
8082
|
// src/cli/commands/bridge.ts
|
|
7656
8083
|
init_kernel();
|
|
7657
|
-
import { execFileSync as
|
|
8084
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
7658
8085
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
7659
|
-
import { existsSync as
|
|
7660
|
-
import { homedir as
|
|
7661
|
-
import { join as
|
|
8086
|
+
import { existsSync as existsSync19, readdirSync as readdirSync2, readFileSync as readFileSync16, rmSync as rmSync3 } from "fs";
|
|
8087
|
+
import { homedir as homedir13, tmpdir as tmpdir3 } from "os";
|
|
8088
|
+
import { join as join22, resolve as resolve5 } from "path";
|
|
7662
8089
|
import { Command as Command2 } from "commander";
|
|
7663
8090
|
import { ulid as ulid9 } from "ulid";
|
|
7664
8091
|
|
|
@@ -7669,11 +8096,12 @@ import fs from "fs";
|
|
|
7669
8096
|
// src/cli/llm/client.ts
|
|
7670
8097
|
init_kernel();
|
|
7671
8098
|
import { spawn as spawn2 } from "child_process";
|
|
7672
|
-
import { existsSync as
|
|
8099
|
+
import { existsSync as existsSync16, readFileSync as readFileSync12, statSync as statSync2 } from "fs";
|
|
7673
8100
|
var DEFAULT_LLM_URL = "http://localhost:8000/v1";
|
|
7674
8101
|
var DEFAULT_LLM_MAX_TOKENS = 1e4;
|
|
7675
8102
|
var RECALL_QUESTION_MAX_OUTPUT_TOKENS = 400;
|
|
7676
8103
|
var RECALL_EVALUATION_MAX_OUTPUT_TOKENS = 1200;
|
|
8104
|
+
var RECALL_DISCUSSION_MAX_OUTPUT_TOKENS = 1200;
|
|
7677
8105
|
var RECALL_ENDPOINT_CACHE_MS = 6e4;
|
|
7678
8106
|
var cachedRecallEndpoint = null;
|
|
7679
8107
|
function recallEndpointSignature(cfg) {
|
|
@@ -8036,6 +8464,64 @@ Evaluation:`;
|
|
|
8036
8464
|
providerName: endpoint.providerName
|
|
8037
8465
|
};
|
|
8038
8466
|
}
|
|
8467
|
+
async function discussReviewViaLLM(db, input8) {
|
|
8468
|
+
const cfg = await getProviderForRole(db, "recall");
|
|
8469
|
+
const endpoint = await resolveUsableRecallEndpoint(db);
|
|
8470
|
+
const langName = LANGUAGE_NAMES[cfg.locale] || "English";
|
|
8471
|
+
const systemPrompt = `You are ZAM, a warm, precise, and encouraging skills trainer in a follow-up discussion about one flashcard.
|
|
8472
|
+
The learner has already answered, the reference answer is revealed, and your evaluation feedback was shown \u2014 nothing about this card is a spoiler anymore.
|
|
8473
|
+
|
|
8474
|
+
Guidelines:
|
|
8475
|
+
1. Answer the learner's follow-up directly and concretely in ${langName}, grounded in the card's target concept, context, and source reference.
|
|
8476
|
+
2. Stay scoped to this card and its concept. If the learner drifts to unrelated territory, answer briefly and steer back to the concept.
|
|
8477
|
+
3. Keep replies conversational and short (a few sentences) unless the learner explicitly asks for depth. Plain text only \u2014 no markdown wrapper, headers, or bullet lists.
|
|
8478
|
+
4. The self-rating is the learner's own choice. If asked, explain the FSRS scale (1 forgot, 2 hard, 3 good, 4 easy) but never pressure them toward a specific rating.`;
|
|
8479
|
+
const cardFrame = `The card under discussion:
|
|
8480
|
+
Domain: ${input8.domain}
|
|
8481
|
+
Slug: ${input8.slug}
|
|
8482
|
+
Recall Question: ${input8.question}
|
|
8483
|
+
Learner's Answer: ${input8.userAnswer}
|
|
8484
|
+
|
|
8485
|
+
Target Concept (Correct Answer): ${input8.concept}
|
|
8486
|
+
Target Context: ${input8.context || "(none)"}
|
|
8487
|
+
${input8.sourceLinkContent ? `Source Code Reference:
|
|
8488
|
+
${input8.sourceLinkContent}` : ""}`;
|
|
8489
|
+
const messages = [
|
|
8490
|
+
{ role: "system", content: systemPrompt },
|
|
8491
|
+
{ role: "user", content: cardFrame }
|
|
8492
|
+
];
|
|
8493
|
+
const feedback = input8.feedback?.trim();
|
|
8494
|
+
if (feedback) {
|
|
8495
|
+
messages.push({ role: "assistant", content: feedback });
|
|
8496
|
+
}
|
|
8497
|
+
for (const turn of input8.thread) {
|
|
8498
|
+
messages.push({ role: turn.role, content: turn.content });
|
|
8499
|
+
}
|
|
8500
|
+
messages.push({ role: "user", content: input8.message });
|
|
8501
|
+
const res = await fetchWithInteractiveTimeout(
|
|
8502
|
+
`${endpoint.url}/chat/completions`,
|
|
8503
|
+
{
|
|
8504
|
+
method: "POST",
|
|
8505
|
+
headers: {
|
|
8506
|
+
"Content-Type": "application/json",
|
|
8507
|
+
Authorization: `Bearer ${endpoint.apiKey}`
|
|
8508
|
+
},
|
|
8509
|
+
body: JSON.stringify({
|
|
8510
|
+
model: endpoint.model,
|
|
8511
|
+
messages,
|
|
8512
|
+
temperature: 0.3,
|
|
8513
|
+
max_tokens: RECALL_DISCUSSION_MAX_OUTPUT_TOKENS
|
|
8514
|
+
}),
|
|
8515
|
+
locale: cfg.locale
|
|
8516
|
+
}
|
|
8517
|
+
);
|
|
8518
|
+
const text = await readChatContent(res, "LLM discussion");
|
|
8519
|
+
return {
|
|
8520
|
+
text,
|
|
8521
|
+
model: endpoint.model,
|
|
8522
|
+
providerName: endpoint.providerName
|
|
8523
|
+
};
|
|
8524
|
+
}
|
|
8039
8525
|
var MAX_IMPORT_TEXT_CHARS = 2e5;
|
|
8040
8526
|
var VALID_GENERATED_MODES = /* @__PURE__ */ new Set(["shadowing", "copilot", "autonomy"]);
|
|
8041
8527
|
function parseGeneratedCardArray(responseText, label, limits) {
|
|
@@ -8385,7 +8871,7 @@ async function extractTextFromScanViaLLM(db, imagePath) {
|
|
|
8385
8871
|
"Vision role is not enabled in settings (llm.vision.enabled)"
|
|
8386
8872
|
);
|
|
8387
8873
|
}
|
|
8388
|
-
if (!
|
|
8874
|
+
if (!existsSync16(imagePath)) {
|
|
8389
8875
|
throw new Error(`Scan file not found: ${imagePath}`);
|
|
8390
8876
|
}
|
|
8391
8877
|
const stat = statSync2(imagePath);
|
|
@@ -8406,7 +8892,7 @@ async function extractTextFromScanViaLLM(db, imagePath) {
|
|
|
8406
8892
|
if (!mime) {
|
|
8407
8893
|
throw new Error("Unsupported scan format; use PNG, JPEG, or WebP");
|
|
8408
8894
|
}
|
|
8409
|
-
const imageBytes =
|
|
8895
|
+
const imageBytes = readFileSync12(imagePath);
|
|
8410
8896
|
const dataUrl = `data:${mime};base64,${imageBytes.toString("base64")}`;
|
|
8411
8897
|
const visionEndpoint = await getVisionConfig(db);
|
|
8412
8898
|
const langName = LANGUAGE_NAMES[p.locale] || "English";
|
|
@@ -8838,7 +9324,7 @@ function spawnLocalRunner(url, model, hint) {
|
|
|
8838
9324
|
const { runner, port } = detectRunner(url, model, hint);
|
|
8839
9325
|
try {
|
|
8840
9326
|
if (runner === "fastflowlm") {
|
|
8841
|
-
const flmExe =
|
|
9327
|
+
const flmExe = existsSync16("C:\\Program Files\\flm\\flm.exe") ? "C:\\Program Files\\flm\\flm.exe" : "flm";
|
|
8842
9328
|
if (!hasCommand("flm") && flmExe === "flm") return;
|
|
8843
9329
|
spawn2(flmExe, ["serve", model, "--port", port], {
|
|
8844
9330
|
detached: true,
|
|
@@ -8859,7 +9345,7 @@ function spawnLocalRunner(url, model, hint) {
|
|
|
8859
9345
|
async function startLocalRunner(url, model, locale, hint) {
|
|
8860
9346
|
const { runner, port } = detectRunner(url, model, hint);
|
|
8861
9347
|
if (runner === "fastflowlm") {
|
|
8862
|
-
const flmExe =
|
|
9348
|
+
const flmExe = existsSync16("C:\\Program Files\\flm\\flm.exe") ? "C:\\Program Files\\flm\\flm.exe" : "flm";
|
|
8863
9349
|
if (!hasCommand("flm") && flmExe === "flm") {
|
|
8864
9350
|
console.warn(
|
|
8865
9351
|
"\x1B[31m\u2717 FastFlowLM is configured but could not be found on the system.\x1B[0m"
|
|
@@ -9273,7 +9759,7 @@ async function readWebLink(url) {
|
|
|
9273
9759
|
redirect: "manual",
|
|
9274
9760
|
signal: controller.signal,
|
|
9275
9761
|
headers: {
|
|
9276
|
-
"User-Agent": "ZAM-Content-Studio/0.10.
|
|
9762
|
+
"User-Agent": "ZAM-Content-Studio/0.10.4"
|
|
9277
9763
|
}
|
|
9278
9764
|
});
|
|
9279
9765
|
if (res.status >= 300 && res.status < 400) {
|
|
@@ -9338,8 +9824,8 @@ async function readImageOCR(db, imagePath) {
|
|
|
9338
9824
|
|
|
9339
9825
|
// src/cli/bridge-handlers.ts
|
|
9340
9826
|
init_kernel();
|
|
9341
|
-
import { mkdirSync as
|
|
9342
|
-
import { join as
|
|
9827
|
+
import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync10 } from "fs";
|
|
9828
|
+
import { join as join18 } from "path";
|
|
9343
9829
|
|
|
9344
9830
|
// src/cli/knowledge-contexts.ts
|
|
9345
9831
|
init_kernel();
|
|
@@ -9586,16 +10072,16 @@ async function resolveSuggestMinSimilarity(db) {
|
|
|
9586
10072
|
}
|
|
9587
10073
|
|
|
9588
10074
|
// src/cli/update/latest-version.ts
|
|
9589
|
-
import { readFileSync as
|
|
9590
|
-
import { dirname as
|
|
9591
|
-
import { fileURLToPath as
|
|
10075
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
10076
|
+
import { dirname as dirname8, join as join16 } from "path";
|
|
10077
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
9592
10078
|
var GITHUB_REPO = "zam-os/zam";
|
|
9593
10079
|
function currentVersion() {
|
|
9594
|
-
const here =
|
|
10080
|
+
const here = dirname8(fileURLToPath4(import.meta.url));
|
|
9595
10081
|
for (const up of ["..", "../..", "../../.."]) {
|
|
9596
10082
|
try {
|
|
9597
10083
|
const pkg2 = JSON.parse(
|
|
9598
|
-
|
|
10084
|
+
readFileSync13(join16(here, up, "package.json"), "utf-8")
|
|
9599
10085
|
);
|
|
9600
10086
|
if (pkg2.version) return pkg2.version;
|
|
9601
10087
|
} catch {
|
|
@@ -9625,13 +10111,13 @@ async function fetchLatestVersion(repo) {
|
|
|
9625
10111
|
|
|
9626
10112
|
// src/cli/workspaces/active.ts
|
|
9627
10113
|
init_kernel();
|
|
9628
|
-
import { existsSync as
|
|
9629
|
-
import { homedir as
|
|
9630
|
-
import { basename as basename3, join as
|
|
10114
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync11 } from "fs";
|
|
10115
|
+
import { homedir as homedir12 } from "os";
|
|
10116
|
+
import { basename as basename3, join as join17, resolve as resolve4 } from "path";
|
|
9631
10117
|
var LEGACY_WORKSPACE_DIR_KEY = "personal.workspace_dir";
|
|
9632
10118
|
var DEFAULT_WORKSPACE_ID = "personal";
|
|
9633
10119
|
function defaultWorkspaceDir() {
|
|
9634
|
-
return
|
|
10120
|
+
return join17(homedir12(), "Documents", "zam");
|
|
9635
10121
|
}
|
|
9636
10122
|
function normalizeWorkspacePath(path) {
|
|
9637
10123
|
const resolved = resolve4(path);
|
|
@@ -9686,13 +10172,13 @@ async function ensureActiveWorkspace(db) {
|
|
|
9686
10172
|
await migrateLegacyWorkspaceDir(db);
|
|
9687
10173
|
const active = getActiveWorkspace();
|
|
9688
10174
|
if (active) {
|
|
9689
|
-
|
|
10175
|
+
mkdirSync11(active.path, { recursive: true });
|
|
9690
10176
|
return active;
|
|
9691
10177
|
}
|
|
9692
10178
|
const configured = getConfiguredWorkspaces()[0];
|
|
9693
10179
|
if (configured) {
|
|
9694
10180
|
setActiveWorkspaceId(configured.id);
|
|
9695
|
-
|
|
10181
|
+
mkdirSync11(configured.path, { recursive: true });
|
|
9696
10182
|
return configured;
|
|
9697
10183
|
}
|
|
9698
10184
|
const workspace = {
|
|
@@ -9701,7 +10187,7 @@ async function ensureActiveWorkspace(db) {
|
|
|
9701
10187
|
kind: "personal",
|
|
9702
10188
|
path: defaultWorkspaceDir()
|
|
9703
10189
|
};
|
|
9704
|
-
|
|
10190
|
+
mkdirSync11(workspace.path, { recursive: true });
|
|
9705
10191
|
upsertConfiguredWorkspace(workspace);
|
|
9706
10192
|
setActiveWorkspaceId(workspace.id);
|
|
9707
10193
|
await clearLegacyWorkspaceDir(db);
|
|
@@ -9740,7 +10226,7 @@ async function removeWorkspaceAndResolveActive(db, id) {
|
|
|
9740
10226
|
};
|
|
9741
10227
|
}
|
|
9742
10228
|
function existingWorkspaceDirOrHome(workspace) {
|
|
9743
|
-
return
|
|
10229
|
+
return existsSync17(workspace.path) ? workspace.path : homedir12();
|
|
9744
10230
|
}
|
|
9745
10231
|
|
|
9746
10232
|
// src/cli/bridge-handlers.ts
|
|
@@ -10512,11 +10998,11 @@ async function backupCreate(db, params) {
|
|
|
10512
10998
|
const targetDir = params.dir || (await ensureActiveWorkspace(db)).path;
|
|
10513
10999
|
const snapshot = await exportSnapshot(db);
|
|
10514
11000
|
const manifest = verifySnapshot(snapshot);
|
|
10515
|
-
const backupDir =
|
|
10516
|
-
|
|
11001
|
+
const backupDir = join18(targetDir, "zam-backups");
|
|
11002
|
+
mkdirSync12(backupDir, { recursive: true });
|
|
10517
11003
|
const stamp = manifest.createdAt.replace(/[:.]/g, "-");
|
|
10518
|
-
const path =
|
|
10519
|
-
|
|
11004
|
+
const path = join18(backupDir, `zam-snapshot-${stamp}.sql`);
|
|
11005
|
+
writeFileSync10(path, snapshot, "utf-8");
|
|
10520
11006
|
return {
|
|
10521
11007
|
ok: true,
|
|
10522
11008
|
path,
|
|
@@ -13201,9 +13687,9 @@ function getCurriculumProvider(id) {
|
|
|
13201
13687
|
// src/cli/llm/vision.ts
|
|
13202
13688
|
init_kernel();
|
|
13203
13689
|
import { randomBytes } from "crypto";
|
|
13204
|
-
import { readFileSync as
|
|
13690
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
13205
13691
|
import { tmpdir as tmpdir2 } from "os";
|
|
13206
|
-
import { basename as basename4, join as
|
|
13692
|
+
import { basename as basename4, join as join19 } from "path";
|
|
13207
13693
|
var LANGUAGE_NAMES2 = {
|
|
13208
13694
|
en: "English",
|
|
13209
13695
|
de: "German",
|
|
@@ -13239,13 +13725,13 @@ async function observeUiSnapshotViaLLM(db, input8) {
|
|
|
13239
13725
|
const imageUrls = [];
|
|
13240
13726
|
const isVideo = /\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(input8.imagePath);
|
|
13241
13727
|
if (isVideo) {
|
|
13242
|
-
const { mkdirSync:
|
|
13728
|
+
const { mkdirSync: mkdirSync18, readdirSync: readdirSync3, rmSync: rmSync4 } = await import("fs");
|
|
13243
13729
|
const { execSync: execSync6 } = await import("child_process");
|
|
13244
|
-
const tempDir =
|
|
13730
|
+
const tempDir = join19(
|
|
13245
13731
|
tmpdir2(),
|
|
13246
13732
|
`zam-frames-${randomBytes(4).toString("hex")}`
|
|
13247
13733
|
);
|
|
13248
|
-
|
|
13734
|
+
mkdirSync18(tempDir, { recursive: true });
|
|
13249
13735
|
try {
|
|
13250
13736
|
execSync6(
|
|
13251
13737
|
`ffmpeg -i "${input8.imagePath}" -vf "fps=1/3,scale=1280:-1" -vsync vfr "${tempDir}/frame_%03d.png"`,
|
|
@@ -13274,7 +13760,7 @@ async function observeUiSnapshotViaLLM(db, input8) {
|
|
|
13274
13760
|
}
|
|
13275
13761
|
}
|
|
13276
13762
|
for (const file of sampledFiles) {
|
|
13277
|
-
const bytes =
|
|
13763
|
+
const bytes = readFileSync14(join19(tempDir, file));
|
|
13278
13764
|
imageUrls.push(`data:image/png;base64,${bytes.toString("base64")}`);
|
|
13279
13765
|
}
|
|
13280
13766
|
} finally {
|
|
@@ -13284,7 +13770,7 @@ async function observeUiSnapshotViaLLM(db, input8) {
|
|
|
13284
13770
|
}
|
|
13285
13771
|
}
|
|
13286
13772
|
} else {
|
|
13287
|
-
const imageBytes =
|
|
13773
|
+
const imageBytes = readFileSync14(input8.imagePath);
|
|
13288
13774
|
const ext = input8.imagePath.split(".").pop()?.toLowerCase();
|
|
13289
13775
|
const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : "image/png";
|
|
13290
13776
|
imageUrls.push(`data:${mime};base64,${imageBytes.toString("base64")}`);
|
|
@@ -13753,36 +14239,36 @@ async function withProviderScope(machine, action) {
|
|
|
13753
14239
|
|
|
13754
14240
|
// src/cli/provisioning/index.ts
|
|
13755
14241
|
import {
|
|
13756
|
-
existsSync as
|
|
14242
|
+
existsSync as existsSync18,
|
|
13757
14243
|
lstatSync as lstatSync2,
|
|
13758
|
-
mkdirSync as
|
|
13759
|
-
readFileSync as
|
|
14244
|
+
mkdirSync as mkdirSync13,
|
|
14245
|
+
readFileSync as readFileSync15,
|
|
13760
14246
|
realpathSync,
|
|
13761
14247
|
rmSync as rmSync2,
|
|
13762
14248
|
symlinkSync,
|
|
13763
|
-
writeFileSync as
|
|
14249
|
+
writeFileSync as writeFileSync11
|
|
13764
14250
|
} from "fs";
|
|
13765
|
-
import { basename as basename5, dirname as
|
|
13766
|
-
import { fileURLToPath as
|
|
13767
|
-
var
|
|
13768
|
-
|
|
13769
|
-
|
|
13770
|
-
].find((candidate) =>
|
|
14251
|
+
import { basename as basename5, dirname as dirname9, join as join20 } from "path";
|
|
14252
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
14253
|
+
var packageRoot3 = [
|
|
14254
|
+
fileURLToPath5(new URL("../..", import.meta.url)),
|
|
14255
|
+
fileURLToPath5(new URL("../../..", import.meta.url))
|
|
14256
|
+
].find((candidate) => existsSync18(join20(candidate, "package.json"))) ?? fileURLToPath5(new URL("../..", import.meta.url));
|
|
13771
14257
|
var ALL_SETUP_AGENTS = ["claude", "copilot", "codex", "agent"];
|
|
13772
14258
|
var SKILL_PAIRS = [
|
|
13773
14259
|
{
|
|
13774
|
-
from:
|
|
13775
|
-
to:
|
|
14260
|
+
from: join20(packageRoot3, ".claude", "skills", "zam", "SKILL.md"),
|
|
14261
|
+
to: join20(".claude", "skills", "zam", "SKILL.md"),
|
|
13776
14262
|
agents: ["claude", "copilot"]
|
|
13777
14263
|
},
|
|
13778
14264
|
{
|
|
13779
|
-
from:
|
|
13780
|
-
to:
|
|
14265
|
+
from: join20(packageRoot3, ".agent", "skills", "zam", "SKILL.md"),
|
|
14266
|
+
to: join20(".agent", "skills", "zam", "SKILL.md"),
|
|
13781
14267
|
agents: ["agent"]
|
|
13782
14268
|
},
|
|
13783
14269
|
{
|
|
13784
|
-
from:
|
|
13785
|
-
to:
|
|
14270
|
+
from: join20(packageRoot3, ".agents", "skills", "zam", "SKILL.md"),
|
|
14271
|
+
to: join20(".agents", "skills", "zam", "SKILL.md"),
|
|
13786
14272
|
agents: ["codex"]
|
|
13787
14273
|
}
|
|
13788
14274
|
];
|
|
@@ -13842,15 +14328,15 @@ function linkPointsTo(sourceDir, destinationDir) {
|
|
|
13842
14328
|
function isZamSkillCopy(destinationDir) {
|
|
13843
14329
|
try {
|
|
13844
14330
|
if (!lstatSync2(destinationDir).isDirectory()) return false;
|
|
13845
|
-
const skillFile =
|
|
13846
|
-
if (!
|
|
13847
|
-
return /^name:\s*zam\s*$/m.test(
|
|
14331
|
+
const skillFile = join20(destinationDir, "SKILL.md");
|
|
14332
|
+
if (!existsSync18(skillFile)) return false;
|
|
14333
|
+
return /^name:\s*zam\s*$/m.test(readFileSync15(skillFile, "utf8"));
|
|
13848
14334
|
} catch {
|
|
13849
14335
|
return false;
|
|
13850
14336
|
}
|
|
13851
14337
|
}
|
|
13852
14338
|
function classifySkillDestination(sourceDir, destinationDir) {
|
|
13853
|
-
if (!
|
|
14339
|
+
if (!existsSync18(sourceDir)) return "source-missing";
|
|
13854
14340
|
if (!isSymbolicLink(destinationDir) && pathsResolveToSameDirectory(sourceDir, destinationDir)) {
|
|
13855
14341
|
return "source-directory";
|
|
13856
14342
|
}
|
|
@@ -13867,9 +14353,9 @@ function wireSkills(cwd = process.cwd(), agents = parseSetupAgents(), opts = {})
|
|
|
13867
14353
|
};
|
|
13868
14354
|
for (const { from, to, agents: pairAgents } of SKILL_PAIRS) {
|
|
13869
14355
|
if (!pairAgents.some((agent) => agents.has(agent))) continue;
|
|
13870
|
-
const sourceDir =
|
|
13871
|
-
const destinationDir =
|
|
13872
|
-
const label =
|
|
14356
|
+
const sourceDir = dirname9(from);
|
|
14357
|
+
const destinationDir = dirname9(join20(cwd, to));
|
|
14358
|
+
const label = dirname9(to);
|
|
13873
14359
|
const state = classifySkillDestination(sourceDir, destinationDir);
|
|
13874
14360
|
if (state === "source-missing") {
|
|
13875
14361
|
if (!opts.quiet) {
|
|
@@ -13926,7 +14412,7 @@ function wireSkills(cwd = process.cwd(), agents = parseSetupAgents(), opts = {})
|
|
|
13926
14412
|
if (destinationExists) {
|
|
13927
14413
|
rmSync2(destinationDir, { recursive: true, force: true });
|
|
13928
14414
|
}
|
|
13929
|
-
|
|
14415
|
+
mkdirSync13(dirname9(destinationDir), { recursive: true });
|
|
13930
14416
|
symlinkSync(
|
|
13931
14417
|
sourceDir,
|
|
13932
14418
|
destinationDir,
|
|
@@ -13942,8 +14428,8 @@ function inspectSkillLinks(cwd = process.cwd(), agents = parseSetupAgents()) {
|
|
|
13942
14428
|
const results = [];
|
|
13943
14429
|
for (const { from, to, agents: pairAgents } of SKILL_PAIRS) {
|
|
13944
14430
|
if (!pairAgents.some((agent) => agents.has(agent))) continue;
|
|
13945
|
-
const sourceDir =
|
|
13946
|
-
const destinationDir =
|
|
14431
|
+
const sourceDir = dirname9(from);
|
|
14432
|
+
const destinationDir = dirname9(join20(cwd, to));
|
|
13947
14433
|
results.push({
|
|
13948
14434
|
agents: pairAgents,
|
|
13949
14435
|
source: sourceDir,
|
|
@@ -13969,15 +14455,15 @@ function upsertMarkedBlock(dest, blockBody, dryRun) {
|
|
|
13969
14455
|
const block = `${ZAM_BLOCK_START}
|
|
13970
14456
|
${blockBody.trim()}
|
|
13971
14457
|
${ZAM_BLOCK_END}`;
|
|
13972
|
-
if (!
|
|
14458
|
+
if (!existsSync18(dest)) {
|
|
13973
14459
|
if (!dryRun) {
|
|
13974
|
-
|
|
13975
|
-
|
|
14460
|
+
mkdirSync13(dirname9(dest), { recursive: true });
|
|
14461
|
+
writeFileSync11(dest, `${block}
|
|
13976
14462
|
`, "utf8");
|
|
13977
14463
|
}
|
|
13978
14464
|
return "write";
|
|
13979
14465
|
}
|
|
13980
|
-
const existing =
|
|
14466
|
+
const existing = readFileSync15(dest, "utf8");
|
|
13981
14467
|
if (existing.includes(block)) return "skip";
|
|
13982
14468
|
const start = existing.indexOf(ZAM_BLOCK_START);
|
|
13983
14469
|
const end = existing.indexOf(ZAM_BLOCK_END);
|
|
@@ -13985,7 +14471,7 @@ ${ZAM_BLOCK_END}`;
|
|
|
13985
14471
|
|
|
13986
14472
|
${block}
|
|
13987
14473
|
`;
|
|
13988
|
-
if (!dryRun)
|
|
14474
|
+
if (!dryRun) writeFileSync11(dest, next, "utf8");
|
|
13989
14475
|
return start >= 0 && end > start ? "update" : "write";
|
|
13990
14476
|
}
|
|
13991
14477
|
function logInstructionAction(action, label, dryRun) {
|
|
@@ -13997,8 +14483,8 @@ function logInstructionAction(action, label, dryRun) {
|
|
|
13997
14483
|
}
|
|
13998
14484
|
function writeClaudeMd(skipClaudeMd, cwd = process.cwd(), opts = {}) {
|
|
13999
14485
|
if (skipClaudeMd) return;
|
|
14000
|
-
const dest =
|
|
14001
|
-
if (
|
|
14486
|
+
const dest = join20(cwd, "CLAUDE.md");
|
|
14487
|
+
if (existsSync18(dest)) {
|
|
14002
14488
|
if (!opts.updateExisting) {
|
|
14003
14489
|
console.log(` skip CLAUDE.md (already present)`);
|
|
14004
14490
|
return;
|
|
@@ -14041,14 +14527,14 @@ Use \`zam connector setup turso\` to store cloud credentials in
|
|
|
14041
14527
|
if (opts.dryRun) {
|
|
14042
14528
|
console.log(` would write CLAUDE.md`);
|
|
14043
14529
|
} else {
|
|
14044
|
-
|
|
14530
|
+
writeFileSync11(dest, content, "utf8");
|
|
14045
14531
|
console.log(` write CLAUDE.md`);
|
|
14046
14532
|
}
|
|
14047
14533
|
}
|
|
14048
14534
|
function writeAgentsMd(skipAgentsMd, cwd = process.cwd(), opts = {}) {
|
|
14049
14535
|
if (skipAgentsMd) return;
|
|
14050
|
-
const dest =
|
|
14051
|
-
if (
|
|
14536
|
+
const dest = join20(cwd, "AGENTS.md");
|
|
14537
|
+
if (existsSync18(dest)) {
|
|
14052
14538
|
if (!opts.updateExisting) {
|
|
14053
14539
|
console.log(` skip AGENTS.md (already present)`);
|
|
14054
14540
|
return;
|
|
@@ -14098,12 +14584,12 @@ Codex discovers repository skills under \`.agents/skills/\`. Run
|
|
|
14098
14584
|
if (opts.dryRun) {
|
|
14099
14585
|
console.log(` would write AGENTS.md`);
|
|
14100
14586
|
} else {
|
|
14101
|
-
|
|
14587
|
+
writeFileSync11(dest, content, "utf8");
|
|
14102
14588
|
console.log(` write AGENTS.md`);
|
|
14103
14589
|
}
|
|
14104
14590
|
}
|
|
14105
14591
|
function writeCopilotInstructions(cwd = process.cwd(), opts = {}) {
|
|
14106
|
-
const dest =
|
|
14592
|
+
const dest = join20(cwd, ".github", "copilot-instructions.md");
|
|
14107
14593
|
const action = upsertMarkedBlock(
|
|
14108
14594
|
dest,
|
|
14109
14595
|
`## ZAM learning sessions
|
|
@@ -14144,13 +14630,13 @@ async function resolveUser(opts, db, resolveOpts) {
|
|
|
14144
14630
|
}
|
|
14145
14631
|
|
|
14146
14632
|
// src/cli/workspaces/backup.ts
|
|
14147
|
-
import { mkdirSync as
|
|
14148
|
-
import { join as
|
|
14633
|
+
import { mkdirSync as mkdirSync14 } from "fs";
|
|
14634
|
+
import { join as join21 } from "path";
|
|
14149
14635
|
async function backupDatabaseTo(db, targetDir) {
|
|
14150
|
-
const backupDir =
|
|
14151
|
-
|
|
14636
|
+
const backupDir = join21(targetDir, "zam-backups");
|
|
14637
|
+
mkdirSync14(backupDir, { recursive: true });
|
|
14152
14638
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
14153
|
-
const dest =
|
|
14639
|
+
const dest = join21(backupDir, `zam-${stamp}.db`);
|
|
14154
14640
|
await db.exec(`VACUUM INTO '${dest.replace(/'/g, "''")}'`);
|
|
14155
14641
|
return dest;
|
|
14156
14642
|
}
|
|
@@ -14281,20 +14767,20 @@ bridgeCommand.command("workspace-info").description("Report the workspace dir, i
|
|
|
14281
14767
|
activeWorkspace,
|
|
14282
14768
|
workspaceDir: activeWorkspace.path,
|
|
14283
14769
|
defaultWorkspaceDir: defaultWorkspaceDir(),
|
|
14284
|
-
dataDir:
|
|
14770
|
+
dataDir: join22(homedir13(), ".zam")
|
|
14285
14771
|
});
|
|
14286
14772
|
});
|
|
14287
14773
|
});
|
|
14288
14774
|
function provisionConfiguredWorkspaces() {
|
|
14289
14775
|
return getConfiguredWorkspaces().flatMap(
|
|
14290
|
-
(workspace) =>
|
|
14776
|
+
(workspace) => existsSync19(workspace.path) ? wireSkills(workspace.path, parseSetupAgents(), { quiet: true }) : []
|
|
14291
14777
|
);
|
|
14292
14778
|
}
|
|
14293
14779
|
function buildWorkspaceLinkHealth(workspaces) {
|
|
14294
14780
|
const agents = parseSetupAgents();
|
|
14295
14781
|
const map = {};
|
|
14296
14782
|
for (const workspace of workspaces) {
|
|
14297
|
-
if (!
|
|
14783
|
+
if (!existsSync19(workspace.path)) continue;
|
|
14298
14784
|
const links = inspectSkillLinks(workspace.path, agents);
|
|
14299
14785
|
map[workspace.id] = {
|
|
14300
14786
|
health: summarizeSkillLinkHealth(links),
|
|
@@ -14324,7 +14810,7 @@ bridgeCommand.command("workspace-list").description("List configured ZAM workspa
|
|
|
14324
14810
|
activeWorkspace,
|
|
14325
14811
|
workspaceDir: activeWorkspace.path,
|
|
14326
14812
|
defaultWorkspaceDir: defaultWorkspaceDir(),
|
|
14327
|
-
dataDir:
|
|
14813
|
+
dataDir: join22(homedir13(), ".zam"),
|
|
14328
14814
|
linkHealth: buildWorkspaceLinkHealth(workspaces)
|
|
14329
14815
|
});
|
|
14330
14816
|
});
|
|
@@ -14339,7 +14825,7 @@ bridgeCommand.command("workspace-repair-links").description(
|
|
|
14339
14825
|
if (!id) jsonError("A non-empty --id is required");
|
|
14340
14826
|
const workspace = getConfiguredWorkspaces().find((item) => item.id === id);
|
|
14341
14827
|
if (!workspace) jsonError(`Workspace "${id}" is not configured`);
|
|
14342
|
-
if (!
|
|
14828
|
+
if (!existsSync19(workspace.path)) {
|
|
14343
14829
|
jsonError(`Workspace path does not exist: ${workspace.path}`);
|
|
14344
14830
|
}
|
|
14345
14831
|
const agents = parseSetupAgents(opts.agents);
|
|
@@ -14371,7 +14857,7 @@ bridgeCommand.command("workspace-add").description("Register an existing directo
|
|
|
14371
14857
|
const raw = String(opts.path ?? "").trim();
|
|
14372
14858
|
if (!raw) jsonError("A non-empty --path is required");
|
|
14373
14859
|
const path = resolve5(raw);
|
|
14374
|
-
if (!
|
|
14860
|
+
if (!existsSync19(path)) jsonError(`Workspace path does not exist: ${path}`);
|
|
14375
14861
|
const id = opts.id ? String(opts.id).trim() : void 0;
|
|
14376
14862
|
if (opts.id && !id) jsonError("A non-empty --id is required");
|
|
14377
14863
|
const kind = parseBridgeWorkspaceKind(opts.kind);
|
|
@@ -14416,7 +14902,7 @@ bridgeCommand.command("set-workspace-dir").description("Set the personal workspa
|
|
|
14416
14902
|
const raw = String(opts.dir ?? "").trim();
|
|
14417
14903
|
if (!raw) jsonError("A non-empty --dir is required");
|
|
14418
14904
|
const dir = resolve5(raw);
|
|
14419
|
-
if (!
|
|
14905
|
+
if (!existsSync19(dir)) jsonError(`Workspace path does not exist: ${dir}`);
|
|
14420
14906
|
const skillLinks = wireSkills(dir, parseSetupAgents(), { quiet: true });
|
|
14421
14907
|
await withDb2(async (db) => {
|
|
14422
14908
|
const workspace = await activateWorkspacePath(db, dir);
|
|
@@ -14479,7 +14965,7 @@ bridgeCommand.command("agent-open").description("Launch an agent harness in the
|
|
|
14479
14965
|
jsonError(`Workspace is not configured: ${opts.workspace}`);
|
|
14480
14966
|
}
|
|
14481
14967
|
const activeWorkspace = await ensureActiveWorkspace(db);
|
|
14482
|
-
const workspace = opts.dir ?
|
|
14968
|
+
const workspace = opts.dir ? existsSync19(opts.dir) ? opts.dir : homedir13() : existingWorkspaceDirOrHome(configuredWorkspace ?? activeWorkspace);
|
|
14483
14969
|
launchHarness(harness, {
|
|
14484
14970
|
executable,
|
|
14485
14971
|
workspace,
|
|
@@ -14851,7 +15337,7 @@ bridgeCommand.command("discover-skills").description(
|
|
|
14851
15337
|
"20"
|
|
14852
15338
|
).action(async (opts) => {
|
|
14853
15339
|
try {
|
|
14854
|
-
const monitorDir =
|
|
15340
|
+
const monitorDir = join22(homedir13(), ".zam", "monitor");
|
|
14855
15341
|
let files;
|
|
14856
15342
|
try {
|
|
14857
15343
|
files = readdirSync2(monitorDir).filter((f) => f.endsWith(".jsonl"));
|
|
@@ -14864,7 +15350,7 @@ bridgeCommand.command("discover-skills").description(
|
|
|
14864
15350
|
return;
|
|
14865
15351
|
}
|
|
14866
15352
|
const limit = Number(opts.limit);
|
|
14867
|
-
const sorted = files.map((f) => ({ name: f, path:
|
|
15353
|
+
const sorted = files.map((f) => ({ name: f, path: join22(monitorDir, f) })).sort((a, b) => b.name.localeCompare(a.name)).slice(0, limit);
|
|
14868
15354
|
const sessionCommands = /* @__PURE__ */ new Map();
|
|
14869
15355
|
for (const file of sorted) {
|
|
14870
15356
|
const sessionId = file.name.replace(".jsonl", "");
|
|
@@ -14976,7 +15462,7 @@ bridgeCommand.command("observe-ui-snapshot").description(
|
|
|
14976
15462
|
});
|
|
14977
15463
|
function resolveWindowsPowerShell() {
|
|
14978
15464
|
try {
|
|
14979
|
-
|
|
15465
|
+
execFileSync4("where.exe", ["pwsh.exe"], { stdio: "ignore" });
|
|
14980
15466
|
return "pwsh";
|
|
14981
15467
|
} catch {
|
|
14982
15468
|
return "powershell";
|
|
@@ -14991,7 +15477,7 @@ function captureScreenshot(outputPath, hwnd, processName) {
|
|
|
14991
15477
|
throw new Error(`Invalid process name format: ${processName}`);
|
|
14992
15478
|
}
|
|
14993
15479
|
if (platform === "win32") {
|
|
14994
|
-
const stdout =
|
|
15480
|
+
const stdout = execFileSync4(
|
|
14995
15481
|
resolveWindowsPowerShell(),
|
|
14996
15482
|
[
|
|
14997
15483
|
"-NoProfile",
|
|
@@ -15304,13 +15790,13 @@ Write-CaptureResult "fullscreen" $hwndVal $matchedBy
|
|
|
15304
15790
|
} else if (platform === "darwin") {
|
|
15305
15791
|
if (hwnd) {
|
|
15306
15792
|
const parsedHwnd = hwnd.startsWith("0x") ? parseInt(hwnd, 16) : parseInt(hwnd, 10);
|
|
15307
|
-
|
|
15793
|
+
execFileSync4("screencapture", ["-l", String(parsedHwnd), outputPath], {
|
|
15308
15794
|
stdio: "pipe"
|
|
15309
15795
|
});
|
|
15310
15796
|
return { method: "screencapture-window", target: null };
|
|
15311
15797
|
} else if (processName) {
|
|
15312
15798
|
try {
|
|
15313
|
-
const windowId =
|
|
15799
|
+
const windowId = execFileSync4(
|
|
15314
15800
|
"osascript",
|
|
15315
15801
|
[
|
|
15316
15802
|
"-e",
|
|
@@ -15319,17 +15805,17 @@ Write-CaptureResult "fullscreen" $hwndVal $matchedBy
|
|
|
15319
15805
|
{ encoding: "utf8", stdio: ["pipe", "pipe", "ignore"] }
|
|
15320
15806
|
).trim();
|
|
15321
15807
|
if (windowId && /^\d+$/.test(windowId)) {
|
|
15322
|
-
|
|
15808
|
+
execFileSync4("screencapture", ["-l", windowId, outputPath], {
|
|
15323
15809
|
stdio: "pipe"
|
|
15324
15810
|
});
|
|
15325
15811
|
return { method: "screencapture-window", target: null };
|
|
15326
15812
|
}
|
|
15327
15813
|
} catch {
|
|
15328
15814
|
}
|
|
15329
|
-
|
|
15815
|
+
execFileSync4("screencapture", ["-x", outputPath], { stdio: "pipe" });
|
|
15330
15816
|
return { method: "screencapture-full", target: null };
|
|
15331
15817
|
} else {
|
|
15332
|
-
|
|
15818
|
+
execFileSync4("screencapture", ["-x", outputPath], { stdio: "pipe" });
|
|
15333
15819
|
return { method: "screencapture-full", target: null };
|
|
15334
15820
|
}
|
|
15335
15821
|
} else {
|
|
@@ -15366,7 +15852,7 @@ bridgeCommand.command("capture-ui").description("Capture a screenshot for agent-
|
|
|
15366
15852
|
return;
|
|
15367
15853
|
}
|
|
15368
15854
|
}
|
|
15369
|
-
const outputPath = opts.image ?? opts.output ??
|
|
15855
|
+
const outputPath = opts.image ?? opts.output ?? join22(tmpdir3(), `zam-capture-${randomBytes2(4).toString("hex")}.png`);
|
|
15370
15856
|
const captureResult = isProvided ? { method: "provided", target: null } : captureScreenshot(outputPath, opts.hwnd, opts.processName);
|
|
15371
15857
|
if (!isProvided) {
|
|
15372
15858
|
const post = decidePostCapture(policy, {
|
|
@@ -15394,7 +15880,7 @@ bridgeCommand.command("capture-ui").description("Capture a screenshot for agent-
|
|
|
15394
15880
|
return;
|
|
15395
15881
|
}
|
|
15396
15882
|
}
|
|
15397
|
-
const imageBytes =
|
|
15883
|
+
const imageBytes = readFileSync16(outputPath);
|
|
15398
15884
|
const base64 = imageBytes.toString("base64");
|
|
15399
15885
|
jsonOut2({
|
|
15400
15886
|
sessionId: opts.session ?? null,
|
|
@@ -15421,11 +15907,11 @@ bridgeCommand.command("start-recording").description("Start screen recording in
|
|
|
15421
15907
|
return;
|
|
15422
15908
|
}
|
|
15423
15909
|
const sessionId = opts.session;
|
|
15424
|
-
const statePath =
|
|
15910
|
+
const statePath = join22(tmpdir3(), `zam-recording-${sessionId}.json`);
|
|
15425
15911
|
const defaultExt = platform === "win32" ? ".mkv" : ".mov";
|
|
15426
|
-
const outputPath = opts.output ??
|
|
15427
|
-
const { existsSync:
|
|
15428
|
-
if (
|
|
15912
|
+
const outputPath = opts.output ?? join22(tmpdir3(), `zam-recording-${sessionId}${defaultExt}`);
|
|
15913
|
+
const { existsSync: existsSync28, writeFileSync: writeFileSync16, openSync, closeSync } = await import("fs");
|
|
15914
|
+
if (existsSync28(statePath)) {
|
|
15429
15915
|
jsonOut2({
|
|
15430
15916
|
sessionId,
|
|
15431
15917
|
started: false,
|
|
@@ -15433,7 +15919,7 @@ bridgeCommand.command("start-recording").description("Start screen recording in
|
|
|
15433
15919
|
});
|
|
15434
15920
|
return;
|
|
15435
15921
|
}
|
|
15436
|
-
const logPath =
|
|
15922
|
+
const logPath = join22(tmpdir3(), `zam-recording-${sessionId}.log`);
|
|
15437
15923
|
let logFd;
|
|
15438
15924
|
try {
|
|
15439
15925
|
logFd = openSync(logPath, "w");
|
|
@@ -15479,7 +15965,7 @@ bridgeCommand.command("start-recording").description("Start screen recording in
|
|
|
15479
15965
|
}
|
|
15480
15966
|
child.unref();
|
|
15481
15967
|
if (child.pid) {
|
|
15482
|
-
|
|
15968
|
+
writeFileSync16(
|
|
15483
15969
|
statePath,
|
|
15484
15970
|
JSON.stringify({
|
|
15485
15971
|
pid: child.pid,
|
|
@@ -15515,9 +16001,9 @@ bridgeCommand.command("stop-recording").description(
|
|
|
15515
16001
|
return;
|
|
15516
16002
|
}
|
|
15517
16003
|
const sessionId = opts.session;
|
|
15518
|
-
const statePath =
|
|
15519
|
-
const { existsSync:
|
|
15520
|
-
if (!
|
|
16004
|
+
const statePath = join22(tmpdir3(), `zam-recording-${sessionId}.json`);
|
|
16005
|
+
const { existsSync: existsSync28, readFileSync: readFileSync21, rmSync: rmSync4 } = await import("fs");
|
|
16006
|
+
if (!existsSync28(statePath)) {
|
|
15521
16007
|
jsonOut2({
|
|
15522
16008
|
sessionId,
|
|
15523
16009
|
stopped: false,
|
|
@@ -15525,7 +16011,7 @@ bridgeCommand.command("stop-recording").description(
|
|
|
15525
16011
|
});
|
|
15526
16012
|
return;
|
|
15527
16013
|
}
|
|
15528
|
-
const state = JSON.parse(
|
|
16014
|
+
const state = JSON.parse(readFileSync21(statePath, "utf8"));
|
|
15529
16015
|
const { pid, outputPath } = state;
|
|
15530
16016
|
try {
|
|
15531
16017
|
process.kill(pid, "SIGINT");
|
|
@@ -15554,7 +16040,7 @@ bridgeCommand.command("stop-recording").description(
|
|
|
15554
16040
|
rmSync4(statePath, { force: true });
|
|
15555
16041
|
} catch {
|
|
15556
16042
|
}
|
|
15557
|
-
if (!
|
|
16043
|
+
if (!existsSync28(outputPath)) {
|
|
15558
16044
|
jsonOut2({
|
|
15559
16045
|
sessionId,
|
|
15560
16046
|
stopped: false,
|
|
@@ -15791,7 +16277,7 @@ bridgeCommand.command("cloud-model-hint").description("Suggest a cloud model for
|
|
|
15791
16277
|
});
|
|
15792
16278
|
bridgeCommand.command("local-llm-hints").description("Detect installed local LLM servers and suggest defaults (JSON)").action(() => {
|
|
15793
16279
|
const profile = getSystemProfile();
|
|
15794
|
-
const flmInstalled = hasCommand("flm") ||
|
|
16280
|
+
const flmInstalled = hasCommand("flm") || existsSync19("C:\\Program Files\\flm\\flm.exe");
|
|
15795
16281
|
const ollamaInstalled = isOllamaInstalled();
|
|
15796
16282
|
const runners = [
|
|
15797
16283
|
{ id: "flm", label: "FastFlowLM", installed: flmInstalled },
|
|
@@ -15931,6 +16417,93 @@ bridgeCommand.command("evaluate-answer").description(
|
|
|
15931
16417
|
}
|
|
15932
16418
|
});
|
|
15933
16419
|
});
|
|
16420
|
+
function parseDiscussionThread(raw) {
|
|
16421
|
+
if (!raw) return [];
|
|
16422
|
+
let parsed;
|
|
16423
|
+
try {
|
|
16424
|
+
parsed = JSON.parse(raw);
|
|
16425
|
+
} catch {
|
|
16426
|
+
throw new Error("Invalid --thread: not valid JSON");
|
|
16427
|
+
}
|
|
16428
|
+
if (!Array.isArray(parsed)) {
|
|
16429
|
+
throw new Error("Invalid --thread: expected a JSON array of turns");
|
|
16430
|
+
}
|
|
16431
|
+
return parsed.map((turn, index) => {
|
|
16432
|
+
const candidate = turn;
|
|
16433
|
+
if (candidate?.role !== "user" && candidate?.role !== "assistant" || typeof candidate?.content !== "string") {
|
|
16434
|
+
throw new Error(
|
|
16435
|
+
`Invalid --thread: turn ${index} must be {"role": "user"|"assistant", "content": string}`
|
|
16436
|
+
);
|
|
16437
|
+
}
|
|
16438
|
+
return { role: candidate.role, content: candidate.content };
|
|
16439
|
+
});
|
|
16440
|
+
}
|
|
16441
|
+
bridgeCommand.command("discuss-review").description(
|
|
16442
|
+
"Answer one turn of the post-reveal follow-up discussion about a card (JSON)"
|
|
16443
|
+
).requiredOption("--slug <slug>", "Token slug").requiredOption("--concept <concept>", "Target concept text").requiredOption("--domain <domain>", "Token domain").requiredOption("--bloom-level <level>", "Bloom taxonomy level").requiredOption("--question <question>", "Question prompt presented").requiredOption("--user-answer <answer>", "User's typed answer").requiredOption("--message <text>", "The learner's newest discussion turn").option("--feedback <text>", "AI feedback already shown for this answer").option(
|
|
16444
|
+
"--thread <json>",
|
|
16445
|
+
'Prior turns, oldest first, as JSON: [{"role":"user"|"assistant","content":"\u2026"},\u2026]'
|
|
16446
|
+
).option("--context <context>", "Optional token context details").option("--source-link <link>", "Optional source link").option(
|
|
16447
|
+
"--source-content <content>",
|
|
16448
|
+
"Pre-resolved source reference content (skips re-fetch when set)"
|
|
16449
|
+
).action(async (opts) => {
|
|
16450
|
+
await withDb2(async (db) => {
|
|
16451
|
+
const isEnabled = await getSetting(db, "llm.enabled") === "true";
|
|
16452
|
+
if (!isEnabled) {
|
|
16453
|
+
jsonOut2({
|
|
16454
|
+
success: false,
|
|
16455
|
+
error: "LLM integration is disabled",
|
|
16456
|
+
reply: ""
|
|
16457
|
+
});
|
|
16458
|
+
return;
|
|
16459
|
+
}
|
|
16460
|
+
let thread;
|
|
16461
|
+
try {
|
|
16462
|
+
thread = parseDiscussionThread(opts.thread);
|
|
16463
|
+
} catch (err) {
|
|
16464
|
+
jsonOut2({
|
|
16465
|
+
success: false,
|
|
16466
|
+
error: err.message,
|
|
16467
|
+
reply: ""
|
|
16468
|
+
});
|
|
16469
|
+
return;
|
|
16470
|
+
}
|
|
16471
|
+
let resolvedContextContent = opts.sourceContent ?? null;
|
|
16472
|
+
if (resolvedContextContent == null && opts.sourceLink) {
|
|
16473
|
+
try {
|
|
16474
|
+
const resolved = await resolveReviewContext(opts.sourceLink);
|
|
16475
|
+
resolvedContextContent = resolved?.content ?? null;
|
|
16476
|
+
} catch {
|
|
16477
|
+
}
|
|
16478
|
+
}
|
|
16479
|
+
try {
|
|
16480
|
+
const result = await discussReviewViaLLM(db, {
|
|
16481
|
+
slug: opts.slug,
|
|
16482
|
+
concept: opts.concept,
|
|
16483
|
+
domain: opts.domain,
|
|
16484
|
+
bloomLevel: Number(opts.bloomLevel),
|
|
16485
|
+
context: opts.context,
|
|
16486
|
+
question: opts.question,
|
|
16487
|
+
userAnswer: opts.userAnswer,
|
|
16488
|
+
sourceLinkContent: resolvedContextContent,
|
|
16489
|
+
feedback: opts.feedback ?? null,
|
|
16490
|
+
thread,
|
|
16491
|
+
message: opts.message
|
|
16492
|
+
});
|
|
16493
|
+
jsonOut2({
|
|
16494
|
+
success: true,
|
|
16495
|
+
reply: result.text,
|
|
16496
|
+
replyModel: result.model
|
|
16497
|
+
});
|
|
16498
|
+
} catch (err) {
|
|
16499
|
+
jsonOut2({
|
|
16500
|
+
success: false,
|
|
16501
|
+
error: err.message,
|
|
16502
|
+
reply: ""
|
|
16503
|
+
});
|
|
16504
|
+
}
|
|
16505
|
+
});
|
|
16506
|
+
});
|
|
15934
16507
|
bridgeCommand.command("desktop-bootstrap").description("Initialize first-run desktop state (JSON)").option("--user <id>", "Preferred user ID when none is configured").action(async (opts) => {
|
|
15935
16508
|
await withDb2(async (db) => {
|
|
15936
16509
|
const userId = await ensureDefaultUser(db, opts.user);
|
|
@@ -15959,6 +16532,54 @@ bridgeCommand.command("get-settings").description("Get active ZAM settings (JSON
|
|
|
15959
16532
|
});
|
|
15960
16533
|
});
|
|
15961
16534
|
});
|
|
16535
|
+
bridgeCommand.command("agent-harness-status").description(
|
|
16536
|
+
"Detect installed agent harnesses and their ZAM MCP configuration state (JSON)"
|
|
16537
|
+
).action(() => {
|
|
16538
|
+
const report = inspectConnectHarnesses();
|
|
16539
|
+
jsonOut2({
|
|
16540
|
+
success: true,
|
|
16541
|
+
zamOnPath: report.zamOnPath,
|
|
16542
|
+
harnesses: report.harnesses
|
|
16543
|
+
});
|
|
16544
|
+
});
|
|
16545
|
+
bridgeCommand.command("agent-connect").description(
|
|
16546
|
+
"Run the idempotent agent-connect flow for one or all detected harnesses (JSON)"
|
|
16547
|
+
).option("--harness <id>", "Explicit harness id (default: all detected)").option(
|
|
16548
|
+
"--auto-once",
|
|
16549
|
+
"First-run mode: skip when the auto-connect marker is already set; set the marker after a run that detected at least one harness"
|
|
16550
|
+
).action(async (opts) => {
|
|
16551
|
+
if (opts.harness && !isConnectHarnessId(opts.harness)) {
|
|
16552
|
+
jsonOut2({
|
|
16553
|
+
success: false,
|
|
16554
|
+
error: `Unsupported harness: ${opts.harness}`
|
|
16555
|
+
});
|
|
16556
|
+
return;
|
|
16557
|
+
}
|
|
16558
|
+
const harness = opts.harness;
|
|
16559
|
+
const run = () => {
|
|
16560
|
+
const report = performAgentConnect({ harness });
|
|
16561
|
+
return {
|
|
16562
|
+
success: report.success,
|
|
16563
|
+
detected: report.detected,
|
|
16564
|
+
zamOnPath: report.zamOnPath,
|
|
16565
|
+
results: report.results.map(({ content: _content, ...rest }) => rest),
|
|
16566
|
+
skills: report.skills
|
|
16567
|
+
};
|
|
16568
|
+
};
|
|
16569
|
+
if (opts.autoOnce) {
|
|
16570
|
+
if (getAgentConnectAutoDone()) {
|
|
16571
|
+
jsonOut2({ success: true, skipped: true });
|
|
16572
|
+
return;
|
|
16573
|
+
}
|
|
16574
|
+
const payload = run();
|
|
16575
|
+
if (payload.detected.length > 0) {
|
|
16576
|
+
setAgentConnectAutoDone(true);
|
|
16577
|
+
}
|
|
16578
|
+
jsonOut2(payload);
|
|
16579
|
+
return;
|
|
16580
|
+
}
|
|
16581
|
+
jsonOut2(run());
|
|
16582
|
+
});
|
|
15962
16583
|
async function readDatabaseUserSummaries(db) {
|
|
15963
16584
|
return await db.prepare(
|
|
15964
16585
|
`SELECT user_id AS id, COUNT(*) AS cardCount
|
|
@@ -18341,26 +18962,26 @@ var doctorCommand = new Command5("doctor").description(
|
|
|
18341
18962
|
// src/cli/commands/git-sync.ts
|
|
18342
18963
|
init_kernel();
|
|
18343
18964
|
import { execSync as execSync5 } from "child_process";
|
|
18344
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
18345
|
-
import { join as
|
|
18965
|
+
import { chmodSync as chmodSync2, existsSync as existsSync20, writeFileSync as writeFileSync12 } from "fs";
|
|
18966
|
+
import { join as join23 } from "path";
|
|
18346
18967
|
import { Command as Command6 } from "commander";
|
|
18347
18968
|
function installHook2() {
|
|
18348
|
-
const gitDir =
|
|
18349
|
-
if (!
|
|
18969
|
+
const gitDir = join23(process.cwd(), ".git");
|
|
18970
|
+
if (!existsSync20(gitDir)) {
|
|
18350
18971
|
console.error(
|
|
18351
18972
|
"Error: Current directory is not the root of a Git repository."
|
|
18352
18973
|
);
|
|
18353
18974
|
process.exit(1);
|
|
18354
18975
|
}
|
|
18355
|
-
const hooksDir =
|
|
18356
|
-
const hookPath =
|
|
18976
|
+
const hooksDir = join23(gitDir, "hooks");
|
|
18977
|
+
const hookPath = join23(hooksDir, "post-commit");
|
|
18357
18978
|
const hookContent = `#!/bin/sh
|
|
18358
18979
|
# ZAM Spaced Repetition Auto-Stale Hook
|
|
18359
18980
|
# Triggered automatically on git commits to decay modified concept cards.
|
|
18360
18981
|
zam git-sync --commit HEAD --quiet
|
|
18361
18982
|
`;
|
|
18362
18983
|
try {
|
|
18363
|
-
|
|
18984
|
+
writeFileSync12(hookPath, hookContent, { encoding: "utf-8", flag: "w" });
|
|
18364
18985
|
try {
|
|
18365
18986
|
chmodSync2(hookPath, "755");
|
|
18366
18987
|
} catch (_e) {
|
|
@@ -18463,7 +19084,7 @@ ZAM Auto-Stale Complete: Scanned ${changedFiles.length} file(s).`
|
|
|
18463
19084
|
|
|
18464
19085
|
// src/cli/commands/goal.ts
|
|
18465
19086
|
init_kernel();
|
|
18466
|
-
import { existsSync as
|
|
19087
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync15 } from "fs";
|
|
18467
19088
|
import { resolve as resolve6 } from "path";
|
|
18468
19089
|
import { input as input2 } from "@inquirer/prompts";
|
|
18469
19090
|
import { Command as Command7 } from "commander";
|
|
@@ -18487,7 +19108,7 @@ goalCommand.command("list").description("List all goals").option(
|
|
|
18487
19108
|
"Filter by status (active, completed, paused, abandoned)"
|
|
18488
19109
|
).option("--tree", "Show goals as a tree with parent/child relationships").option("--json", "Output as JSON").action(async (opts) => {
|
|
18489
19110
|
const goalsDir = await resolveGoalsDir();
|
|
18490
|
-
if (!
|
|
19111
|
+
if (!existsSync21(goalsDir)) {
|
|
18491
19112
|
console.error(`Goals directory not found: ${goalsDir}`);
|
|
18492
19113
|
console.error(
|
|
18493
19114
|
"Set it with: zam settings set personal.goals_dir /path/to/goals"
|
|
@@ -18588,8 +19209,8 @@ ${"\u2500".repeat(50)}`);
|
|
|
18588
19209
|
});
|
|
18589
19210
|
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) => {
|
|
18590
19211
|
const goalsDir = await resolveGoalsDir();
|
|
18591
|
-
if (!
|
|
18592
|
-
|
|
19212
|
+
if (!existsSync21(goalsDir)) {
|
|
19213
|
+
mkdirSync15(goalsDir, { recursive: true });
|
|
18593
19214
|
}
|
|
18594
19215
|
let slug = opts.slug;
|
|
18595
19216
|
let title = opts.title;
|
|
@@ -18649,22 +19270,22 @@ goalCommand.command("status <slug> <status>").description("Update a goal's statu
|
|
|
18649
19270
|
|
|
18650
19271
|
// src/cli/commands/init.ts
|
|
18651
19272
|
init_kernel();
|
|
18652
|
-
import { existsSync as
|
|
18653
|
-
import { homedir as
|
|
18654
|
-
import { join as
|
|
19273
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync16, writeFileSync as writeFileSync13 } from "fs";
|
|
19274
|
+
import { homedir as homedir14 } from "os";
|
|
19275
|
+
import { join as join24, resolve as resolve7 } from "path";
|
|
18655
19276
|
import { confirm, input as input3 } from "@inquirer/prompts";
|
|
18656
19277
|
import { Command as Command8 } from "commander";
|
|
18657
|
-
var HOME2 =
|
|
19278
|
+
var HOME2 = homedir14();
|
|
18658
19279
|
function printLine(char = "\u2550", len = 60, color = "\x1B[36m") {
|
|
18659
19280
|
console.log(`${color}${char.repeat(len)}\x1B[0m`);
|
|
18660
19281
|
}
|
|
18661
19282
|
function bootstrapSandboxWorkspace(workspaceDir) {
|
|
18662
|
-
|
|
18663
|
-
|
|
18664
|
-
|
|
18665
|
-
const worldviewFile =
|
|
18666
|
-
if (!
|
|
18667
|
-
|
|
19283
|
+
mkdirSync16(join24(workspaceDir, "beliefs"), { recursive: true });
|
|
19284
|
+
mkdirSync16(join24(workspaceDir, "goals"), { recursive: true });
|
|
19285
|
+
mkdirSync16(join24(workspaceDir, "skills"), { recursive: true });
|
|
19286
|
+
const worldviewFile = join24(workspaceDir, "beliefs", "worldview.md");
|
|
19287
|
+
if (!existsSync22(worldviewFile)) {
|
|
19288
|
+
writeFileSync13(
|
|
18668
19289
|
worldviewFile,
|
|
18669
19290
|
`# Personal Worldview
|
|
18670
19291
|
|
|
@@ -18676,9 +19297,9 @@ Here, I declare the core concepts and principles I want to master.
|
|
|
18676
19297
|
"utf8"
|
|
18677
19298
|
);
|
|
18678
19299
|
}
|
|
18679
|
-
const goalsFile =
|
|
18680
|
-
if (!
|
|
18681
|
-
|
|
19300
|
+
const goalsFile = join24(workspaceDir, "goals", "goals.md");
|
|
19301
|
+
if (!existsSync22(goalsFile)) {
|
|
19302
|
+
writeFileSync13(
|
|
18682
19303
|
goalsFile,
|
|
18683
19304
|
`# Personal Goals
|
|
18684
19305
|
|
|
@@ -18700,7 +19321,7 @@ var initCommand = new Command8("init").description("Launch the guided interactiv
|
|
|
18700
19321
|
);
|
|
18701
19322
|
printLine();
|
|
18702
19323
|
console.log("\n\x1B[1m[1/5] Setting up Local Workspace Sandbox\x1B[0m");
|
|
18703
|
-
const defaultWorkspace =
|
|
19324
|
+
const defaultWorkspace = join24(HOME2, "Documents", "zam");
|
|
18704
19325
|
const workspacePath = resolve7(
|
|
18705
19326
|
await input3({
|
|
18706
19327
|
message: "Choose your ZAM workspace directory:",
|
|
@@ -19902,7 +20523,7 @@ observerCommand.command("revoke <process>").description("Remove a process from o
|
|
|
19902
20523
|
|
|
19903
20524
|
// src/cli/commands/profile.ts
|
|
19904
20525
|
init_kernel();
|
|
19905
|
-
import { dirname as
|
|
20526
|
+
import { dirname as dirname10, resolve as resolve8 } from "path";
|
|
19906
20527
|
import { Command as Command13 } from "commander";
|
|
19907
20528
|
var C2 = {
|
|
19908
20529
|
reset: "\x1B[0m",
|
|
@@ -19943,7 +20564,7 @@ var profileCommand = new Command13("profile").description("Show or change this m
|
|
|
19943
20564
|
mode: getInstallMode(),
|
|
19944
20565
|
personalDir,
|
|
19945
20566
|
syncProvider: detectSyncProvider(personalDir),
|
|
19946
|
-
dataDir:
|
|
20567
|
+
dataDir: dirname10(dbPath),
|
|
19947
20568
|
dbPath
|
|
19948
20569
|
};
|
|
19949
20570
|
if (opts.json) {
|
|
@@ -20332,7 +20953,7 @@ ${"\u2550".repeat(50)}`);
|
|
|
20332
20953
|
|
|
20333
20954
|
// src/cli/commands/session.ts
|
|
20334
20955
|
init_kernel();
|
|
20335
|
-
import { readFileSync as
|
|
20956
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
20336
20957
|
import { input as input6, select as select2 } from "@inquirer/prompts";
|
|
20337
20958
|
import { Command as Command16 } from "commander";
|
|
20338
20959
|
var sessionCommand = new Command16("session").description(
|
|
@@ -20523,7 +21144,7 @@ function loadPatternFile(path) {
|
|
|
20523
21144
|
if (!path) return [];
|
|
20524
21145
|
let parsed;
|
|
20525
21146
|
try {
|
|
20526
|
-
parsed = JSON.parse(
|
|
21147
|
+
parsed = JSON.parse(readFileSync17(path, "utf-8"));
|
|
20527
21148
|
} catch (err) {
|
|
20528
21149
|
throw new Error(
|
|
20529
21150
|
`Cannot read synthesis patterns from ${path}: ${err.message}`
|
|
@@ -20714,7 +21335,7 @@ sessionCommand.command("end").description("End a session and show summary").requ
|
|
|
20714
21335
|
|
|
20715
21336
|
// src/cli/commands/settings.ts
|
|
20716
21337
|
init_kernel();
|
|
20717
|
-
import { existsSync as
|
|
21338
|
+
import { existsSync as existsSync23 } from "fs";
|
|
20718
21339
|
import { Command as Command17 } from "commander";
|
|
20719
21340
|
var settingsCommand = new Command17("settings").description(
|
|
20720
21341
|
"Manage user settings"
|
|
@@ -20879,7 +21500,7 @@ settingsCommand.command("repos").description("Show or set Personal, Team, and Or
|
|
|
20879
21500
|
console.log("\nValidation:");
|
|
20880
21501
|
for (const [name, path] of Object.entries(paths)) {
|
|
20881
21502
|
if (path) {
|
|
20882
|
-
const exists =
|
|
21503
|
+
const exists = existsSync23(path);
|
|
20883
21504
|
console.log(
|
|
20884
21505
|
` ${name.padEnd(9)}: ${exists ? "\x1B[32m\u2713 Valid folder\x1B[0m" : "\x1B[31m\u2717 Directory does not exist\x1B[0m"}`
|
|
20885
21506
|
);
|
|
@@ -20934,9 +21555,49 @@ async function activateMachineProviderConfig(db) {
|
|
|
20934
21555
|
` activate ${providerCount} machine-local provider(s) from ~/.zam/config.json`
|
|
20935
21556
|
);
|
|
20936
21557
|
}
|
|
21558
|
+
function connectDetectedAgentHarnesses(skip, dryRun) {
|
|
21559
|
+
if (skip) return;
|
|
21560
|
+
try {
|
|
21561
|
+
const report = performAgentConnect({ dryRun });
|
|
21562
|
+
if (report.detected.length === 0) {
|
|
21563
|
+
console.log(
|
|
21564
|
+
" skip no supported agent harness detected (zam agent connect <harness> configures one explicitly)"
|
|
21565
|
+
);
|
|
21566
|
+
return;
|
|
21567
|
+
}
|
|
21568
|
+
for (const result of report.results) {
|
|
21569
|
+
if (result.error) {
|
|
21570
|
+
console.warn(` warn ${result.harness}: ${result.error}`);
|
|
21571
|
+
} else if (dryRun) {
|
|
21572
|
+
console.log(
|
|
21573
|
+
` plan ${result.harness}: would ensure MCP config at ${result.path}`
|
|
21574
|
+
);
|
|
21575
|
+
} else if (result.alreadyConfigured) {
|
|
21576
|
+
console.log(
|
|
21577
|
+
` skip ${result.harness}: MCP already configured (${result.path})`
|
|
21578
|
+
);
|
|
21579
|
+
} else {
|
|
21580
|
+
console.log(
|
|
21581
|
+
` wire ${result.harness}: MCP configured (${result.path})`
|
|
21582
|
+
);
|
|
21583
|
+
}
|
|
21584
|
+
}
|
|
21585
|
+
if (report.skills) {
|
|
21586
|
+
console.log(
|
|
21587
|
+
` wire global ZAM skill: ${report.skills.refreshed}/${report.skills.total} locations`
|
|
21588
|
+
);
|
|
21589
|
+
}
|
|
21590
|
+
} catch (err) {
|
|
21591
|
+
console.warn(` warn agent connect: ${err.message}`);
|
|
21592
|
+
}
|
|
21593
|
+
}
|
|
20937
21594
|
var setupCommand = new Command18("setup").description(
|
|
20938
21595
|
"Link ZAM skill directories into this workspace and initialize the database"
|
|
20939
|
-
).option("--force", "replace an unmanaged existing ZAM skill directory", false).option("--skip-init", "skip database initialization", false).option("--skip-claude-md", "skip CLAUDE.md generation", false).option("--skip-agents-md", "skip AGENTS.md generation", false).option(
|
|
21596
|
+
).option("--force", "replace an unmanaged existing ZAM skill directory", false).option("--skip-init", "skip database initialization", false).option("--skip-claude-md", "skip CLAUDE.md generation", false).option("--skip-agents-md", "skip AGENTS.md generation", false).option(
|
|
21597
|
+
"--skip-agent-connect",
|
|
21598
|
+
"skip wiring detected agent harnesses via MCP",
|
|
21599
|
+
false
|
|
21600
|
+
).option("--target <path>", "repository/workspace directory to set up").option(
|
|
20940
21601
|
"--agents <list>",
|
|
20941
21602
|
"comma-separated agents to wire: all, claude, copilot, codex, agent"
|
|
20942
21603
|
).option(
|
|
@@ -20963,6 +21624,7 @@ var setupCommand = new Command18("setup").description(
|
|
|
20963
21624
|
dryRun: opts.dryRun
|
|
20964
21625
|
});
|
|
20965
21626
|
await initDatabase(opts.skipInit || opts.dryRun);
|
|
21627
|
+
connectDetectedAgentHarnesses(opts.skipAgentConnect, opts.dryRun);
|
|
20966
21628
|
if (agents.has("claude")) {
|
|
20967
21629
|
writeClaudeMd(opts.skipClaudeMd, target, {
|
|
20968
21630
|
dryRun: opts.dryRun,
|
|
@@ -21076,8 +21738,8 @@ skillCommand.command("add").description("Register a new agent skill").requiredOp
|
|
|
21076
21738
|
|
|
21077
21739
|
// src/cli/commands/snapshot.ts
|
|
21078
21740
|
init_kernel();
|
|
21079
|
-
import { existsSync as
|
|
21080
|
-
import { dirname as
|
|
21741
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync17, readFileSync as readFileSync18, writeFileSync as writeFileSync14 } from "fs";
|
|
21742
|
+
import { dirname as dirname11, join as join25 } from "path";
|
|
21081
21743
|
import { Command as Command20 } from "commander";
|
|
21082
21744
|
function defaultOutName() {
|
|
21083
21745
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+Z$/, "").replace(/:/g, "-");
|
|
@@ -21106,12 +21768,12 @@ var exportCmd = new Command20("export").description("Write a portable SQL-text s
|
|
|
21106
21768
|
process.stdout.write(snapshot);
|
|
21107
21769
|
return;
|
|
21108
21770
|
}
|
|
21109
|
-
const out = opts.out ??
|
|
21110
|
-
const dir =
|
|
21111
|
-
if (dir && dir !== "." && !
|
|
21112
|
-
|
|
21771
|
+
const out = opts.out ?? join25(personalDir, "snapshots", defaultOutName());
|
|
21772
|
+
const dir = dirname11(out);
|
|
21773
|
+
if (dir && dir !== "." && !existsSync24(dir)) {
|
|
21774
|
+
mkdirSync17(dir, { recursive: true });
|
|
21113
21775
|
}
|
|
21114
|
-
|
|
21776
|
+
writeFileSync14(out, snapshot, "utf-8");
|
|
21115
21777
|
console.log(`Snapshot written: ${out}`);
|
|
21116
21778
|
console.log(
|
|
21117
21779
|
` ${total} row(s)${nonEmpty.length ? ` \u2014 ${nonEmpty.join(", ")}` : ""}`
|
|
@@ -21125,11 +21787,11 @@ var exportCmd = new Command20("export").description("Write a portable SQL-text s
|
|
|
21125
21787
|
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) => {
|
|
21126
21788
|
let db;
|
|
21127
21789
|
try {
|
|
21128
|
-
if (!
|
|
21790
|
+
if (!existsSync24(file)) {
|
|
21129
21791
|
console.error(`Error: Snapshot file not found: ${file}`);
|
|
21130
21792
|
process.exit(1);
|
|
21131
21793
|
}
|
|
21132
|
-
const snapshot =
|
|
21794
|
+
const snapshot = readFileSync18(file, "utf-8");
|
|
21133
21795
|
db = await openDatabaseWithSync({ initialize: true });
|
|
21134
21796
|
const result = await importSnapshot(db, snapshot, { force: opts.force });
|
|
21135
21797
|
await db.close();
|
|
@@ -21147,11 +21809,11 @@ var importCmd = new Command20("import").description("Restore a snapshot into the
|
|
|
21147
21809
|
});
|
|
21148
21810
|
var verifyCmd = new Command20("verify").description("Check a snapshot's manifest and checksum without importing").argument("<file>", "Snapshot file to verify").action((file) => {
|
|
21149
21811
|
try {
|
|
21150
|
-
if (!
|
|
21812
|
+
if (!existsSync24(file)) {
|
|
21151
21813
|
console.error(`Error: Snapshot file not found: ${file}`);
|
|
21152
21814
|
process.exit(1);
|
|
21153
21815
|
}
|
|
21154
|
-
const manifest = verifySnapshot(
|
|
21816
|
+
const manifest = verifySnapshot(readFileSync18(file, "utf-8"));
|
|
21155
21817
|
const { total, nonEmpty } = summarize(manifest.tables);
|
|
21156
21818
|
console.log(`Valid snapshot (format v${manifest.version})`);
|
|
21157
21819
|
console.log(` created: ${manifest.createdAt}`);
|
|
@@ -21700,10 +22362,10 @@ tokenCommand.command("reembed").description("Backfill or refresh semantic-search
|
|
|
21700
22362
|
// src/cli/commands/ui.ts
|
|
21701
22363
|
init_kernel();
|
|
21702
22364
|
import { spawn as spawn3, spawnSync } from "child_process";
|
|
21703
|
-
import { existsSync as
|
|
21704
|
-
import { homedir as
|
|
21705
|
-
import { dirname as
|
|
21706
|
-
import { fileURLToPath as
|
|
22365
|
+
import { existsSync as existsSync25 } from "fs";
|
|
22366
|
+
import { homedir as homedir15 } from "os";
|
|
22367
|
+
import { dirname as dirname12, join as join26 } from "path";
|
|
22368
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
21707
22369
|
import { Command as Command23 } from "commander";
|
|
21708
22370
|
var C3 = {
|
|
21709
22371
|
reset: "\x1B[0m",
|
|
@@ -21714,14 +22376,14 @@ var C3 = {
|
|
|
21714
22376
|
dim: "\x1B[2m"
|
|
21715
22377
|
};
|
|
21716
22378
|
function findDesktopDir() {
|
|
21717
|
-
const starts = [process.cwd(),
|
|
22379
|
+
const starts = [process.cwd(), dirname12(fileURLToPath6(import.meta.url))];
|
|
21718
22380
|
for (const start of starts) {
|
|
21719
22381
|
let dir = start;
|
|
21720
22382
|
for (let i = 0; i < 10; i++) {
|
|
21721
|
-
if (
|
|
21722
|
-
return
|
|
22383
|
+
if (existsSync25(join26(dir, "desktop", "src-tauri", "tauri.conf.json"))) {
|
|
22384
|
+
return join26(dir, "desktop");
|
|
21723
22385
|
}
|
|
21724
|
-
const parent =
|
|
22386
|
+
const parent = dirname12(dir);
|
|
21725
22387
|
if (parent === dir) break;
|
|
21726
22388
|
dir = parent;
|
|
21727
22389
|
}
|
|
@@ -21729,30 +22391,30 @@ function findDesktopDir() {
|
|
|
21729
22391
|
return null;
|
|
21730
22392
|
}
|
|
21731
22393
|
function findBuiltApp(desktopDir) {
|
|
21732
|
-
const releaseDir =
|
|
22394
|
+
const releaseDir = join26(desktopDir, "src-tauri", "target", "release");
|
|
21733
22395
|
if (process.platform === "win32") {
|
|
21734
22396
|
for (const name of ["ZAM.exe", "zam.exe", "zam-desktop.exe"]) {
|
|
21735
|
-
const p =
|
|
21736
|
-
if (
|
|
22397
|
+
const p = join26(releaseDir, name);
|
|
22398
|
+
if (existsSync25(p)) return p;
|
|
21737
22399
|
}
|
|
21738
22400
|
} else if (process.platform === "darwin") {
|
|
21739
|
-
const app =
|
|
21740
|
-
if (
|
|
22401
|
+
const app = join26(releaseDir, "bundle", "macos", "ZAM.app");
|
|
22402
|
+
if (existsSync25(app)) return app;
|
|
21741
22403
|
} else {
|
|
21742
22404
|
for (const name of ["zam", "ZAM", "zam-desktop"]) {
|
|
21743
|
-
const p =
|
|
21744
|
-
if (
|
|
22405
|
+
const p = join26(releaseDir, name);
|
|
22406
|
+
if (existsSync25(p)) return p;
|
|
21745
22407
|
}
|
|
21746
22408
|
}
|
|
21747
22409
|
return null;
|
|
21748
22410
|
}
|
|
21749
22411
|
function findInstalledApp() {
|
|
21750
22412
|
const candidates = process.platform === "win32" ? [
|
|
21751
|
-
process.env.LOCALAPPDATA &&
|
|
21752
|
-
process.env.ProgramFiles &&
|
|
21753
|
-
process.env["ProgramFiles(x86)"] &&
|
|
21754
|
-
] : process.platform === "darwin" ? ["/Applications/ZAM.app",
|
|
21755
|
-
return candidates.find((candidate) => candidate &&
|
|
22413
|
+
process.env.LOCALAPPDATA && join26(process.env.LOCALAPPDATA, "Programs", "ZAM", "ZAM.exe"),
|
|
22414
|
+
process.env.ProgramFiles && join26(process.env.ProgramFiles, "ZAM", "ZAM.exe"),
|
|
22415
|
+
process.env["ProgramFiles(x86)"] && join26(process.env["ProgramFiles(x86)"], "ZAM", "ZAM.exe")
|
|
22416
|
+
] : process.platform === "darwin" ? ["/Applications/ZAM.app", join26(homedir15(), "Applications", "ZAM.app")] : ["/opt/ZAM/zam", "/usr/bin/zam-desktop"];
|
|
22417
|
+
return candidates.find((candidate) => candidate && existsSync25(candidate)) || null;
|
|
21756
22418
|
}
|
|
21757
22419
|
function runNpm(args, opts) {
|
|
21758
22420
|
const res = spawnSync("npm", args, {
|
|
@@ -21763,7 +22425,7 @@ function runNpm(args, opts) {
|
|
|
21763
22425
|
return res.status ?? 1;
|
|
21764
22426
|
}
|
|
21765
22427
|
function ensureDesktopDeps(desktopDir) {
|
|
21766
|
-
if (
|
|
22428
|
+
if (existsSync25(join26(desktopDir, "node_modules"))) return true;
|
|
21767
22429
|
console.log(
|
|
21768
22430
|
`${C3.cyan}Installing desktop dependencies (one-time)...${C3.reset}`
|
|
21769
22431
|
);
|
|
@@ -21789,13 +22451,13 @@ function requireRust() {
|
|
|
21789
22451
|
function hasMsvcBuildTools() {
|
|
21790
22452
|
if (process.platform !== "win32") return true;
|
|
21791
22453
|
const pf86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
|
21792
|
-
const vswhere =
|
|
22454
|
+
const vswhere = join26(
|
|
21793
22455
|
pf86,
|
|
21794
22456
|
"Microsoft Visual Studio",
|
|
21795
22457
|
"Installer",
|
|
21796
22458
|
"vswhere.exe"
|
|
21797
22459
|
);
|
|
21798
|
-
if (!
|
|
22460
|
+
if (!existsSync25(vswhere)) return false;
|
|
21799
22461
|
const res = spawnSync(
|
|
21800
22462
|
vswhere,
|
|
21801
22463
|
[
|
|
@@ -21827,7 +22489,7 @@ function requireMsvcOnWindows() {
|
|
|
21827
22489
|
return false;
|
|
21828
22490
|
}
|
|
21829
22491
|
function warnIfCliMissing(repoRoot) {
|
|
21830
|
-
if (!
|
|
22492
|
+
if (!existsSync25(join26(repoRoot, "dist", "cli", "index.js"))) {
|
|
21831
22493
|
console.warn(
|
|
21832
22494
|
`${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}`
|
|
21833
22495
|
);
|
|
@@ -21890,7 +22552,7 @@ var uiCommand = new Command23("ui").description("Launch the ZAM Desktop GUI (Act
|
|
|
21890
22552
|
).option("--shortcut", "Create Desktop + Start-menu shortcuts to the GUI").action((opts) => {
|
|
21891
22553
|
const installedApp = findInstalledApp();
|
|
21892
22554
|
if (!opts.dev && !opts.build && !opts.shortcut && installedApp) {
|
|
21893
|
-
launchApp(installedApp,
|
|
22555
|
+
launchApp(installedApp, homedir15());
|
|
21894
22556
|
return;
|
|
21895
22557
|
}
|
|
21896
22558
|
const desktopDir = findDesktopDir();
|
|
@@ -21900,7 +22562,7 @@ var uiCommand = new Command23("ui").description("Launch the ZAM Desktop GUI (Act
|
|
|
21900
22562
|
);
|
|
21901
22563
|
process.exit(1);
|
|
21902
22564
|
}
|
|
21903
|
-
const repoRoot =
|
|
22565
|
+
const repoRoot = dirname12(desktopDir);
|
|
21904
22566
|
if (opts.build) {
|
|
21905
22567
|
if (!requireRust()) process.exit(1);
|
|
21906
22568
|
if (!requireMsvcOnWindows()) process.exit(1);
|
|
@@ -21911,7 +22573,7 @@ var uiCommand = new Command23("ui").description("Launch the ZAM Desktop GUI (Act
|
|
|
21911
22573
|
);
|
|
21912
22574
|
const code = runNpm(["run", "tauri", "build"], { cwd: desktopDir });
|
|
21913
22575
|
if (code === 0) {
|
|
21914
|
-
const bundle =
|
|
22576
|
+
const bundle = join26(
|
|
21915
22577
|
desktopDir,
|
|
21916
22578
|
"src-tauri",
|
|
21917
22579
|
"target",
|
|
@@ -21951,7 +22613,7 @@ ${C3.green}\u2713 Done. Installer is in:${C3.reset}
|
|
|
21951
22613
|
);
|
|
21952
22614
|
process.exit(1);
|
|
21953
22615
|
}
|
|
21954
|
-
createShortcuts(shortcutTarget,
|
|
22616
|
+
createShortcuts(shortcutTarget, dirname12(shortcutTarget));
|
|
21955
22617
|
return;
|
|
21956
22618
|
}
|
|
21957
22619
|
if (builtApp) {
|
|
@@ -21989,9 +22651,9 @@ ${C3.dim}After that, 'zam ui' launches it directly.${C3.reset}`
|
|
|
21989
22651
|
// src/cli/commands/update.ts
|
|
21990
22652
|
init_kernel();
|
|
21991
22653
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
21992
|
-
import { existsSync as
|
|
21993
|
-
import { dirname as
|
|
21994
|
-
import { fileURLToPath as
|
|
22654
|
+
import { existsSync as existsSync26, readFileSync as readFileSync19, realpathSync as realpathSync2 } from "fs";
|
|
22655
|
+
import { dirname as dirname13, join as join27 } from "path";
|
|
22656
|
+
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
21995
22657
|
import { confirm as confirm3 } from "@inquirer/prompts";
|
|
21996
22658
|
import { Command as Command24 } from "commander";
|
|
21997
22659
|
var CHANNELS = [
|
|
@@ -22012,7 +22674,7 @@ var C4 = {
|
|
|
22012
22674
|
function versionAt(dir) {
|
|
22013
22675
|
try {
|
|
22014
22676
|
const pkg2 = JSON.parse(
|
|
22015
|
-
|
|
22677
|
+
readFileSync19(join27(dir, "package.json"), "utf-8")
|
|
22016
22678
|
);
|
|
22017
22679
|
return pkg2.version ?? "unknown";
|
|
22018
22680
|
} catch {
|
|
@@ -22070,14 +22732,14 @@ var checkCmd = new Command24("check").description("Check whether a newer ZAM has
|
|
|
22070
22732
|
}
|
|
22071
22733
|
);
|
|
22072
22734
|
function findSourceRepo() {
|
|
22073
|
-
let dir = realpathSync2(
|
|
22074
|
-
let parent =
|
|
22735
|
+
let dir = realpathSync2(dirname13(fileURLToPath7(import.meta.url)));
|
|
22736
|
+
let parent = dirname13(dir);
|
|
22075
22737
|
while (parent !== dir) {
|
|
22076
|
-
if (
|
|
22738
|
+
if (existsSync26(join27(dir, ".git"))) return dir;
|
|
22077
22739
|
dir = parent;
|
|
22078
|
-
parent =
|
|
22740
|
+
parent = dirname13(dir);
|
|
22079
22741
|
}
|
|
22080
|
-
return
|
|
22742
|
+
return existsSync26(join27(dir, ".git")) ? dir : null;
|
|
22081
22743
|
}
|
|
22082
22744
|
function runGit(cwd, args, capture) {
|
|
22083
22745
|
const res = spawnSync2("git", args, {
|
|
@@ -22098,7 +22760,7 @@ function runNpm2(args, cwd) {
|
|
|
22098
22760
|
function smokeTestBuild(src) {
|
|
22099
22761
|
const res = spawnSync2(
|
|
22100
22762
|
process.execPath,
|
|
22101
|
-
[
|
|
22763
|
+
[join27(src, "dist", "cli", "index.js"), "--version"],
|
|
22102
22764
|
{
|
|
22103
22765
|
cwd: src,
|
|
22104
22766
|
encoding: "utf8",
|
|
@@ -22178,7 +22840,7 @@ Fix it manually in ${src} (try \`npm ci && npm run build\`, and check your Node
|
|
|
22178
22840
|
console.log(`${C4.dim}\u2192 zam setup --force${C4.reset}`);
|
|
22179
22841
|
const setup = spawnSync2(
|
|
22180
22842
|
process.execPath,
|
|
22181
|
-
[
|
|
22843
|
+
[join27(src, "dist", "cli", "index.js"), "setup", "--force"],
|
|
22182
22844
|
{ cwd: process.cwd(), stdio: "inherit" }
|
|
22183
22845
|
);
|
|
22184
22846
|
if (setup.status !== 0) {
|
|
@@ -22290,15 +22952,15 @@ var whoamiCommand = new Command25("whoami").description("Show or set the default
|
|
|
22290
22952
|
|
|
22291
22953
|
// src/cli/commands/workspace.ts
|
|
22292
22954
|
init_kernel();
|
|
22293
|
-
import { execFileSync as
|
|
22294
|
-
import { existsSync as
|
|
22295
|
-
import { homedir as
|
|
22296
|
-
import { join as
|
|
22955
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
22956
|
+
import { existsSync as existsSync27, writeFileSync as writeFileSync15 } from "fs";
|
|
22957
|
+
import { homedir as homedir16 } from "os";
|
|
22958
|
+
import { join as join28, resolve as resolve10 } from "path";
|
|
22297
22959
|
import { confirm as confirm4, input as input7 } from "@inquirer/prompts";
|
|
22298
22960
|
import { Command as Command26 } from "commander";
|
|
22299
22961
|
function runGit2(cwd, args) {
|
|
22300
22962
|
try {
|
|
22301
|
-
return
|
|
22963
|
+
return execFileSync5("git", args, {
|
|
22302
22964
|
cwd,
|
|
22303
22965
|
stdio: "pipe",
|
|
22304
22966
|
encoding: "utf8"
|
|
@@ -22377,7 +23039,7 @@ workspaceCommand.command("list").description("List configured ZAM workspaces").o
|
|
|
22377
23039
|
const workspaces = getConfiguredWorkspaces();
|
|
22378
23040
|
const agents = parseSetupAgents();
|
|
22379
23041
|
const linkHealth = Object.fromEntries(
|
|
22380
|
-
workspaces.filter((workspace) =>
|
|
23042
|
+
workspaces.filter((workspace) => existsSync27(workspace.path)).map((workspace) => [
|
|
22381
23043
|
workspace.id,
|
|
22382
23044
|
summarizeSkillLinkHealth(inspectSkillLinks(workspace.path, agents))
|
|
22383
23045
|
])
|
|
@@ -22420,7 +23082,7 @@ workspaceCommand.command("add <id>").description("Register an existing directory
|
|
|
22420
23082
|
).option("--scopes <list>", "Comma-separated knowledge scopes").option("--default-agent <id>", "Default agent harness for this workspace").action((id, opts) => {
|
|
22421
23083
|
try {
|
|
22422
23084
|
const path = resolve10(String(opts.path));
|
|
22423
|
-
if (!
|
|
23085
|
+
if (!existsSync27(path)) {
|
|
22424
23086
|
console.error(`Workspace path does not exist: ${path}`);
|
|
22425
23087
|
process.exit(1);
|
|
22426
23088
|
}
|
|
@@ -22514,7 +23176,7 @@ workspaceCommand.command("publish").description("Publish your local workspace sa
|
|
|
22514
23176
|
);
|
|
22515
23177
|
process.exit(1);
|
|
22516
23178
|
}
|
|
22517
|
-
if (!
|
|
23179
|
+
if (!existsSync27(workspaceDir)) {
|
|
22518
23180
|
console.error(
|
|
22519
23181
|
`\x1B[31m\u2717 Workspace directory does not exist: ${workspaceDir}\x1B[0m`
|
|
22520
23182
|
);
|
|
@@ -22528,15 +23190,15 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
|
|
|
22528
23190
|
);
|
|
22529
23191
|
process.exit(1);
|
|
22530
23192
|
}
|
|
22531
|
-
const gitignorePath =
|
|
22532
|
-
if (!
|
|
22533
|
-
|
|
23193
|
+
const gitignorePath = join28(workspaceDir, ".gitignore");
|
|
23194
|
+
if (!existsSync27(gitignorePath)) {
|
|
23195
|
+
writeFileSync15(
|
|
22534
23196
|
gitignorePath,
|
|
22535
23197
|
"node_modules/\n.agent/\n.agents/\n.claude/\n.gemini/\n.goose/\n*.log\n",
|
|
22536
23198
|
"utf8"
|
|
22537
23199
|
);
|
|
22538
23200
|
}
|
|
22539
|
-
const hasGitRepo =
|
|
23201
|
+
const hasGitRepo = existsSync27(join28(workspaceDir, ".git"));
|
|
22540
23202
|
if (!hasGitRepo) {
|
|
22541
23203
|
console.log("Initializing local Git repository...");
|
|
22542
23204
|
runGit2(workspaceDir, ["init", "-b", "main"]);
|
|
@@ -22568,7 +23230,7 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
|
|
|
22568
23230
|
if (proceedGh) {
|
|
22569
23231
|
try {
|
|
22570
23232
|
console.log(`Creating GitHub repository ${repoName}...`);
|
|
22571
|
-
|
|
23233
|
+
execFileSync5("gh", ghRepoCreateArgs(repoName, repoVisibility), {
|
|
22572
23234
|
cwd: workspaceDir,
|
|
22573
23235
|
stdio: "inherit"
|
|
22574
23236
|
});
|
|
@@ -22623,7 +23285,7 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
|
|
|
22623
23285
|
}
|
|
22624
23286
|
});
|
|
22625
23287
|
workspaceCommand.command("data-dir").description("Print the ZAM data directory (database, credentials, config)").option("--json", "Output as JSON").action((opts) => {
|
|
22626
|
-
const dir =
|
|
23288
|
+
const dir = join28(homedir16(), ".zam");
|
|
22627
23289
|
console.log(opts.json ? JSON.stringify({ dataDir: dir }) : dir);
|
|
22628
23290
|
});
|
|
22629
23291
|
workspaceCommand.command("backup").description("Back up the local ZAM database into your workspace").option(
|
|
@@ -22664,9 +23326,9 @@ workspaceCommand.command("backup").description("Back up the local ZAM database i
|
|
|
22664
23326
|
});
|
|
22665
23327
|
|
|
22666
23328
|
// src/cli/app.ts
|
|
22667
|
-
var __dirname =
|
|
23329
|
+
var __dirname = dirname14(fileURLToPath8(import.meta.url));
|
|
22668
23330
|
var pkg = JSON.parse(
|
|
22669
|
-
|
|
23331
|
+
readFileSync20(join29(__dirname, "..", "..", "package.json"), "utf-8")
|
|
22670
23332
|
);
|
|
22671
23333
|
var program = new Command27();
|
|
22672
23334
|
program.name("zam").description(
|