zam-core 0.10.3 → 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/dist/cli/app.js +470 -101
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/commands/mcp.js +1021 -103
- 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.3.vsix → ZAM_Companion_0.10.4.vsix} +0 -0
- package/dist/vscode-extension/manifest.json +2 -2
- package/package.json +1 -1
package/dist/cli/app.js
CHANGED
|
@@ -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,
|
|
@@ -6815,11 +6829,16 @@ import { Command as Command27 } from "commander";
|
|
|
6815
6829
|
|
|
6816
6830
|
// src/cli/commands/agent.ts
|
|
6817
6831
|
init_kernel();
|
|
6818
|
-
import { existsSync as existsSync15
|
|
6819
|
-
import {
|
|
6820
|
-
import { dirname as dirname7, join as join15 } 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";
|
|
@@ -7673,16 +7692,7 @@ function installVscodeExtension(options) {
|
|
|
7673
7692
|
};
|
|
7674
7693
|
}
|
|
7675
7694
|
|
|
7676
|
-
// src/cli/
|
|
7677
|
-
var C = {
|
|
7678
|
-
reset: "\x1B[0m",
|
|
7679
|
-
bold: "\x1B[1m",
|
|
7680
|
-
cyan: "\x1B[36m",
|
|
7681
|
-
dim: "\x1B[2m",
|
|
7682
|
-
green: "\x1B[32m",
|
|
7683
|
-
yellow: "\x1B[33m"
|
|
7684
|
-
};
|
|
7685
|
-
var SUPPORTED_AGENTS = ["opencode"];
|
|
7695
|
+
// src/cli/agent-connect.ts
|
|
7686
7696
|
var CONNECT_HARNESSES = [
|
|
7687
7697
|
"claude-code",
|
|
7688
7698
|
"claude-desktop",
|
|
@@ -7693,9 +7703,189 @@ var CONNECT_HARNESSES = [
|
|
|
7693
7703
|
"goose",
|
|
7694
7704
|
"copilot"
|
|
7695
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
|
+
};
|
|
7696
7725
|
function isConnectHarnessId(value) {
|
|
7697
7726
|
return CONNECT_HARNESSES.includes(value);
|
|
7698
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"];
|
|
7699
7889
|
function agentsMdPresent(cwd = process.cwd()) {
|
|
7700
7890
|
return existsSync15(join15(cwd, "AGENTS.md"));
|
|
7701
7891
|
}
|
|
@@ -7823,125 +8013,69 @@ var connectCmd = new Command("connect").description(
|
|
|
7823
8013
|
explicitHarness = harnessArg;
|
|
7824
8014
|
}
|
|
7825
8015
|
}
|
|
7826
|
-
const
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
copilotHome: process.env.COPILOT_HOME
|
|
8016
|
+
const report = performAgentConnect({
|
|
8017
|
+
harness: explicitHarness,
|
|
8018
|
+
dryRun: Boolean(opts.print)
|
|
7830
8019
|
});
|
|
7831
|
-
if (
|
|
8020
|
+
if (report.detected.length === 0) {
|
|
7832
8021
|
console.error(
|
|
7833
8022
|
"No supported user-scoped agent harness was detected. Install Codex, VS Code, or another supported host, or pass a harness explicitly."
|
|
7834
8023
|
);
|
|
7835
8024
|
process.exit(1);
|
|
7836
8025
|
}
|
|
7837
|
-
|
|
7838
|
-
if (!zamPath) {
|
|
8026
|
+
if (!report.zamOnPath) {
|
|
7839
8027
|
console.warn(
|
|
7840
8028
|
`${C.yellow}Warning: 'zam' executable was not found on your PATH. Falling back to literal 'zam'.${C.reset}`
|
|
7841
8029
|
);
|
|
7842
|
-
zamPath = "zam";
|
|
7843
8030
|
}
|
|
7844
|
-
for (const
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
zamPath,
|
|
7849
|
-
cwd: process.cwd(),
|
|
7850
|
-
home,
|
|
7851
|
-
copilotHome: process.env.COPILOT_HOME
|
|
7852
|
-
});
|
|
7853
|
-
} catch (error) {
|
|
7854
|
-
console.error(
|
|
7855
|
-
`Error preparing ${harness} MCP configuration: ${error instanceof Error ? error.message : String(error)}`
|
|
7856
|
-
);
|
|
7857
|
-
process.exit(1);
|
|
7858
|
-
}
|
|
7859
|
-
let copilotExtension;
|
|
7860
|
-
let vscodeExtension;
|
|
7861
|
-
try {
|
|
7862
|
-
if (harness === "copilot") {
|
|
7863
|
-
copilotExtension = installCopilotExtension({
|
|
7864
|
-
home,
|
|
7865
|
-
zamPath,
|
|
7866
|
-
dryRun: Boolean(opts.print)
|
|
7867
|
-
});
|
|
7868
|
-
} else if (harness === "vscode") {
|
|
7869
|
-
vscodeExtension = installVscodeExtension({
|
|
7870
|
-
home,
|
|
7871
|
-
zamPath,
|
|
7872
|
-
dryRun: Boolean(opts.print)
|
|
7873
|
-
});
|
|
7874
|
-
} else if (harness === "antigravity") {
|
|
7875
|
-
const antigravityPath = resolveAntigravityIdeExecutable();
|
|
7876
|
-
if (antigravityPath) {
|
|
7877
|
-
vscodeExtension = installVscodeExtension({
|
|
7878
|
-
home,
|
|
7879
|
-
zamPath,
|
|
7880
|
-
codePath: antigravityPath,
|
|
7881
|
-
dryRun: Boolean(opts.print)
|
|
7882
|
-
});
|
|
7883
|
-
}
|
|
7884
|
-
}
|
|
7885
|
-
} catch (error) {
|
|
7886
|
-
console.error(
|
|
7887
|
-
`Error preparing ${harness} companion extension: ${error instanceof Error ? error.message : String(error)}`
|
|
7888
|
-
);
|
|
7889
|
-
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;
|
|
7890
8035
|
}
|
|
7891
8036
|
if (opts.print) {
|
|
7892
|
-
if (
|
|
8037
|
+
if (report.results.length > 1)
|
|
8038
|
+
console.log(`${C.bold}${result.harness}${C.reset}`);
|
|
7893
8039
|
console.log(`Path: ${result.path}`);
|
|
7894
8040
|
console.log(`Content:
|
|
7895
8041
|
${result.content}`);
|
|
7896
|
-
if (
|
|
7897
|
-
console.log(`Extension: ${
|
|
7898
|
-
console.log(
|
|
7899
|
-
|
|
7900
|
-
);
|
|
7901
|
-
|
|
7902
|
-
if (vscodeExtension) {
|
|
7903
|
-
console.log(`Extension: ${vscodeExtension.vsixPath}`);
|
|
7904
|
-
console.log(`Launch config: ${vscodeExtension.launchConfigPath}`);
|
|
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}`);
|
|
7905
8048
|
}
|
|
7906
8049
|
continue;
|
|
7907
8050
|
}
|
|
7908
8051
|
if (result.alreadyConfigured) {
|
|
7909
8052
|
console.log(
|
|
7910
|
-
`${C.green}\u2713${C.reset} ${harness}: MCP server 'zam' already configured in ${result.path}`
|
|
8053
|
+
`${C.green}\u2713${C.reset} ${result.harness}: MCP server 'zam' already configured in ${result.path}`
|
|
7911
8054
|
);
|
|
7912
8055
|
} else {
|
|
7913
|
-
try {
|
|
7914
|
-
mkdirSync10(dirname7(result.path), { recursive: true });
|
|
7915
|
-
writeFileSync9(result.path, result.content, "utf-8");
|
|
7916
|
-
console.log(
|
|
7917
|
-
`${C.green}\u2713${C.reset} ${harness}: wrote MCP configuration to ${result.path}`
|
|
7918
|
-
);
|
|
7919
|
-
} catch (error) {
|
|
7920
|
-
console.error(
|
|
7921
|
-
`Error writing ${harness} MCP configuration: ${error instanceof Error ? error.message : String(error)}`
|
|
7922
|
-
);
|
|
7923
|
-
process.exit(1);
|
|
7924
|
-
}
|
|
7925
|
-
}
|
|
7926
|
-
if (copilotExtension) {
|
|
7927
8056
|
console.log(
|
|
7928
|
-
`${C.green}\u2713${C.reset}
|
|
8057
|
+
`${C.green}\u2713${C.reset} ${result.harness}: wrote MCP configuration to ${result.path}`
|
|
7929
8058
|
);
|
|
7930
8059
|
}
|
|
7931
|
-
if (
|
|
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) {
|
|
7932
8065
|
console.log(
|
|
7933
|
-
`${C.green}\u2713${C.reset} ZAM Companion ${
|
|
8066
|
+
`${C.green}\u2713${C.reset} ZAM Companion ${result.extension.action} from ${result.extension.location}`
|
|
7934
8067
|
);
|
|
7935
8068
|
}
|
|
7936
8069
|
console.log(` ${C.dim}${result.hint}${C.reset}`);
|
|
7937
8070
|
}
|
|
7938
|
-
if (
|
|
7939
|
-
const skills = distributeGlobalSkills(home);
|
|
7940
|
-
const installed = skills.filter((result) => result.success).length;
|
|
8071
|
+
if (report.skills) {
|
|
7941
8072
|
console.log(
|
|
7942
|
-
`${C.green}\u2713${C.reset} Refreshed ${
|
|
8073
|
+
`${C.green}\u2713${C.reset} Refreshed ${report.skills.refreshed}/${report.skills.total} global ZAM skill installations`
|
|
7943
8074
|
);
|
|
7944
8075
|
}
|
|
8076
|
+
if (!report.success) {
|
|
8077
|
+
process.exit(1);
|
|
8078
|
+
}
|
|
7945
8079
|
});
|
|
7946
8080
|
var agentCommand = new Command("agent").description("Provision and inspect the agent that drives ZAM sessions").addCommand(installCmd).addCommand(statusCmd).addCommand(openCmd).addCommand(listCmd).addCommand(connectCmd).action(printStatus);
|
|
7947
8081
|
|
|
@@ -7967,6 +8101,7 @@ var DEFAULT_LLM_URL = "http://localhost:8000/v1";
|
|
|
7967
8101
|
var DEFAULT_LLM_MAX_TOKENS = 1e4;
|
|
7968
8102
|
var RECALL_QUESTION_MAX_OUTPUT_TOKENS = 400;
|
|
7969
8103
|
var RECALL_EVALUATION_MAX_OUTPUT_TOKENS = 1200;
|
|
8104
|
+
var RECALL_DISCUSSION_MAX_OUTPUT_TOKENS = 1200;
|
|
7970
8105
|
var RECALL_ENDPOINT_CACHE_MS = 6e4;
|
|
7971
8106
|
var cachedRecallEndpoint = null;
|
|
7972
8107
|
function recallEndpointSignature(cfg) {
|
|
@@ -8329,6 +8464,64 @@ Evaluation:`;
|
|
|
8329
8464
|
providerName: endpoint.providerName
|
|
8330
8465
|
};
|
|
8331
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
|
+
}
|
|
8332
8525
|
var MAX_IMPORT_TEXT_CHARS = 2e5;
|
|
8333
8526
|
var VALID_GENERATED_MODES = /* @__PURE__ */ new Set(["shadowing", "copilot", "autonomy"]);
|
|
8334
8527
|
function parseGeneratedCardArray(responseText, label, limits) {
|
|
@@ -9566,7 +9759,7 @@ async function readWebLink(url) {
|
|
|
9566
9759
|
redirect: "manual",
|
|
9567
9760
|
signal: controller.signal,
|
|
9568
9761
|
headers: {
|
|
9569
|
-
"User-Agent": "ZAM-Content-Studio/0.10.
|
|
9762
|
+
"User-Agent": "ZAM-Content-Studio/0.10.4"
|
|
9570
9763
|
}
|
|
9571
9764
|
});
|
|
9572
9765
|
if (res.status >= 300 && res.status < 400) {
|
|
@@ -16224,6 +16417,93 @@ bridgeCommand.command("evaluate-answer").description(
|
|
|
16224
16417
|
}
|
|
16225
16418
|
});
|
|
16226
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
|
+
});
|
|
16227
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) => {
|
|
16228
16508
|
await withDb2(async (db) => {
|
|
16229
16509
|
const userId = await ensureDefaultUser(db, opts.user);
|
|
@@ -16252,6 +16532,54 @@ bridgeCommand.command("get-settings").description("Get active ZAM settings (JSON
|
|
|
16252
16532
|
});
|
|
16253
16533
|
});
|
|
16254
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
|
+
});
|
|
16255
16583
|
async function readDatabaseUserSummaries(db) {
|
|
16256
16584
|
return await db.prepare(
|
|
16257
16585
|
`SELECT user_id AS id, COUNT(*) AS cardCount
|
|
@@ -21227,9 +21555,49 @@ async function activateMachineProviderConfig(db) {
|
|
|
21227
21555
|
` activate ${providerCount} machine-local provider(s) from ~/.zam/config.json`
|
|
21228
21556
|
);
|
|
21229
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
|
+
}
|
|
21230
21594
|
var setupCommand = new Command18("setup").description(
|
|
21231
21595
|
"Link ZAM skill directories into this workspace and initialize the database"
|
|
21232
|
-
).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(
|
|
21233
21601
|
"--agents <list>",
|
|
21234
21602
|
"comma-separated agents to wire: all, claude, copilot, codex, agent"
|
|
21235
21603
|
).option(
|
|
@@ -21256,6 +21624,7 @@ var setupCommand = new Command18("setup").description(
|
|
|
21256
21624
|
dryRun: opts.dryRun
|
|
21257
21625
|
});
|
|
21258
21626
|
await initDatabase(opts.skipInit || opts.dryRun);
|
|
21627
|
+
connectDetectedAgentHarnesses(opts.skipAgentConnect, opts.dryRun);
|
|
21259
21628
|
if (agents.has("claude")) {
|
|
21260
21629
|
writeClaudeMd(opts.skipClaudeMd, target, {
|
|
21261
21630
|
dryRun: opts.dryRun,
|