zam-core 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +293 -106
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -4477,6 +4477,13 @@ function upsertConfiguredWorkspace(workspace, path = defaultConfigPath()) {
|
|
|
4477
4477
|
saveConfiguredWorkspaces(next, path);
|
|
4478
4478
|
return next;
|
|
4479
4479
|
}
|
|
4480
|
+
function removeConfiguredWorkspace(id, path = defaultConfigPath()) {
|
|
4481
|
+
const next = getConfiguredWorkspaces(path).filter(
|
|
4482
|
+
(workspace) => workspace.id !== id
|
|
4483
|
+
);
|
|
4484
|
+
saveConfiguredWorkspaces(next, path);
|
|
4485
|
+
return next;
|
|
4486
|
+
}
|
|
4480
4487
|
function detectSyncProvider(dir) {
|
|
4481
4488
|
const p = dir.toLowerCase();
|
|
4482
4489
|
if (p.includes("onedrive")) return "OneDrive";
|
|
@@ -5352,7 +5359,13 @@ var agentCommand = new Command("agent").description("Provision and inspect the a
|
|
|
5352
5359
|
// src/cli/commands/bridge.ts
|
|
5353
5360
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
5354
5361
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
5355
|
-
import {
|
|
5362
|
+
import {
|
|
5363
|
+
existsSync as existsSync17,
|
|
5364
|
+
mkdirSync as mkdirSync10,
|
|
5365
|
+
readdirSync as readdirSync3,
|
|
5366
|
+
readFileSync as readFileSync11,
|
|
5367
|
+
rmSync as rmSync3
|
|
5368
|
+
} from "fs";
|
|
5356
5369
|
import { homedir as homedir10, tmpdir as tmpdir3 } from "os";
|
|
5357
5370
|
import { basename as basename5, join as join17, resolve as resolve5 } from "path";
|
|
5358
5371
|
import { Command as Command4 } from "commander";
|
|
@@ -5974,7 +5987,7 @@ async function startLocalRunner(url, model, locale) {
|
|
|
5974
5987
|
let attempts = 0;
|
|
5975
5988
|
const dotsPerLine = 30;
|
|
5976
5989
|
while (true) {
|
|
5977
|
-
await new Promise((
|
|
5990
|
+
await new Promise((resolve9) => setTimeout(resolve9, 500));
|
|
5978
5991
|
if (await isLlmOnline(url)) {
|
|
5979
5992
|
if (attempts > 0) process.stdout.write("\n");
|
|
5980
5993
|
return true;
|
|
@@ -6129,8 +6142,8 @@ async function fetchWithInteractiveTimeout(url, options = {}) {
|
|
|
6129
6142
|
const dotsPerLine = 30;
|
|
6130
6143
|
while (true) {
|
|
6131
6144
|
let timeoutId;
|
|
6132
|
-
const timeoutPromise = new Promise((
|
|
6133
|
-
timeoutId = setTimeout(() =>
|
|
6145
|
+
const timeoutPromise = new Promise((resolve9) => {
|
|
6146
|
+
timeoutId = setTimeout(() => resolve9("timeout"), timeoutMs);
|
|
6134
6147
|
});
|
|
6135
6148
|
const dotsInterval = setInterval(() => {
|
|
6136
6149
|
process.stdout.write(".");
|
|
@@ -6240,25 +6253,25 @@ async function observeUiSnapshotViaLLM(db, input8) {
|
|
|
6240
6253
|
const imageUrls = [];
|
|
6241
6254
|
const isVideo = /\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(input8.imagePath);
|
|
6242
6255
|
if (isVideo) {
|
|
6243
|
-
const { mkdirSync:
|
|
6256
|
+
const { mkdirSync: mkdirSync14, readdirSync: readdirSync4, rmSync: rmSync4 } = await import("fs");
|
|
6244
6257
|
const { execSync: execSync6 } = await import("child_process");
|
|
6245
6258
|
const tempDir = join14(
|
|
6246
6259
|
tmpdir2(),
|
|
6247
6260
|
`zam-frames-${randomBytes(4).toString("hex")}`
|
|
6248
6261
|
);
|
|
6249
|
-
|
|
6262
|
+
mkdirSync14(tempDir, { recursive: true });
|
|
6250
6263
|
try {
|
|
6251
6264
|
execSync6(
|
|
6252
6265
|
`ffmpeg -i "${input8.imagePath}" -vf "fps=1/3,scale=1280:-1" -vsync vfr "${tempDir}/frame_%03d.png"`,
|
|
6253
6266
|
{ stdio: "ignore" }
|
|
6254
6267
|
);
|
|
6255
|
-
let files =
|
|
6268
|
+
let files = readdirSync4(tempDir).filter((f) => f.endsWith(".png")).sort();
|
|
6256
6269
|
if (files.length === 0) {
|
|
6257
6270
|
execSync6(
|
|
6258
6271
|
`ffmpeg -i "${input8.imagePath}" -vframes 1 "${tempDir}/frame_001.png"`,
|
|
6259
6272
|
{ stdio: "ignore" }
|
|
6260
6273
|
);
|
|
6261
|
-
files =
|
|
6274
|
+
files = readdirSync4(tempDir).filter((f) => f.endsWith(".png")).sort();
|
|
6262
6275
|
}
|
|
6263
6276
|
const maxFrames = cfg.maxFrames ?? 100;
|
|
6264
6277
|
let sampledFiles = files;
|
|
@@ -6280,7 +6293,7 @@ async function observeUiSnapshotViaLLM(db, input8) {
|
|
|
6280
6293
|
}
|
|
6281
6294
|
} finally {
|
|
6282
6295
|
try {
|
|
6283
|
-
|
|
6296
|
+
rmSync4(tempDir, { recursive: true, force: true });
|
|
6284
6297
|
} catch {
|
|
6285
6298
|
}
|
|
6286
6299
|
}
|
|
@@ -6635,40 +6648,16 @@ async function resolveUser(opts, db, resolveOpts) {
|
|
|
6635
6648
|
process.exit(1);
|
|
6636
6649
|
}
|
|
6637
6650
|
|
|
6638
|
-
// src/cli/commands/shared/db.ts
|
|
6639
|
-
function defaultErrorHandler(message) {
|
|
6640
|
-
console.error("Error:", message);
|
|
6641
|
-
process.exit(1);
|
|
6642
|
-
}
|
|
6643
|
-
async function withDb(fn, onError = defaultErrorHandler) {
|
|
6644
|
-
let db;
|
|
6645
|
-
try {
|
|
6646
|
-
db = await openDatabase();
|
|
6647
|
-
await fn(db);
|
|
6648
|
-
} catch (err) {
|
|
6649
|
-
onError(err.message);
|
|
6650
|
-
} finally {
|
|
6651
|
-
await db?.close();
|
|
6652
|
-
}
|
|
6653
|
-
}
|
|
6654
|
-
function jsonOut(data) {
|
|
6655
|
-
console.log(JSON.stringify(data, null, 2));
|
|
6656
|
-
}
|
|
6657
|
-
|
|
6658
|
-
// src/cli/commands/workspace.ts
|
|
6659
|
-
import { execFileSync as execFileSync3 } from "child_process";
|
|
6660
|
-
import { existsSync as existsSync16, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8 } from "fs";
|
|
6661
|
-
import { homedir as homedir9 } from "os";
|
|
6662
|
-
import { join as join16, resolve as resolve4 } from "path";
|
|
6663
|
-
import { confirm, input } from "@inquirer/prompts";
|
|
6664
|
-
import { Command as Command3 } from "commander";
|
|
6665
|
-
|
|
6666
6651
|
// src/cli/commands/setup.ts
|
|
6667
6652
|
import {
|
|
6668
|
-
copyFileSync as copyFileSync2,
|
|
6669
6653
|
existsSync as existsSync15,
|
|
6654
|
+
lstatSync,
|
|
6670
6655
|
mkdirSync as mkdirSync8,
|
|
6656
|
+
readdirSync as readdirSync2,
|
|
6671
6657
|
readFileSync as readFileSync10,
|
|
6658
|
+
realpathSync,
|
|
6659
|
+
rmSync as rmSync2,
|
|
6660
|
+
symlinkSync,
|
|
6672
6661
|
writeFileSync as writeFileSync7
|
|
6673
6662
|
} from "fs";
|
|
6674
6663
|
import { basename as basename4, dirname as dirname5, join as join15, resolve as resolve3 } from "path";
|
|
@@ -6683,7 +6672,7 @@ var SKILL_PAIRS = [
|
|
|
6683
6672
|
{
|
|
6684
6673
|
from: join15(packageRoot, ".claude", "skills", "zam", "SKILL.md"),
|
|
6685
6674
|
to: join15(".claude", "skills", "zam", "SKILL.md"),
|
|
6686
|
-
agents: ["claude"]
|
|
6675
|
+
agents: ["claude", "copilot"]
|
|
6687
6676
|
},
|
|
6688
6677
|
{
|
|
6689
6678
|
from: join15(packageRoot, ".agent", "skills", "zam", "SKILL.md"),
|
|
@@ -6693,7 +6682,7 @@ var SKILL_PAIRS = [
|
|
|
6693
6682
|
{
|
|
6694
6683
|
from: join15(packageRoot, ".agents", "skills", "zam", "SKILL.md"),
|
|
6695
6684
|
to: join15(".agents", "skills", "zam", "SKILL.md"),
|
|
6696
|
-
agents: ["
|
|
6685
|
+
agents: ["codex"]
|
|
6697
6686
|
}
|
|
6698
6687
|
];
|
|
6699
6688
|
function parseSetupAgents(value) {
|
|
@@ -6720,34 +6709,119 @@ function parseSetupAgents(value) {
|
|
|
6720
6709
|
}
|
|
6721
6710
|
return selected;
|
|
6722
6711
|
}
|
|
6723
|
-
function
|
|
6724
|
-
|
|
6712
|
+
function pathExists(path) {
|
|
6713
|
+
try {
|
|
6714
|
+
lstatSync(path);
|
|
6715
|
+
return true;
|
|
6716
|
+
} catch {
|
|
6717
|
+
return false;
|
|
6718
|
+
}
|
|
6719
|
+
}
|
|
6720
|
+
function comparableRealPath(path) {
|
|
6721
|
+
const real = realpathSync(path);
|
|
6722
|
+
return process.platform === "win32" ? real.toLowerCase() : real;
|
|
6723
|
+
}
|
|
6724
|
+
function pathsResolveToSameDirectory(sourceDir, destinationDir) {
|
|
6725
|
+
try {
|
|
6726
|
+
return comparableRealPath(destinationDir) === comparableRealPath(sourceDir);
|
|
6727
|
+
} catch {
|
|
6728
|
+
return false;
|
|
6729
|
+
}
|
|
6730
|
+
}
|
|
6731
|
+
function linkPointsTo(sourceDir, destinationDir) {
|
|
6732
|
+
try {
|
|
6733
|
+
return lstatSync(destinationDir).isSymbolicLink() && pathsResolveToSameDirectory(sourceDir, destinationDir);
|
|
6734
|
+
} catch {
|
|
6735
|
+
return false;
|
|
6736
|
+
}
|
|
6737
|
+
}
|
|
6738
|
+
function isReplaceableCopiedSkill(destinationDir) {
|
|
6739
|
+
try {
|
|
6740
|
+
if (!lstatSync(destinationDir).isDirectory()) return false;
|
|
6741
|
+
const entries = readdirSync2(destinationDir);
|
|
6742
|
+
if (entries.length !== 1 || entries[0] !== "SKILL.md") return false;
|
|
6743
|
+
return /^name:\s*zam\s*$/m.test(
|
|
6744
|
+
readFileSync10(join15(destinationDir, "SKILL.md"), "utf8")
|
|
6745
|
+
);
|
|
6746
|
+
} catch {
|
|
6747
|
+
return false;
|
|
6748
|
+
}
|
|
6749
|
+
}
|
|
6750
|
+
function wireSkills(cwd = process.cwd(), agents = parseSetupAgents(), opts = {}) {
|
|
6751
|
+
const results = [];
|
|
6752
|
+
const log = (message) => {
|
|
6753
|
+
if (!opts.quiet) console.log(message);
|
|
6754
|
+
};
|
|
6725
6755
|
for (const { from, to, agents: pairAgents } of SKILL_PAIRS) {
|
|
6726
6756
|
if (!pairAgents.some((agent) => agents.has(agent))) continue;
|
|
6727
|
-
const
|
|
6728
|
-
|
|
6729
|
-
|
|
6757
|
+
const sourceDir = dirname5(from);
|
|
6758
|
+
const destinationDir = dirname5(join15(cwd, to));
|
|
6759
|
+
if (!existsSync15(sourceDir)) {
|
|
6760
|
+
if (!opts.quiet) {
|
|
6761
|
+
console.warn(` warn source not found, skipping: ${sourceDir}`);
|
|
6762
|
+
}
|
|
6763
|
+
results.push({
|
|
6764
|
+
source: sourceDir,
|
|
6765
|
+
destination: destinationDir,
|
|
6766
|
+
action: "skipped",
|
|
6767
|
+
reason: "source-not-found"
|
|
6768
|
+
});
|
|
6730
6769
|
continue;
|
|
6731
6770
|
}
|
|
6732
|
-
if (
|
|
6733
|
-
|
|
6771
|
+
if (pathsResolveToSameDirectory(sourceDir, destinationDir) && !lstatSync(destinationDir).isSymbolicLink()) {
|
|
6772
|
+
log(` skip ${dirname5(to)} (package source)`);
|
|
6773
|
+
results.push({
|
|
6774
|
+
source: sourceDir,
|
|
6775
|
+
destination: destinationDir,
|
|
6776
|
+
action: "skipped",
|
|
6777
|
+
reason: "source-directory"
|
|
6778
|
+
});
|
|
6734
6779
|
continue;
|
|
6735
6780
|
}
|
|
6736
|
-
if (
|
|
6737
|
-
|
|
6738
|
-
|
|
6781
|
+
if (linkPointsTo(sourceDir, destinationDir)) {
|
|
6782
|
+
log(` skip ${dirname5(to)} (already linked)`);
|
|
6783
|
+
results.push({
|
|
6784
|
+
source: sourceDir,
|
|
6785
|
+
destination: destinationDir,
|
|
6786
|
+
action: "skipped",
|
|
6787
|
+
reason: "already-linked"
|
|
6788
|
+
});
|
|
6739
6789
|
continue;
|
|
6740
6790
|
}
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6791
|
+
const destinationExists = pathExists(destinationDir);
|
|
6792
|
+
const replaceExisting = destinationExists && (Boolean(opts.force) || isReplaceableCopiedSkill(destinationDir));
|
|
6793
|
+
if (destinationExists && !replaceExisting) {
|
|
6794
|
+
if (!opts.quiet) {
|
|
6795
|
+
console.warn(
|
|
6796
|
+
` warn ${dirname5(to)} already exists and is not managed by ZAM; use --force to replace it`
|
|
6797
|
+
);
|
|
6798
|
+
}
|
|
6799
|
+
results.push({
|
|
6800
|
+
source: sourceDir,
|
|
6801
|
+
destination: destinationDir,
|
|
6802
|
+
action: "skipped",
|
|
6803
|
+
reason: "unmanaged-destination"
|
|
6804
|
+
});
|
|
6805
|
+
continue;
|
|
6806
|
+
}
|
|
6807
|
+
const action = destinationExists ? "relinked" : "linked";
|
|
6808
|
+
if (opts.dryRun) {
|
|
6809
|
+
log(` would ${action === "linked" ? "link" : "relink"} ${dirname5(to)}`);
|
|
6810
|
+
} else {
|
|
6811
|
+
if (destinationExists) {
|
|
6812
|
+
rmSync2(destinationDir, { recursive: true, force: true });
|
|
6813
|
+
}
|
|
6814
|
+
mkdirSync8(dirname5(destinationDir), { recursive: true });
|
|
6815
|
+
symlinkSync(
|
|
6816
|
+
sourceDir,
|
|
6817
|
+
destinationDir,
|
|
6818
|
+
process.platform === "win32" ? "junction" : "dir"
|
|
6819
|
+
);
|
|
6820
|
+
log(` ${action === "linked" ? "link" : "relink"} ${dirname5(to)}`);
|
|
6821
|
+
}
|
|
6822
|
+
results.push({ source: sourceDir, destination: destinationDir, action });
|
|
6750
6823
|
}
|
|
6824
|
+
return results;
|
|
6751
6825
|
}
|
|
6752
6826
|
function formatDatabaseInitTarget(target) {
|
|
6753
6827
|
switch (target.kind) {
|
|
@@ -6825,7 +6899,7 @@ ZAM is available in this repository. Use the \`zam\` skill in Claude Code to tur
|
|
|
6825
6899
|
|
|
6826
6900
|
- Skill files live under \`.claude/skills/zam/\`.
|
|
6827
6901
|
- Fast-changing review data lives in \`~/.zam/\`, not in this repository.
|
|
6828
|
-
-
|
|
6902
|
+
- The skill directory is linked to this ZAM installation and updates with it.`,
|
|
6829
6903
|
Boolean(opts.dryRun)
|
|
6830
6904
|
);
|
|
6831
6905
|
logInstructionAction(action, "CLAUDE.md", Boolean(opts.dryRun));
|
|
@@ -6875,7 +6949,7 @@ ZAM is available in this repository. Select the \`zam\` skill through \`/skills\
|
|
|
6875
6949
|
|
|
6876
6950
|
- Skill files live under \`.agents/skills/zam/\`.
|
|
6877
6951
|
- Fast-changing review data lives in \`~/.zam/\`, not in this repository.
|
|
6878
|
-
-
|
|
6952
|
+
- The skill directory is linked to this ZAM installation and updates with it.`,
|
|
6879
6953
|
Boolean(opts.dryRun)
|
|
6880
6954
|
);
|
|
6881
6955
|
logInstructionAction(action, "AGENTS.md", Boolean(opts.dryRun));
|
|
@@ -6922,10 +6996,10 @@ function writeCopilotInstructions(cwd = process.cwd(), opts = {}) {
|
|
|
6922
6996
|
dest,
|
|
6923
6997
|
`## ZAM learning sessions
|
|
6924
6998
|
|
|
6925
|
-
ZAM is available in this repository through \`.
|
|
6999
|
+
ZAM is available in this repository through \`.claude/skills/zam/\`. Use the \`zam\` project skill from Copilot-compatible skill selection surfaces to turn real work into an observed learning session with active recall and FSRS scheduling.
|
|
6926
7000
|
|
|
6927
7001
|
- Fast-changing review data lives in \`~/.zam/\`, not in this repository.
|
|
6928
|
-
-
|
|
7002
|
+
- The skill directory is linked to this ZAM installation and updates with it.`,
|
|
6929
7003
|
Boolean(opts.dryRun)
|
|
6930
7004
|
);
|
|
6931
7005
|
logInstructionAction(
|
|
@@ -6935,12 +7009,8 @@ ZAM is available in this repository through \`.agents/skills/zam/\`. Use the \`z
|
|
|
6935
7009
|
);
|
|
6936
7010
|
}
|
|
6937
7011
|
var setupCommand = new Command2("setup").description(
|
|
6938
|
-
"
|
|
6939
|
-
).option(
|
|
6940
|
-
"--force",
|
|
6941
|
-
"overwrite existing skill files (use after upgrading zam)",
|
|
6942
|
-
false
|
|
6943
|
-
).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("--target <path>", "repository/workspace directory to set up").option(
|
|
7012
|
+
"Link ZAM skill directories into this workspace and initialize the database"
|
|
7013
|
+
).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("--target <path>", "repository/workspace directory to set up").option(
|
|
6944
7014
|
"--agents <list>",
|
|
6945
7015
|
"comma-separated agents to wire: all, claude, copilot, codex, agent"
|
|
6946
7016
|
).option(
|
|
@@ -6962,7 +7032,10 @@ var setupCommand = new Command2("setup").description(
|
|
|
6962
7032
|
`Setting up ZAM in ${target}${opts.dryRun ? " (dry run)" : ""}
|
|
6963
7033
|
`
|
|
6964
7034
|
);
|
|
6965
|
-
|
|
7035
|
+
wireSkills(target, agents, {
|
|
7036
|
+
force: opts.force,
|
|
7037
|
+
dryRun: opts.dryRun
|
|
7038
|
+
});
|
|
6966
7039
|
await initDatabase(opts.skipInit || opts.dryRun);
|
|
6967
7040
|
if (agents.has("claude")) {
|
|
6968
7041
|
writeClaudeMd(opts.skipClaudeMd, target, {
|
|
@@ -6970,7 +7043,7 @@ var setupCommand = new Command2("setup").description(
|
|
|
6970
7043
|
updateExisting: updateExistingInstructions
|
|
6971
7044
|
});
|
|
6972
7045
|
}
|
|
6973
|
-
if (agents.has("
|
|
7046
|
+
if (agents.has("codex") || agents.has("agent")) {
|
|
6974
7047
|
writeAgentsMd(opts.skipAgentsMd, target, {
|
|
6975
7048
|
dryRun: opts.dryRun,
|
|
6976
7049
|
updateExisting: updateExistingInstructions
|
|
@@ -6983,12 +7056,38 @@ var setupCommand = new Command2("setup").description(
|
|
|
6983
7056
|
});
|
|
6984
7057
|
}
|
|
6985
7058
|
console.log(
|
|
6986
|
-
"\nDone. Run `zam whoami --set <your-id>` to set your identity. Start the `zam` skill with `/zam` in Claude/Gemini-compatible clients or `$zam` (or `/skills`) in Codex."
|
|
7059
|
+
"\nDone. Run `zam whoami --set <your-id>` to set your identity. Start the `zam` skill with `/zam` in Claude/Copilot/Gemini-compatible clients or `$zam` (or `/skills`) in Codex."
|
|
6987
7060
|
);
|
|
6988
7061
|
}
|
|
6989
7062
|
);
|
|
6990
7063
|
|
|
7064
|
+
// src/cli/commands/shared/db.ts
|
|
7065
|
+
function defaultErrorHandler(message) {
|
|
7066
|
+
console.error("Error:", message);
|
|
7067
|
+
process.exit(1);
|
|
7068
|
+
}
|
|
7069
|
+
async function withDb(fn, onError = defaultErrorHandler) {
|
|
7070
|
+
let db;
|
|
7071
|
+
try {
|
|
7072
|
+
db = await openDatabase();
|
|
7073
|
+
await fn(db);
|
|
7074
|
+
} catch (err) {
|
|
7075
|
+
onError(err.message);
|
|
7076
|
+
} finally {
|
|
7077
|
+
await db?.close();
|
|
7078
|
+
}
|
|
7079
|
+
}
|
|
7080
|
+
function jsonOut(data) {
|
|
7081
|
+
console.log(JSON.stringify(data, null, 2));
|
|
7082
|
+
}
|
|
7083
|
+
|
|
6991
7084
|
// src/cli/commands/workspace.ts
|
|
7085
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
7086
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8 } from "fs";
|
|
7087
|
+
import { homedir as homedir9 } from "os";
|
|
7088
|
+
import { join as join16, resolve as resolve4 } from "path";
|
|
7089
|
+
import { confirm, input } from "@inquirer/prompts";
|
|
7090
|
+
import { Command as Command3 } from "commander";
|
|
6992
7091
|
function runGit(cwd, args) {
|
|
6993
7092
|
try {
|
|
6994
7093
|
return execFileSync3("git", args, {
|
|
@@ -7105,13 +7204,25 @@ workspaceCommand.command("add <id>").description("Register an existing directory
|
|
|
7105
7204
|
...parseScopes(opts.scopes) ? { knowledgeScopes: parseScopes(opts.scopes) } : {},
|
|
7106
7205
|
...opts.defaultAgent ? { defaultAgent: opts.defaultAgent } : {}
|
|
7107
7206
|
};
|
|
7207
|
+
wireSkills(path, parseSetupAgents());
|
|
7108
7208
|
upsertConfiguredWorkspace(workspace);
|
|
7109
|
-
console.log(`Registered workspace "${id}" at ${path}.`);
|
|
7209
|
+
console.log(`Registered and linked workspace "${id}" at ${path}.`);
|
|
7110
7210
|
} catch (err) {
|
|
7111
7211
|
console.error(`Error: ${err.message}`);
|
|
7112
7212
|
process.exit(1);
|
|
7113
7213
|
}
|
|
7114
7214
|
});
|
|
7215
|
+
workspaceCommand.command("remove <id>").description("Unregister a ZAM workspace without deleting its files").action((id) => {
|
|
7216
|
+
const existing = getConfiguredWorkspaces().find((item) => item.id === id);
|
|
7217
|
+
if (!existing) {
|
|
7218
|
+
console.error(`Workspace "${id}" is not configured.`);
|
|
7219
|
+
process.exit(1);
|
|
7220
|
+
}
|
|
7221
|
+
removeConfiguredWorkspace(id);
|
|
7222
|
+
console.log(
|
|
7223
|
+
`Unregistered workspace "${id}". Files in ${existing.path} were not changed.`
|
|
7224
|
+
);
|
|
7225
|
+
});
|
|
7115
7226
|
workspaceCommand.command("setup <id>").description("Install ZAM skills into a configured workspace").option(
|
|
7116
7227
|
"--agents <list>",
|
|
7117
7228
|
"comma-separated agents to wire: all, claude, copilot, codex, agent"
|
|
@@ -7131,19 +7242,17 @@ workspaceCommand.command("setup <id>").description("Install ZAM skills into a co
|
|
|
7131
7242
|
`Setting up workspace "${workspace.id}" in ${workspace.path}${opts.dryRun ? " (dry run)" : ""}
|
|
7132
7243
|
`
|
|
7133
7244
|
);
|
|
7134
|
-
|
|
7135
|
-
Boolean(opts.force),
|
|
7136
|
-
|
|
7137
|
-
|
|
7138
|
-
Boolean(opts.dryRun)
|
|
7139
|
-
);
|
|
7245
|
+
wireSkills(workspace.path, agents, {
|
|
7246
|
+
force: Boolean(opts.force),
|
|
7247
|
+
dryRun: Boolean(opts.dryRun)
|
|
7248
|
+
});
|
|
7140
7249
|
if (agents.has("claude")) {
|
|
7141
7250
|
writeClaudeMd(false, workspace.path, {
|
|
7142
7251
|
dryRun: Boolean(opts.dryRun),
|
|
7143
7252
|
updateExisting: true
|
|
7144
7253
|
});
|
|
7145
7254
|
}
|
|
7146
|
-
if (agents.has("
|
|
7255
|
+
if (agents.has("codex") || agents.has("agent")) {
|
|
7147
7256
|
writeAgentsMd(false, workspace.path, {
|
|
7148
7257
|
dryRun: Boolean(opts.dryRun),
|
|
7149
7258
|
updateExisting: true
|
|
@@ -7447,6 +7556,37 @@ bridgeCommand.command("workspace-info").description("Report the workspace dir, i
|
|
|
7447
7556
|
});
|
|
7448
7557
|
});
|
|
7449
7558
|
});
|
|
7559
|
+
function sameWorkspacePath(left, right) {
|
|
7560
|
+
const normalize = (value) => {
|
|
7561
|
+
const path = resolve5(value);
|
|
7562
|
+
return process.platform === "win32" ? path.toLowerCase() : path;
|
|
7563
|
+
};
|
|
7564
|
+
return normalize(left) === normalize(right);
|
|
7565
|
+
}
|
|
7566
|
+
async function ensureDesktopWorkspace(db) {
|
|
7567
|
+
const configured = getConfiguredWorkspaces();
|
|
7568
|
+
const savedWorkspaceDir = await getSetting(db, "personal.workspace_dir");
|
|
7569
|
+
const workspaceDir = savedWorkspaceDir || configured.find((workspace) => workspace.kind === "personal")?.path || join17(homedir10(), "Documents", "zam");
|
|
7570
|
+
mkdirSync10(workspaceDir, { recursive: true });
|
|
7571
|
+
if (!savedWorkspaceDir) {
|
|
7572
|
+
await setSetting(db, "personal.workspace_dir", workspaceDir);
|
|
7573
|
+
}
|
|
7574
|
+
if (!configured.some(
|
|
7575
|
+
(workspace) => sameWorkspacePath(workspace.path, workspaceDir)
|
|
7576
|
+
)) {
|
|
7577
|
+
const id = configured.some((workspace) => workspace.id === "personal") ? workspaceIdFromPath(workspaceDir) : "personal";
|
|
7578
|
+
upsertConfiguredWorkspace({
|
|
7579
|
+
id,
|
|
7580
|
+
label: basename5(workspaceDir) || "ZAM",
|
|
7581
|
+
kind: "personal",
|
|
7582
|
+
path: workspaceDir
|
|
7583
|
+
});
|
|
7584
|
+
}
|
|
7585
|
+
const skillLinks = getConfiguredWorkspaces().flatMap(
|
|
7586
|
+
(workspace) => existsSync17(workspace.path) ? wireSkills(workspace.path, parseSetupAgents(), { quiet: true }) : []
|
|
7587
|
+
);
|
|
7588
|
+
return { workspaceDir, skillLinks };
|
|
7589
|
+
}
|
|
7450
7590
|
bridgeCommand.command("workspace-list").description("List configured ZAM workspaces (JSON)").action(async () => {
|
|
7451
7591
|
await withDb2(async (db) => {
|
|
7452
7592
|
const workspaceDir = await getSetting(db, "personal.workspace_dir") || null;
|
|
@@ -7487,6 +7627,7 @@ bridgeCommand.command("workspace-add").description("Register an existing directo
|
|
|
7487
7627
|
kind: parseBridgeWorkspaceKind(opts.kind),
|
|
7488
7628
|
path
|
|
7489
7629
|
};
|
|
7630
|
+
const skillLinks = wireSkills(path, parseSetupAgents(), { quiet: true });
|
|
7490
7631
|
await withDb2(async (db) => {
|
|
7491
7632
|
await setSetting(db, "personal.workspace_dir", path);
|
|
7492
7633
|
upsertConfiguredWorkspace(workspace);
|
|
@@ -7494,7 +7635,39 @@ bridgeCommand.command("workspace-add").description("Register an existing directo
|
|
|
7494
7635
|
ok: true,
|
|
7495
7636
|
workspace,
|
|
7496
7637
|
workspaces: getConfiguredWorkspaces(),
|
|
7497
|
-
workspaceDir: path
|
|
7638
|
+
workspaceDir: path,
|
|
7639
|
+
skillLinks
|
|
7640
|
+
});
|
|
7641
|
+
});
|
|
7642
|
+
});
|
|
7643
|
+
bridgeCommand.command("workspace-remove").description("Unregister a ZAM workspace without deleting its files (JSON)").requiredOption("--id <id>", "Workspace id").action(async (opts) => {
|
|
7644
|
+
const id = String(opts.id ?? "").trim();
|
|
7645
|
+
if (!id) jsonError("A non-empty --id is required");
|
|
7646
|
+
const workspace = getConfiguredWorkspaces().find((item) => item.id === id);
|
|
7647
|
+
if (!workspace) jsonError(`Workspace "${id}" is not configured`);
|
|
7648
|
+
await withDb2(async (db) => {
|
|
7649
|
+
const activeWorkspaceDir = await getSetting(db, "personal.workspace_dir") || null;
|
|
7650
|
+
const remaining = removeConfiguredWorkspace(id);
|
|
7651
|
+
let workspaceDir = activeWorkspaceDir;
|
|
7652
|
+
let skillLinks = [];
|
|
7653
|
+
if (activeWorkspaceDir && sameWorkspacePath(activeWorkspaceDir, workspace.path)) {
|
|
7654
|
+
if (remaining.length > 0) {
|
|
7655
|
+
workspaceDir = remaining[0].path;
|
|
7656
|
+
await setSetting(db, "personal.workspace_dir", workspaceDir);
|
|
7657
|
+
} else {
|
|
7658
|
+
workspaceDir = join17(homedir10(), "Documents", "zam");
|
|
7659
|
+
await setSetting(db, "personal.workspace_dir", workspaceDir);
|
|
7660
|
+
const ensured = await ensureDesktopWorkspace(db);
|
|
7661
|
+
workspaceDir = ensured.workspaceDir;
|
|
7662
|
+
skillLinks = ensured.skillLinks;
|
|
7663
|
+
}
|
|
7664
|
+
}
|
|
7665
|
+
jsonOut2({
|
|
7666
|
+
ok: true,
|
|
7667
|
+
removed: workspace,
|
|
7668
|
+
workspaces: getConfiguredWorkspaces(),
|
|
7669
|
+
workspaceDir,
|
|
7670
|
+
skillLinks
|
|
7498
7671
|
});
|
|
7499
7672
|
});
|
|
7500
7673
|
});
|
|
@@ -7502,9 +7675,11 @@ bridgeCommand.command("set-workspace-dir").description("Set the personal workspa
|
|
|
7502
7675
|
const raw = String(opts.dir ?? "").trim();
|
|
7503
7676
|
if (!raw) jsonError("A non-empty --dir is required");
|
|
7504
7677
|
const dir = resolve5(raw);
|
|
7678
|
+
if (!existsSync17(dir)) jsonError(`Workspace path does not exist: ${dir}`);
|
|
7679
|
+
const skillLinks = wireSkills(dir, parseSetupAgents(), { quiet: true });
|
|
7505
7680
|
await withDb2(async (db) => {
|
|
7506
7681
|
await setSetting(db, "personal.workspace_dir", dir);
|
|
7507
|
-
jsonOut2({ ok: true, workspaceDir: dir });
|
|
7682
|
+
jsonOut2({ ok: true, workspaceDir: dir, skillLinks });
|
|
7508
7683
|
});
|
|
7509
7684
|
});
|
|
7510
7685
|
bridgeCommand.command("agent-list").description("List agent harnesses with detection state + the default (JSON)").action(async () => {
|
|
@@ -7932,7 +8107,7 @@ bridgeCommand.command("discover-skills").description(
|
|
|
7932
8107
|
const monitorDir = join17(homedir10(), ".zam", "monitor");
|
|
7933
8108
|
let files;
|
|
7934
8109
|
try {
|
|
7935
|
-
files =
|
|
8110
|
+
files = readdirSync3(monitorDir).filter((f) => f.endsWith(".jsonl"));
|
|
7936
8111
|
} catch {
|
|
7937
8112
|
jsonOut2({ proposals: [], message: "No monitor logs found." });
|
|
7938
8113
|
return;
|
|
@@ -8455,7 +8630,7 @@ bridgeCommand.command("capture-ui").description("Capture a screenshot for agent-
|
|
|
8455
8630
|
if (!post.allowed) {
|
|
8456
8631
|
if (!opts.output) {
|
|
8457
8632
|
try {
|
|
8458
|
-
|
|
8633
|
+
rmSync3(outputPath, { force: true });
|
|
8459
8634
|
} catch {
|
|
8460
8635
|
}
|
|
8461
8636
|
}
|
|
@@ -8594,7 +8769,7 @@ bridgeCommand.command("stop-recording").description(
|
|
|
8594
8769
|
}
|
|
8595
8770
|
const sessionId = opts.session;
|
|
8596
8771
|
const statePath = join17(tmpdir3(), `zam-recording-${sessionId}.json`);
|
|
8597
|
-
const { existsSync: existsSync25, readFileSync: readFileSync16, rmSync:
|
|
8772
|
+
const { existsSync: existsSync25, readFileSync: readFileSync16, rmSync: rmSync4 } = await import("fs");
|
|
8598
8773
|
if (!existsSync25(statePath)) {
|
|
8599
8774
|
jsonOut2({
|
|
8600
8775
|
sessionId,
|
|
@@ -8619,7 +8794,7 @@ bridgeCommand.command("stop-recording").description(
|
|
|
8619
8794
|
};
|
|
8620
8795
|
let attempts = 0;
|
|
8621
8796
|
while (isProcessRunning(pid) && attempts < 20) {
|
|
8622
|
-
await new Promise((
|
|
8797
|
+
await new Promise((resolve9) => setTimeout(resolve9, 250));
|
|
8623
8798
|
attempts++;
|
|
8624
8799
|
}
|
|
8625
8800
|
if (isProcessRunning(pid)) {
|
|
@@ -8629,7 +8804,7 @@ bridgeCommand.command("stop-recording").description(
|
|
|
8629
8804
|
}
|
|
8630
8805
|
}
|
|
8631
8806
|
try {
|
|
8632
|
-
|
|
8807
|
+
rmSync4(statePath, { force: true });
|
|
8633
8808
|
} catch {
|
|
8634
8809
|
}
|
|
8635
8810
|
if (!existsSync25(outputPath)) {
|
|
@@ -8658,7 +8833,7 @@ bridgeCommand.command("stop-recording").description(
|
|
|
8658
8833
|
return;
|
|
8659
8834
|
}
|
|
8660
8835
|
try {
|
|
8661
|
-
|
|
8836
|
+
rmSync4(outputPath, { force: true });
|
|
8662
8837
|
} catch {
|
|
8663
8838
|
}
|
|
8664
8839
|
jsonOut2({
|
|
@@ -8823,10 +8998,13 @@ bridgeCommand.command("desktop-bootstrap").description("Initialize first-run des
|
|
|
8823
8998
|
await withDb2(async (db) => {
|
|
8824
8999
|
const userId = await ensureDefaultUser(db, opts.user);
|
|
8825
9000
|
const { enabled, url, model, locale } = await getLlmConfig(db);
|
|
9001
|
+
const { workspaceDir, skillLinks } = await ensureDesktopWorkspace(db);
|
|
8826
9002
|
jsonOut2({
|
|
8827
9003
|
userId,
|
|
8828
9004
|
locale,
|
|
8829
|
-
llm: { enabled, url, model }
|
|
9005
|
+
llm: { enabled, url, model },
|
|
9006
|
+
workspaceDir,
|
|
9007
|
+
skillLinks
|
|
8830
9008
|
});
|
|
8831
9009
|
});
|
|
8832
9010
|
});
|
|
@@ -9510,7 +9688,7 @@ ZAM Auto-Stale Complete: Scanned ${changedFiles.length} file(s).`
|
|
|
9510
9688
|
});
|
|
9511
9689
|
|
|
9512
9690
|
// src/cli/commands/goal.ts
|
|
9513
|
-
import { existsSync as existsSync19, mkdirSync as
|
|
9691
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync11 } from "fs";
|
|
9514
9692
|
import { resolve as resolve6 } from "path";
|
|
9515
9693
|
import { input as input3 } from "@inquirer/prompts";
|
|
9516
9694
|
import { Command as Command8 } from "commander";
|
|
@@ -9636,7 +9814,7 @@ ${"\u2500".repeat(50)}`);
|
|
|
9636
9814
|
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) => {
|
|
9637
9815
|
const goalsDir = await resolveGoalsDir();
|
|
9638
9816
|
if (!existsSync19(goalsDir)) {
|
|
9639
|
-
|
|
9817
|
+
mkdirSync11(goalsDir, { recursive: true });
|
|
9640
9818
|
}
|
|
9641
9819
|
let slug = opts.slug;
|
|
9642
9820
|
let title = opts.title;
|
|
@@ -9695,9 +9873,9 @@ goalCommand.command("status <slug> <status>").description("Update a goal's statu
|
|
|
9695
9873
|
});
|
|
9696
9874
|
|
|
9697
9875
|
// src/cli/commands/init.ts
|
|
9698
|
-
import { existsSync as existsSync20, mkdirSync as
|
|
9876
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync12, writeFileSync as writeFileSync10 } from "fs";
|
|
9699
9877
|
import { homedir as homedir11 } from "os";
|
|
9700
|
-
import { join as join19 } from "path";
|
|
9878
|
+
import { join as join19, resolve as resolve7 } from "path";
|
|
9701
9879
|
import { confirm as confirm2, input as input4 } from "@inquirer/prompts";
|
|
9702
9880
|
import { Command as Command9 } from "commander";
|
|
9703
9881
|
var HOME2 = homedir11();
|
|
@@ -9705,9 +9883,9 @@ function printLine(char = "\u2550", len = 60, color = "\x1B[36m") {
|
|
|
9705
9883
|
console.log(`${color}${char.repeat(len)}\x1B[0m`);
|
|
9706
9884
|
}
|
|
9707
9885
|
function bootstrapSandboxWorkspace(workspaceDir) {
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
|
|
9886
|
+
mkdirSync12(join19(workspaceDir, "beliefs"), { recursive: true });
|
|
9887
|
+
mkdirSync12(join19(workspaceDir, "goals"), { recursive: true });
|
|
9888
|
+
mkdirSync12(join19(workspaceDir, "skills"), { recursive: true });
|
|
9711
9889
|
const worldviewFile = join19(workspaceDir, "beliefs", "worldview.md");
|
|
9712
9890
|
if (!existsSync20(worldviewFile)) {
|
|
9713
9891
|
writeFileSync10(
|
|
@@ -9747,12 +9925,21 @@ var initCommand = new Command9("init").description("Launch the guided interactiv
|
|
|
9747
9925
|
printLine();
|
|
9748
9926
|
console.log("\n\x1B[1m[1/5] Setting up Local Workspace Sandbox\x1B[0m");
|
|
9749
9927
|
const defaultWorkspace = join19(HOME2, "Documents", "zam");
|
|
9750
|
-
const workspacePath =
|
|
9751
|
-
|
|
9752
|
-
|
|
9753
|
-
|
|
9928
|
+
const workspacePath = resolve7(
|
|
9929
|
+
await input4({
|
|
9930
|
+
message: "Choose your ZAM workspace directory:",
|
|
9931
|
+
default: defaultWorkspace
|
|
9932
|
+
})
|
|
9933
|
+
);
|
|
9754
9934
|
try {
|
|
9755
9935
|
bootstrapSandboxWorkspace(workspacePath);
|
|
9936
|
+
wireSkills(workspacePath, parseSetupAgents());
|
|
9937
|
+
upsertConfiguredWorkspace({
|
|
9938
|
+
id: "personal",
|
|
9939
|
+
label: "Personal",
|
|
9940
|
+
kind: "personal",
|
|
9941
|
+
path: workspacePath
|
|
9942
|
+
});
|
|
9756
9943
|
console.log(
|
|
9757
9944
|
`\x1B[32m\u2713 Local Sandbox created at: ${workspacePath}\x1B[0m`
|
|
9758
9945
|
);
|
|
@@ -10732,7 +10919,7 @@ observerCommand.command("revoke <process>").description("Remove a process from o
|
|
|
10732
10919
|
|
|
10733
10920
|
// src/cli/commands/profile.ts
|
|
10734
10921
|
import { homedir as homedir12 } from "os";
|
|
10735
|
-
import { dirname as dirname6, join as join20, resolve as
|
|
10922
|
+
import { dirname as dirname6, join as join20, resolve as resolve8 } from "path";
|
|
10736
10923
|
import { Command as Command13 } from "commander";
|
|
10737
10924
|
var C2 = {
|
|
10738
10925
|
reset: "\x1B[0m",
|
|
@@ -10763,7 +10950,7 @@ var profileCommand = new Command13("profile").description("Show or change this m
|
|
|
10763
10950
|
if (opts.mode) setInstallMode(opts.mode);
|
|
10764
10951
|
db = await openDatabaseWithSync({ initialize: true });
|
|
10765
10952
|
if (opts.dir) {
|
|
10766
|
-
await setSetting(db, "personal.workspace_dir",
|
|
10953
|
+
await setSetting(db, "personal.workspace_dir", resolve8(opts.dir));
|
|
10767
10954
|
}
|
|
10768
10955
|
const personalDir = await getSetting(db, "personal.workspace_dir") || defaultPersonalDir();
|
|
10769
10956
|
await db.close();
|
|
@@ -11897,7 +12084,7 @@ skillCommand.command("add").description("Register a new agent skill").requiredOp
|
|
|
11897
12084
|
});
|
|
11898
12085
|
|
|
11899
12086
|
// src/cli/commands/snapshot.ts
|
|
11900
|
-
import { existsSync as existsSync22, mkdirSync as
|
|
12087
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync13, readFileSync as readFileSync13, writeFileSync as writeFileSync11 } from "fs";
|
|
11901
12088
|
import { homedir as homedir13 } from "os";
|
|
11902
12089
|
import { dirname as dirname7, join as join21 } from "path";
|
|
11903
12090
|
import { Command as Command19 } from "commander";
|
|
@@ -11931,7 +12118,7 @@ var exportCmd = new Command19("export").description("Write a portable SQL-text s
|
|
|
11931
12118
|
const out = opts.out ?? join21(personalDir, "snapshots", defaultOutName());
|
|
11932
12119
|
const dir = dirname7(out);
|
|
11933
12120
|
if (dir && dir !== "." && !existsSync22(dir)) {
|
|
11934
|
-
|
|
12121
|
+
mkdirSync13(dir, { recursive: true });
|
|
11935
12122
|
}
|
|
11936
12123
|
writeFileSync11(out, snapshot, "utf-8");
|
|
11937
12124
|
console.log(`Snapshot written: ${out}`);
|
|
@@ -12601,7 +12788,7 @@ ${C3.dim}After that, 'zam ui' launches it directly.${C3.reset}`
|
|
|
12601
12788
|
|
|
12602
12789
|
// src/cli/commands/update.ts
|
|
12603
12790
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
12604
|
-
import { existsSync as existsSync24, readFileSync as readFileSync14, realpathSync } from "fs";
|
|
12791
|
+
import { existsSync as existsSync24, readFileSync as readFileSync14, realpathSync as realpathSync2 } from "fs";
|
|
12605
12792
|
import { dirname as dirname9, join as join23 } from "path";
|
|
12606
12793
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
12607
12794
|
import { confirm as confirm4 } from "@inquirer/prompts";
|
|
@@ -12715,7 +12902,7 @@ var checkCmd = new Command23("check").description("Check whether a newer ZAM has
|
|
|
12715
12902
|
}
|
|
12716
12903
|
);
|
|
12717
12904
|
function findSourceRepo() {
|
|
12718
|
-
let dir =
|
|
12905
|
+
let dir = realpathSync2(dirname9(fileURLToPath4(import.meta.url)));
|
|
12719
12906
|
let parent = dirname9(dir);
|
|
12720
12907
|
while (parent !== dir) {
|
|
12721
12908
|
if (existsSync24(join23(dir, ".git"))) return dir;
|