zam-core 0.13.0 → 0.14.0
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 +804 -217
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/commands/mcp.js +565 -224
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/copilot-extension/extension.mjs +34 -0
- 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 +32 -1
- package/dist/index.js +70 -4
- package/dist/index.js.map +1 -1
- package/dist/ui/okf-panel.html +121 -21
- package/dist/vscode-extension/ZAM_Companion_0.14.0.vsix +0 -0
- package/dist/vscode-extension/extension.cjs +15 -15
- package/dist/vscode-extension/manifest.json +2 -2
- package/package.json +1 -1
- package/dist/vscode-extension/ZAM_Companion_0.13.0.vsix +0 -0
package/dist/cli/app.js
CHANGED
|
@@ -590,7 +590,13 @@ CREATE TABLE IF NOT EXISTS tokens (
|
|
|
590
590
|
-- validated in code, not via CHECK. Column default is 'llm' so unlabeled
|
|
591
591
|
-- writes (pre-M013 rows, old snapshot restores) count as LLM-era content;
|
|
592
592
|
-- createToken() defaults to 'manual' for API callers instead.
|
|
593
|
-
question_source TEXT NOT NULL DEFAULT 'llm'
|
|
593
|
+
question_source TEXT NOT NULL DEFAULT 'llm',
|
|
594
|
+
-- Maintenance state (ADR 2026-07-18): when set, the token's binding to
|
|
595
|
+
-- its source is unclear (e.g. stale source_link after an article split,
|
|
596
|
+
-- or an ambiguous re-import). Cards of a token in maintenance are
|
|
597
|
+
-- excluded from scheduling until repaired; learning state is preserved.
|
|
598
|
+
maintenance_at TEXT,
|
|
599
|
+
maintenance_reason TEXT
|
|
594
600
|
);
|
|
595
601
|
|
|
596
602
|
-- Prerequisite dependency graph: "to learn A, first know B"
|
|
@@ -1140,6 +1146,10 @@ async function runMigrations(db) {
|
|
|
1140
1146
|
`ALTER TABLE tokens ADD COLUMN question_source TEXT NOT NULL DEFAULT 'llm'`
|
|
1141
1147
|
);
|
|
1142
1148
|
}
|
|
1149
|
+
if (tokenCols.length > 0 && !tokenCols.some((c) => c.name === "maintenance_at")) {
|
|
1150
|
+
await db.exec(`ALTER TABLE tokens ADD COLUMN maintenance_at TEXT`);
|
|
1151
|
+
await db.exec(`ALTER TABLE tokens ADD COLUMN maintenance_reason TEXT`);
|
|
1152
|
+
}
|
|
1143
1153
|
}
|
|
1144
1154
|
var DEFAULT_DB_DIR, DEFAULT_DB_PATH, nodeRequire, TRANSIENT_REMOTE_ERROR_PATTERNS;
|
|
1145
1155
|
var init_connection = __esm({
|
|
@@ -1630,6 +1640,23 @@ async function updateCard(db, cardId, updates) {
|
|
|
1630
1640
|
}
|
|
1631
1641
|
return await db.prepare("SELECT * FROM cards WHERE id = ?").get(cardId);
|
|
1632
1642
|
}
|
|
1643
|
+
async function resetCardsForToken(db, tokenId, now) {
|
|
1644
|
+
const ts = now ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1645
|
+
const result = await db.prepare(
|
|
1646
|
+
`UPDATE cards SET
|
|
1647
|
+
stability = 0.0,
|
|
1648
|
+
difficulty = 0.5,
|
|
1649
|
+
elapsed_days = 0.0,
|
|
1650
|
+
scheduled_days = 0.0,
|
|
1651
|
+
reps = 0,
|
|
1652
|
+
lapses = 0,
|
|
1653
|
+
state = 'new',
|
|
1654
|
+
due_at = ?,
|
|
1655
|
+
last_review_at = NULL
|
|
1656
|
+
WHERE token_id = ?`
|
|
1657
|
+
).run(ts, tokenId);
|
|
1658
|
+
return result.changes;
|
|
1659
|
+
}
|
|
1633
1660
|
async function getCardDeletionImpact(db, tokenId, userId) {
|
|
1634
1661
|
const card = await getCard(db, tokenId, userId);
|
|
1635
1662
|
if (!card) {
|
|
@@ -1652,7 +1679,8 @@ async function getDueCards(db, userId, now, domain, knowledgeContext) {
|
|
|
1652
1679
|
let sql = `SELECT c.*, t.slug, t.concept, t.domain, t.bloom_level
|
|
1653
1680
|
FROM cards c
|
|
1654
1681
|
JOIN tokens t ON t.id = c.token_id
|
|
1655
|
-
WHERE c.user_id = ? AND c.blocked = 0 AND c.due_at <=
|
|
1682
|
+
WHERE c.user_id = ? AND c.blocked = 0 AND c.due_at <= ?
|
|
1683
|
+
AND t.maintenance_at IS NULL`;
|
|
1656
1684
|
const params = [userId, cutoff];
|
|
1657
1685
|
if (domain) {
|
|
1658
1686
|
sql += " AND t.domain = ?";
|
|
@@ -1953,6 +1981,38 @@ async function deprecateToken(db, slug) {
|
|
|
1953
1981
|
).run(now, now, slug);
|
|
1954
1982
|
return await getTokenBySlug(db, slug);
|
|
1955
1983
|
}
|
|
1984
|
+
async function getTokensBySourceLinkBase(db, base) {
|
|
1985
|
+
return await db.prepare(
|
|
1986
|
+
`SELECT * FROM tokens
|
|
1987
|
+
WHERE (source_link = ? OR source_link LIKE ? || '#%' ESCAPE '\\')
|
|
1988
|
+
AND deprecated_at IS NULL`
|
|
1989
|
+
).all(base, escapeLike(base));
|
|
1990
|
+
}
|
|
1991
|
+
function escapeLike(value) {
|
|
1992
|
+
return value.replace(/[\\%_]/g, (ch) => `\\${ch}`);
|
|
1993
|
+
}
|
|
1994
|
+
async function setTokenMaintenance(db, slug, reason) {
|
|
1995
|
+
const token = await getTokenBySlug(db, slug);
|
|
1996
|
+
if (!token) {
|
|
1997
|
+
throw new Error(`Token not found: ${slug}`);
|
|
1998
|
+
}
|
|
1999
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2000
|
+
await db.prepare(
|
|
2001
|
+
"UPDATE tokens SET maintenance_at = ?, maintenance_reason = ?, updated_at = ? WHERE slug = ?"
|
|
2002
|
+
).run(now, reason, now, slug);
|
|
2003
|
+
return await getTokenBySlug(db, slug);
|
|
2004
|
+
}
|
|
2005
|
+
async function clearTokenMaintenance(db, slug) {
|
|
2006
|
+
const token = await getTokenBySlug(db, slug);
|
|
2007
|
+
if (!token) {
|
|
2008
|
+
throw new Error(`Token not found: ${slug}`);
|
|
2009
|
+
}
|
|
2010
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2011
|
+
await db.prepare(
|
|
2012
|
+
"UPDATE tokens SET maintenance_at = NULL, maintenance_reason = NULL, updated_at = ? WHERE slug = ?"
|
|
2013
|
+
).run(now, slug);
|
|
2014
|
+
return await getTokenBySlug(db, slug);
|
|
2015
|
+
}
|
|
1956
2016
|
async function getTokenDeleteImpact(db, slug) {
|
|
1957
2017
|
const token = await getTokenBySlug(db, slug);
|
|
1958
2018
|
if (!token) {
|
|
@@ -5268,7 +5328,8 @@ async function buildReviewQueue(db, options) {
|
|
|
5268
5328
|
AND c.blocked = 0
|
|
5269
5329
|
AND c.due_at <= ?
|
|
5270
5330
|
AND c.state IN ('review', 'relearning', 'learning')
|
|
5271
|
-
AND t.deprecated_at IS NULL
|
|
5331
|
+
AND t.deprecated_at IS NULL
|
|
5332
|
+
AND t.maintenance_at IS NULL`;
|
|
5272
5333
|
const dueParams = [options.userId, nowISO];
|
|
5273
5334
|
if (options.knowledgeContext) {
|
|
5274
5335
|
dueSql += ` AND EXISTS (
|
|
@@ -5298,7 +5359,8 @@ async function buildReviewQueue(db, options) {
|
|
|
5298
5359
|
WHERE c.user_id = ?
|
|
5299
5360
|
AND c.blocked = 0
|
|
5300
5361
|
AND c.state = 'new'
|
|
5301
|
-
AND t.deprecated_at IS NULL
|
|
5362
|
+
AND t.deprecated_at IS NULL
|
|
5363
|
+
AND t.maintenance_at IS NULL`;
|
|
5302
5364
|
const newParams = [options.userId];
|
|
5303
5365
|
if (options.knowledgeContext) {
|
|
5304
5366
|
newSql += ` AND EXISTS (
|
|
@@ -6992,6 +7054,7 @@ __export(kernel_exports, {
|
|
|
6992
7054
|
clearADOCredentials: () => clearADOCredentials,
|
|
6993
7055
|
clearProviderApiKey: () => clearProviderApiKey,
|
|
6994
7056
|
clearReviewContextCache: () => clearReviewContextCache,
|
|
7057
|
+
clearTokenMaintenance: () => clearTokenMaintenance,
|
|
6995
7058
|
clearTursoCredentials: () => clearTursoCredentials,
|
|
6996
7059
|
compareVersions: () => compareVersions,
|
|
6997
7060
|
computeContentHash: () => computeContentHash,
|
|
@@ -7100,6 +7163,7 @@ __export(kernel_exports, {
|
|
|
7100
7163
|
getTokenDeleteImpact: () => getTokenDeleteImpact,
|
|
7101
7164
|
getTokenEmbedding: () => getTokenEmbedding,
|
|
7102
7165
|
getTokenNeighborhood: () => getTokenNeighborhood,
|
|
7166
|
+
getTokensBySourceLinkBase: () => getTokensBySourceLinkBase,
|
|
7103
7167
|
getTursoCredentials: () => getTursoCredentials,
|
|
7104
7168
|
getUiObservationPath: () => getUiObservationPath,
|
|
7105
7169
|
getUiObserverDir: () => getUiObserverDir,
|
|
@@ -7154,6 +7218,7 @@ __export(kernel_exports, {
|
|
|
7154
7218
|
readMonitorLog: () => readMonitorLog,
|
|
7155
7219
|
readUiObservationLog: () => readUiObservationLog,
|
|
7156
7220
|
removeConfiguredWorkspace: () => removeConfiguredWorkspace,
|
|
7221
|
+
resetCardsForToken: () => resetCardsForToken,
|
|
7157
7222
|
resolveAllBeliefPaths: () => resolveAllBeliefPaths,
|
|
7158
7223
|
resolveAllGoalPaths: () => resolveAllGoalPaths,
|
|
7159
7224
|
resolveObserverPolicy: () => resolveObserverPolicy,
|
|
@@ -7185,6 +7250,7 @@ __export(kernel_exports, {
|
|
|
7185
7250
|
setLastRepairedVersion: () => setLastRepairedVersion,
|
|
7186
7251
|
setProviderApiKey: () => setProviderApiKey,
|
|
7187
7252
|
setSetting: () => setSetting,
|
|
7253
|
+
setTokenMaintenance: () => setTokenMaintenance,
|
|
7188
7254
|
setTursoCredentials: () => setTursoCredentials,
|
|
7189
7255
|
slugify: () => slugify,
|
|
7190
7256
|
startSession: () => startSession,
|
|
@@ -7259,10 +7325,330 @@ var init_kernel = __esm({
|
|
|
7259
7325
|
}
|
|
7260
7326
|
});
|
|
7261
7327
|
|
|
7328
|
+
// src/cli/okf/bundle.ts
|
|
7329
|
+
var bundle_exports = {};
|
|
7330
|
+
__export(bundle_exports, {
|
|
7331
|
+
OKF_VERSION: () => OKF_VERSION,
|
|
7332
|
+
RESERVED_FILES: () => RESERVED_FILES,
|
|
7333
|
+
appendLog: () => appendLog,
|
|
7334
|
+
buildCatalog: () => buildCatalog,
|
|
7335
|
+
isReservedFile: () => isReservedFile,
|
|
7336
|
+
parseFrontmatter: () => parseFrontmatter,
|
|
7337
|
+
renderIndex: () => renderIndex,
|
|
7338
|
+
toCatalogEntry: () => toCatalogEntry,
|
|
7339
|
+
validateArticle: () => validateArticle
|
|
7340
|
+
});
|
|
7341
|
+
function isReservedFile(file) {
|
|
7342
|
+
return RESERVED_FILES.includes(file);
|
|
7343
|
+
}
|
|
7344
|
+
function unquote(raw) {
|
|
7345
|
+
const v = raw.trim();
|
|
7346
|
+
if (v.length >= 2 && (v.startsWith('"') && v.endsWith('"') || v.startsWith("'") && v.endsWith("'"))) {
|
|
7347
|
+
return v.slice(1, -1);
|
|
7348
|
+
}
|
|
7349
|
+
return v;
|
|
7350
|
+
}
|
|
7351
|
+
function parseFrontmatter(markdown) {
|
|
7352
|
+
const lines = markdown.split("\n");
|
|
7353
|
+
if (lines[0]?.trim() !== "---") {
|
|
7354
|
+
throw new Error("frontmatter: file must start with a --- fence");
|
|
7355
|
+
}
|
|
7356
|
+
const fields = {};
|
|
7357
|
+
let listKey = null;
|
|
7358
|
+
let i = 1;
|
|
7359
|
+
for (; i < lines.length; i++) {
|
|
7360
|
+
const line = lines[i];
|
|
7361
|
+
if (line.trim() === "---") break;
|
|
7362
|
+
if (line.trim() === "") continue;
|
|
7363
|
+
const listItem = /^\s+-\s+(.+)$/.exec(line);
|
|
7364
|
+
if (listItem) {
|
|
7365
|
+
if (!listKey) {
|
|
7366
|
+
throw new Error(`frontmatter line ${i + 1}: list item without a key`);
|
|
7367
|
+
}
|
|
7368
|
+
fields[listKey].push(unquote(listItem[1]));
|
|
7369
|
+
continue;
|
|
7370
|
+
}
|
|
7371
|
+
const pair = /^([A-Za-z0-9_-]+):(.*)$/.exec(line);
|
|
7372
|
+
if (!pair) {
|
|
7373
|
+
throw new Error(
|
|
7374
|
+
`frontmatter line ${i + 1}: expected "key: value" or "- item"`
|
|
7375
|
+
);
|
|
7376
|
+
}
|
|
7377
|
+
const key = pair[1];
|
|
7378
|
+
const rest = pair[2].trim();
|
|
7379
|
+
if (rest === "") {
|
|
7380
|
+
fields[key] = [];
|
|
7381
|
+
listKey = key;
|
|
7382
|
+
} else {
|
|
7383
|
+
fields[key] = unquote(rest);
|
|
7384
|
+
listKey = null;
|
|
7385
|
+
}
|
|
7386
|
+
}
|
|
7387
|
+
if (i >= lines.length) {
|
|
7388
|
+
throw new Error("frontmatter: missing closing --- fence");
|
|
7389
|
+
}
|
|
7390
|
+
return { fields, body: lines.slice(i + 1).join("\n") };
|
|
7391
|
+
}
|
|
7392
|
+
function scalar(fields, key) {
|
|
7393
|
+
const v = fields[key];
|
|
7394
|
+
return typeof v === "string" && v.trim() !== "" ? v.trim() : void 0;
|
|
7395
|
+
}
|
|
7396
|
+
function validateArticle(file, markdown) {
|
|
7397
|
+
const problems = [];
|
|
7398
|
+
if (isReservedFile(file)) {
|
|
7399
|
+
return { ok: false, problems: [`${file}: reserved OKF file name`] };
|
|
7400
|
+
}
|
|
7401
|
+
if (!FILE_NAME_RE.test(file)) {
|
|
7402
|
+
problems.push(`${file}: file name must be kebab-case and end in .md`);
|
|
7403
|
+
}
|
|
7404
|
+
let parsed = null;
|
|
7405
|
+
try {
|
|
7406
|
+
parsed = parseFrontmatter(markdown);
|
|
7407
|
+
} catch (err) {
|
|
7408
|
+
problems.push(
|
|
7409
|
+
`${file}: ${err instanceof Error ? err.message : String(err)}`
|
|
7410
|
+
);
|
|
7411
|
+
}
|
|
7412
|
+
if (parsed) {
|
|
7413
|
+
if (!scalar(parsed.fields, "type")) {
|
|
7414
|
+
problems.push(`${file}: frontmatter field "type" is required`);
|
|
7415
|
+
}
|
|
7416
|
+
if (!scalar(parsed.fields, "description")) {
|
|
7417
|
+
problems.push(`${file}: frontmatter field "description" is required`);
|
|
7418
|
+
}
|
|
7419
|
+
if (parsed.body.trim() === "") {
|
|
7420
|
+
problems.push(`${file}: article body is empty`);
|
|
7421
|
+
}
|
|
7422
|
+
}
|
|
7423
|
+
return { ok: problems.length === 0, problems };
|
|
7424
|
+
}
|
|
7425
|
+
function toCatalogEntry(file, markdown) {
|
|
7426
|
+
const { fields } = parseFrontmatter(markdown);
|
|
7427
|
+
const tags = Array.isArray(fields.tags) ? fields.tags : [];
|
|
7428
|
+
return {
|
|
7429
|
+
file,
|
|
7430
|
+
type: scalar(fields, "type") ?? "",
|
|
7431
|
+
title: scalar(fields, "title") ?? file.replace(/\.md$/, ""),
|
|
7432
|
+
description: scalar(fields, "description") ?? "",
|
|
7433
|
+
tags,
|
|
7434
|
+
resource: scalar(fields, "resource"),
|
|
7435
|
+
timestamp: scalar(fields, "timestamp")
|
|
7436
|
+
};
|
|
7437
|
+
}
|
|
7438
|
+
function buildCatalog(articles) {
|
|
7439
|
+
return articles.map(({ file, markdown }) => toCatalogEntry(file, markdown)).sort((a, b) => a.file.localeCompare(b.file));
|
|
7440
|
+
}
|
|
7441
|
+
function renderIndex(catalog, okfVersion = OKF_VERSION) {
|
|
7442
|
+
const types = [...new Set(catalog.map((e) => e.type))].sort();
|
|
7443
|
+
const sections = types.map((type) => {
|
|
7444
|
+
const rows = catalog.filter((e) => e.type === type).map((e) => `- [${e.title}](${e.file}) \u2014 ${e.description}`).join("\n");
|
|
7445
|
+
return `## ${type}
|
|
7446
|
+
|
|
7447
|
+
${rows}`;
|
|
7448
|
+
});
|
|
7449
|
+
return [
|
|
7450
|
+
"---",
|
|
7451
|
+
`okf_version: "${okfVersion}"`,
|
|
7452
|
+
"---",
|
|
7453
|
+
"",
|
|
7454
|
+
"# ZAM Knowledge Base",
|
|
7455
|
+
"",
|
|
7456
|
+
"Living reference knowledge for this repository in",
|
|
7457
|
+
"[Open Knowledge Format](https://github.com/GoogleCloudPlatform/knowledge-catalog).",
|
|
7458
|
+
"Current truth only \u2014 the *why* behind it lives in [../adr/](../adr/)",
|
|
7459
|
+
"(ADR 2026-07-17). Do not edit by hand: write through the",
|
|
7460
|
+
"`zam_okf_upsert` MCP tool.",
|
|
7461
|
+
"",
|
|
7462
|
+
sections.join("\n\n"),
|
|
7463
|
+
""
|
|
7464
|
+
].join("\n");
|
|
7465
|
+
}
|
|
7466
|
+
function appendLog(existing, date, line) {
|
|
7467
|
+
const header = `## ${date}`;
|
|
7468
|
+
const entry = `- ${line}`;
|
|
7469
|
+
const trimmed = existing.trim();
|
|
7470
|
+
if (trimmed === "") {
|
|
7471
|
+
return `# Log
|
|
7472
|
+
|
|
7473
|
+
${header}
|
|
7474
|
+
|
|
7475
|
+
${entry}
|
|
7476
|
+
`;
|
|
7477
|
+
}
|
|
7478
|
+
const lines = trimmed.split("\n");
|
|
7479
|
+
const firstHeaderIdx = lines.findIndex((l) => l.startsWith("## "));
|
|
7480
|
+
if (firstHeaderIdx !== -1 && lines[firstHeaderIdx].trim() === header) {
|
|
7481
|
+
lines.splice(firstHeaderIdx + 2, 0, entry);
|
|
7482
|
+
return `${lines.join("\n")}
|
|
7483
|
+
`;
|
|
7484
|
+
}
|
|
7485
|
+
const insertAt = firstHeaderIdx === -1 ? lines.length : firstHeaderIdx;
|
|
7486
|
+
lines.splice(insertAt, 0, header, "", entry, "");
|
|
7487
|
+
return `${lines.join("\n")}
|
|
7488
|
+
`;
|
|
7489
|
+
}
|
|
7490
|
+
var OKF_VERSION, RESERVED_FILES, FILE_NAME_RE;
|
|
7491
|
+
var init_bundle = __esm({
|
|
7492
|
+
"src/cli/okf/bundle.ts"() {
|
|
7493
|
+
"use strict";
|
|
7494
|
+
OKF_VERSION = "0.1";
|
|
7495
|
+
RESERVED_FILES = ["index.md", "log.md"];
|
|
7496
|
+
FILE_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*\.md$/;
|
|
7497
|
+
}
|
|
7498
|
+
});
|
|
7499
|
+
|
|
7500
|
+
// src/cli/okf/io.ts
|
|
7501
|
+
var io_exports = {};
|
|
7502
|
+
__export(io_exports, {
|
|
7503
|
+
DEFAULT_BUNDLE_DIR: () => DEFAULT_BUNDLE_DIR,
|
|
7504
|
+
findRepoRoot: () => findRepoRoot,
|
|
7505
|
+
loadBundle: () => loadBundle,
|
|
7506
|
+
resolveArticlePath: () => resolveArticlePath,
|
|
7507
|
+
resolveBundleDirFromRoots: () => resolveBundleDirFromRoots,
|
|
7508
|
+
resolveCitationPath: () => resolveCitationPath,
|
|
7509
|
+
upsertArticle: () => upsertArticle
|
|
7510
|
+
});
|
|
7511
|
+
import {
|
|
7512
|
+
existsSync as existsSync18,
|
|
7513
|
+
mkdirSync as mkdirSync12,
|
|
7514
|
+
readdirSync as readdirSync2,
|
|
7515
|
+
readFileSync as readFileSync14,
|
|
7516
|
+
realpathSync,
|
|
7517
|
+
writeFileSync as writeFileSync10
|
|
7518
|
+
} from "fs";
|
|
7519
|
+
import { dirname as dirname9, isAbsolute as isAbsolute2, join as join18, relative, resolve as resolve5, sep } from "path";
|
|
7520
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
7521
|
+
function resolveBundleDirFromRoots(rootUris, fallback) {
|
|
7522
|
+
const dirs = [];
|
|
7523
|
+
for (const uri of rootUris) {
|
|
7524
|
+
if (!uri?.startsWith("file:")) continue;
|
|
7525
|
+
try {
|
|
7526
|
+
dirs.push(join18(fileURLToPath5(uri), DEFAULT_BUNDLE_DIR));
|
|
7527
|
+
} catch {
|
|
7528
|
+
}
|
|
7529
|
+
}
|
|
7530
|
+
return dirs.find((dir) => existsSync18(dir)) ?? dirs[0] ?? fallback;
|
|
7531
|
+
}
|
|
7532
|
+
function resolveArticlePath(dir, file) {
|
|
7533
|
+
if (file.includes("/") || file.includes("\\") || file.includes("..")) {
|
|
7534
|
+
throw new Error(`invalid article file name: ${file}`);
|
|
7535
|
+
}
|
|
7536
|
+
if (isReservedFile(file)) {
|
|
7537
|
+
throw new Error(`refusing to address reserved file: ${file}`);
|
|
7538
|
+
}
|
|
7539
|
+
return join18(resolve5(dir), file);
|
|
7540
|
+
}
|
|
7541
|
+
function findRepoRoot(startDir) {
|
|
7542
|
+
let dir = resolve5(startDir);
|
|
7543
|
+
for (; ; ) {
|
|
7544
|
+
if (existsSync18(join18(dir, ".git"))) return dir;
|
|
7545
|
+
const parent = dirname9(dir);
|
|
7546
|
+
if (parent === dir) return resolve5(startDir, "..");
|
|
7547
|
+
dir = parent;
|
|
7548
|
+
}
|
|
7549
|
+
}
|
|
7550
|
+
function resolveCitationPath(bundleDir, target) {
|
|
7551
|
+
if (isAbsolute2(target)) {
|
|
7552
|
+
throw new Error(
|
|
7553
|
+
`invalid citation target: absolute paths are not allowed (${target})`
|
|
7554
|
+
);
|
|
7555
|
+
}
|
|
7556
|
+
if (!target.endsWith(".md")) {
|
|
7557
|
+
throw new Error(
|
|
7558
|
+
`invalid citation target: only .md files are readable (${target})`
|
|
7559
|
+
);
|
|
7560
|
+
}
|
|
7561
|
+
const root = findRepoRoot(bundleDir);
|
|
7562
|
+
const resolved = resolve5(bundleDir, target);
|
|
7563
|
+
assertContained(root, resolved, target);
|
|
7564
|
+
if (existsSync18(resolved)) {
|
|
7565
|
+
assertContained(realpathSync(root), realpathSync(resolved), target);
|
|
7566
|
+
}
|
|
7567
|
+
return resolved;
|
|
7568
|
+
}
|
|
7569
|
+
function assertContained(root, candidate, target) {
|
|
7570
|
+
const rel = relative(root, candidate);
|
|
7571
|
+
if (rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute2(rel)) {
|
|
7572
|
+
throw new Error(
|
|
7573
|
+
`invalid citation target: resolves outside the repository root (${target})`
|
|
7574
|
+
);
|
|
7575
|
+
}
|
|
7576
|
+
}
|
|
7577
|
+
function loadBundle(dir) {
|
|
7578
|
+
const root = resolve5(dir);
|
|
7579
|
+
let entries;
|
|
7580
|
+
try {
|
|
7581
|
+
entries = readdirSync2(root).filter(
|
|
7582
|
+
(name) => name.endsWith(".md") && !isReservedFile(name)
|
|
7583
|
+
);
|
|
7584
|
+
} catch {
|
|
7585
|
+
throw new Error(`OKF bundle directory not found: ${root}`);
|
|
7586
|
+
}
|
|
7587
|
+
const articles = entries.sort().map((file) => ({
|
|
7588
|
+
file,
|
|
7589
|
+
markdown: readFileSync14(join18(root, file), "utf8")
|
|
7590
|
+
}));
|
|
7591
|
+
const problems = articles.flatMap(
|
|
7592
|
+
({ file, markdown }) => validateArticle(file, markdown).problems
|
|
7593
|
+
);
|
|
7594
|
+
const catalog = problems.length === 0 ? buildCatalog(articles) : safeCatalog(articles);
|
|
7595
|
+
return { dir: root, articles, catalog, problems };
|
|
7596
|
+
}
|
|
7597
|
+
function safeCatalog(articles) {
|
|
7598
|
+
const entries = [];
|
|
7599
|
+
for (const { file, markdown } of articles) {
|
|
7600
|
+
try {
|
|
7601
|
+
entries.push(toCatalogEntry(file, markdown));
|
|
7602
|
+
} catch {
|
|
7603
|
+
}
|
|
7604
|
+
}
|
|
7605
|
+
return entries.sort((a, b) => a.file.localeCompare(b.file));
|
|
7606
|
+
}
|
|
7607
|
+
function upsertArticle(dir, file, markdown, today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)) {
|
|
7608
|
+
const target = resolveArticlePath(dir, file);
|
|
7609
|
+
const validation = validateArticle(file, markdown);
|
|
7610
|
+
if (!validation.ok) return { validation };
|
|
7611
|
+
const root = resolve5(dir);
|
|
7612
|
+
mkdirSync12(root, { recursive: true });
|
|
7613
|
+
let created = true;
|
|
7614
|
+
try {
|
|
7615
|
+
readFileSync14(target, "utf8");
|
|
7616
|
+
created = false;
|
|
7617
|
+
} catch {
|
|
7618
|
+
}
|
|
7619
|
+
writeFileSync10(target, markdown, "utf8");
|
|
7620
|
+
const bundle = loadBundle(root);
|
|
7621
|
+
writeFileSync10(join18(root, "index.md"), renderIndex(bundle.catalog), "utf8");
|
|
7622
|
+
let log = "";
|
|
7623
|
+
try {
|
|
7624
|
+
log = readFileSync14(join18(root, "log.md"), "utf8");
|
|
7625
|
+
} catch {
|
|
7626
|
+
}
|
|
7627
|
+
const entry = bundle.catalog.find((e) => e.file === file);
|
|
7628
|
+
writeFileSync10(
|
|
7629
|
+
join18(root, "log.md"),
|
|
7630
|
+
appendLog(
|
|
7631
|
+
log,
|
|
7632
|
+
today,
|
|
7633
|
+
`**${created ? "Creation" : "Update"}** \u2014 [${entry?.title ?? file}](${file})`
|
|
7634
|
+
),
|
|
7635
|
+
"utf8"
|
|
7636
|
+
);
|
|
7637
|
+
return { validation, entry, created };
|
|
7638
|
+
}
|
|
7639
|
+
var DEFAULT_BUNDLE_DIR;
|
|
7640
|
+
var init_io = __esm({
|
|
7641
|
+
"src/cli/okf/io.ts"() {
|
|
7642
|
+
"use strict";
|
|
7643
|
+
init_bundle();
|
|
7644
|
+
DEFAULT_BUNDLE_DIR = "docs/okf";
|
|
7645
|
+
}
|
|
7646
|
+
});
|
|
7647
|
+
|
|
7262
7648
|
// src/cli/app.ts
|
|
7263
|
-
import { readFileSync as
|
|
7264
|
-
import { dirname as
|
|
7265
|
-
import { fileURLToPath as
|
|
7649
|
+
import { readFileSync as readFileSync22 } from "fs";
|
|
7650
|
+
import { dirname as dirname15, join as join31 } from "path";
|
|
7651
|
+
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
7266
7652
|
import { Command as Command27 } from "commander";
|
|
7267
7653
|
|
|
7268
7654
|
// src/cli/commands/agent.ts
|
|
@@ -8600,9 +8986,9 @@ var agentCommand = new Command("agent").description("Provision and inspect the a
|
|
|
8600
8986
|
init_kernel();
|
|
8601
8987
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
8602
8988
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
8603
|
-
import { existsSync as
|
|
8989
|
+
import { existsSync as existsSync22, readdirSync as readdirSync3, readFileSync as readFileSync18, rmSync as rmSync3 } from "fs";
|
|
8604
8990
|
import { homedir as homedir14, tmpdir as tmpdir3 } from "os";
|
|
8605
|
-
import { join as
|
|
8991
|
+
import { join as join24, resolve as resolve7 } from "path";
|
|
8606
8992
|
import { Command as Command2 } from "commander";
|
|
8607
8993
|
import { ulid as ulid10 } from "ulid";
|
|
8608
8994
|
|
|
@@ -9689,7 +10075,7 @@ async function prepareRecallChain(db, opts) {
|
|
|
9689
10075
|
} else {
|
|
9690
10076
|
spawnLocalRunner(endpoint.url, endpoint.model, endpoint.runner);
|
|
9691
10077
|
while (Date.now() < deadline) {
|
|
9692
|
-
await new Promise((
|
|
10078
|
+
await new Promise((resolve13) => setTimeout(resolve13, 1e3));
|
|
9693
10079
|
if (await isLlmOnline(endpoint.url)) {
|
|
9694
10080
|
online = true;
|
|
9695
10081
|
break;
|
|
@@ -9982,7 +10368,7 @@ async function startLocalRunner(url, model, locale, hint) {
|
|
|
9982
10368
|
let attempts = 0;
|
|
9983
10369
|
const dotsPerLine = 30;
|
|
9984
10370
|
while (true) {
|
|
9985
|
-
await new Promise((
|
|
10371
|
+
await new Promise((resolve13) => setTimeout(resolve13, 500));
|
|
9986
10372
|
if (await isLlmOnline(url)) {
|
|
9987
10373
|
if (attempts > 0) process.stdout.write("\n");
|
|
9988
10374
|
return true;
|
|
@@ -10071,8 +10457,8 @@ async function fetchWithInteractiveTimeout(url, options = {}) {
|
|
|
10071
10457
|
const dotsPerLine = 30;
|
|
10072
10458
|
while (true) {
|
|
10073
10459
|
let timeoutId;
|
|
10074
|
-
const timeoutPromise = new Promise((
|
|
10075
|
-
timeoutId = setTimeout(() =>
|
|
10460
|
+
const timeoutPromise = new Promise((resolve13) => {
|
|
10461
|
+
timeoutId = setTimeout(() => resolve13("timeout"), timeoutMs);
|
|
10076
10462
|
});
|
|
10077
10463
|
const dotsInterval = setInterval(() => {
|
|
10078
10464
|
process.stdout.write(".");
|
|
@@ -10339,7 +10725,7 @@ async function readWebLink(url) {
|
|
|
10339
10725
|
redirect: "manual",
|
|
10340
10726
|
signal: controller.signal,
|
|
10341
10727
|
headers: {
|
|
10342
|
-
"User-Agent": "ZAM-Content-Studio/0.
|
|
10728
|
+
"User-Agent": "ZAM-Content-Studio/0.14.0"
|
|
10343
10729
|
}
|
|
10344
10730
|
});
|
|
10345
10731
|
if (res.status >= 300 && res.status < 400) {
|
|
@@ -10404,8 +10790,8 @@ async function readImageOCR(db, imagePath) {
|
|
|
10404
10790
|
|
|
10405
10791
|
// src/cli/bridge-handlers.ts
|
|
10406
10792
|
init_kernel();
|
|
10407
|
-
import { mkdirSync as
|
|
10408
|
-
import { join as
|
|
10793
|
+
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync11 } from "fs";
|
|
10794
|
+
import { join as join19 } from "path";
|
|
10409
10795
|
|
|
10410
10796
|
// src/cli/knowledge-contexts.ts
|
|
10411
10797
|
init_kernel();
|
|
@@ -11284,6 +11670,161 @@ async function addToken(db, params) {
|
|
|
11284
11670
|
possible_duplicates: possibleDuplicates
|
|
11285
11671
|
};
|
|
11286
11672
|
}
|
|
11673
|
+
async function importOkfTokens(db, params) {
|
|
11674
|
+
const userId = await resolveHandlerUser(db, params.user);
|
|
11675
|
+
if (!params.file?.trim()) throw new Error("file must be non-empty");
|
|
11676
|
+
if (!Array.isArray(params.tokens) || params.tokens.length === 0) {
|
|
11677
|
+
throw new Error("tokens must be a non-empty array");
|
|
11678
|
+
}
|
|
11679
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11680
|
+
for (const input8 of params.tokens) {
|
|
11681
|
+
const slug = input8.slug?.trim();
|
|
11682
|
+
if (!slug || !input8.concept?.trim()) {
|
|
11683
|
+
throw new Error("every token needs a non-empty slug and concept");
|
|
11684
|
+
}
|
|
11685
|
+
if (seen.has(slug)) throw new Error(`duplicate token slug: ${slug}`);
|
|
11686
|
+
seen.add(slug);
|
|
11687
|
+
if (input8.bloomLevel !== void 0 && (!Number.isInteger(input8.bloomLevel) || input8.bloomLevel < 1 || input8.bloomLevel > 5)) {
|
|
11688
|
+
throw new Error(`bloomLevel must be 1-5 (token: ${slug})`);
|
|
11689
|
+
}
|
|
11690
|
+
}
|
|
11691
|
+
const { DEFAULT_BUNDLE_DIR: DEFAULT_BUNDLE_DIR2, resolveArticlePath: resolveArticlePath2 } = await Promise.resolve().then(() => (init_io(), io_exports));
|
|
11692
|
+
const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_bundle(), bundle_exports));
|
|
11693
|
+
const { readFileSync: readFileSync23 } = await import("fs");
|
|
11694
|
+
const bundleDir = params.bundleDir ?? DEFAULT_BUNDLE_DIR2;
|
|
11695
|
+
const articlePath = resolveArticlePath2(bundleDir, params.file);
|
|
11696
|
+
let markdown;
|
|
11697
|
+
try {
|
|
11698
|
+
markdown = readFileSync23(articlePath, "utf8");
|
|
11699
|
+
} catch {
|
|
11700
|
+
throw new Error(`Article not found: ${articlePath}`);
|
|
11701
|
+
}
|
|
11702
|
+
let resource;
|
|
11703
|
+
try {
|
|
11704
|
+
const { fields } = parseFrontmatter2(markdown);
|
|
11705
|
+
resource = typeof fields.resource === "string" ? fields.resource : void 0;
|
|
11706
|
+
} catch {
|
|
11707
|
+
resource = void 0;
|
|
11708
|
+
}
|
|
11709
|
+
const sourceBase = resource ?? articlePath;
|
|
11710
|
+
const sourceLinkFor = (anchor) => anchor?.trim() ? `${sourceBase}#${anchor.trim()}` : sourceBase;
|
|
11711
|
+
const created = [];
|
|
11712
|
+
const updated = [];
|
|
11713
|
+
const replaced = [];
|
|
11714
|
+
const maintenance = [];
|
|
11715
|
+
let cardsEnsured = 0;
|
|
11716
|
+
await db.transaction(async (tx) => {
|
|
11717
|
+
const inImport = /* @__PURE__ */ new Map();
|
|
11718
|
+
for (const input8 of params.tokens) {
|
|
11719
|
+
const mode = input8.mode ?? "new";
|
|
11720
|
+
const existing = await getTokenBySlug(tx, input8.slug);
|
|
11721
|
+
if (mode === "new") {
|
|
11722
|
+
if (existing) {
|
|
11723
|
+
throw new Error(
|
|
11724
|
+
`Token '${input8.slug}' already exists. Use mode "update" to refresh it (keeps learning state), "replace" if the concept changed (resets learning state), or choose a different slug.`
|
|
11725
|
+
);
|
|
11726
|
+
}
|
|
11727
|
+
const token2 = await createToken(tx, {
|
|
11728
|
+
slug: input8.slug,
|
|
11729
|
+
title: input8.title,
|
|
11730
|
+
concept: input8.concept,
|
|
11731
|
+
domain: input8.domain,
|
|
11732
|
+
bloom_level: input8.bloomLevel ?? 1,
|
|
11733
|
+
source_link: sourceLinkFor(input8.anchor),
|
|
11734
|
+
question: input8.question ?? null,
|
|
11735
|
+
question_source: input8.question ? "llm" : void 0
|
|
11736
|
+
});
|
|
11737
|
+
inImport.set(input8.slug, token2);
|
|
11738
|
+
created.push(input8.slug);
|
|
11739
|
+
} else {
|
|
11740
|
+
if (!existing) {
|
|
11741
|
+
throw new Error(
|
|
11742
|
+
`Token '${input8.slug}' does not exist \u2014 mode "${mode}" requires an existing token.`
|
|
11743
|
+
);
|
|
11744
|
+
}
|
|
11745
|
+
const updates = {
|
|
11746
|
+
concept: input8.concept,
|
|
11747
|
+
source_link: sourceLinkFor(input8.anchor)
|
|
11748
|
+
};
|
|
11749
|
+
if (input8.title !== void 0) updates.title = input8.title;
|
|
11750
|
+
if (input8.domain !== void 0) updates.domain = input8.domain;
|
|
11751
|
+
if (input8.bloomLevel !== void 0) {
|
|
11752
|
+
updates.bloom_level = input8.bloomLevel;
|
|
11753
|
+
}
|
|
11754
|
+
if (input8.question !== void 0) {
|
|
11755
|
+
updates.question = input8.question;
|
|
11756
|
+
updates.question_source = input8.question ? "llm" : void 0;
|
|
11757
|
+
}
|
|
11758
|
+
const token2 = await updateToken(tx, input8.slug, updates);
|
|
11759
|
+
if (existing.maintenance_at) {
|
|
11760
|
+
await clearTokenMaintenance(tx, input8.slug);
|
|
11761
|
+
}
|
|
11762
|
+
if (mode === "replace") {
|
|
11763
|
+
await resetCardsForToken(tx, token2.id);
|
|
11764
|
+
replaced.push(input8.slug);
|
|
11765
|
+
} else {
|
|
11766
|
+
updated.push(input8.slug);
|
|
11767
|
+
}
|
|
11768
|
+
inImport.set(input8.slug, token2);
|
|
11769
|
+
}
|
|
11770
|
+
const ctxNames = parseKnowledgeContextNames(input8.knowledgeContexts);
|
|
11771
|
+
const assigned = await resolveKnowledgeContexts(tx, ctxNames);
|
|
11772
|
+
const token = inImport.get(input8.slug);
|
|
11773
|
+
if (token) {
|
|
11774
|
+
for (const context of assigned) {
|
|
11775
|
+
await assignTokenToContext(tx, token.id, context.id);
|
|
11776
|
+
}
|
|
11777
|
+
}
|
|
11778
|
+
}
|
|
11779
|
+
for (const input8 of params.tokens) {
|
|
11780
|
+
const token = inImport.get(input8.slug);
|
|
11781
|
+
if (!token) continue;
|
|
11782
|
+
const prereqSlugs = [
|
|
11783
|
+
...new Set(
|
|
11784
|
+
(input8.prerequisites ?? []).map((s) => s.trim()).filter(Boolean)
|
|
11785
|
+
)
|
|
11786
|
+
];
|
|
11787
|
+
for (const prereqSlug of prereqSlugs) {
|
|
11788
|
+
const target = inImport.get(prereqSlug) ?? await getTokenBySlug(tx, prereqSlug);
|
|
11789
|
+
if (!target) {
|
|
11790
|
+
throw new Error(
|
|
11791
|
+
`Prerequisite token not found: ${prereqSlug} (for '${input8.slug}')`
|
|
11792
|
+
);
|
|
11793
|
+
}
|
|
11794
|
+
await addPrerequisite(tx, token.id, target.id);
|
|
11795
|
+
}
|
|
11796
|
+
}
|
|
11797
|
+
for (const token of inImport.values()) {
|
|
11798
|
+
await ensureCard(tx, token.id, userId);
|
|
11799
|
+
cardsEnsured++;
|
|
11800
|
+
}
|
|
11801
|
+
const prior = await getTokensBySourceLinkBase(tx, sourceBase);
|
|
11802
|
+
for (const token of prior) {
|
|
11803
|
+
if (!inImport.has(token.slug)) {
|
|
11804
|
+
await setTokenMaintenance(
|
|
11805
|
+
tx,
|
|
11806
|
+
token.slug,
|
|
11807
|
+
`absent from re-import of ${params.file}`
|
|
11808
|
+
);
|
|
11809
|
+
maintenance.push(token.slug);
|
|
11810
|
+
}
|
|
11811
|
+
}
|
|
11812
|
+
});
|
|
11813
|
+
try {
|
|
11814
|
+
await ensureTokenEmbeddings(db, { limit: 16 });
|
|
11815
|
+
} catch {
|
|
11816
|
+
}
|
|
11817
|
+
return {
|
|
11818
|
+
success: true,
|
|
11819
|
+
user: userId,
|
|
11820
|
+
article: { file: params.file, source_link: sourceBase },
|
|
11821
|
+
created,
|
|
11822
|
+
updated,
|
|
11823
|
+
replaced,
|
|
11824
|
+
maintenance,
|
|
11825
|
+
cards: cardsEnsured
|
|
11826
|
+
};
|
|
11827
|
+
}
|
|
11287
11828
|
async function findTokens2(db, params) {
|
|
11288
11829
|
const userId = await resolveHandlerUser(db, params.user);
|
|
11289
11830
|
if (!params.context.trim()) {
|
|
@@ -11580,11 +12121,11 @@ async function backupCreate(db, params) {
|
|
|
11580
12121
|
const targetDir = params.dir || (await ensureActiveWorkspace(db)).path;
|
|
11581
12122
|
const snapshot = await exportSnapshot(db);
|
|
11582
12123
|
const manifest = verifySnapshot(snapshot);
|
|
11583
|
-
const backupDir =
|
|
11584
|
-
|
|
12124
|
+
const backupDir = join19(targetDir, "zam-backups");
|
|
12125
|
+
mkdirSync13(backupDir, { recursive: true });
|
|
11585
12126
|
const stamp = manifest.createdAt.replace(/[:.]/g, "-");
|
|
11586
|
-
const path =
|
|
11587
|
-
|
|
12127
|
+
const path = join19(backupDir, `zam-snapshot-${stamp}.sql`);
|
|
12128
|
+
writeFileSync11(path, snapshot, "utf-8");
|
|
11588
12129
|
return {
|
|
11589
12130
|
ok: true,
|
|
11590
12131
|
path,
|
|
@@ -11608,16 +12149,16 @@ async function updateCheck(params) {
|
|
|
11608
12149
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
11609
12150
|
import {
|
|
11610
12151
|
chmodSync as chmodSync2,
|
|
11611
|
-
existsSync as
|
|
11612
|
-
mkdirSync as
|
|
11613
|
-
readFileSync as
|
|
11614
|
-
writeFileSync as
|
|
12152
|
+
existsSync as existsSync19,
|
|
12153
|
+
mkdirSync as mkdirSync14,
|
|
12154
|
+
readFileSync as readFileSync15,
|
|
12155
|
+
writeFileSync as writeFileSync12
|
|
11615
12156
|
} from "fs";
|
|
11616
12157
|
import { homedir as homedir13 } from "os";
|
|
11617
|
-
import { delimiter as delimiter2, join as
|
|
12158
|
+
import { delimiter as delimiter2, join as join20, resolve as resolve6 } from "path";
|
|
11618
12159
|
var BUILT_CLI_PATTERN = /[\\/]dist[\\/]cli[\\/]index\.js$/;
|
|
11619
12160
|
function normalizeForCompare(path, platform) {
|
|
11620
|
-
const resolved =
|
|
12161
|
+
const resolved = resolve6(path);
|
|
11621
12162
|
return platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
11622
12163
|
}
|
|
11623
12164
|
function windowsShimContent(nodePath, cliPath) {
|
|
@@ -11657,14 +12198,14 @@ function ensureWindowsUserPath(binDir) {
|
|
|
11657
12198
|
return output.endsWith("updated");
|
|
11658
12199
|
}
|
|
11659
12200
|
function ensureUnixUserPath(home, platform) {
|
|
11660
|
-
const profile =
|
|
11661
|
-
const existing =
|
|
12201
|
+
const profile = join20(home, platform === "darwin" ? ".zprofile" : ".profile");
|
|
12202
|
+
const existing = existsSync19(profile) ? readFileSync15(profile, "utf8") : "";
|
|
11662
12203
|
if (existing.includes(".zam/bin")) return false;
|
|
11663
12204
|
const block = `
|
|
11664
12205
|
# Added by ZAM: keep the zam CLI on PATH
|
|
11665
12206
|
export PATH="$HOME/.zam/bin:$PATH"
|
|
11666
12207
|
`;
|
|
11667
|
-
|
|
12208
|
+
writeFileSync12(profile, existing + block, "utf8");
|
|
11668
12209
|
return true;
|
|
11669
12210
|
}
|
|
11670
12211
|
function installCliShim(options = {}) {
|
|
@@ -11672,10 +12213,10 @@ function installCliShim(options = {}) {
|
|
|
11672
12213
|
const platform = options.platform ?? process.platform;
|
|
11673
12214
|
const env = options.env ?? process.env;
|
|
11674
12215
|
const find = options.find ?? findExecutable;
|
|
11675
|
-
const nodePath =
|
|
11676
|
-
const cliPath =
|
|
11677
|
-
const binDir =
|
|
11678
|
-
const shimPath =
|
|
12216
|
+
const nodePath = resolve6(options.nodePath ?? process.execPath);
|
|
12217
|
+
const cliPath = resolve6(options.cliPath ?? process.argv[1] ?? "");
|
|
12218
|
+
const binDir = join20(home, ".zam", "bin");
|
|
12219
|
+
const shimPath = join20(binDir, platform === "win32" ? "zam.cmd" : "zam");
|
|
11679
12220
|
const report = {
|
|
11680
12221
|
status: "ok",
|
|
11681
12222
|
binDir,
|
|
@@ -11686,7 +12227,7 @@ function installCliShim(options = {}) {
|
|
|
11686
12227
|
pathUpdated: false,
|
|
11687
12228
|
needsNewTerminal: false
|
|
11688
12229
|
};
|
|
11689
|
-
if (!BUILT_CLI_PATTERN.test(cliPath) || !
|
|
12230
|
+
if (!BUILT_CLI_PATTERN.test(cliPath) || !existsSync19(cliPath)) {
|
|
11690
12231
|
report.status = "skipped";
|
|
11691
12232
|
report.detail = `Not running from a built CLI entry (${cliPath}); nothing to link.`;
|
|
11692
12233
|
return report;
|
|
@@ -11700,11 +12241,11 @@ function installCliShim(options = {}) {
|
|
|
11700
12241
|
return report;
|
|
11701
12242
|
}
|
|
11702
12243
|
const content = platform === "win32" ? windowsShimContent(nodePath, cliPath) : unixShimContent(nodePath, cliPath);
|
|
11703
|
-
const existed =
|
|
11704
|
-
const changed = !existed ||
|
|
12244
|
+
const existed = existsSync19(shimPath);
|
|
12245
|
+
const changed = !existed || readFileSync15(shimPath, "utf8") !== content;
|
|
11705
12246
|
if (changed) {
|
|
11706
|
-
|
|
11707
|
-
|
|
12247
|
+
mkdirSync14(binDir, { recursive: true });
|
|
12248
|
+
writeFileSync12(shimPath, content, "utf8");
|
|
11708
12249
|
if (platform !== "win32") chmodSync2(shimPath, 493);
|
|
11709
12250
|
}
|
|
11710
12251
|
report.status = !existed ? "installed" : changed ? "refreshed" : "ok";
|
|
@@ -28413,40 +28954,40 @@ function getCurriculumProvider(id) {
|
|
|
28413
28954
|
|
|
28414
28955
|
// src/cli/install-repair.ts
|
|
28415
28956
|
init_kernel();
|
|
28416
|
-
import { existsSync as
|
|
28957
|
+
import { existsSync as existsSync21 } from "fs";
|
|
28417
28958
|
|
|
28418
28959
|
// src/cli/provisioning/index.ts
|
|
28419
28960
|
import {
|
|
28420
|
-
existsSync as
|
|
28961
|
+
existsSync as existsSync20,
|
|
28421
28962
|
lstatSync as lstatSync2,
|
|
28422
|
-
mkdirSync as
|
|
28423
|
-
readFileSync as
|
|
28424
|
-
realpathSync,
|
|
28963
|
+
mkdirSync as mkdirSync15,
|
|
28964
|
+
readFileSync as readFileSync16,
|
|
28965
|
+
realpathSync as realpathSync2,
|
|
28425
28966
|
rmSync as rmSync2,
|
|
28426
28967
|
symlinkSync,
|
|
28427
|
-
writeFileSync as
|
|
28968
|
+
writeFileSync as writeFileSync13
|
|
28428
28969
|
} from "fs";
|
|
28429
|
-
import { basename as basename4, dirname as
|
|
28430
|
-
import { fileURLToPath as
|
|
28970
|
+
import { basename as basename4, dirname as dirname10, join as join21 } from "path";
|
|
28971
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
28431
28972
|
var packageRoot3 = [
|
|
28432
|
-
|
|
28433
|
-
|
|
28434
|
-
].find((candidate) =>
|
|
28973
|
+
fileURLToPath6(new URL("../..", import.meta.url)),
|
|
28974
|
+
fileURLToPath6(new URL("../../..", import.meta.url))
|
|
28975
|
+
].find((candidate) => existsSync20(join21(candidate, "package.json"))) ?? fileURLToPath6(new URL("../..", import.meta.url));
|
|
28435
28976
|
var ALL_SETUP_AGENTS = ["claude", "copilot", "codex", "agent"];
|
|
28436
28977
|
var SKILL_PAIRS = [
|
|
28437
28978
|
{
|
|
28438
|
-
from:
|
|
28439
|
-
to:
|
|
28979
|
+
from: join21(packageRoot3, ".claude", "skills", "zam", "SKILL.md"),
|
|
28980
|
+
to: join21(".claude", "skills", "zam", "SKILL.md"),
|
|
28440
28981
|
agents: ["claude", "copilot"]
|
|
28441
28982
|
},
|
|
28442
28983
|
{
|
|
28443
|
-
from:
|
|
28444
|
-
to:
|
|
28984
|
+
from: join21(packageRoot3, ".agent", "skills", "zam", "SKILL.md"),
|
|
28985
|
+
to: join21(".agent", "skills", "zam", "SKILL.md"),
|
|
28445
28986
|
agents: ["agent"]
|
|
28446
28987
|
},
|
|
28447
28988
|
{
|
|
28448
|
-
from:
|
|
28449
|
-
to:
|
|
28989
|
+
from: join21(packageRoot3, ".agents", "skills", "zam", "SKILL.md"),
|
|
28990
|
+
to: join21(".agents", "skills", "zam", "SKILL.md"),
|
|
28450
28991
|
agents: ["codex"]
|
|
28451
28992
|
}
|
|
28452
28993
|
];
|
|
@@ -28490,7 +29031,7 @@ function isSymbolicLink(path) {
|
|
|
28490
29031
|
}
|
|
28491
29032
|
}
|
|
28492
29033
|
function comparableRealPath(path) {
|
|
28493
|
-
const real =
|
|
29034
|
+
const real = realpathSync2(path);
|
|
28494
29035
|
return process.platform === "win32" ? real.toLowerCase() : real;
|
|
28495
29036
|
}
|
|
28496
29037
|
function pathsResolveToSameDirectory(sourceDir, destinationDir) {
|
|
@@ -28506,15 +29047,15 @@ function linkPointsTo(sourceDir, destinationDir) {
|
|
|
28506
29047
|
function isZamSkillCopy(destinationDir) {
|
|
28507
29048
|
try {
|
|
28508
29049
|
if (!lstatSync2(destinationDir).isDirectory()) return false;
|
|
28509
|
-
const skillFile =
|
|
28510
|
-
if (!
|
|
28511
|
-
return /^name:\s*zam\s*$/m.test(
|
|
29050
|
+
const skillFile = join21(destinationDir, "SKILL.md");
|
|
29051
|
+
if (!existsSync20(skillFile)) return false;
|
|
29052
|
+
return /^name:\s*zam\s*$/m.test(readFileSync16(skillFile, "utf8"));
|
|
28512
29053
|
} catch {
|
|
28513
29054
|
return false;
|
|
28514
29055
|
}
|
|
28515
29056
|
}
|
|
28516
29057
|
function classifySkillDestination(sourceDir, destinationDir) {
|
|
28517
|
-
if (!
|
|
29058
|
+
if (!existsSync20(sourceDir)) return "source-missing";
|
|
28518
29059
|
if (!isSymbolicLink(destinationDir) && pathsResolveToSameDirectory(sourceDir, destinationDir)) {
|
|
28519
29060
|
return "source-directory";
|
|
28520
29061
|
}
|
|
@@ -28531,9 +29072,9 @@ function wireSkills(cwd = process.cwd(), agents = parseSetupAgents(), opts = {})
|
|
|
28531
29072
|
};
|
|
28532
29073
|
for (const { from, to, agents: pairAgents } of SKILL_PAIRS) {
|
|
28533
29074
|
if (!pairAgents.some((agent) => agents.has(agent))) continue;
|
|
28534
|
-
const sourceDir =
|
|
28535
|
-
const destinationDir =
|
|
28536
|
-
const label =
|
|
29075
|
+
const sourceDir = dirname10(from);
|
|
29076
|
+
const destinationDir = dirname10(join21(cwd, to));
|
|
29077
|
+
const label = dirname10(to);
|
|
28537
29078
|
const state = classifySkillDestination(sourceDir, destinationDir);
|
|
28538
29079
|
if (state === "source-missing") {
|
|
28539
29080
|
if (!opts.quiet) {
|
|
@@ -28590,7 +29131,7 @@ function wireSkills(cwd = process.cwd(), agents = parseSetupAgents(), opts = {})
|
|
|
28590
29131
|
if (destinationExists) {
|
|
28591
29132
|
rmSync2(destinationDir, { recursive: true, force: true });
|
|
28592
29133
|
}
|
|
28593
|
-
|
|
29134
|
+
mkdirSync15(dirname10(destinationDir), { recursive: true });
|
|
28594
29135
|
symlinkSync(
|
|
28595
29136
|
sourceDir,
|
|
28596
29137
|
destinationDir,
|
|
@@ -28606,8 +29147,8 @@ function inspectSkillLinks(cwd = process.cwd(), agents = parseSetupAgents()) {
|
|
|
28606
29147
|
const results = [];
|
|
28607
29148
|
for (const { from, to, agents: pairAgents } of SKILL_PAIRS) {
|
|
28608
29149
|
if (!pairAgents.some((agent) => agents.has(agent))) continue;
|
|
28609
|
-
const sourceDir =
|
|
28610
|
-
const destinationDir =
|
|
29150
|
+
const sourceDir = dirname10(from);
|
|
29151
|
+
const destinationDir = dirname10(join21(cwd, to));
|
|
28611
29152
|
results.push({
|
|
28612
29153
|
agents: pairAgents,
|
|
28613
29154
|
source: sourceDir,
|
|
@@ -28633,15 +29174,15 @@ function upsertMarkedBlock(dest, blockBody, dryRun) {
|
|
|
28633
29174
|
const block = `${ZAM_BLOCK_START}
|
|
28634
29175
|
${blockBody.trim()}
|
|
28635
29176
|
${ZAM_BLOCK_END}`;
|
|
28636
|
-
if (!
|
|
29177
|
+
if (!existsSync20(dest)) {
|
|
28637
29178
|
if (!dryRun) {
|
|
28638
|
-
|
|
28639
|
-
|
|
29179
|
+
mkdirSync15(dirname10(dest), { recursive: true });
|
|
29180
|
+
writeFileSync13(dest, `${block}
|
|
28640
29181
|
`, "utf8");
|
|
28641
29182
|
}
|
|
28642
29183
|
return "write";
|
|
28643
29184
|
}
|
|
28644
|
-
const existing =
|
|
29185
|
+
const existing = readFileSync16(dest, "utf8");
|
|
28645
29186
|
if (existing.includes(block)) return "skip";
|
|
28646
29187
|
const start = existing.indexOf(ZAM_BLOCK_START);
|
|
28647
29188
|
const end = existing.indexOf(ZAM_BLOCK_END);
|
|
@@ -28649,7 +29190,7 @@ ${ZAM_BLOCK_END}`;
|
|
|
28649
29190
|
|
|
28650
29191
|
${block}
|
|
28651
29192
|
`;
|
|
28652
|
-
if (!dryRun)
|
|
29193
|
+
if (!dryRun) writeFileSync13(dest, next, "utf8");
|
|
28653
29194
|
return start >= 0 && end > start ? "update" : "write";
|
|
28654
29195
|
}
|
|
28655
29196
|
function logInstructionAction(action, label, dryRun) {
|
|
@@ -28661,8 +29202,8 @@ function logInstructionAction(action, label, dryRun) {
|
|
|
28661
29202
|
}
|
|
28662
29203
|
function writeClaudeMd(skipClaudeMd, cwd = process.cwd(), opts = {}) {
|
|
28663
29204
|
if (skipClaudeMd) return;
|
|
28664
|
-
const dest =
|
|
28665
|
-
if (
|
|
29205
|
+
const dest = join21(cwd, "CLAUDE.md");
|
|
29206
|
+
if (existsSync20(dest)) {
|
|
28666
29207
|
if (!opts.updateExisting) {
|
|
28667
29208
|
console.log(` skip CLAUDE.md (already present)`);
|
|
28668
29209
|
return;
|
|
@@ -28705,14 +29246,14 @@ Use \`zam connector setup turso\` to store cloud credentials in
|
|
|
28705
29246
|
if (opts.dryRun) {
|
|
28706
29247
|
console.log(` would write CLAUDE.md`);
|
|
28707
29248
|
} else {
|
|
28708
|
-
|
|
29249
|
+
writeFileSync13(dest, content, "utf8");
|
|
28709
29250
|
console.log(` write CLAUDE.md`);
|
|
28710
29251
|
}
|
|
28711
29252
|
}
|
|
28712
29253
|
function writeAgentsMd(skipAgentsMd, cwd = process.cwd(), opts = {}) {
|
|
28713
29254
|
if (skipAgentsMd) return;
|
|
28714
|
-
const dest =
|
|
28715
|
-
if (
|
|
29255
|
+
const dest = join21(cwd, "AGENTS.md");
|
|
29256
|
+
if (existsSync20(dest)) {
|
|
28716
29257
|
if (!opts.updateExisting) {
|
|
28717
29258
|
console.log(` skip AGENTS.md (already present)`);
|
|
28718
29259
|
return;
|
|
@@ -28762,12 +29303,12 @@ Codex discovers repository skills under \`.agents/skills/\`. Run
|
|
|
28762
29303
|
if (opts.dryRun) {
|
|
28763
29304
|
console.log(` would write AGENTS.md`);
|
|
28764
29305
|
} else {
|
|
28765
|
-
|
|
29306
|
+
writeFileSync13(dest, content, "utf8");
|
|
28766
29307
|
console.log(` write AGENTS.md`);
|
|
28767
29308
|
}
|
|
28768
29309
|
}
|
|
28769
29310
|
function writeCopilotInstructions(cwd = process.cwd(), opts = {}) {
|
|
28770
|
-
const dest =
|
|
29311
|
+
const dest = join21(cwd, ".github", "copilot-instructions.md");
|
|
28771
29312
|
const action = upsertMarkedBlock(
|
|
28772
29313
|
dest,
|
|
28773
29314
|
`## ZAM learning sessions
|
|
@@ -28795,7 +29336,7 @@ function defaultRepairWorkspaces() {
|
|
|
28795
29336
|
try {
|
|
28796
29337
|
const agents = parseSetupAgents();
|
|
28797
29338
|
for (const workspace of getConfiguredWorkspaces()) {
|
|
28798
|
-
if (!
|
|
29339
|
+
if (!existsSync21(workspace.path)) {
|
|
28799
29340
|
summary.missing += 1;
|
|
28800
29341
|
continue;
|
|
28801
29342
|
}
|
|
@@ -28983,9 +29524,9 @@ function validateModelSave(entry, probe, now = () => (/* @__PURE__ */ new Date()
|
|
|
28983
29524
|
// src/cli/llm/vision.ts
|
|
28984
29525
|
init_kernel();
|
|
28985
29526
|
import { randomBytes } from "crypto";
|
|
28986
|
-
import { readFileSync as
|
|
29527
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
28987
29528
|
import { tmpdir as tmpdir2 } from "os";
|
|
28988
|
-
import { basename as basename5, join as
|
|
29529
|
+
import { basename as basename5, join as join22 } from "path";
|
|
28989
29530
|
var LANGUAGE_NAMES2 = {
|
|
28990
29531
|
en: "English",
|
|
28991
29532
|
de: "German",
|
|
@@ -29021,25 +29562,25 @@ async function observeUiSnapshotViaLLM(db, input8) {
|
|
|
29021
29562
|
const imageUrls = [];
|
|
29022
29563
|
const isVideo = /\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(input8.imagePath);
|
|
29023
29564
|
if (isVideo) {
|
|
29024
|
-
const { mkdirSync:
|
|
29565
|
+
const { mkdirSync: mkdirSync20, readdirSync: readdirSync4, rmSync: rmSync4 } = await import("fs");
|
|
29025
29566
|
const { execSync: execSync6 } = await import("child_process");
|
|
29026
|
-
const tempDir =
|
|
29567
|
+
const tempDir = join22(
|
|
29027
29568
|
tmpdir2(),
|
|
29028
29569
|
`zam-frames-${randomBytes(4).toString("hex")}`
|
|
29029
29570
|
);
|
|
29030
|
-
|
|
29571
|
+
mkdirSync20(tempDir, { recursive: true });
|
|
29031
29572
|
try {
|
|
29032
29573
|
execSync6(
|
|
29033
29574
|
`ffmpeg -i "${input8.imagePath}" -vf "fps=1/3,scale=1280:-1" -vsync vfr "${tempDir}/frame_%03d.png"`,
|
|
29034
29575
|
{ stdio: "ignore" }
|
|
29035
29576
|
);
|
|
29036
|
-
let files =
|
|
29577
|
+
let files = readdirSync4(tempDir).filter((f) => f.endsWith(".png")).sort();
|
|
29037
29578
|
if (files.length === 0) {
|
|
29038
29579
|
execSync6(
|
|
29039
29580
|
`ffmpeg -i "${input8.imagePath}" -vframes 1 "${tempDir}/frame_001.png"`,
|
|
29040
29581
|
{ stdio: "ignore" }
|
|
29041
29582
|
);
|
|
29042
|
-
files =
|
|
29583
|
+
files = readdirSync4(tempDir).filter((f) => f.endsWith(".png")).sort();
|
|
29043
29584
|
}
|
|
29044
29585
|
const maxFrames = cfg.maxFrames ?? 100;
|
|
29045
29586
|
let sampledFiles = files;
|
|
@@ -29056,7 +29597,7 @@ async function observeUiSnapshotViaLLM(db, input8) {
|
|
|
29056
29597
|
}
|
|
29057
29598
|
}
|
|
29058
29599
|
for (const file of sampledFiles) {
|
|
29059
|
-
const bytes =
|
|
29600
|
+
const bytes = readFileSync17(join22(tempDir, file));
|
|
29060
29601
|
imageUrls.push(`data:image/png;base64,${bytes.toString("base64")}`);
|
|
29061
29602
|
}
|
|
29062
29603
|
} finally {
|
|
@@ -29066,7 +29607,7 @@ async function observeUiSnapshotViaLLM(db, input8) {
|
|
|
29066
29607
|
}
|
|
29067
29608
|
}
|
|
29068
29609
|
} else {
|
|
29069
|
-
const imageBytes =
|
|
29610
|
+
const imageBytes = readFileSync17(input8.imagePath);
|
|
29070
29611
|
const ext = input8.imagePath.split(".").pop()?.toLowerCase();
|
|
29071
29612
|
const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : "image/png";
|
|
29072
29613
|
imageUrls.push(`data:${mime};base64,${imageBytes.toString("base64")}`);
|
|
@@ -29577,13 +30118,13 @@ async function resolveUser(opts, db, resolveOpts) {
|
|
|
29577
30118
|
}
|
|
29578
30119
|
|
|
29579
30120
|
// src/cli/workspaces/backup.ts
|
|
29580
|
-
import { mkdirSync as
|
|
29581
|
-
import { join as
|
|
30121
|
+
import { mkdirSync as mkdirSync16 } from "fs";
|
|
30122
|
+
import { join as join23 } from "path";
|
|
29582
30123
|
async function backupDatabaseTo(db, targetDir) {
|
|
29583
|
-
const backupDir =
|
|
29584
|
-
|
|
30124
|
+
const backupDir = join23(targetDir, "zam-backups");
|
|
30125
|
+
mkdirSync16(backupDir, { recursive: true });
|
|
29585
30126
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
29586
|
-
const dest =
|
|
30127
|
+
const dest = join23(backupDir, `zam-${stamp}.db`);
|
|
29587
30128
|
await db.exec(`VACUUM INTO '${dest.replace(/'/g, "''")}'`);
|
|
29588
30129
|
return dest;
|
|
29589
30130
|
}
|
|
@@ -29717,20 +30258,20 @@ bridgeCommand.command("workspace-info").description("Report the workspace dir, i
|
|
|
29717
30258
|
activeWorkspace,
|
|
29718
30259
|
workspaceDir: activeWorkspace.path,
|
|
29719
30260
|
defaultWorkspaceDir: defaultWorkspaceDir(),
|
|
29720
|
-
dataDir:
|
|
30261
|
+
dataDir: join24(homedir14(), ".zam")
|
|
29721
30262
|
});
|
|
29722
30263
|
});
|
|
29723
30264
|
});
|
|
29724
30265
|
function provisionConfiguredWorkspaces() {
|
|
29725
30266
|
return getConfiguredWorkspaces().flatMap(
|
|
29726
|
-
(workspace) =>
|
|
30267
|
+
(workspace) => existsSync22(workspace.path) ? wireSkills(workspace.path, parseSetupAgents(), { quiet: true }) : []
|
|
29727
30268
|
);
|
|
29728
30269
|
}
|
|
29729
30270
|
function buildWorkspaceLinkHealth(workspaces) {
|
|
29730
30271
|
const agents = parseSetupAgents();
|
|
29731
30272
|
const map = {};
|
|
29732
30273
|
for (const workspace of workspaces) {
|
|
29733
|
-
if (!
|
|
30274
|
+
if (!existsSync22(workspace.path)) continue;
|
|
29734
30275
|
const links = inspectSkillLinks(workspace.path, agents);
|
|
29735
30276
|
map[workspace.id] = {
|
|
29736
30277
|
health: summarizeSkillLinkHealth(links),
|
|
@@ -29760,7 +30301,7 @@ bridgeCommand.command("workspace-list").description("List configured ZAM workspa
|
|
|
29760
30301
|
activeWorkspace,
|
|
29761
30302
|
workspaceDir: activeWorkspace.path,
|
|
29762
30303
|
defaultWorkspaceDir: defaultWorkspaceDir(),
|
|
29763
|
-
dataDir:
|
|
30304
|
+
dataDir: join24(homedir14(), ".zam"),
|
|
29764
30305
|
linkHealth: buildWorkspaceLinkHealth(workspaces)
|
|
29765
30306
|
});
|
|
29766
30307
|
});
|
|
@@ -29775,7 +30316,7 @@ bridgeCommand.command("workspace-repair-links").description(
|
|
|
29775
30316
|
if (!id) jsonError("A non-empty --id is required");
|
|
29776
30317
|
const workspace = getConfiguredWorkspaces().find((item) => item.id === id);
|
|
29777
30318
|
if (!workspace) jsonError(`Workspace "${id}" is not configured`);
|
|
29778
|
-
if (!
|
|
30319
|
+
if (!existsSync22(workspace.path)) {
|
|
29779
30320
|
jsonError(`Workspace path does not exist: ${workspace.path}`);
|
|
29780
30321
|
}
|
|
29781
30322
|
const agents = parseSetupAgents(opts.agents);
|
|
@@ -29806,8 +30347,8 @@ function parseBridgeWorkspaceKind(value) {
|
|
|
29806
30347
|
bridgeCommand.command("workspace-add").description("Register an existing directory as a ZAM workspace (JSON)").requiredOption("--path <dir>", "Existing workspace/repository directory").option("--id <id>", "Workspace id").option("--label <label>", "Human-readable label").option("--kind <kind>", "Workspace kind", "custom").action(async (opts) => {
|
|
29807
30348
|
const raw = String(opts.path ?? "").trim();
|
|
29808
30349
|
if (!raw) jsonError("A non-empty --path is required");
|
|
29809
|
-
const path =
|
|
29810
|
-
if (!
|
|
30350
|
+
const path = resolve7(raw);
|
|
30351
|
+
if (!existsSync22(path)) jsonError(`Workspace path does not exist: ${path}`);
|
|
29811
30352
|
const id = opts.id ? String(opts.id).trim() : void 0;
|
|
29812
30353
|
if (opts.id && !id) jsonError("A non-empty --id is required");
|
|
29813
30354
|
const kind = parseBridgeWorkspaceKind(opts.kind);
|
|
@@ -29851,8 +30392,8 @@ bridgeCommand.command("workspace-remove").description("Unregister a ZAM workspac
|
|
|
29851
30392
|
bridgeCommand.command("set-workspace-dir").description("Set the personal workspace directory (JSON)").requiredOption("--dir <path>", "Path to the workspace directory").action(async (opts) => {
|
|
29852
30393
|
const raw = String(opts.dir ?? "").trim();
|
|
29853
30394
|
if (!raw) jsonError("A non-empty --dir is required");
|
|
29854
|
-
const dir =
|
|
29855
|
-
if (!
|
|
30395
|
+
const dir = resolve7(raw);
|
|
30396
|
+
if (!existsSync22(dir)) jsonError(`Workspace path does not exist: ${dir}`);
|
|
29856
30397
|
const skillLinks = wireSkills(dir, parseSetupAgents(), { quiet: true });
|
|
29857
30398
|
await withOptionalDb2(async (db) => {
|
|
29858
30399
|
const workspace = await activateWorkspacePath(db, dir);
|
|
@@ -29915,7 +30456,7 @@ bridgeCommand.command("agent-open").description("Launch an agent harness in the
|
|
|
29915
30456
|
jsonError(`Workspace is not configured: ${opts.workspace}`);
|
|
29916
30457
|
}
|
|
29917
30458
|
const activeWorkspace = await ensureActiveWorkspace(db);
|
|
29918
|
-
const workspace = opts.dir ?
|
|
30459
|
+
const workspace = opts.dir ? existsSync22(opts.dir) ? opts.dir : homedir14() : existingWorkspaceDirOrHome(configuredWorkspace ?? activeWorkspace);
|
|
29919
30460
|
launchHarness(harness, {
|
|
29920
30461
|
executable,
|
|
29921
30462
|
workspace,
|
|
@@ -30191,6 +30732,52 @@ bridgeCommand.command("add-token").description("Create a token + card from JSON
|
|
|
30191
30732
|
jsonError(err.message);
|
|
30192
30733
|
}
|
|
30193
30734
|
});
|
|
30735
|
+
bridgeCommand.command("okf-import").description(
|
|
30736
|
+
"Record an agent's decomposition of an OKF article as learning tokens (JSON stdin, ADR 2026-07-18)"
|
|
30737
|
+
).option("--user <id>", "User ID (default: whoami)").action(async (opts) => {
|
|
30738
|
+
try {
|
|
30739
|
+
let raw;
|
|
30740
|
+
if (isServeMode) {
|
|
30741
|
+
raw = serveStdinPayload ?? "";
|
|
30742
|
+
} else {
|
|
30743
|
+
const chunks = [];
|
|
30744
|
+
for await (const chunk of process.stdin) {
|
|
30745
|
+
chunks.push(chunk);
|
|
30746
|
+
}
|
|
30747
|
+
raw = Buffer.concat(chunks).toString("utf-8").trim();
|
|
30748
|
+
}
|
|
30749
|
+
if (!raw) {
|
|
30750
|
+
jsonError(
|
|
30751
|
+
"No input received on stdin. Pipe JSON: { file, bundle_dir?, tokens: [...] }"
|
|
30752
|
+
);
|
|
30753
|
+
}
|
|
30754
|
+
let data;
|
|
30755
|
+
try {
|
|
30756
|
+
data = JSON.parse(raw);
|
|
30757
|
+
} catch {
|
|
30758
|
+
jsonError("Invalid JSON input");
|
|
30759
|
+
}
|
|
30760
|
+
if (!data?.file || !Array.isArray(data?.tokens)) {
|
|
30761
|
+
jsonError("JSON must include 'file' and a 'tokens' array");
|
|
30762
|
+
}
|
|
30763
|
+
await withDb2(async (db) => {
|
|
30764
|
+
try {
|
|
30765
|
+
const userId = await resolveUser(opts, db, { json: true });
|
|
30766
|
+
const result = await importOkfTokens(db, {
|
|
30767
|
+
user: userId,
|
|
30768
|
+
bundleDir: data.bundle_dir,
|
|
30769
|
+
file: data.file,
|
|
30770
|
+
tokens: data.tokens
|
|
30771
|
+
});
|
|
30772
|
+
jsonOut2(result);
|
|
30773
|
+
} catch (err) {
|
|
30774
|
+
jsonError(err.message);
|
|
30775
|
+
}
|
|
30776
|
+
});
|
|
30777
|
+
} catch (err) {
|
|
30778
|
+
jsonError(err.message);
|
|
30779
|
+
}
|
|
30780
|
+
});
|
|
30194
30781
|
bridgeCommand.command("relevant-tokens").description("Find tokens relevant to a given context").option("--user <id>", "User ID (default: whoami)").action(async (opts) => {
|
|
30195
30782
|
try {
|
|
30196
30783
|
let raw;
|
|
@@ -30287,10 +30874,10 @@ bridgeCommand.command("discover-skills").description(
|
|
|
30287
30874
|
"20"
|
|
30288
30875
|
).action(async (opts) => {
|
|
30289
30876
|
try {
|
|
30290
|
-
const monitorDir =
|
|
30877
|
+
const monitorDir = join24(homedir14(), ".zam", "monitor");
|
|
30291
30878
|
let files;
|
|
30292
30879
|
try {
|
|
30293
|
-
files =
|
|
30880
|
+
files = readdirSync3(monitorDir).filter((f) => f.endsWith(".jsonl"));
|
|
30294
30881
|
} catch {
|
|
30295
30882
|
jsonOut2({ proposals: [], message: "No monitor logs found." });
|
|
30296
30883
|
return;
|
|
@@ -30300,7 +30887,7 @@ bridgeCommand.command("discover-skills").description(
|
|
|
30300
30887
|
return;
|
|
30301
30888
|
}
|
|
30302
30889
|
const limit = Number(opts.limit);
|
|
30303
|
-
const sorted = files.map((f) => ({ name: f, path:
|
|
30890
|
+
const sorted = files.map((f) => ({ name: f, path: join24(monitorDir, f) })).sort((a, b) => b.name.localeCompare(a.name)).slice(0, limit);
|
|
30304
30891
|
const sessionCommands = /* @__PURE__ */ new Map();
|
|
30305
30892
|
for (const file of sorted) {
|
|
30306
30893
|
const sessionId = file.name.replace(".jsonl", "");
|
|
@@ -30802,7 +31389,7 @@ bridgeCommand.command("capture-ui").description("Capture a screenshot for agent-
|
|
|
30802
31389
|
return;
|
|
30803
31390
|
}
|
|
30804
31391
|
}
|
|
30805
|
-
const outputPath = opts.image ?? opts.output ??
|
|
31392
|
+
const outputPath = opts.image ?? opts.output ?? join24(tmpdir3(), `zam-capture-${randomBytes2(4).toString("hex")}.png`);
|
|
30806
31393
|
const captureResult = isProvided ? { method: "provided", target: null } : captureScreenshot(outputPath, opts.hwnd, opts.processName);
|
|
30807
31394
|
if (!isProvided) {
|
|
30808
31395
|
const post = decidePostCapture(policy, {
|
|
@@ -30830,7 +31417,7 @@ bridgeCommand.command("capture-ui").description("Capture a screenshot for agent-
|
|
|
30830
31417
|
return;
|
|
30831
31418
|
}
|
|
30832
31419
|
}
|
|
30833
|
-
const imageBytes =
|
|
31420
|
+
const imageBytes = readFileSync18(outputPath);
|
|
30834
31421
|
const base64 = imageBytes.toString("base64");
|
|
30835
31422
|
jsonOut2({
|
|
30836
31423
|
sessionId: opts.session ?? null,
|
|
@@ -30857,11 +31444,11 @@ bridgeCommand.command("start-recording").description("Start screen recording in
|
|
|
30857
31444
|
return;
|
|
30858
31445
|
}
|
|
30859
31446
|
const sessionId = opts.session;
|
|
30860
|
-
const statePath =
|
|
31447
|
+
const statePath = join24(tmpdir3(), `zam-recording-${sessionId}.json`);
|
|
30861
31448
|
const defaultExt = platform === "win32" ? ".mkv" : ".mov";
|
|
30862
|
-
const outputPath = opts.output ??
|
|
30863
|
-
const { existsSync:
|
|
30864
|
-
if (
|
|
31449
|
+
const outputPath = opts.output ?? join24(tmpdir3(), `zam-recording-${sessionId}${defaultExt}`);
|
|
31450
|
+
const { existsSync: existsSync31, writeFileSync: writeFileSync18, openSync, closeSync } = await import("fs");
|
|
31451
|
+
if (existsSync31(statePath)) {
|
|
30865
31452
|
jsonOut2({
|
|
30866
31453
|
sessionId,
|
|
30867
31454
|
started: false,
|
|
@@ -30869,7 +31456,7 @@ bridgeCommand.command("start-recording").description("Start screen recording in
|
|
|
30869
31456
|
});
|
|
30870
31457
|
return;
|
|
30871
31458
|
}
|
|
30872
|
-
const logPath =
|
|
31459
|
+
const logPath = join24(tmpdir3(), `zam-recording-${sessionId}.log`);
|
|
30873
31460
|
let logFd;
|
|
30874
31461
|
try {
|
|
30875
31462
|
logFd = openSync(logPath, "w");
|
|
@@ -30915,7 +31502,7 @@ bridgeCommand.command("start-recording").description("Start screen recording in
|
|
|
30915
31502
|
}
|
|
30916
31503
|
child.unref();
|
|
30917
31504
|
if (child.pid) {
|
|
30918
|
-
|
|
31505
|
+
writeFileSync18(
|
|
30919
31506
|
statePath,
|
|
30920
31507
|
JSON.stringify({
|
|
30921
31508
|
pid: child.pid,
|
|
@@ -30951,9 +31538,9 @@ bridgeCommand.command("stop-recording").description(
|
|
|
30951
31538
|
return;
|
|
30952
31539
|
}
|
|
30953
31540
|
const sessionId = opts.session;
|
|
30954
|
-
const statePath =
|
|
30955
|
-
const { existsSync:
|
|
30956
|
-
if (!
|
|
31541
|
+
const statePath = join24(tmpdir3(), `zam-recording-${sessionId}.json`);
|
|
31542
|
+
const { existsSync: existsSync31, readFileSync: readFileSync23, rmSync: rmSync4 } = await import("fs");
|
|
31543
|
+
if (!existsSync31(statePath)) {
|
|
30957
31544
|
jsonOut2({
|
|
30958
31545
|
sessionId,
|
|
30959
31546
|
stopped: false,
|
|
@@ -30961,7 +31548,7 @@ bridgeCommand.command("stop-recording").description(
|
|
|
30961
31548
|
});
|
|
30962
31549
|
return;
|
|
30963
31550
|
}
|
|
30964
|
-
const state = JSON.parse(
|
|
31551
|
+
const state = JSON.parse(readFileSync23(statePath, "utf8"));
|
|
30965
31552
|
const { pid, outputPath } = state;
|
|
30966
31553
|
try {
|
|
30967
31554
|
process.kill(pid, "SIGINT");
|
|
@@ -30977,7 +31564,7 @@ bridgeCommand.command("stop-recording").description(
|
|
|
30977
31564
|
};
|
|
30978
31565
|
let attempts = 0;
|
|
30979
31566
|
while (isProcessRunning(pid) && attempts < 20) {
|
|
30980
|
-
await new Promise((
|
|
31567
|
+
await new Promise((resolve13) => setTimeout(resolve13, 250));
|
|
30981
31568
|
attempts++;
|
|
30982
31569
|
}
|
|
30983
31570
|
if (isProcessRunning(pid)) {
|
|
@@ -30990,7 +31577,7 @@ bridgeCommand.command("stop-recording").description(
|
|
|
30990
31577
|
rmSync4(statePath, { force: true });
|
|
30991
31578
|
} catch {
|
|
30992
31579
|
}
|
|
30993
|
-
if (!
|
|
31580
|
+
if (!existsSync31(outputPath)) {
|
|
30994
31581
|
jsonOut2({
|
|
30995
31582
|
sessionId,
|
|
30996
31583
|
stopped: false,
|
|
@@ -31420,7 +32007,7 @@ bridgeCommand.command("cloud-model-hint").description("Suggest a cloud model for
|
|
|
31420
32007
|
});
|
|
31421
32008
|
bridgeCommand.command("local-llm-hints").description("Detect installed local LLM servers and suggest defaults (JSON)").action(() => {
|
|
31422
32009
|
const profile = getSystemProfile();
|
|
31423
|
-
const flmInstalled = hasCommand("flm") ||
|
|
32010
|
+
const flmInstalled = hasCommand("flm") || existsSync22("C:\\Program Files\\flm\\flm.exe");
|
|
31424
32011
|
const ollamaInstalled = isOllamaInstalled();
|
|
31425
32012
|
const runners = [
|
|
31426
32013
|
{ id: "flm", label: "FastFlowLM", installed: flmInstalled },
|
|
@@ -34645,26 +35232,26 @@ var doctorCommand = new Command5("doctor").description(
|
|
|
34645
35232
|
// src/cli/commands/git-sync.ts
|
|
34646
35233
|
init_kernel();
|
|
34647
35234
|
import { execSync as execSync5 } from "child_process";
|
|
34648
|
-
import { chmodSync as chmodSync3, existsSync as
|
|
34649
|
-
import { join as
|
|
35235
|
+
import { chmodSync as chmodSync3, existsSync as existsSync23, writeFileSync as writeFileSync14 } from "fs";
|
|
35236
|
+
import { join as join25 } from "path";
|
|
34650
35237
|
import { Command as Command6 } from "commander";
|
|
34651
35238
|
function installHook2() {
|
|
34652
|
-
const gitDir =
|
|
34653
|
-
if (!
|
|
35239
|
+
const gitDir = join25(process.cwd(), ".git");
|
|
35240
|
+
if (!existsSync23(gitDir)) {
|
|
34654
35241
|
console.error(
|
|
34655
35242
|
"Error: Current directory is not the root of a Git repository."
|
|
34656
35243
|
);
|
|
34657
35244
|
process.exit(1);
|
|
34658
35245
|
}
|
|
34659
|
-
const hooksDir =
|
|
34660
|
-
const hookPath =
|
|
35246
|
+
const hooksDir = join25(gitDir, "hooks");
|
|
35247
|
+
const hookPath = join25(hooksDir, "post-commit");
|
|
34661
35248
|
const hookContent = `#!/bin/sh
|
|
34662
35249
|
# ZAM Spaced Repetition Auto-Stale Hook
|
|
34663
35250
|
# Triggered automatically on git commits to decay modified concept cards.
|
|
34664
35251
|
zam git-sync --commit HEAD --quiet
|
|
34665
35252
|
`;
|
|
34666
35253
|
try {
|
|
34667
|
-
|
|
35254
|
+
writeFileSync14(hookPath, hookContent, { encoding: "utf-8", flag: "w" });
|
|
34668
35255
|
try {
|
|
34669
35256
|
chmodSync3(hookPath, "755");
|
|
34670
35257
|
} catch (_e) {
|
|
@@ -34767,8 +35354,8 @@ ZAM Auto-Stale Complete: Scanned ${changedFiles.length} file(s).`
|
|
|
34767
35354
|
|
|
34768
35355
|
// src/cli/commands/goal.ts
|
|
34769
35356
|
init_kernel();
|
|
34770
|
-
import { existsSync as
|
|
34771
|
-
import { resolve as
|
|
35357
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync17 } from "fs";
|
|
35358
|
+
import { resolve as resolve8 } from "path";
|
|
34772
35359
|
import { input as input2 } from "@inquirer/prompts";
|
|
34773
35360
|
import { Command as Command7 } from "commander";
|
|
34774
35361
|
async function resolveGoalsDir() {
|
|
@@ -34781,7 +35368,7 @@ async function resolveGoalsDir() {
|
|
|
34781
35368
|
} finally {
|
|
34782
35369
|
await db?.close();
|
|
34783
35370
|
}
|
|
34784
|
-
return goalsDir ?
|
|
35371
|
+
return goalsDir ? resolve8(goalsDir) : resolve8("goals");
|
|
34785
35372
|
}
|
|
34786
35373
|
var goalCommand = new Command7("goal").description(
|
|
34787
35374
|
"Manage learning goals (markdown files)"
|
|
@@ -34791,7 +35378,7 @@ goalCommand.command("list").description("List all goals").option(
|
|
|
34791
35378
|
"Filter by status (active, completed, paused, abandoned)"
|
|
34792
35379
|
).option("--tree", "Show goals as a tree with parent/child relationships").option("--json", "Output as JSON").action(async (opts) => {
|
|
34793
35380
|
const goalsDir = await resolveGoalsDir();
|
|
34794
|
-
if (!
|
|
35381
|
+
if (!existsSync24(goalsDir)) {
|
|
34795
35382
|
console.error(`Goals directory not found: ${goalsDir}`);
|
|
34796
35383
|
console.error(
|
|
34797
35384
|
"Set it with: zam settings set personal.goals_dir /path/to/goals"
|
|
@@ -34892,8 +35479,8 @@ ${"\u2500".repeat(50)}`);
|
|
|
34892
35479
|
});
|
|
34893
35480
|
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) => {
|
|
34894
35481
|
const goalsDir = await resolveGoalsDir();
|
|
34895
|
-
if (!
|
|
34896
|
-
|
|
35482
|
+
if (!existsSync24(goalsDir)) {
|
|
35483
|
+
mkdirSync17(goalsDir, { recursive: true });
|
|
34897
35484
|
}
|
|
34898
35485
|
let slug = opts.slug;
|
|
34899
35486
|
let title = opts.title;
|
|
@@ -34953,9 +35540,9 @@ goalCommand.command("status <slug> <status>").description("Update a goal's statu
|
|
|
34953
35540
|
|
|
34954
35541
|
// src/cli/commands/init.ts
|
|
34955
35542
|
init_kernel();
|
|
34956
|
-
import { existsSync as
|
|
35543
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync18, writeFileSync as writeFileSync15 } from "fs";
|
|
34957
35544
|
import { homedir as homedir15 } from "os";
|
|
34958
|
-
import { join as
|
|
35545
|
+
import { join as join26, resolve as resolve9 } from "path";
|
|
34959
35546
|
import { confirm, input as input3 } from "@inquirer/prompts";
|
|
34960
35547
|
import { Command as Command8 } from "commander";
|
|
34961
35548
|
var HOME2 = homedir15();
|
|
@@ -34963,12 +35550,12 @@ function printLine(char = "\u2550", len = 60, color = "\x1B[36m") {
|
|
|
34963
35550
|
console.log(`${color}${char.repeat(len)}\x1B[0m`);
|
|
34964
35551
|
}
|
|
34965
35552
|
function bootstrapSandboxWorkspace(workspaceDir) {
|
|
34966
|
-
|
|
34967
|
-
|
|
34968
|
-
|
|
34969
|
-
const worldviewFile =
|
|
34970
|
-
if (!
|
|
34971
|
-
|
|
35553
|
+
mkdirSync18(join26(workspaceDir, "beliefs"), { recursive: true });
|
|
35554
|
+
mkdirSync18(join26(workspaceDir, "goals"), { recursive: true });
|
|
35555
|
+
mkdirSync18(join26(workspaceDir, "skills"), { recursive: true });
|
|
35556
|
+
const worldviewFile = join26(workspaceDir, "beliefs", "worldview.md");
|
|
35557
|
+
if (!existsSync25(worldviewFile)) {
|
|
35558
|
+
writeFileSync15(
|
|
34972
35559
|
worldviewFile,
|
|
34973
35560
|
`# Personal Worldview
|
|
34974
35561
|
|
|
@@ -34980,9 +35567,9 @@ Here, I declare the core concepts and principles I want to master.
|
|
|
34980
35567
|
"utf8"
|
|
34981
35568
|
);
|
|
34982
35569
|
}
|
|
34983
|
-
const goalsFile =
|
|
34984
|
-
if (!
|
|
34985
|
-
|
|
35570
|
+
const goalsFile = join26(workspaceDir, "goals", "goals.md");
|
|
35571
|
+
if (!existsSync25(goalsFile)) {
|
|
35572
|
+
writeFileSync15(
|
|
34986
35573
|
goalsFile,
|
|
34987
35574
|
`# Personal Goals
|
|
34988
35575
|
|
|
@@ -35004,8 +35591,8 @@ var initCommand = new Command8("init").description("Launch the guided interactiv
|
|
|
35004
35591
|
);
|
|
35005
35592
|
printLine();
|
|
35006
35593
|
console.log("\n\x1B[1m[1/5] Setting up Local Workspace Sandbox\x1B[0m");
|
|
35007
|
-
const defaultWorkspace =
|
|
35008
|
-
const workspacePath =
|
|
35594
|
+
const defaultWorkspace = join26(HOME2, "Documents", "zam");
|
|
35595
|
+
const workspacePath = resolve9(
|
|
35009
35596
|
await input3({
|
|
35010
35597
|
message: "Choose your ZAM workspace directory:",
|
|
35011
35598
|
default: defaultWorkspace
|
|
@@ -36206,7 +36793,7 @@ observerCommand.command("revoke <process>").description("Remove a process from o
|
|
|
36206
36793
|
|
|
36207
36794
|
// src/cli/commands/profile.ts
|
|
36208
36795
|
init_kernel();
|
|
36209
|
-
import { dirname as
|
|
36796
|
+
import { dirname as dirname11, resolve as resolve10 } from "path";
|
|
36210
36797
|
import { Command as Command13 } from "commander";
|
|
36211
36798
|
var C2 = {
|
|
36212
36799
|
reset: "\x1B[0m",
|
|
@@ -36234,7 +36821,7 @@ var profileCommand = new Command13("profile").description("Show or change this m
|
|
|
36234
36821
|
if (opts.mode) setInstallMode(opts.mode);
|
|
36235
36822
|
db = await openDatabaseWithSync({ initialize: true });
|
|
36236
36823
|
if (opts.dir) {
|
|
36237
|
-
await activateWorkspacePath(db,
|
|
36824
|
+
await activateWorkspacePath(db, resolve10(opts.dir), {
|
|
36238
36825
|
kind: "personal",
|
|
36239
36826
|
label: "Personal"
|
|
36240
36827
|
});
|
|
@@ -36247,7 +36834,7 @@ var profileCommand = new Command13("profile").description("Show or change this m
|
|
|
36247
36834
|
mode: getInstallMode(),
|
|
36248
36835
|
personalDir,
|
|
36249
36836
|
syncProvider: detectSyncProvider(personalDir),
|
|
36250
|
-
dataDir:
|
|
36837
|
+
dataDir: dirname11(dbPath),
|
|
36251
36838
|
dbPath
|
|
36252
36839
|
};
|
|
36253
36840
|
if (opts.json) {
|
|
@@ -36636,7 +37223,7 @@ ${"\u2550".repeat(50)}`);
|
|
|
36636
37223
|
|
|
36637
37224
|
// src/cli/commands/session.ts
|
|
36638
37225
|
init_kernel();
|
|
36639
|
-
import { readFileSync as
|
|
37226
|
+
import { readFileSync as readFileSync19 } from "fs";
|
|
36640
37227
|
import { input as input6, select as select2 } from "@inquirer/prompts";
|
|
36641
37228
|
import { Command as Command16 } from "commander";
|
|
36642
37229
|
var sessionCommand = new Command16("session").description(
|
|
@@ -36827,7 +37414,7 @@ function loadPatternFile(path) {
|
|
|
36827
37414
|
if (!path) return [];
|
|
36828
37415
|
let parsed;
|
|
36829
37416
|
try {
|
|
36830
|
-
parsed = JSON.parse(
|
|
37417
|
+
parsed = JSON.parse(readFileSync19(path, "utf-8"));
|
|
36831
37418
|
} catch (err) {
|
|
36832
37419
|
throw new Error(
|
|
36833
37420
|
`Cannot read synthesis patterns from ${path}: ${err.message}`
|
|
@@ -37018,7 +37605,7 @@ sessionCommand.command("end").description("End a session and show summary").requ
|
|
|
37018
37605
|
|
|
37019
37606
|
// src/cli/commands/settings.ts
|
|
37020
37607
|
init_kernel();
|
|
37021
|
-
import { existsSync as
|
|
37608
|
+
import { existsSync as existsSync26 } from "fs";
|
|
37022
37609
|
import { Command as Command17 } from "commander";
|
|
37023
37610
|
var settingsCommand = new Command17("settings").description(
|
|
37024
37611
|
"Manage user settings"
|
|
@@ -37187,7 +37774,7 @@ settingsCommand.command("repos").description("Show or set Personal, Team, and Or
|
|
|
37187
37774
|
console.log("\nValidation:");
|
|
37188
37775
|
for (const [name, path] of Object.entries(paths)) {
|
|
37189
37776
|
if (path) {
|
|
37190
|
-
const exists =
|
|
37777
|
+
const exists = existsSync26(path);
|
|
37191
37778
|
console.log(
|
|
37192
37779
|
` ${name.padEnd(9)}: ${exists ? "\x1B[32m\u2713 Valid folder\x1B[0m" : "\x1B[31m\u2717 Directory does not exist\x1B[0m"}`
|
|
37193
37780
|
);
|
|
@@ -37200,7 +37787,7 @@ settingsCommand.command("repos").description("Show or set Personal, Team, and Or
|
|
|
37200
37787
|
|
|
37201
37788
|
// src/cli/commands/setup.ts
|
|
37202
37789
|
init_kernel();
|
|
37203
|
-
import { resolve as
|
|
37790
|
+
import { resolve as resolve11 } from "path";
|
|
37204
37791
|
import { Command as Command18 } from "commander";
|
|
37205
37792
|
function formatDatabaseInitTarget(target) {
|
|
37206
37793
|
switch (target.kind) {
|
|
@@ -37300,7 +37887,7 @@ var setupCommand = new Command18("setup").description(
|
|
|
37300
37887
|
console.error(`Error: ${err.message}`);
|
|
37301
37888
|
process.exit(1);
|
|
37302
37889
|
}
|
|
37303
|
-
const target =
|
|
37890
|
+
const target = resolve11(opts.target ?? process.cwd());
|
|
37304
37891
|
const updateExistingInstructions = Boolean(opts.target) || opts.force;
|
|
37305
37892
|
console.log(
|
|
37306
37893
|
`Setting up ZAM in ${target}${opts.dryRun ? " (dry run)" : ""}
|
|
@@ -37425,8 +38012,8 @@ skillCommand.command("add").description("Register a new agent skill").requiredOp
|
|
|
37425
38012
|
|
|
37426
38013
|
// src/cli/commands/snapshot.ts
|
|
37427
38014
|
init_kernel();
|
|
37428
|
-
import { existsSync as
|
|
37429
|
-
import { dirname as
|
|
38015
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync19, readFileSync as readFileSync20, writeFileSync as writeFileSync16 } from "fs";
|
|
38016
|
+
import { dirname as dirname12, join as join27 } from "path";
|
|
37430
38017
|
import { Command as Command20 } from "commander";
|
|
37431
38018
|
function defaultOutName() {
|
|
37432
38019
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+Z$/, "").replace(/:/g, "-");
|
|
@@ -37455,12 +38042,12 @@ var exportCmd = new Command20("export").description("Write a portable SQL-text s
|
|
|
37455
38042
|
process.stdout.write(snapshot);
|
|
37456
38043
|
return;
|
|
37457
38044
|
}
|
|
37458
|
-
const out = opts.out ??
|
|
37459
|
-
const dir =
|
|
37460
|
-
if (dir && dir !== "." && !
|
|
37461
|
-
|
|
38045
|
+
const out = opts.out ?? join27(personalDir, "snapshots", defaultOutName());
|
|
38046
|
+
const dir = dirname12(out);
|
|
38047
|
+
if (dir && dir !== "." && !existsSync27(dir)) {
|
|
38048
|
+
mkdirSync19(dir, { recursive: true });
|
|
37462
38049
|
}
|
|
37463
|
-
|
|
38050
|
+
writeFileSync16(out, snapshot, "utf-8");
|
|
37464
38051
|
console.log(`Snapshot written: ${out}`);
|
|
37465
38052
|
console.log(
|
|
37466
38053
|
` ${total} row(s)${nonEmpty.length ? ` \u2014 ${nonEmpty.join(", ")}` : ""}`
|
|
@@ -37474,11 +38061,11 @@ var exportCmd = new Command20("export").description("Write a portable SQL-text s
|
|
|
37474
38061
|
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) => {
|
|
37475
38062
|
let db;
|
|
37476
38063
|
try {
|
|
37477
|
-
if (!
|
|
38064
|
+
if (!existsSync27(file)) {
|
|
37478
38065
|
console.error(`Error: Snapshot file not found: ${file}`);
|
|
37479
38066
|
process.exit(1);
|
|
37480
38067
|
}
|
|
37481
|
-
const snapshot =
|
|
38068
|
+
const snapshot = readFileSync20(file, "utf-8");
|
|
37482
38069
|
db = await openDatabaseWithSync({ initialize: true });
|
|
37483
38070
|
const result = await importSnapshot(db, snapshot, { force: opts.force });
|
|
37484
38071
|
await db.close();
|
|
@@ -37496,11 +38083,11 @@ var importCmd = new Command20("import").description("Restore a snapshot into the
|
|
|
37496
38083
|
});
|
|
37497
38084
|
var verifyCmd = new Command20("verify").description("Check a snapshot's manifest and checksum without importing").argument("<file>", "Snapshot file to verify").action((file) => {
|
|
37498
38085
|
try {
|
|
37499
|
-
if (!
|
|
38086
|
+
if (!existsSync27(file)) {
|
|
37500
38087
|
console.error(`Error: Snapshot file not found: ${file}`);
|
|
37501
38088
|
process.exit(1);
|
|
37502
38089
|
}
|
|
37503
|
-
const manifest = verifySnapshot(
|
|
38090
|
+
const manifest = verifySnapshot(readFileSync20(file, "utf-8"));
|
|
37504
38091
|
const { total, nonEmpty } = summarize(manifest.tables);
|
|
37505
38092
|
console.log(`Valid snapshot (format v${manifest.version})`);
|
|
37506
38093
|
console.log(` created: ${manifest.createdAt}`);
|
|
@@ -38049,10 +38636,10 @@ tokenCommand.command("reembed").description("Backfill or refresh semantic-search
|
|
|
38049
38636
|
// src/cli/commands/ui.ts
|
|
38050
38637
|
init_kernel();
|
|
38051
38638
|
import { spawn as spawn3, spawnSync } from "child_process";
|
|
38052
|
-
import { existsSync as
|
|
38639
|
+
import { existsSync as existsSync28 } from "fs";
|
|
38053
38640
|
import { homedir as homedir16 } from "os";
|
|
38054
|
-
import { dirname as
|
|
38055
|
-
import { fileURLToPath as
|
|
38641
|
+
import { dirname as dirname13, join as join28 } from "path";
|
|
38642
|
+
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
38056
38643
|
import { Command as Command23 } from "commander";
|
|
38057
38644
|
var C3 = {
|
|
38058
38645
|
reset: "\x1B[0m",
|
|
@@ -38063,14 +38650,14 @@ var C3 = {
|
|
|
38063
38650
|
dim: "\x1B[2m"
|
|
38064
38651
|
};
|
|
38065
38652
|
function findDesktopDir() {
|
|
38066
|
-
const starts = [process.cwd(),
|
|
38653
|
+
const starts = [process.cwd(), dirname13(fileURLToPath7(import.meta.url))];
|
|
38067
38654
|
for (const start of starts) {
|
|
38068
38655
|
let dir = start;
|
|
38069
38656
|
for (let i = 0; i < 10; i++) {
|
|
38070
|
-
if (
|
|
38071
|
-
return
|
|
38657
|
+
if (existsSync28(join28(dir, "desktop", "src-tauri", "tauri.conf.json"))) {
|
|
38658
|
+
return join28(dir, "desktop");
|
|
38072
38659
|
}
|
|
38073
|
-
const parent =
|
|
38660
|
+
const parent = dirname13(dir);
|
|
38074
38661
|
if (parent === dir) break;
|
|
38075
38662
|
dir = parent;
|
|
38076
38663
|
}
|
|
@@ -38078,30 +38665,30 @@ function findDesktopDir() {
|
|
|
38078
38665
|
return null;
|
|
38079
38666
|
}
|
|
38080
38667
|
function findBuiltApp(desktopDir) {
|
|
38081
|
-
const releaseDir =
|
|
38668
|
+
const releaseDir = join28(desktopDir, "src-tauri", "target", "release");
|
|
38082
38669
|
if (process.platform === "win32") {
|
|
38083
38670
|
for (const name of ["ZAM.exe", "zam.exe", "zam-desktop.exe"]) {
|
|
38084
|
-
const p =
|
|
38085
|
-
if (
|
|
38671
|
+
const p = join28(releaseDir, name);
|
|
38672
|
+
if (existsSync28(p)) return p;
|
|
38086
38673
|
}
|
|
38087
38674
|
} else if (process.platform === "darwin") {
|
|
38088
|
-
const app =
|
|
38089
|
-
if (
|
|
38675
|
+
const app = join28(releaseDir, "bundle", "macos", "ZAM.app");
|
|
38676
|
+
if (existsSync28(app)) return app;
|
|
38090
38677
|
} else {
|
|
38091
38678
|
for (const name of ["zam", "ZAM", "zam-desktop"]) {
|
|
38092
|
-
const p =
|
|
38093
|
-
if (
|
|
38679
|
+
const p = join28(releaseDir, name);
|
|
38680
|
+
if (existsSync28(p)) return p;
|
|
38094
38681
|
}
|
|
38095
38682
|
}
|
|
38096
38683
|
return null;
|
|
38097
38684
|
}
|
|
38098
38685
|
function findInstalledApp() {
|
|
38099
38686
|
const candidates = process.platform === "win32" ? [
|
|
38100
|
-
process.env.LOCALAPPDATA &&
|
|
38101
|
-
process.env.ProgramFiles &&
|
|
38102
|
-
process.env["ProgramFiles(x86)"] &&
|
|
38103
|
-
] : process.platform === "darwin" ? ["/Applications/ZAM.app",
|
|
38104
|
-
return candidates.find((candidate) => candidate &&
|
|
38687
|
+
process.env.LOCALAPPDATA && join28(process.env.LOCALAPPDATA, "Programs", "ZAM", "ZAM.exe"),
|
|
38688
|
+
process.env.ProgramFiles && join28(process.env.ProgramFiles, "ZAM", "ZAM.exe"),
|
|
38689
|
+
process.env["ProgramFiles(x86)"] && join28(process.env["ProgramFiles(x86)"], "ZAM", "ZAM.exe")
|
|
38690
|
+
] : process.platform === "darwin" ? ["/Applications/ZAM.app", join28(homedir16(), "Applications", "ZAM.app")] : ["/opt/ZAM/zam", "/usr/bin/zam-desktop"];
|
|
38691
|
+
return candidates.find((candidate) => candidate && existsSync28(candidate)) || null;
|
|
38105
38692
|
}
|
|
38106
38693
|
function runNpm(args, opts) {
|
|
38107
38694
|
const res = spawnSync("npm", args, {
|
|
@@ -38112,7 +38699,7 @@ function runNpm(args, opts) {
|
|
|
38112
38699
|
return res.status ?? 1;
|
|
38113
38700
|
}
|
|
38114
38701
|
function ensureDesktopDeps(desktopDir) {
|
|
38115
|
-
if (
|
|
38702
|
+
if (existsSync28(join28(desktopDir, "node_modules"))) return true;
|
|
38116
38703
|
console.log(
|
|
38117
38704
|
`${C3.cyan}Installing desktop dependencies (one-time)...${C3.reset}`
|
|
38118
38705
|
);
|
|
@@ -38138,13 +38725,13 @@ function requireRust() {
|
|
|
38138
38725
|
function hasMsvcBuildTools() {
|
|
38139
38726
|
if (process.platform !== "win32") return true;
|
|
38140
38727
|
const pf86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
|
38141
|
-
const vswhere =
|
|
38728
|
+
const vswhere = join28(
|
|
38142
38729
|
pf86,
|
|
38143
38730
|
"Microsoft Visual Studio",
|
|
38144
38731
|
"Installer",
|
|
38145
38732
|
"vswhere.exe"
|
|
38146
38733
|
);
|
|
38147
|
-
if (!
|
|
38734
|
+
if (!existsSync28(vswhere)) return false;
|
|
38148
38735
|
const res = spawnSync(
|
|
38149
38736
|
vswhere,
|
|
38150
38737
|
[
|
|
@@ -38176,7 +38763,7 @@ function requireMsvcOnWindows() {
|
|
|
38176
38763
|
return false;
|
|
38177
38764
|
}
|
|
38178
38765
|
function warnIfCliMissing(repoRoot) {
|
|
38179
|
-
if (!
|
|
38766
|
+
if (!existsSync28(join28(repoRoot, "dist", "cli", "index.js"))) {
|
|
38180
38767
|
console.warn(
|
|
38181
38768
|
`${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}`
|
|
38182
38769
|
);
|
|
@@ -38249,7 +38836,7 @@ var uiCommand = new Command23("ui").description("Launch the ZAM Desktop GUI (Act
|
|
|
38249
38836
|
);
|
|
38250
38837
|
process.exit(1);
|
|
38251
38838
|
}
|
|
38252
|
-
const repoRoot =
|
|
38839
|
+
const repoRoot = dirname13(desktopDir);
|
|
38253
38840
|
if (opts.build) {
|
|
38254
38841
|
if (!requireRust()) process.exit(1);
|
|
38255
38842
|
if (!requireMsvcOnWindows()) process.exit(1);
|
|
@@ -38260,7 +38847,7 @@ var uiCommand = new Command23("ui").description("Launch the ZAM Desktop GUI (Act
|
|
|
38260
38847
|
);
|
|
38261
38848
|
const code = runNpm(["run", "tauri", "build"], { cwd: desktopDir });
|
|
38262
38849
|
if (code === 0) {
|
|
38263
|
-
const bundle =
|
|
38850
|
+
const bundle = join28(
|
|
38264
38851
|
desktopDir,
|
|
38265
38852
|
"src-tauri",
|
|
38266
38853
|
"target",
|
|
@@ -38300,7 +38887,7 @@ ${C3.green}\u2713 Done. Installer is in:${C3.reset}
|
|
|
38300
38887
|
);
|
|
38301
38888
|
process.exit(1);
|
|
38302
38889
|
}
|
|
38303
|
-
createShortcuts(shortcutTarget,
|
|
38890
|
+
createShortcuts(shortcutTarget, dirname13(shortcutTarget));
|
|
38304
38891
|
return;
|
|
38305
38892
|
}
|
|
38306
38893
|
if (builtApp) {
|
|
@@ -38338,9 +38925,9 @@ ${C3.dim}After that, 'zam ui' launches it directly.${C3.reset}`
|
|
|
38338
38925
|
// src/cli/commands/update.ts
|
|
38339
38926
|
init_kernel();
|
|
38340
38927
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
38341
|
-
import { existsSync as
|
|
38342
|
-
import { dirname as
|
|
38343
|
-
import { fileURLToPath as
|
|
38928
|
+
import { existsSync as existsSync29, readFileSync as readFileSync21, realpathSync as realpathSync3 } from "fs";
|
|
38929
|
+
import { dirname as dirname14, join as join29 } from "path";
|
|
38930
|
+
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
38344
38931
|
import { confirm as confirm3 } from "@inquirer/prompts";
|
|
38345
38932
|
import { Command as Command24 } from "commander";
|
|
38346
38933
|
var CHANNELS = [
|
|
@@ -38361,7 +38948,7 @@ var C4 = {
|
|
|
38361
38948
|
function versionAt(dir) {
|
|
38362
38949
|
try {
|
|
38363
38950
|
const pkg2 = JSON.parse(
|
|
38364
|
-
|
|
38951
|
+
readFileSync21(join29(dir, "package.json"), "utf-8")
|
|
38365
38952
|
);
|
|
38366
38953
|
return pkg2.version ?? "unknown";
|
|
38367
38954
|
} catch {
|
|
@@ -38419,14 +39006,14 @@ var checkCmd = new Command24("check").description("Check whether a newer ZAM has
|
|
|
38419
39006
|
}
|
|
38420
39007
|
);
|
|
38421
39008
|
function findSourceRepo() {
|
|
38422
|
-
let dir =
|
|
38423
|
-
let parent =
|
|
39009
|
+
let dir = realpathSync3(dirname14(fileURLToPath8(import.meta.url)));
|
|
39010
|
+
let parent = dirname14(dir);
|
|
38424
39011
|
while (parent !== dir) {
|
|
38425
|
-
if (
|
|
39012
|
+
if (existsSync29(join29(dir, ".git"))) return dir;
|
|
38426
39013
|
dir = parent;
|
|
38427
|
-
parent =
|
|
39014
|
+
parent = dirname14(dir);
|
|
38428
39015
|
}
|
|
38429
|
-
return
|
|
39016
|
+
return existsSync29(join29(dir, ".git")) ? dir : null;
|
|
38430
39017
|
}
|
|
38431
39018
|
function runGit(cwd, args, capture) {
|
|
38432
39019
|
const res = spawnSync2("git", args, {
|
|
@@ -38447,7 +39034,7 @@ function runNpm2(args, cwd) {
|
|
|
38447
39034
|
function smokeTestBuild(src) {
|
|
38448
39035
|
const res = spawnSync2(
|
|
38449
39036
|
process.execPath,
|
|
38450
|
-
[
|
|
39037
|
+
[join29(src, "dist", "cli", "index.js"), "--version"],
|
|
38451
39038
|
{
|
|
38452
39039
|
cwd: src,
|
|
38453
39040
|
encoding: "utf8",
|
|
@@ -38527,7 +39114,7 @@ Fix it manually in ${src} (try \`npm ci && npm run build\`, and check your Node
|
|
|
38527
39114
|
console.log(`${C4.dim}\u2192 zam setup --force${C4.reset}`);
|
|
38528
39115
|
const setup = spawnSync2(
|
|
38529
39116
|
process.execPath,
|
|
38530
|
-
[
|
|
39117
|
+
[join29(src, "dist", "cli", "index.js"), "setup", "--force"],
|
|
38531
39118
|
{ cwd: process.cwd(), stdio: "inherit" }
|
|
38532
39119
|
);
|
|
38533
39120
|
if (setup.status !== 0) {
|
|
@@ -38640,9 +39227,9 @@ var whoamiCommand = new Command25("whoami").description("Show or set the default
|
|
|
38640
39227
|
// src/cli/commands/workspace.ts
|
|
38641
39228
|
init_kernel();
|
|
38642
39229
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
38643
|
-
import { existsSync as
|
|
39230
|
+
import { existsSync as existsSync30, writeFileSync as writeFileSync17 } from "fs";
|
|
38644
39231
|
import { homedir as homedir17 } from "os";
|
|
38645
|
-
import { join as
|
|
39232
|
+
import { join as join30, resolve as resolve12 } from "path";
|
|
38646
39233
|
import { confirm as confirm4, input as input7 } from "@inquirer/prompts";
|
|
38647
39234
|
import { Command as Command26 } from "commander";
|
|
38648
39235
|
function runGit2(cwd, args) {
|
|
@@ -38726,7 +39313,7 @@ workspaceCommand.command("list").description("List configured ZAM workspaces").o
|
|
|
38726
39313
|
const workspaces = getConfiguredWorkspaces();
|
|
38727
39314
|
const agents = parseSetupAgents();
|
|
38728
39315
|
const linkHealth = Object.fromEntries(
|
|
38729
|
-
workspaces.filter((workspace) =>
|
|
39316
|
+
workspaces.filter((workspace) => existsSync30(workspace.path)).map((workspace) => [
|
|
38730
39317
|
workspace.id,
|
|
38731
39318
|
summarizeSkillLinkHealth(inspectSkillLinks(workspace.path, agents))
|
|
38732
39319
|
])
|
|
@@ -38768,8 +39355,8 @@ workspaceCommand.command("add <id>").description("Register an existing directory
|
|
|
38768
39355
|
`Source-control provider (${WORKSPACE_SOURCE_CONTROLS.join(" | ")})`
|
|
38769
39356
|
).option("--scopes <list>", "Comma-separated knowledge scopes").option("--default-agent <id>", "Default agent harness for this workspace").action((id, opts) => {
|
|
38770
39357
|
try {
|
|
38771
|
-
const path =
|
|
38772
|
-
if (!
|
|
39358
|
+
const path = resolve12(String(opts.path));
|
|
39359
|
+
if (!existsSync30(path)) {
|
|
38773
39360
|
console.error(`Workspace path does not exist: ${path}`);
|
|
38774
39361
|
process.exit(1);
|
|
38775
39362
|
}
|
|
@@ -38863,7 +39450,7 @@ workspaceCommand.command("publish").description("Publish your local workspace sa
|
|
|
38863
39450
|
);
|
|
38864
39451
|
process.exit(1);
|
|
38865
39452
|
}
|
|
38866
|
-
if (!
|
|
39453
|
+
if (!existsSync30(workspaceDir)) {
|
|
38867
39454
|
console.error(
|
|
38868
39455
|
`\x1B[31m\u2717 Workspace directory does not exist: ${workspaceDir}\x1B[0m`
|
|
38869
39456
|
);
|
|
@@ -38877,15 +39464,15 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
|
|
|
38877
39464
|
);
|
|
38878
39465
|
process.exit(1);
|
|
38879
39466
|
}
|
|
38880
|
-
const gitignorePath =
|
|
38881
|
-
if (!
|
|
38882
|
-
|
|
39467
|
+
const gitignorePath = join30(workspaceDir, ".gitignore");
|
|
39468
|
+
if (!existsSync30(gitignorePath)) {
|
|
39469
|
+
writeFileSync17(
|
|
38883
39470
|
gitignorePath,
|
|
38884
39471
|
"node_modules/\n.agent/\n.agents/\n.claude/\n.gemini/\n.goose/\n*.log\n",
|
|
38885
39472
|
"utf8"
|
|
38886
39473
|
);
|
|
38887
39474
|
}
|
|
38888
|
-
const hasGitRepo =
|
|
39475
|
+
const hasGitRepo = existsSync30(join30(workspaceDir, ".git"));
|
|
38889
39476
|
if (!hasGitRepo) {
|
|
38890
39477
|
console.log("Initializing local Git repository...");
|
|
38891
39478
|
runGit2(workspaceDir, ["init", "-b", "main"]);
|
|
@@ -38972,7 +39559,7 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
|
|
|
38972
39559
|
}
|
|
38973
39560
|
});
|
|
38974
39561
|
workspaceCommand.command("data-dir").description("Print the ZAM data directory (database, credentials, config)").option("--json", "Output as JSON").action((opts) => {
|
|
38975
|
-
const dir =
|
|
39562
|
+
const dir = join30(homedir17(), ".zam");
|
|
38976
39563
|
console.log(opts.json ? JSON.stringify({ dataDir: dir }) : dir);
|
|
38977
39564
|
});
|
|
38978
39565
|
workspaceCommand.command("backup").description("Back up the local ZAM database into your workspace").option(
|
|
@@ -39013,9 +39600,9 @@ workspaceCommand.command("backup").description("Back up the local ZAM database i
|
|
|
39013
39600
|
});
|
|
39014
39601
|
|
|
39015
39602
|
// src/cli/app.ts
|
|
39016
|
-
var __dirname =
|
|
39603
|
+
var __dirname = dirname15(fileURLToPath9(import.meta.url));
|
|
39017
39604
|
var pkg = JSON.parse(
|
|
39018
|
-
|
|
39605
|
+
readFileSync22(join31(__dirname, "..", "..", "package.json"), "utf-8")
|
|
39019
39606
|
);
|
|
39020
39607
|
var program = new Command27();
|
|
39021
39608
|
program.name("zam").description(
|