zam-core 0.13.0 → 0.15.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 +828 -218
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/commands/mcp.js +712 -234
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/copilot-extension/extension.mjs +35 -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 +39 -1
- package/dist/index.js +79 -4
- package/dist/index.js.map +1 -1
- package/dist/ui/graph-panel.html +80 -40
- package/dist/ui/okf-panel.html +146 -21
- package/dist/vscode-extension/ZAM_Companion_0.15.0.vsix +0 -0
- package/dist/vscode-extension/extension.cjs +19 -18
- package/dist/vscode-extension/host.bundle.js +3 -2
- 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) {
|
|
@@ -2080,6 +2140,15 @@ async function listTokens(db, options) {
|
|
|
2080
2140
|
)`);
|
|
2081
2141
|
params.push(options.knowledgeContext);
|
|
2082
2142
|
}
|
|
2143
|
+
if (options?.sourceLinkBases) {
|
|
2144
|
+
const clauses = options.sourceLinkBases.map(
|
|
2145
|
+
() => `(source_link = ? OR source_link LIKE ? || '#%' ESCAPE '\\')`
|
|
2146
|
+
);
|
|
2147
|
+
whereClauses.push(clauses.length ? `(${clauses.join(" OR ")})` : "0 = 1");
|
|
2148
|
+
for (const base of options.sourceLinkBases) {
|
|
2149
|
+
params.push(base, escapeLike(base));
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2083
2152
|
const orderBy = options?.domain || options?.domainPrefix ? "ORDER BY bloom_level, slug" : "ORDER BY bloom_level, domain, slug";
|
|
2084
2153
|
const sql = `SELECT * FROM tokens WHERE ${whereClauses.join(" AND ")} ${orderBy}`;
|
|
2085
2154
|
const tokens = await db.prepare(sql).all(...params);
|
|
@@ -5268,7 +5337,8 @@ async function buildReviewQueue(db, options) {
|
|
|
5268
5337
|
AND c.blocked = 0
|
|
5269
5338
|
AND c.due_at <= ?
|
|
5270
5339
|
AND c.state IN ('review', 'relearning', 'learning')
|
|
5271
|
-
AND t.deprecated_at IS NULL
|
|
5340
|
+
AND t.deprecated_at IS NULL
|
|
5341
|
+
AND t.maintenance_at IS NULL`;
|
|
5272
5342
|
const dueParams = [options.userId, nowISO];
|
|
5273
5343
|
if (options.knowledgeContext) {
|
|
5274
5344
|
dueSql += ` AND EXISTS (
|
|
@@ -5298,7 +5368,8 @@ async function buildReviewQueue(db, options) {
|
|
|
5298
5368
|
WHERE c.user_id = ?
|
|
5299
5369
|
AND c.blocked = 0
|
|
5300
5370
|
AND c.state = 'new'
|
|
5301
|
-
AND t.deprecated_at IS NULL
|
|
5371
|
+
AND t.deprecated_at IS NULL
|
|
5372
|
+
AND t.maintenance_at IS NULL`;
|
|
5302
5373
|
const newParams = [options.userId];
|
|
5303
5374
|
if (options.knowledgeContext) {
|
|
5304
5375
|
newSql += ` AND EXISTS (
|
|
@@ -6992,6 +7063,7 @@ __export(kernel_exports, {
|
|
|
6992
7063
|
clearADOCredentials: () => clearADOCredentials,
|
|
6993
7064
|
clearProviderApiKey: () => clearProviderApiKey,
|
|
6994
7065
|
clearReviewContextCache: () => clearReviewContextCache,
|
|
7066
|
+
clearTokenMaintenance: () => clearTokenMaintenance,
|
|
6995
7067
|
clearTursoCredentials: () => clearTursoCredentials,
|
|
6996
7068
|
compareVersions: () => compareVersions,
|
|
6997
7069
|
computeContentHash: () => computeContentHash,
|
|
@@ -7100,6 +7172,7 @@ __export(kernel_exports, {
|
|
|
7100
7172
|
getTokenDeleteImpact: () => getTokenDeleteImpact,
|
|
7101
7173
|
getTokenEmbedding: () => getTokenEmbedding,
|
|
7102
7174
|
getTokenNeighborhood: () => getTokenNeighborhood,
|
|
7175
|
+
getTokensBySourceLinkBase: () => getTokensBySourceLinkBase,
|
|
7103
7176
|
getTursoCredentials: () => getTursoCredentials,
|
|
7104
7177
|
getUiObservationPath: () => getUiObservationPath,
|
|
7105
7178
|
getUiObserverDir: () => getUiObserverDir,
|
|
@@ -7154,6 +7227,7 @@ __export(kernel_exports, {
|
|
|
7154
7227
|
readMonitorLog: () => readMonitorLog,
|
|
7155
7228
|
readUiObservationLog: () => readUiObservationLog,
|
|
7156
7229
|
removeConfiguredWorkspace: () => removeConfiguredWorkspace,
|
|
7230
|
+
resetCardsForToken: () => resetCardsForToken,
|
|
7157
7231
|
resolveAllBeliefPaths: () => resolveAllBeliefPaths,
|
|
7158
7232
|
resolveAllGoalPaths: () => resolveAllGoalPaths,
|
|
7159
7233
|
resolveObserverPolicy: () => resolveObserverPolicy,
|
|
@@ -7185,6 +7259,7 @@ __export(kernel_exports, {
|
|
|
7185
7259
|
setLastRepairedVersion: () => setLastRepairedVersion,
|
|
7186
7260
|
setProviderApiKey: () => setProviderApiKey,
|
|
7187
7261
|
setSetting: () => setSetting,
|
|
7262
|
+
setTokenMaintenance: () => setTokenMaintenance,
|
|
7188
7263
|
setTursoCredentials: () => setTursoCredentials,
|
|
7189
7264
|
slugify: () => slugify,
|
|
7190
7265
|
startSession: () => startSession,
|
|
@@ -7259,10 +7334,337 @@ var init_kernel = __esm({
|
|
|
7259
7334
|
}
|
|
7260
7335
|
});
|
|
7261
7336
|
|
|
7337
|
+
// src/cli/okf/bundle.ts
|
|
7338
|
+
var bundle_exports = {};
|
|
7339
|
+
__export(bundle_exports, {
|
|
7340
|
+
OKF_VERSION: () => OKF_VERSION,
|
|
7341
|
+
RESERVED_FILES: () => RESERVED_FILES,
|
|
7342
|
+
appendLog: () => appendLog,
|
|
7343
|
+
buildCatalog: () => buildCatalog,
|
|
7344
|
+
isReservedFile: () => isReservedFile,
|
|
7345
|
+
parseFrontmatter: () => parseFrontmatter,
|
|
7346
|
+
renderIndex: () => renderIndex,
|
|
7347
|
+
toCatalogEntry: () => toCatalogEntry,
|
|
7348
|
+
validateArticle: () => validateArticle
|
|
7349
|
+
});
|
|
7350
|
+
function isReservedFile(file) {
|
|
7351
|
+
return RESERVED_FILES.includes(file);
|
|
7352
|
+
}
|
|
7353
|
+
function unquote(raw) {
|
|
7354
|
+
const v = raw.trim();
|
|
7355
|
+
if (v.length >= 2 && (v.startsWith('"') && v.endsWith('"') || v.startsWith("'") && v.endsWith("'"))) {
|
|
7356
|
+
return v.slice(1, -1);
|
|
7357
|
+
}
|
|
7358
|
+
return v;
|
|
7359
|
+
}
|
|
7360
|
+
function parseFrontmatter(markdown) {
|
|
7361
|
+
const lines = markdown.split("\n");
|
|
7362
|
+
if (lines[0]?.trim() !== "---") {
|
|
7363
|
+
throw new Error("frontmatter: file must start with a --- fence");
|
|
7364
|
+
}
|
|
7365
|
+
const fields = {};
|
|
7366
|
+
let listKey = null;
|
|
7367
|
+
let i = 1;
|
|
7368
|
+
for (; i < lines.length; i++) {
|
|
7369
|
+
const line = lines[i];
|
|
7370
|
+
if (line.trim() === "---") break;
|
|
7371
|
+
if (line.trim() === "") continue;
|
|
7372
|
+
const listItem = /^\s+-\s+(.+)$/.exec(line);
|
|
7373
|
+
if (listItem) {
|
|
7374
|
+
if (!listKey) {
|
|
7375
|
+
throw new Error(`frontmatter line ${i + 1}: list item without a key`);
|
|
7376
|
+
}
|
|
7377
|
+
fields[listKey].push(unquote(listItem[1]));
|
|
7378
|
+
continue;
|
|
7379
|
+
}
|
|
7380
|
+
const pair = /^([A-Za-z0-9_-]+):(.*)$/.exec(line);
|
|
7381
|
+
if (!pair) {
|
|
7382
|
+
throw new Error(
|
|
7383
|
+
`frontmatter line ${i + 1}: expected "key: value" or "- item"`
|
|
7384
|
+
);
|
|
7385
|
+
}
|
|
7386
|
+
const key = pair[1];
|
|
7387
|
+
const rest = pair[2].trim();
|
|
7388
|
+
if (rest === "") {
|
|
7389
|
+
fields[key] = [];
|
|
7390
|
+
listKey = key;
|
|
7391
|
+
} else {
|
|
7392
|
+
fields[key] = unquote(rest);
|
|
7393
|
+
listKey = null;
|
|
7394
|
+
}
|
|
7395
|
+
}
|
|
7396
|
+
if (i >= lines.length) {
|
|
7397
|
+
throw new Error("frontmatter: missing closing --- fence");
|
|
7398
|
+
}
|
|
7399
|
+
return { fields, body: lines.slice(i + 1).join("\n") };
|
|
7400
|
+
}
|
|
7401
|
+
function scalar(fields, key) {
|
|
7402
|
+
const v = fields[key];
|
|
7403
|
+
return typeof v === "string" && v.trim() !== "" ? v.trim() : void 0;
|
|
7404
|
+
}
|
|
7405
|
+
function validateArticle(file, markdown) {
|
|
7406
|
+
const problems = [];
|
|
7407
|
+
if (isReservedFile(file)) {
|
|
7408
|
+
return { ok: false, problems: [`${file}: reserved OKF file name`] };
|
|
7409
|
+
}
|
|
7410
|
+
if (!FILE_NAME_RE.test(file)) {
|
|
7411
|
+
problems.push(`${file}: file name must be kebab-case and end in .md`);
|
|
7412
|
+
}
|
|
7413
|
+
let parsed = null;
|
|
7414
|
+
try {
|
|
7415
|
+
parsed = parseFrontmatter(markdown);
|
|
7416
|
+
} catch (err) {
|
|
7417
|
+
problems.push(
|
|
7418
|
+
`${file}: ${err instanceof Error ? err.message : String(err)}`
|
|
7419
|
+
);
|
|
7420
|
+
}
|
|
7421
|
+
if (parsed) {
|
|
7422
|
+
if (!scalar(parsed.fields, "type")) {
|
|
7423
|
+
problems.push(`${file}: frontmatter field "type" is required`);
|
|
7424
|
+
}
|
|
7425
|
+
if (!scalar(parsed.fields, "description")) {
|
|
7426
|
+
problems.push(`${file}: frontmatter field "description" is required`);
|
|
7427
|
+
}
|
|
7428
|
+
if (parsed.body.trim() === "") {
|
|
7429
|
+
problems.push(`${file}: article body is empty`);
|
|
7430
|
+
}
|
|
7431
|
+
}
|
|
7432
|
+
return { ok: problems.length === 0, problems };
|
|
7433
|
+
}
|
|
7434
|
+
function toCatalogEntry(file, markdown) {
|
|
7435
|
+
const { fields } = parseFrontmatter(markdown);
|
|
7436
|
+
const tags = Array.isArray(fields.tags) ? fields.tags : [];
|
|
7437
|
+
return {
|
|
7438
|
+
file,
|
|
7439
|
+
type: scalar(fields, "type") ?? "",
|
|
7440
|
+
title: scalar(fields, "title") ?? file.replace(/\.md$/, ""),
|
|
7441
|
+
description: scalar(fields, "description") ?? "",
|
|
7442
|
+
tags,
|
|
7443
|
+
resource: scalar(fields, "resource"),
|
|
7444
|
+
timestamp: scalar(fields, "timestamp")
|
|
7445
|
+
};
|
|
7446
|
+
}
|
|
7447
|
+
function buildCatalog(articles) {
|
|
7448
|
+
return articles.map(({ file, markdown }) => toCatalogEntry(file, markdown)).sort((a, b) => a.file.localeCompare(b.file));
|
|
7449
|
+
}
|
|
7450
|
+
function renderIndex(catalog, okfVersion = OKF_VERSION) {
|
|
7451
|
+
const types = [...new Set(catalog.map((e) => e.type))].sort();
|
|
7452
|
+
const sections = types.map((type) => {
|
|
7453
|
+
const rows = catalog.filter((e) => e.type === type).map((e) => `- [${e.title}](${e.file}) \u2014 ${e.description}`).join("\n");
|
|
7454
|
+
return `## ${type}
|
|
7455
|
+
|
|
7456
|
+
${rows}`;
|
|
7457
|
+
});
|
|
7458
|
+
return [
|
|
7459
|
+
"---",
|
|
7460
|
+
`okf_version: "${okfVersion}"`,
|
|
7461
|
+
"---",
|
|
7462
|
+
"",
|
|
7463
|
+
"# ZAM Knowledge Base",
|
|
7464
|
+
"",
|
|
7465
|
+
"Living reference knowledge for this repository in",
|
|
7466
|
+
"[Open Knowledge Format](https://github.com/GoogleCloudPlatform/knowledge-catalog).",
|
|
7467
|
+
"Current truth only \u2014 the *why* behind it lives in [../adr/](../adr/)",
|
|
7468
|
+
"(ADR 2026-07-17). Do not edit by hand: write through the",
|
|
7469
|
+
"`zam_okf_upsert` MCP tool.",
|
|
7470
|
+
"",
|
|
7471
|
+
sections.join("\n\n"),
|
|
7472
|
+
""
|
|
7473
|
+
].join("\n");
|
|
7474
|
+
}
|
|
7475
|
+
function appendLog(existing, date, line) {
|
|
7476
|
+
const header = `## ${date}`;
|
|
7477
|
+
const entry = `- ${line}`;
|
|
7478
|
+
const trimmed = existing.trim();
|
|
7479
|
+
if (trimmed === "") {
|
|
7480
|
+
return `# Log
|
|
7481
|
+
|
|
7482
|
+
${header}
|
|
7483
|
+
|
|
7484
|
+
${entry}
|
|
7485
|
+
`;
|
|
7486
|
+
}
|
|
7487
|
+
const lines = trimmed.split("\n");
|
|
7488
|
+
const firstHeaderIdx = lines.findIndex((l) => l.startsWith("## "));
|
|
7489
|
+
if (firstHeaderIdx !== -1 && lines[firstHeaderIdx].trim() === header) {
|
|
7490
|
+
lines.splice(firstHeaderIdx + 2, 0, entry);
|
|
7491
|
+
return `${lines.join("\n")}
|
|
7492
|
+
`;
|
|
7493
|
+
}
|
|
7494
|
+
const insertAt = firstHeaderIdx === -1 ? lines.length : firstHeaderIdx;
|
|
7495
|
+
lines.splice(insertAt, 0, header, "", entry, "");
|
|
7496
|
+
return `${lines.join("\n")}
|
|
7497
|
+
`;
|
|
7498
|
+
}
|
|
7499
|
+
var OKF_VERSION, RESERVED_FILES, FILE_NAME_RE;
|
|
7500
|
+
var init_bundle = __esm({
|
|
7501
|
+
"src/cli/okf/bundle.ts"() {
|
|
7502
|
+
"use strict";
|
|
7503
|
+
OKF_VERSION = "0.1";
|
|
7504
|
+
RESERVED_FILES = ["index.md", "log.md"];
|
|
7505
|
+
FILE_NAME_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*\.md$/;
|
|
7506
|
+
}
|
|
7507
|
+
});
|
|
7508
|
+
|
|
7509
|
+
// src/cli/okf/io.ts
|
|
7510
|
+
var io_exports = {};
|
|
7511
|
+
__export(io_exports, {
|
|
7512
|
+
DEFAULT_BUNDLE_DIR: () => DEFAULT_BUNDLE_DIR,
|
|
7513
|
+
collectSourceLinkBases: () => collectSourceLinkBases,
|
|
7514
|
+
findRepoRoot: () => findRepoRoot,
|
|
7515
|
+
loadBundle: () => loadBundle,
|
|
7516
|
+
resolveArticlePath: () => resolveArticlePath,
|
|
7517
|
+
resolveBundleDirFromRoots: () => resolveBundleDirFromRoots,
|
|
7518
|
+
resolveCitationPath: () => resolveCitationPath,
|
|
7519
|
+
upsertArticle: () => upsertArticle
|
|
7520
|
+
});
|
|
7521
|
+
import {
|
|
7522
|
+
existsSync as existsSync18,
|
|
7523
|
+
mkdirSync as mkdirSync12,
|
|
7524
|
+
readdirSync as readdirSync2,
|
|
7525
|
+
readFileSync as readFileSync14,
|
|
7526
|
+
realpathSync,
|
|
7527
|
+
writeFileSync as writeFileSync10
|
|
7528
|
+
} from "fs";
|
|
7529
|
+
import { dirname as dirname9, isAbsolute as isAbsolute2, join as join18, relative, resolve as resolve5, sep } from "path";
|
|
7530
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
7531
|
+
function resolveBundleDirFromRoots(rootUris, fallback) {
|
|
7532
|
+
const dirs = [];
|
|
7533
|
+
for (const uri of rootUris) {
|
|
7534
|
+
if (!uri?.startsWith("file:")) continue;
|
|
7535
|
+
try {
|
|
7536
|
+
dirs.push(join18(fileURLToPath5(uri), DEFAULT_BUNDLE_DIR));
|
|
7537
|
+
} catch {
|
|
7538
|
+
}
|
|
7539
|
+
}
|
|
7540
|
+
return dirs.find((dir) => existsSync18(dir)) ?? dirs[0] ?? fallback;
|
|
7541
|
+
}
|
|
7542
|
+
function collectSourceLinkBases(dir) {
|
|
7543
|
+
const bundle = loadBundle(dir);
|
|
7544
|
+
return bundle.catalog.map(
|
|
7545
|
+
(entry) => entry.resource ?? resolveArticlePath(dir, entry.file)
|
|
7546
|
+
);
|
|
7547
|
+
}
|
|
7548
|
+
function resolveArticlePath(dir, file) {
|
|
7549
|
+
if (file.includes("/") || file.includes("\\") || file.includes("..")) {
|
|
7550
|
+
throw new Error(`invalid article file name: ${file}`);
|
|
7551
|
+
}
|
|
7552
|
+
if (isReservedFile(file)) {
|
|
7553
|
+
throw new Error(`refusing to address reserved file: ${file}`);
|
|
7554
|
+
}
|
|
7555
|
+
return join18(resolve5(dir), file);
|
|
7556
|
+
}
|
|
7557
|
+
function findRepoRoot(startDir) {
|
|
7558
|
+
let dir = resolve5(startDir);
|
|
7559
|
+
for (; ; ) {
|
|
7560
|
+
if (existsSync18(join18(dir, ".git"))) return dir;
|
|
7561
|
+
const parent = dirname9(dir);
|
|
7562
|
+
if (parent === dir) return resolve5(startDir, "..");
|
|
7563
|
+
dir = parent;
|
|
7564
|
+
}
|
|
7565
|
+
}
|
|
7566
|
+
function resolveCitationPath(bundleDir, target) {
|
|
7567
|
+
if (isAbsolute2(target)) {
|
|
7568
|
+
throw new Error(
|
|
7569
|
+
`invalid citation target: absolute paths are not allowed (${target})`
|
|
7570
|
+
);
|
|
7571
|
+
}
|
|
7572
|
+
if (!target.endsWith(".md")) {
|
|
7573
|
+
throw new Error(
|
|
7574
|
+
`invalid citation target: only .md files are readable (${target})`
|
|
7575
|
+
);
|
|
7576
|
+
}
|
|
7577
|
+
const root = findRepoRoot(bundleDir);
|
|
7578
|
+
const resolved = resolve5(bundleDir, target);
|
|
7579
|
+
assertContained(root, resolved, target);
|
|
7580
|
+
if (existsSync18(resolved)) {
|
|
7581
|
+
assertContained(realpathSync(root), realpathSync(resolved), target);
|
|
7582
|
+
}
|
|
7583
|
+
return resolved;
|
|
7584
|
+
}
|
|
7585
|
+
function assertContained(root, candidate, target) {
|
|
7586
|
+
const rel = relative(root, candidate);
|
|
7587
|
+
if (rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute2(rel)) {
|
|
7588
|
+
throw new Error(
|
|
7589
|
+
`invalid citation target: resolves outside the repository root (${target})`
|
|
7590
|
+
);
|
|
7591
|
+
}
|
|
7592
|
+
}
|
|
7593
|
+
function loadBundle(dir) {
|
|
7594
|
+
const root = resolve5(dir);
|
|
7595
|
+
let entries;
|
|
7596
|
+
try {
|
|
7597
|
+
entries = readdirSync2(root).filter(
|
|
7598
|
+
(name) => name.endsWith(".md") && !isReservedFile(name)
|
|
7599
|
+
);
|
|
7600
|
+
} catch {
|
|
7601
|
+
throw new Error(`OKF bundle directory not found: ${root}`);
|
|
7602
|
+
}
|
|
7603
|
+
const articles = entries.sort().map((file) => ({
|
|
7604
|
+
file,
|
|
7605
|
+
markdown: readFileSync14(join18(root, file), "utf8")
|
|
7606
|
+
}));
|
|
7607
|
+
const problems = articles.flatMap(
|
|
7608
|
+
({ file, markdown }) => validateArticle(file, markdown).problems
|
|
7609
|
+
);
|
|
7610
|
+
const catalog = problems.length === 0 ? buildCatalog(articles) : safeCatalog(articles);
|
|
7611
|
+
return { dir: root, articles, catalog, problems };
|
|
7612
|
+
}
|
|
7613
|
+
function safeCatalog(articles) {
|
|
7614
|
+
const entries = [];
|
|
7615
|
+
for (const { file, markdown } of articles) {
|
|
7616
|
+
try {
|
|
7617
|
+
entries.push(toCatalogEntry(file, markdown));
|
|
7618
|
+
} catch {
|
|
7619
|
+
}
|
|
7620
|
+
}
|
|
7621
|
+
return entries.sort((a, b) => a.file.localeCompare(b.file));
|
|
7622
|
+
}
|
|
7623
|
+
function upsertArticle(dir, file, markdown, today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)) {
|
|
7624
|
+
const target = resolveArticlePath(dir, file);
|
|
7625
|
+
const validation = validateArticle(file, markdown);
|
|
7626
|
+
if (!validation.ok) return { validation };
|
|
7627
|
+
const root = resolve5(dir);
|
|
7628
|
+
mkdirSync12(root, { recursive: true });
|
|
7629
|
+
let created = true;
|
|
7630
|
+
try {
|
|
7631
|
+
readFileSync14(target, "utf8");
|
|
7632
|
+
created = false;
|
|
7633
|
+
} catch {
|
|
7634
|
+
}
|
|
7635
|
+
writeFileSync10(target, markdown, "utf8");
|
|
7636
|
+
const bundle = loadBundle(root);
|
|
7637
|
+
writeFileSync10(join18(root, "index.md"), renderIndex(bundle.catalog), "utf8");
|
|
7638
|
+
let log = "";
|
|
7639
|
+
try {
|
|
7640
|
+
log = readFileSync14(join18(root, "log.md"), "utf8");
|
|
7641
|
+
} catch {
|
|
7642
|
+
}
|
|
7643
|
+
const entry = bundle.catalog.find((e) => e.file === file);
|
|
7644
|
+
writeFileSync10(
|
|
7645
|
+
join18(root, "log.md"),
|
|
7646
|
+
appendLog(
|
|
7647
|
+
log,
|
|
7648
|
+
today,
|
|
7649
|
+
`**${created ? "Creation" : "Update"}** \u2014 [${entry?.title ?? file}](${file})`
|
|
7650
|
+
),
|
|
7651
|
+
"utf8"
|
|
7652
|
+
);
|
|
7653
|
+
return { validation, entry, created };
|
|
7654
|
+
}
|
|
7655
|
+
var DEFAULT_BUNDLE_DIR;
|
|
7656
|
+
var init_io = __esm({
|
|
7657
|
+
"src/cli/okf/io.ts"() {
|
|
7658
|
+
"use strict";
|
|
7659
|
+
init_bundle();
|
|
7660
|
+
DEFAULT_BUNDLE_DIR = "docs/okf";
|
|
7661
|
+
}
|
|
7662
|
+
});
|
|
7663
|
+
|
|
7262
7664
|
// src/cli/app.ts
|
|
7263
|
-
import { readFileSync as
|
|
7264
|
-
import { dirname as
|
|
7265
|
-
import { fileURLToPath as
|
|
7665
|
+
import { readFileSync as readFileSync22 } from "fs";
|
|
7666
|
+
import { dirname as dirname15, join as join31 } from "path";
|
|
7667
|
+
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
7266
7668
|
import { Command as Command27 } from "commander";
|
|
7267
7669
|
|
|
7268
7670
|
// src/cli/commands/agent.ts
|
|
@@ -8600,9 +9002,9 @@ var agentCommand = new Command("agent").description("Provision and inspect the a
|
|
|
8600
9002
|
init_kernel();
|
|
8601
9003
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
8602
9004
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
8603
|
-
import { existsSync as
|
|
9005
|
+
import { existsSync as existsSync22, readdirSync as readdirSync3, readFileSync as readFileSync18, rmSync as rmSync3 } from "fs";
|
|
8604
9006
|
import { homedir as homedir14, tmpdir as tmpdir3 } from "os";
|
|
8605
|
-
import { join as
|
|
9007
|
+
import { join as join24, resolve as resolve7 } from "path";
|
|
8606
9008
|
import { Command as Command2 } from "commander";
|
|
8607
9009
|
import { ulid as ulid10 } from "ulid";
|
|
8608
9010
|
|
|
@@ -9689,7 +10091,7 @@ async function prepareRecallChain(db, opts) {
|
|
|
9689
10091
|
} else {
|
|
9690
10092
|
spawnLocalRunner(endpoint.url, endpoint.model, endpoint.runner);
|
|
9691
10093
|
while (Date.now() < deadline) {
|
|
9692
|
-
await new Promise((
|
|
10094
|
+
await new Promise((resolve13) => setTimeout(resolve13, 1e3));
|
|
9693
10095
|
if (await isLlmOnline(endpoint.url)) {
|
|
9694
10096
|
online = true;
|
|
9695
10097
|
break;
|
|
@@ -9982,7 +10384,7 @@ async function startLocalRunner(url, model, locale, hint) {
|
|
|
9982
10384
|
let attempts = 0;
|
|
9983
10385
|
const dotsPerLine = 30;
|
|
9984
10386
|
while (true) {
|
|
9985
|
-
await new Promise((
|
|
10387
|
+
await new Promise((resolve13) => setTimeout(resolve13, 500));
|
|
9986
10388
|
if (await isLlmOnline(url)) {
|
|
9987
10389
|
if (attempts > 0) process.stdout.write("\n");
|
|
9988
10390
|
return true;
|
|
@@ -10071,8 +10473,8 @@ async function fetchWithInteractiveTimeout(url, options = {}) {
|
|
|
10071
10473
|
const dotsPerLine = 30;
|
|
10072
10474
|
while (true) {
|
|
10073
10475
|
let timeoutId;
|
|
10074
|
-
const timeoutPromise = new Promise((
|
|
10075
|
-
timeoutId = setTimeout(() =>
|
|
10476
|
+
const timeoutPromise = new Promise((resolve13) => {
|
|
10477
|
+
timeoutId = setTimeout(() => resolve13("timeout"), timeoutMs);
|
|
10076
10478
|
});
|
|
10077
10479
|
const dotsInterval = setInterval(() => {
|
|
10078
10480
|
process.stdout.write(".");
|
|
@@ -10339,7 +10741,7 @@ async function readWebLink(url) {
|
|
|
10339
10741
|
redirect: "manual",
|
|
10340
10742
|
signal: controller.signal,
|
|
10341
10743
|
headers: {
|
|
10342
|
-
"User-Agent": "ZAM-Content-Studio/0.
|
|
10744
|
+
"User-Agent": "ZAM-Content-Studio/0.15.0"
|
|
10343
10745
|
}
|
|
10344
10746
|
});
|
|
10345
10747
|
if (res.status >= 300 && res.status < 400) {
|
|
@@ -10404,8 +10806,8 @@ async function readImageOCR(db, imagePath) {
|
|
|
10404
10806
|
|
|
10405
10807
|
// src/cli/bridge-handlers.ts
|
|
10406
10808
|
init_kernel();
|
|
10407
|
-
import { mkdirSync as
|
|
10408
|
-
import { join as
|
|
10809
|
+
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync11 } from "fs";
|
|
10810
|
+
import { join as join19 } from "path";
|
|
10409
10811
|
|
|
10410
10812
|
// src/cli/knowledge-contexts.ts
|
|
10411
10813
|
init_kernel();
|
|
@@ -11284,6 +11686,161 @@ async function addToken(db, params) {
|
|
|
11284
11686
|
possible_duplicates: possibleDuplicates
|
|
11285
11687
|
};
|
|
11286
11688
|
}
|
|
11689
|
+
async function importOkfTokens(db, params) {
|
|
11690
|
+
const userId = await resolveHandlerUser(db, params.user);
|
|
11691
|
+
if (!params.file?.trim()) throw new Error("file must be non-empty");
|
|
11692
|
+
if (!Array.isArray(params.tokens) || params.tokens.length === 0) {
|
|
11693
|
+
throw new Error("tokens must be a non-empty array");
|
|
11694
|
+
}
|
|
11695
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11696
|
+
for (const input8 of params.tokens) {
|
|
11697
|
+
const slug = input8.slug?.trim();
|
|
11698
|
+
if (!slug || !input8.concept?.trim()) {
|
|
11699
|
+
throw new Error("every token needs a non-empty slug and concept");
|
|
11700
|
+
}
|
|
11701
|
+
if (seen.has(slug)) throw new Error(`duplicate token slug: ${slug}`);
|
|
11702
|
+
seen.add(slug);
|
|
11703
|
+
if (input8.bloomLevel !== void 0 && (!Number.isInteger(input8.bloomLevel) || input8.bloomLevel < 1 || input8.bloomLevel > 5)) {
|
|
11704
|
+
throw new Error(`bloomLevel must be 1-5 (token: ${slug})`);
|
|
11705
|
+
}
|
|
11706
|
+
}
|
|
11707
|
+
const { DEFAULT_BUNDLE_DIR: DEFAULT_BUNDLE_DIR2, resolveArticlePath: resolveArticlePath2 } = await Promise.resolve().then(() => (init_io(), io_exports));
|
|
11708
|
+
const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_bundle(), bundle_exports));
|
|
11709
|
+
const { readFileSync: readFileSync23 } = await import("fs");
|
|
11710
|
+
const bundleDir = params.bundleDir ?? DEFAULT_BUNDLE_DIR2;
|
|
11711
|
+
const articlePath = resolveArticlePath2(bundleDir, params.file);
|
|
11712
|
+
let markdown;
|
|
11713
|
+
try {
|
|
11714
|
+
markdown = readFileSync23(articlePath, "utf8");
|
|
11715
|
+
} catch {
|
|
11716
|
+
throw new Error(`Article not found: ${articlePath}`);
|
|
11717
|
+
}
|
|
11718
|
+
let resource;
|
|
11719
|
+
try {
|
|
11720
|
+
const { fields } = parseFrontmatter2(markdown);
|
|
11721
|
+
resource = typeof fields.resource === "string" ? fields.resource : void 0;
|
|
11722
|
+
} catch {
|
|
11723
|
+
resource = void 0;
|
|
11724
|
+
}
|
|
11725
|
+
const sourceBase = resource ?? articlePath;
|
|
11726
|
+
const sourceLinkFor = (anchor) => anchor?.trim() ? `${sourceBase}#${anchor.trim()}` : sourceBase;
|
|
11727
|
+
const created = [];
|
|
11728
|
+
const updated = [];
|
|
11729
|
+
const replaced = [];
|
|
11730
|
+
const maintenance = [];
|
|
11731
|
+
let cardsEnsured = 0;
|
|
11732
|
+
await db.transaction(async (tx) => {
|
|
11733
|
+
const inImport = /* @__PURE__ */ new Map();
|
|
11734
|
+
for (const input8 of params.tokens) {
|
|
11735
|
+
const mode = input8.mode ?? "new";
|
|
11736
|
+
const existing = await getTokenBySlug(tx, input8.slug);
|
|
11737
|
+
if (mode === "new") {
|
|
11738
|
+
if (existing) {
|
|
11739
|
+
throw new Error(
|
|
11740
|
+
`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.`
|
|
11741
|
+
);
|
|
11742
|
+
}
|
|
11743
|
+
const token2 = await createToken(tx, {
|
|
11744
|
+
slug: input8.slug,
|
|
11745
|
+
title: input8.title,
|
|
11746
|
+
concept: input8.concept,
|
|
11747
|
+
domain: input8.domain,
|
|
11748
|
+
bloom_level: input8.bloomLevel ?? 1,
|
|
11749
|
+
source_link: sourceLinkFor(input8.anchor),
|
|
11750
|
+
question: input8.question ?? null,
|
|
11751
|
+
question_source: input8.question ? "llm" : void 0
|
|
11752
|
+
});
|
|
11753
|
+
inImport.set(input8.slug, token2);
|
|
11754
|
+
created.push(input8.slug);
|
|
11755
|
+
} else {
|
|
11756
|
+
if (!existing) {
|
|
11757
|
+
throw new Error(
|
|
11758
|
+
`Token '${input8.slug}' does not exist \u2014 mode "${mode}" requires an existing token.`
|
|
11759
|
+
);
|
|
11760
|
+
}
|
|
11761
|
+
const updates = {
|
|
11762
|
+
concept: input8.concept,
|
|
11763
|
+
source_link: sourceLinkFor(input8.anchor)
|
|
11764
|
+
};
|
|
11765
|
+
if (input8.title !== void 0) updates.title = input8.title;
|
|
11766
|
+
if (input8.domain !== void 0) updates.domain = input8.domain;
|
|
11767
|
+
if (input8.bloomLevel !== void 0) {
|
|
11768
|
+
updates.bloom_level = input8.bloomLevel;
|
|
11769
|
+
}
|
|
11770
|
+
if (input8.question !== void 0) {
|
|
11771
|
+
updates.question = input8.question;
|
|
11772
|
+
updates.question_source = input8.question ? "llm" : void 0;
|
|
11773
|
+
}
|
|
11774
|
+
const token2 = await updateToken(tx, input8.slug, updates);
|
|
11775
|
+
if (existing.maintenance_at) {
|
|
11776
|
+
await clearTokenMaintenance(tx, input8.slug);
|
|
11777
|
+
}
|
|
11778
|
+
if (mode === "replace") {
|
|
11779
|
+
await resetCardsForToken(tx, token2.id);
|
|
11780
|
+
replaced.push(input8.slug);
|
|
11781
|
+
} else {
|
|
11782
|
+
updated.push(input8.slug);
|
|
11783
|
+
}
|
|
11784
|
+
inImport.set(input8.slug, token2);
|
|
11785
|
+
}
|
|
11786
|
+
const ctxNames = parseKnowledgeContextNames(input8.knowledgeContexts);
|
|
11787
|
+
const assigned = await resolveKnowledgeContexts(tx, ctxNames);
|
|
11788
|
+
const token = inImport.get(input8.slug);
|
|
11789
|
+
if (token) {
|
|
11790
|
+
for (const context of assigned) {
|
|
11791
|
+
await assignTokenToContext(tx, token.id, context.id);
|
|
11792
|
+
}
|
|
11793
|
+
}
|
|
11794
|
+
}
|
|
11795
|
+
for (const input8 of params.tokens) {
|
|
11796
|
+
const token = inImport.get(input8.slug);
|
|
11797
|
+
if (!token) continue;
|
|
11798
|
+
const prereqSlugs = [
|
|
11799
|
+
...new Set(
|
|
11800
|
+
(input8.prerequisites ?? []).map((s) => s.trim()).filter(Boolean)
|
|
11801
|
+
)
|
|
11802
|
+
];
|
|
11803
|
+
for (const prereqSlug of prereqSlugs) {
|
|
11804
|
+
const target = inImport.get(prereqSlug) ?? await getTokenBySlug(tx, prereqSlug);
|
|
11805
|
+
if (!target) {
|
|
11806
|
+
throw new Error(
|
|
11807
|
+
`Prerequisite token not found: ${prereqSlug} (for '${input8.slug}')`
|
|
11808
|
+
);
|
|
11809
|
+
}
|
|
11810
|
+
await addPrerequisite(tx, token.id, target.id);
|
|
11811
|
+
}
|
|
11812
|
+
}
|
|
11813
|
+
for (const token of inImport.values()) {
|
|
11814
|
+
await ensureCard(tx, token.id, userId);
|
|
11815
|
+
cardsEnsured++;
|
|
11816
|
+
}
|
|
11817
|
+
const prior = await getTokensBySourceLinkBase(tx, sourceBase);
|
|
11818
|
+
for (const token of prior) {
|
|
11819
|
+
if (!inImport.has(token.slug)) {
|
|
11820
|
+
await setTokenMaintenance(
|
|
11821
|
+
tx,
|
|
11822
|
+
token.slug,
|
|
11823
|
+
`absent from re-import of ${params.file}`
|
|
11824
|
+
);
|
|
11825
|
+
maintenance.push(token.slug);
|
|
11826
|
+
}
|
|
11827
|
+
}
|
|
11828
|
+
});
|
|
11829
|
+
try {
|
|
11830
|
+
await ensureTokenEmbeddings(db, { limit: 16 });
|
|
11831
|
+
} catch {
|
|
11832
|
+
}
|
|
11833
|
+
return {
|
|
11834
|
+
success: true,
|
|
11835
|
+
user: userId,
|
|
11836
|
+
article: { file: params.file, source_link: sourceBase },
|
|
11837
|
+
created,
|
|
11838
|
+
updated,
|
|
11839
|
+
replaced,
|
|
11840
|
+
maintenance,
|
|
11841
|
+
cards: cardsEnsured
|
|
11842
|
+
};
|
|
11843
|
+
}
|
|
11287
11844
|
async function findTokens2(db, params) {
|
|
11288
11845
|
const userId = await resolveHandlerUser(db, params.user);
|
|
11289
11846
|
if (!params.context.trim()) {
|
|
@@ -11580,11 +12137,11 @@ async function backupCreate(db, params) {
|
|
|
11580
12137
|
const targetDir = params.dir || (await ensureActiveWorkspace(db)).path;
|
|
11581
12138
|
const snapshot = await exportSnapshot(db);
|
|
11582
12139
|
const manifest = verifySnapshot(snapshot);
|
|
11583
|
-
const backupDir =
|
|
11584
|
-
|
|
12140
|
+
const backupDir = join19(targetDir, "zam-backups");
|
|
12141
|
+
mkdirSync13(backupDir, { recursive: true });
|
|
11585
12142
|
const stamp = manifest.createdAt.replace(/[:.]/g, "-");
|
|
11586
|
-
const path =
|
|
11587
|
-
|
|
12143
|
+
const path = join19(backupDir, `zam-snapshot-${stamp}.sql`);
|
|
12144
|
+
writeFileSync11(path, snapshot, "utf-8");
|
|
11588
12145
|
return {
|
|
11589
12146
|
ok: true,
|
|
11590
12147
|
path,
|
|
@@ -11608,16 +12165,16 @@ async function updateCheck(params) {
|
|
|
11608
12165
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
11609
12166
|
import {
|
|
11610
12167
|
chmodSync as chmodSync2,
|
|
11611
|
-
existsSync as
|
|
11612
|
-
mkdirSync as
|
|
11613
|
-
readFileSync as
|
|
11614
|
-
writeFileSync as
|
|
12168
|
+
existsSync as existsSync19,
|
|
12169
|
+
mkdirSync as mkdirSync14,
|
|
12170
|
+
readFileSync as readFileSync15,
|
|
12171
|
+
writeFileSync as writeFileSync12
|
|
11615
12172
|
} from "fs";
|
|
11616
12173
|
import { homedir as homedir13 } from "os";
|
|
11617
|
-
import { delimiter as delimiter2, join as
|
|
12174
|
+
import { delimiter as delimiter2, join as join20, resolve as resolve6 } from "path";
|
|
11618
12175
|
var BUILT_CLI_PATTERN = /[\\/]dist[\\/]cli[\\/]index\.js$/;
|
|
11619
12176
|
function normalizeForCompare(path, platform) {
|
|
11620
|
-
const resolved =
|
|
12177
|
+
const resolved = resolve6(path);
|
|
11621
12178
|
return platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
11622
12179
|
}
|
|
11623
12180
|
function windowsShimContent(nodePath, cliPath) {
|
|
@@ -11657,14 +12214,14 @@ function ensureWindowsUserPath(binDir) {
|
|
|
11657
12214
|
return output.endsWith("updated");
|
|
11658
12215
|
}
|
|
11659
12216
|
function ensureUnixUserPath(home, platform) {
|
|
11660
|
-
const profile =
|
|
11661
|
-
const existing =
|
|
12217
|
+
const profile = join20(home, platform === "darwin" ? ".zprofile" : ".profile");
|
|
12218
|
+
const existing = existsSync19(profile) ? readFileSync15(profile, "utf8") : "";
|
|
11662
12219
|
if (existing.includes(".zam/bin")) return false;
|
|
11663
12220
|
const block = `
|
|
11664
12221
|
# Added by ZAM: keep the zam CLI on PATH
|
|
11665
12222
|
export PATH="$HOME/.zam/bin:$PATH"
|
|
11666
12223
|
`;
|
|
11667
|
-
|
|
12224
|
+
writeFileSync12(profile, existing + block, "utf8");
|
|
11668
12225
|
return true;
|
|
11669
12226
|
}
|
|
11670
12227
|
function installCliShim(options = {}) {
|
|
@@ -11672,10 +12229,10 @@ function installCliShim(options = {}) {
|
|
|
11672
12229
|
const platform = options.platform ?? process.platform;
|
|
11673
12230
|
const env = options.env ?? process.env;
|
|
11674
12231
|
const find = options.find ?? findExecutable;
|
|
11675
|
-
const nodePath =
|
|
11676
|
-
const cliPath =
|
|
11677
|
-
const binDir =
|
|
11678
|
-
const shimPath =
|
|
12232
|
+
const nodePath = resolve6(options.nodePath ?? process.execPath);
|
|
12233
|
+
const cliPath = resolve6(options.cliPath ?? process.argv[1] ?? "");
|
|
12234
|
+
const binDir = join20(home, ".zam", "bin");
|
|
12235
|
+
const shimPath = join20(binDir, platform === "win32" ? "zam.cmd" : "zam");
|
|
11679
12236
|
const report = {
|
|
11680
12237
|
status: "ok",
|
|
11681
12238
|
binDir,
|
|
@@ -11686,7 +12243,7 @@ function installCliShim(options = {}) {
|
|
|
11686
12243
|
pathUpdated: false,
|
|
11687
12244
|
needsNewTerminal: false
|
|
11688
12245
|
};
|
|
11689
|
-
if (!BUILT_CLI_PATTERN.test(cliPath) || !
|
|
12246
|
+
if (!BUILT_CLI_PATTERN.test(cliPath) || !existsSync19(cliPath)) {
|
|
11690
12247
|
report.status = "skipped";
|
|
11691
12248
|
report.detail = `Not running from a built CLI entry (${cliPath}); nothing to link.`;
|
|
11692
12249
|
return report;
|
|
@@ -11700,11 +12257,11 @@ function installCliShim(options = {}) {
|
|
|
11700
12257
|
return report;
|
|
11701
12258
|
}
|
|
11702
12259
|
const content = platform === "win32" ? windowsShimContent(nodePath, cliPath) : unixShimContent(nodePath, cliPath);
|
|
11703
|
-
const existed =
|
|
11704
|
-
const changed = !existed ||
|
|
12260
|
+
const existed = existsSync19(shimPath);
|
|
12261
|
+
const changed = !existed || readFileSync15(shimPath, "utf8") !== content;
|
|
11705
12262
|
if (changed) {
|
|
11706
|
-
|
|
11707
|
-
|
|
12263
|
+
mkdirSync14(binDir, { recursive: true });
|
|
12264
|
+
writeFileSync12(shimPath, content, "utf8");
|
|
11708
12265
|
if (platform !== "win32") chmodSync2(shimPath, 493);
|
|
11709
12266
|
}
|
|
11710
12267
|
report.status = !existed ? "installed" : changed ? "refreshed" : "ok";
|
|
@@ -28413,40 +28970,40 @@ function getCurriculumProvider(id) {
|
|
|
28413
28970
|
|
|
28414
28971
|
// src/cli/install-repair.ts
|
|
28415
28972
|
init_kernel();
|
|
28416
|
-
import { existsSync as
|
|
28973
|
+
import { existsSync as existsSync21 } from "fs";
|
|
28417
28974
|
|
|
28418
28975
|
// src/cli/provisioning/index.ts
|
|
28419
28976
|
import {
|
|
28420
|
-
existsSync as
|
|
28977
|
+
existsSync as existsSync20,
|
|
28421
28978
|
lstatSync as lstatSync2,
|
|
28422
|
-
mkdirSync as
|
|
28423
|
-
readFileSync as
|
|
28424
|
-
realpathSync,
|
|
28979
|
+
mkdirSync as mkdirSync15,
|
|
28980
|
+
readFileSync as readFileSync16,
|
|
28981
|
+
realpathSync as realpathSync2,
|
|
28425
28982
|
rmSync as rmSync2,
|
|
28426
28983
|
symlinkSync,
|
|
28427
|
-
writeFileSync as
|
|
28984
|
+
writeFileSync as writeFileSync13
|
|
28428
28985
|
} from "fs";
|
|
28429
|
-
import { basename as basename4, dirname as
|
|
28430
|
-
import { fileURLToPath as
|
|
28986
|
+
import { basename as basename4, dirname as dirname10, join as join21 } from "path";
|
|
28987
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
28431
28988
|
var packageRoot3 = [
|
|
28432
|
-
|
|
28433
|
-
|
|
28434
|
-
].find((candidate) =>
|
|
28989
|
+
fileURLToPath6(new URL("../..", import.meta.url)),
|
|
28990
|
+
fileURLToPath6(new URL("../../..", import.meta.url))
|
|
28991
|
+
].find((candidate) => existsSync20(join21(candidate, "package.json"))) ?? fileURLToPath6(new URL("../..", import.meta.url));
|
|
28435
28992
|
var ALL_SETUP_AGENTS = ["claude", "copilot", "codex", "agent"];
|
|
28436
28993
|
var SKILL_PAIRS = [
|
|
28437
28994
|
{
|
|
28438
|
-
from:
|
|
28439
|
-
to:
|
|
28995
|
+
from: join21(packageRoot3, ".claude", "skills", "zam", "SKILL.md"),
|
|
28996
|
+
to: join21(".claude", "skills", "zam", "SKILL.md"),
|
|
28440
28997
|
agents: ["claude", "copilot"]
|
|
28441
28998
|
},
|
|
28442
28999
|
{
|
|
28443
|
-
from:
|
|
28444
|
-
to:
|
|
29000
|
+
from: join21(packageRoot3, ".agent", "skills", "zam", "SKILL.md"),
|
|
29001
|
+
to: join21(".agent", "skills", "zam", "SKILL.md"),
|
|
28445
29002
|
agents: ["agent"]
|
|
28446
29003
|
},
|
|
28447
29004
|
{
|
|
28448
|
-
from:
|
|
28449
|
-
to:
|
|
29005
|
+
from: join21(packageRoot3, ".agents", "skills", "zam", "SKILL.md"),
|
|
29006
|
+
to: join21(".agents", "skills", "zam", "SKILL.md"),
|
|
28450
29007
|
agents: ["codex"]
|
|
28451
29008
|
}
|
|
28452
29009
|
];
|
|
@@ -28490,7 +29047,7 @@ function isSymbolicLink(path) {
|
|
|
28490
29047
|
}
|
|
28491
29048
|
}
|
|
28492
29049
|
function comparableRealPath(path) {
|
|
28493
|
-
const real =
|
|
29050
|
+
const real = realpathSync2(path);
|
|
28494
29051
|
return process.platform === "win32" ? real.toLowerCase() : real;
|
|
28495
29052
|
}
|
|
28496
29053
|
function pathsResolveToSameDirectory(sourceDir, destinationDir) {
|
|
@@ -28506,15 +29063,15 @@ function linkPointsTo(sourceDir, destinationDir) {
|
|
|
28506
29063
|
function isZamSkillCopy(destinationDir) {
|
|
28507
29064
|
try {
|
|
28508
29065
|
if (!lstatSync2(destinationDir).isDirectory()) return false;
|
|
28509
|
-
const skillFile =
|
|
28510
|
-
if (!
|
|
28511
|
-
return /^name:\s*zam\s*$/m.test(
|
|
29066
|
+
const skillFile = join21(destinationDir, "SKILL.md");
|
|
29067
|
+
if (!existsSync20(skillFile)) return false;
|
|
29068
|
+
return /^name:\s*zam\s*$/m.test(readFileSync16(skillFile, "utf8"));
|
|
28512
29069
|
} catch {
|
|
28513
29070
|
return false;
|
|
28514
29071
|
}
|
|
28515
29072
|
}
|
|
28516
29073
|
function classifySkillDestination(sourceDir, destinationDir) {
|
|
28517
|
-
if (!
|
|
29074
|
+
if (!existsSync20(sourceDir)) return "source-missing";
|
|
28518
29075
|
if (!isSymbolicLink(destinationDir) && pathsResolveToSameDirectory(sourceDir, destinationDir)) {
|
|
28519
29076
|
return "source-directory";
|
|
28520
29077
|
}
|
|
@@ -28531,9 +29088,9 @@ function wireSkills(cwd = process.cwd(), agents = parseSetupAgents(), opts = {})
|
|
|
28531
29088
|
};
|
|
28532
29089
|
for (const { from, to, agents: pairAgents } of SKILL_PAIRS) {
|
|
28533
29090
|
if (!pairAgents.some((agent) => agents.has(agent))) continue;
|
|
28534
|
-
const sourceDir =
|
|
28535
|
-
const destinationDir =
|
|
28536
|
-
const label =
|
|
29091
|
+
const sourceDir = dirname10(from);
|
|
29092
|
+
const destinationDir = dirname10(join21(cwd, to));
|
|
29093
|
+
const label = dirname10(to);
|
|
28537
29094
|
const state = classifySkillDestination(sourceDir, destinationDir);
|
|
28538
29095
|
if (state === "source-missing") {
|
|
28539
29096
|
if (!opts.quiet) {
|
|
@@ -28590,7 +29147,7 @@ function wireSkills(cwd = process.cwd(), agents = parseSetupAgents(), opts = {})
|
|
|
28590
29147
|
if (destinationExists) {
|
|
28591
29148
|
rmSync2(destinationDir, { recursive: true, force: true });
|
|
28592
29149
|
}
|
|
28593
|
-
|
|
29150
|
+
mkdirSync15(dirname10(destinationDir), { recursive: true });
|
|
28594
29151
|
symlinkSync(
|
|
28595
29152
|
sourceDir,
|
|
28596
29153
|
destinationDir,
|
|
@@ -28606,8 +29163,8 @@ function inspectSkillLinks(cwd = process.cwd(), agents = parseSetupAgents()) {
|
|
|
28606
29163
|
const results = [];
|
|
28607
29164
|
for (const { from, to, agents: pairAgents } of SKILL_PAIRS) {
|
|
28608
29165
|
if (!pairAgents.some((agent) => agents.has(agent))) continue;
|
|
28609
|
-
const sourceDir =
|
|
28610
|
-
const destinationDir =
|
|
29166
|
+
const sourceDir = dirname10(from);
|
|
29167
|
+
const destinationDir = dirname10(join21(cwd, to));
|
|
28611
29168
|
results.push({
|
|
28612
29169
|
agents: pairAgents,
|
|
28613
29170
|
source: sourceDir,
|
|
@@ -28633,15 +29190,15 @@ function upsertMarkedBlock(dest, blockBody, dryRun) {
|
|
|
28633
29190
|
const block = `${ZAM_BLOCK_START}
|
|
28634
29191
|
${blockBody.trim()}
|
|
28635
29192
|
${ZAM_BLOCK_END}`;
|
|
28636
|
-
if (!
|
|
29193
|
+
if (!existsSync20(dest)) {
|
|
28637
29194
|
if (!dryRun) {
|
|
28638
|
-
|
|
28639
|
-
|
|
29195
|
+
mkdirSync15(dirname10(dest), { recursive: true });
|
|
29196
|
+
writeFileSync13(dest, `${block}
|
|
28640
29197
|
`, "utf8");
|
|
28641
29198
|
}
|
|
28642
29199
|
return "write";
|
|
28643
29200
|
}
|
|
28644
|
-
const existing =
|
|
29201
|
+
const existing = readFileSync16(dest, "utf8");
|
|
28645
29202
|
if (existing.includes(block)) return "skip";
|
|
28646
29203
|
const start = existing.indexOf(ZAM_BLOCK_START);
|
|
28647
29204
|
const end = existing.indexOf(ZAM_BLOCK_END);
|
|
@@ -28649,7 +29206,7 @@ ${ZAM_BLOCK_END}`;
|
|
|
28649
29206
|
|
|
28650
29207
|
${block}
|
|
28651
29208
|
`;
|
|
28652
|
-
if (!dryRun)
|
|
29209
|
+
if (!dryRun) writeFileSync13(dest, next, "utf8");
|
|
28653
29210
|
return start >= 0 && end > start ? "update" : "write";
|
|
28654
29211
|
}
|
|
28655
29212
|
function logInstructionAction(action, label, dryRun) {
|
|
@@ -28661,8 +29218,8 @@ function logInstructionAction(action, label, dryRun) {
|
|
|
28661
29218
|
}
|
|
28662
29219
|
function writeClaudeMd(skipClaudeMd, cwd = process.cwd(), opts = {}) {
|
|
28663
29220
|
if (skipClaudeMd) return;
|
|
28664
|
-
const dest =
|
|
28665
|
-
if (
|
|
29221
|
+
const dest = join21(cwd, "CLAUDE.md");
|
|
29222
|
+
if (existsSync20(dest)) {
|
|
28666
29223
|
if (!opts.updateExisting) {
|
|
28667
29224
|
console.log(` skip CLAUDE.md (already present)`);
|
|
28668
29225
|
return;
|
|
@@ -28705,14 +29262,14 @@ Use \`zam connector setup turso\` to store cloud credentials in
|
|
|
28705
29262
|
if (opts.dryRun) {
|
|
28706
29263
|
console.log(` would write CLAUDE.md`);
|
|
28707
29264
|
} else {
|
|
28708
|
-
|
|
29265
|
+
writeFileSync13(dest, content, "utf8");
|
|
28709
29266
|
console.log(` write CLAUDE.md`);
|
|
28710
29267
|
}
|
|
28711
29268
|
}
|
|
28712
29269
|
function writeAgentsMd(skipAgentsMd, cwd = process.cwd(), opts = {}) {
|
|
28713
29270
|
if (skipAgentsMd) return;
|
|
28714
|
-
const dest =
|
|
28715
|
-
if (
|
|
29271
|
+
const dest = join21(cwd, "AGENTS.md");
|
|
29272
|
+
if (existsSync20(dest)) {
|
|
28716
29273
|
if (!opts.updateExisting) {
|
|
28717
29274
|
console.log(` skip AGENTS.md (already present)`);
|
|
28718
29275
|
return;
|
|
@@ -28762,12 +29319,12 @@ Codex discovers repository skills under \`.agents/skills/\`. Run
|
|
|
28762
29319
|
if (opts.dryRun) {
|
|
28763
29320
|
console.log(` would write AGENTS.md`);
|
|
28764
29321
|
} else {
|
|
28765
|
-
|
|
29322
|
+
writeFileSync13(dest, content, "utf8");
|
|
28766
29323
|
console.log(` write AGENTS.md`);
|
|
28767
29324
|
}
|
|
28768
29325
|
}
|
|
28769
29326
|
function writeCopilotInstructions(cwd = process.cwd(), opts = {}) {
|
|
28770
|
-
const dest =
|
|
29327
|
+
const dest = join21(cwd, ".github", "copilot-instructions.md");
|
|
28771
29328
|
const action = upsertMarkedBlock(
|
|
28772
29329
|
dest,
|
|
28773
29330
|
`## ZAM learning sessions
|
|
@@ -28795,7 +29352,7 @@ function defaultRepairWorkspaces() {
|
|
|
28795
29352
|
try {
|
|
28796
29353
|
const agents = parseSetupAgents();
|
|
28797
29354
|
for (const workspace of getConfiguredWorkspaces()) {
|
|
28798
|
-
if (!
|
|
29355
|
+
if (!existsSync21(workspace.path)) {
|
|
28799
29356
|
summary.missing += 1;
|
|
28800
29357
|
continue;
|
|
28801
29358
|
}
|
|
@@ -28983,9 +29540,9 @@ function validateModelSave(entry, probe, now = () => (/* @__PURE__ */ new Date()
|
|
|
28983
29540
|
// src/cli/llm/vision.ts
|
|
28984
29541
|
init_kernel();
|
|
28985
29542
|
import { randomBytes } from "crypto";
|
|
28986
|
-
import { readFileSync as
|
|
29543
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
28987
29544
|
import { tmpdir as tmpdir2 } from "os";
|
|
28988
|
-
import { basename as basename5, join as
|
|
29545
|
+
import { basename as basename5, join as join22 } from "path";
|
|
28989
29546
|
var LANGUAGE_NAMES2 = {
|
|
28990
29547
|
en: "English",
|
|
28991
29548
|
de: "German",
|
|
@@ -29021,25 +29578,25 @@ async function observeUiSnapshotViaLLM(db, input8) {
|
|
|
29021
29578
|
const imageUrls = [];
|
|
29022
29579
|
const isVideo = /\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(input8.imagePath);
|
|
29023
29580
|
if (isVideo) {
|
|
29024
|
-
const { mkdirSync:
|
|
29581
|
+
const { mkdirSync: mkdirSync20, readdirSync: readdirSync4, rmSync: rmSync4 } = await import("fs");
|
|
29025
29582
|
const { execSync: execSync6 } = await import("child_process");
|
|
29026
|
-
const tempDir =
|
|
29583
|
+
const tempDir = join22(
|
|
29027
29584
|
tmpdir2(),
|
|
29028
29585
|
`zam-frames-${randomBytes(4).toString("hex")}`
|
|
29029
29586
|
);
|
|
29030
|
-
|
|
29587
|
+
mkdirSync20(tempDir, { recursive: true });
|
|
29031
29588
|
try {
|
|
29032
29589
|
execSync6(
|
|
29033
29590
|
`ffmpeg -i "${input8.imagePath}" -vf "fps=1/3,scale=1280:-1" -vsync vfr "${tempDir}/frame_%03d.png"`,
|
|
29034
29591
|
{ stdio: "ignore" }
|
|
29035
29592
|
);
|
|
29036
|
-
let files =
|
|
29593
|
+
let files = readdirSync4(tempDir).filter((f) => f.endsWith(".png")).sort();
|
|
29037
29594
|
if (files.length === 0) {
|
|
29038
29595
|
execSync6(
|
|
29039
29596
|
`ffmpeg -i "${input8.imagePath}" -vframes 1 "${tempDir}/frame_001.png"`,
|
|
29040
29597
|
{ stdio: "ignore" }
|
|
29041
29598
|
);
|
|
29042
|
-
files =
|
|
29599
|
+
files = readdirSync4(tempDir).filter((f) => f.endsWith(".png")).sort();
|
|
29043
29600
|
}
|
|
29044
29601
|
const maxFrames = cfg.maxFrames ?? 100;
|
|
29045
29602
|
let sampledFiles = files;
|
|
@@ -29056,7 +29613,7 @@ async function observeUiSnapshotViaLLM(db, input8) {
|
|
|
29056
29613
|
}
|
|
29057
29614
|
}
|
|
29058
29615
|
for (const file of sampledFiles) {
|
|
29059
|
-
const bytes =
|
|
29616
|
+
const bytes = readFileSync17(join22(tempDir, file));
|
|
29060
29617
|
imageUrls.push(`data:image/png;base64,${bytes.toString("base64")}`);
|
|
29061
29618
|
}
|
|
29062
29619
|
} finally {
|
|
@@ -29066,7 +29623,7 @@ async function observeUiSnapshotViaLLM(db, input8) {
|
|
|
29066
29623
|
}
|
|
29067
29624
|
}
|
|
29068
29625
|
} else {
|
|
29069
|
-
const imageBytes =
|
|
29626
|
+
const imageBytes = readFileSync17(input8.imagePath);
|
|
29070
29627
|
const ext = input8.imagePath.split(".").pop()?.toLowerCase();
|
|
29071
29628
|
const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : "image/png";
|
|
29072
29629
|
imageUrls.push(`data:${mime};base64,${imageBytes.toString("base64")}`);
|
|
@@ -29577,13 +30134,13 @@ async function resolveUser(opts, db, resolveOpts) {
|
|
|
29577
30134
|
}
|
|
29578
30135
|
|
|
29579
30136
|
// src/cli/workspaces/backup.ts
|
|
29580
|
-
import { mkdirSync as
|
|
29581
|
-
import { join as
|
|
30137
|
+
import { mkdirSync as mkdirSync16 } from "fs";
|
|
30138
|
+
import { join as join23 } from "path";
|
|
29582
30139
|
async function backupDatabaseTo(db, targetDir) {
|
|
29583
|
-
const backupDir =
|
|
29584
|
-
|
|
30140
|
+
const backupDir = join23(targetDir, "zam-backups");
|
|
30141
|
+
mkdirSync16(backupDir, { recursive: true });
|
|
29585
30142
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
29586
|
-
const dest =
|
|
30143
|
+
const dest = join23(backupDir, `zam-${stamp}.db`);
|
|
29587
30144
|
await db.exec(`VACUUM INTO '${dest.replace(/'/g, "''")}'`);
|
|
29588
30145
|
return dest;
|
|
29589
30146
|
}
|
|
@@ -29717,20 +30274,20 @@ bridgeCommand.command("workspace-info").description("Report the workspace dir, i
|
|
|
29717
30274
|
activeWorkspace,
|
|
29718
30275
|
workspaceDir: activeWorkspace.path,
|
|
29719
30276
|
defaultWorkspaceDir: defaultWorkspaceDir(),
|
|
29720
|
-
dataDir:
|
|
30277
|
+
dataDir: join24(homedir14(), ".zam")
|
|
29721
30278
|
});
|
|
29722
30279
|
});
|
|
29723
30280
|
});
|
|
29724
30281
|
function provisionConfiguredWorkspaces() {
|
|
29725
30282
|
return getConfiguredWorkspaces().flatMap(
|
|
29726
|
-
(workspace) =>
|
|
30283
|
+
(workspace) => existsSync22(workspace.path) ? wireSkills(workspace.path, parseSetupAgents(), { quiet: true }) : []
|
|
29727
30284
|
);
|
|
29728
30285
|
}
|
|
29729
30286
|
function buildWorkspaceLinkHealth(workspaces) {
|
|
29730
30287
|
const agents = parseSetupAgents();
|
|
29731
30288
|
const map = {};
|
|
29732
30289
|
for (const workspace of workspaces) {
|
|
29733
|
-
if (!
|
|
30290
|
+
if (!existsSync22(workspace.path)) continue;
|
|
29734
30291
|
const links = inspectSkillLinks(workspace.path, agents);
|
|
29735
30292
|
map[workspace.id] = {
|
|
29736
30293
|
health: summarizeSkillLinkHealth(links),
|
|
@@ -29760,7 +30317,7 @@ bridgeCommand.command("workspace-list").description("List configured ZAM workspa
|
|
|
29760
30317
|
activeWorkspace,
|
|
29761
30318
|
workspaceDir: activeWorkspace.path,
|
|
29762
30319
|
defaultWorkspaceDir: defaultWorkspaceDir(),
|
|
29763
|
-
dataDir:
|
|
30320
|
+
dataDir: join24(homedir14(), ".zam"),
|
|
29764
30321
|
linkHealth: buildWorkspaceLinkHealth(workspaces)
|
|
29765
30322
|
});
|
|
29766
30323
|
});
|
|
@@ -29775,7 +30332,7 @@ bridgeCommand.command("workspace-repair-links").description(
|
|
|
29775
30332
|
if (!id) jsonError("A non-empty --id is required");
|
|
29776
30333
|
const workspace = getConfiguredWorkspaces().find((item) => item.id === id);
|
|
29777
30334
|
if (!workspace) jsonError(`Workspace "${id}" is not configured`);
|
|
29778
|
-
if (!
|
|
30335
|
+
if (!existsSync22(workspace.path)) {
|
|
29779
30336
|
jsonError(`Workspace path does not exist: ${workspace.path}`);
|
|
29780
30337
|
}
|
|
29781
30338
|
const agents = parseSetupAgents(opts.agents);
|
|
@@ -29806,8 +30363,8 @@ function parseBridgeWorkspaceKind(value) {
|
|
|
29806
30363
|
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
30364
|
const raw = String(opts.path ?? "").trim();
|
|
29808
30365
|
if (!raw) jsonError("A non-empty --path is required");
|
|
29809
|
-
const path =
|
|
29810
|
-
if (!
|
|
30366
|
+
const path = resolve7(raw);
|
|
30367
|
+
if (!existsSync22(path)) jsonError(`Workspace path does not exist: ${path}`);
|
|
29811
30368
|
const id = opts.id ? String(opts.id).trim() : void 0;
|
|
29812
30369
|
if (opts.id && !id) jsonError("A non-empty --id is required");
|
|
29813
30370
|
const kind = parseBridgeWorkspaceKind(opts.kind);
|
|
@@ -29851,8 +30408,8 @@ bridgeCommand.command("workspace-remove").description("Unregister a ZAM workspac
|
|
|
29851
30408
|
bridgeCommand.command("set-workspace-dir").description("Set the personal workspace directory (JSON)").requiredOption("--dir <path>", "Path to the workspace directory").action(async (opts) => {
|
|
29852
30409
|
const raw = String(opts.dir ?? "").trim();
|
|
29853
30410
|
if (!raw) jsonError("A non-empty --dir is required");
|
|
29854
|
-
const dir =
|
|
29855
|
-
if (!
|
|
30411
|
+
const dir = resolve7(raw);
|
|
30412
|
+
if (!existsSync22(dir)) jsonError(`Workspace path does not exist: ${dir}`);
|
|
29856
30413
|
const skillLinks = wireSkills(dir, parseSetupAgents(), { quiet: true });
|
|
29857
30414
|
await withOptionalDb2(async (db) => {
|
|
29858
30415
|
const workspace = await activateWorkspacePath(db, dir);
|
|
@@ -29915,7 +30472,7 @@ bridgeCommand.command("agent-open").description("Launch an agent harness in the
|
|
|
29915
30472
|
jsonError(`Workspace is not configured: ${opts.workspace}`);
|
|
29916
30473
|
}
|
|
29917
30474
|
const activeWorkspace = await ensureActiveWorkspace(db);
|
|
29918
|
-
const workspace = opts.dir ?
|
|
30475
|
+
const workspace = opts.dir ? existsSync22(opts.dir) ? opts.dir : homedir14() : existingWorkspaceDirOrHome(configuredWorkspace ?? activeWorkspace);
|
|
29919
30476
|
launchHarness(harness, {
|
|
29920
30477
|
executable,
|
|
29921
30478
|
workspace,
|
|
@@ -30191,6 +30748,52 @@ bridgeCommand.command("add-token").description("Create a token + card from JSON
|
|
|
30191
30748
|
jsonError(err.message);
|
|
30192
30749
|
}
|
|
30193
30750
|
});
|
|
30751
|
+
bridgeCommand.command("okf-import").description(
|
|
30752
|
+
"Record an agent's decomposition of an OKF article as learning tokens (JSON stdin, ADR 2026-07-18)"
|
|
30753
|
+
).option("--user <id>", "User ID (default: whoami)").action(async (opts) => {
|
|
30754
|
+
try {
|
|
30755
|
+
let raw;
|
|
30756
|
+
if (isServeMode) {
|
|
30757
|
+
raw = serveStdinPayload ?? "";
|
|
30758
|
+
} else {
|
|
30759
|
+
const chunks = [];
|
|
30760
|
+
for await (const chunk of process.stdin) {
|
|
30761
|
+
chunks.push(chunk);
|
|
30762
|
+
}
|
|
30763
|
+
raw = Buffer.concat(chunks).toString("utf-8").trim();
|
|
30764
|
+
}
|
|
30765
|
+
if (!raw) {
|
|
30766
|
+
jsonError(
|
|
30767
|
+
"No input received on stdin. Pipe JSON: { file, bundle_dir?, tokens: [...] }"
|
|
30768
|
+
);
|
|
30769
|
+
}
|
|
30770
|
+
let data;
|
|
30771
|
+
try {
|
|
30772
|
+
data = JSON.parse(raw);
|
|
30773
|
+
} catch {
|
|
30774
|
+
jsonError("Invalid JSON input");
|
|
30775
|
+
}
|
|
30776
|
+
if (!data?.file || !Array.isArray(data?.tokens)) {
|
|
30777
|
+
jsonError("JSON must include 'file' and a 'tokens' array");
|
|
30778
|
+
}
|
|
30779
|
+
await withDb2(async (db) => {
|
|
30780
|
+
try {
|
|
30781
|
+
const userId = await resolveUser(opts, db, { json: true });
|
|
30782
|
+
const result = await importOkfTokens(db, {
|
|
30783
|
+
user: userId,
|
|
30784
|
+
bundleDir: data.bundle_dir,
|
|
30785
|
+
file: data.file,
|
|
30786
|
+
tokens: data.tokens
|
|
30787
|
+
});
|
|
30788
|
+
jsonOut2(result);
|
|
30789
|
+
} catch (err) {
|
|
30790
|
+
jsonError(err.message);
|
|
30791
|
+
}
|
|
30792
|
+
});
|
|
30793
|
+
} catch (err) {
|
|
30794
|
+
jsonError(err.message);
|
|
30795
|
+
}
|
|
30796
|
+
});
|
|
30194
30797
|
bridgeCommand.command("relevant-tokens").description("Find tokens relevant to a given context").option("--user <id>", "User ID (default: whoami)").action(async (opts) => {
|
|
30195
30798
|
try {
|
|
30196
30799
|
let raw;
|
|
@@ -30287,10 +30890,10 @@ bridgeCommand.command("discover-skills").description(
|
|
|
30287
30890
|
"20"
|
|
30288
30891
|
).action(async (opts) => {
|
|
30289
30892
|
try {
|
|
30290
|
-
const monitorDir =
|
|
30893
|
+
const monitorDir = join24(homedir14(), ".zam", "monitor");
|
|
30291
30894
|
let files;
|
|
30292
30895
|
try {
|
|
30293
|
-
files =
|
|
30896
|
+
files = readdirSync3(monitorDir).filter((f) => f.endsWith(".jsonl"));
|
|
30294
30897
|
} catch {
|
|
30295
30898
|
jsonOut2({ proposals: [], message: "No monitor logs found." });
|
|
30296
30899
|
return;
|
|
@@ -30300,7 +30903,7 @@ bridgeCommand.command("discover-skills").description(
|
|
|
30300
30903
|
return;
|
|
30301
30904
|
}
|
|
30302
30905
|
const limit = Number(opts.limit);
|
|
30303
|
-
const sorted = files.map((f) => ({ name: f, path:
|
|
30906
|
+
const sorted = files.map((f) => ({ name: f, path: join24(monitorDir, f) })).sort((a, b) => b.name.localeCompare(a.name)).slice(0, limit);
|
|
30304
30907
|
const sessionCommands = /* @__PURE__ */ new Map();
|
|
30305
30908
|
for (const file of sorted) {
|
|
30306
30909
|
const sessionId = file.name.replace(".jsonl", "");
|
|
@@ -30802,7 +31405,7 @@ bridgeCommand.command("capture-ui").description("Capture a screenshot for agent-
|
|
|
30802
31405
|
return;
|
|
30803
31406
|
}
|
|
30804
31407
|
}
|
|
30805
|
-
const outputPath = opts.image ?? opts.output ??
|
|
31408
|
+
const outputPath = opts.image ?? opts.output ?? join24(tmpdir3(), `zam-capture-${randomBytes2(4).toString("hex")}.png`);
|
|
30806
31409
|
const captureResult = isProvided ? { method: "provided", target: null } : captureScreenshot(outputPath, opts.hwnd, opts.processName);
|
|
30807
31410
|
if (!isProvided) {
|
|
30808
31411
|
const post = decidePostCapture(policy, {
|
|
@@ -30830,7 +31433,7 @@ bridgeCommand.command("capture-ui").description("Capture a screenshot for agent-
|
|
|
30830
31433
|
return;
|
|
30831
31434
|
}
|
|
30832
31435
|
}
|
|
30833
|
-
const imageBytes =
|
|
31436
|
+
const imageBytes = readFileSync18(outputPath);
|
|
30834
31437
|
const base64 = imageBytes.toString("base64");
|
|
30835
31438
|
jsonOut2({
|
|
30836
31439
|
sessionId: opts.session ?? null,
|
|
@@ -30857,11 +31460,11 @@ bridgeCommand.command("start-recording").description("Start screen recording in
|
|
|
30857
31460
|
return;
|
|
30858
31461
|
}
|
|
30859
31462
|
const sessionId = opts.session;
|
|
30860
|
-
const statePath =
|
|
31463
|
+
const statePath = join24(tmpdir3(), `zam-recording-${sessionId}.json`);
|
|
30861
31464
|
const defaultExt = platform === "win32" ? ".mkv" : ".mov";
|
|
30862
|
-
const outputPath = opts.output ??
|
|
30863
|
-
const { existsSync:
|
|
30864
|
-
if (
|
|
31465
|
+
const outputPath = opts.output ?? join24(tmpdir3(), `zam-recording-${sessionId}${defaultExt}`);
|
|
31466
|
+
const { existsSync: existsSync31, writeFileSync: writeFileSync18, openSync, closeSync } = await import("fs");
|
|
31467
|
+
if (existsSync31(statePath)) {
|
|
30865
31468
|
jsonOut2({
|
|
30866
31469
|
sessionId,
|
|
30867
31470
|
started: false,
|
|
@@ -30869,7 +31472,7 @@ bridgeCommand.command("start-recording").description("Start screen recording in
|
|
|
30869
31472
|
});
|
|
30870
31473
|
return;
|
|
30871
31474
|
}
|
|
30872
|
-
const logPath =
|
|
31475
|
+
const logPath = join24(tmpdir3(), `zam-recording-${sessionId}.log`);
|
|
30873
31476
|
let logFd;
|
|
30874
31477
|
try {
|
|
30875
31478
|
logFd = openSync(logPath, "w");
|
|
@@ -30915,7 +31518,7 @@ bridgeCommand.command("start-recording").description("Start screen recording in
|
|
|
30915
31518
|
}
|
|
30916
31519
|
child.unref();
|
|
30917
31520
|
if (child.pid) {
|
|
30918
|
-
|
|
31521
|
+
writeFileSync18(
|
|
30919
31522
|
statePath,
|
|
30920
31523
|
JSON.stringify({
|
|
30921
31524
|
pid: child.pid,
|
|
@@ -30951,9 +31554,9 @@ bridgeCommand.command("stop-recording").description(
|
|
|
30951
31554
|
return;
|
|
30952
31555
|
}
|
|
30953
31556
|
const sessionId = opts.session;
|
|
30954
|
-
const statePath =
|
|
30955
|
-
const { existsSync:
|
|
30956
|
-
if (!
|
|
31557
|
+
const statePath = join24(tmpdir3(), `zam-recording-${sessionId}.json`);
|
|
31558
|
+
const { existsSync: existsSync31, readFileSync: readFileSync23, rmSync: rmSync4 } = await import("fs");
|
|
31559
|
+
if (!existsSync31(statePath)) {
|
|
30957
31560
|
jsonOut2({
|
|
30958
31561
|
sessionId,
|
|
30959
31562
|
stopped: false,
|
|
@@ -30961,7 +31564,7 @@ bridgeCommand.command("stop-recording").description(
|
|
|
30961
31564
|
});
|
|
30962
31565
|
return;
|
|
30963
31566
|
}
|
|
30964
|
-
const state = JSON.parse(
|
|
31567
|
+
const state = JSON.parse(readFileSync23(statePath, "utf8"));
|
|
30965
31568
|
const { pid, outputPath } = state;
|
|
30966
31569
|
try {
|
|
30967
31570
|
process.kill(pid, "SIGINT");
|
|
@@ -30977,7 +31580,7 @@ bridgeCommand.command("stop-recording").description(
|
|
|
30977
31580
|
};
|
|
30978
31581
|
let attempts = 0;
|
|
30979
31582
|
while (isProcessRunning(pid) && attempts < 20) {
|
|
30980
|
-
await new Promise((
|
|
31583
|
+
await new Promise((resolve13) => setTimeout(resolve13, 250));
|
|
30981
31584
|
attempts++;
|
|
30982
31585
|
}
|
|
30983
31586
|
if (isProcessRunning(pid)) {
|
|
@@ -30990,7 +31593,7 @@ bridgeCommand.command("stop-recording").description(
|
|
|
30990
31593
|
rmSync4(statePath, { force: true });
|
|
30991
31594
|
} catch {
|
|
30992
31595
|
}
|
|
30993
|
-
if (!
|
|
31596
|
+
if (!existsSync31(outputPath)) {
|
|
30994
31597
|
jsonOut2({
|
|
30995
31598
|
sessionId,
|
|
30996
31599
|
stopped: false,
|
|
@@ -31420,7 +32023,7 @@ bridgeCommand.command("cloud-model-hint").description("Suggest a cloud model for
|
|
|
31420
32023
|
});
|
|
31421
32024
|
bridgeCommand.command("local-llm-hints").description("Detect installed local LLM servers and suggest defaults (JSON)").action(() => {
|
|
31422
32025
|
const profile = getSystemProfile();
|
|
31423
|
-
const flmInstalled = hasCommand("flm") ||
|
|
32026
|
+
const flmInstalled = hasCommand("flm") || existsSync22("C:\\Program Files\\flm\\flm.exe");
|
|
31424
32027
|
const ollamaInstalled = isOllamaInstalled();
|
|
31425
32028
|
const runners = [
|
|
31426
32029
|
{ id: "flm", label: "FastFlowLM", installed: flmInstalled },
|
|
@@ -31794,7 +32397,12 @@ bridgeCommand.command("list-tokens").description(
|
|
|
31794
32397
|
).option("--domain <domain>", "Filter by exact domain").option(
|
|
31795
32398
|
"--domain-prefix <prefix>",
|
|
31796
32399
|
"Filter by domain prefix (e.g. company-team) \u2014 uses / separator for hierarchy"
|
|
31797
|
-
).option("--knowledge-context <context>", "Filter by knowledge context").
|
|
32400
|
+
).option("--knowledge-context <context>", "Filter by knowledge context").option(
|
|
32401
|
+
"--source-link-base <base>",
|
|
32402
|
+
"Filter by source-link base (exact or '<base>#<anchor>', as written by OKF imports); repeatable",
|
|
32403
|
+
(value, previous) => [...previous, value],
|
|
32404
|
+
[]
|
|
32405
|
+
).action(async (opts) => {
|
|
31798
32406
|
await withDb2(async (db) => {
|
|
31799
32407
|
const userId = opts.user ? await resolveUser(opts, db, { json: true }) : void 0;
|
|
31800
32408
|
const listOpts = {};
|
|
@@ -31802,6 +32410,8 @@ bridgeCommand.command("list-tokens").description(
|
|
|
31802
32410
|
if (opts.domainPrefix) listOpts.domainPrefix = opts.domainPrefix;
|
|
31803
32411
|
if (opts.knowledgeContext)
|
|
31804
32412
|
listOpts.knowledgeContext = opts.knowledgeContext;
|
|
32413
|
+
if (opts.sourceLinkBase.length)
|
|
32414
|
+
listOpts.sourceLinkBases = opts.sourceLinkBase;
|
|
31805
32415
|
const tokens = await listTokens(
|
|
31806
32416
|
db,
|
|
31807
32417
|
Object.keys(listOpts).length ? listOpts : void 0
|
|
@@ -34645,26 +35255,26 @@ var doctorCommand = new Command5("doctor").description(
|
|
|
34645
35255
|
// src/cli/commands/git-sync.ts
|
|
34646
35256
|
init_kernel();
|
|
34647
35257
|
import { execSync as execSync5 } from "child_process";
|
|
34648
|
-
import { chmodSync as chmodSync3, existsSync as
|
|
34649
|
-
import { join as
|
|
35258
|
+
import { chmodSync as chmodSync3, existsSync as existsSync23, writeFileSync as writeFileSync14 } from "fs";
|
|
35259
|
+
import { join as join25 } from "path";
|
|
34650
35260
|
import { Command as Command6 } from "commander";
|
|
34651
35261
|
function installHook2() {
|
|
34652
|
-
const gitDir =
|
|
34653
|
-
if (!
|
|
35262
|
+
const gitDir = join25(process.cwd(), ".git");
|
|
35263
|
+
if (!existsSync23(gitDir)) {
|
|
34654
35264
|
console.error(
|
|
34655
35265
|
"Error: Current directory is not the root of a Git repository."
|
|
34656
35266
|
);
|
|
34657
35267
|
process.exit(1);
|
|
34658
35268
|
}
|
|
34659
|
-
const hooksDir =
|
|
34660
|
-
const hookPath =
|
|
35269
|
+
const hooksDir = join25(gitDir, "hooks");
|
|
35270
|
+
const hookPath = join25(hooksDir, "post-commit");
|
|
34661
35271
|
const hookContent = `#!/bin/sh
|
|
34662
35272
|
# ZAM Spaced Repetition Auto-Stale Hook
|
|
34663
35273
|
# Triggered automatically on git commits to decay modified concept cards.
|
|
34664
35274
|
zam git-sync --commit HEAD --quiet
|
|
34665
35275
|
`;
|
|
34666
35276
|
try {
|
|
34667
|
-
|
|
35277
|
+
writeFileSync14(hookPath, hookContent, { encoding: "utf-8", flag: "w" });
|
|
34668
35278
|
try {
|
|
34669
35279
|
chmodSync3(hookPath, "755");
|
|
34670
35280
|
} catch (_e) {
|
|
@@ -34767,8 +35377,8 @@ ZAM Auto-Stale Complete: Scanned ${changedFiles.length} file(s).`
|
|
|
34767
35377
|
|
|
34768
35378
|
// src/cli/commands/goal.ts
|
|
34769
35379
|
init_kernel();
|
|
34770
|
-
import { existsSync as
|
|
34771
|
-
import { resolve as
|
|
35380
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync17 } from "fs";
|
|
35381
|
+
import { resolve as resolve8 } from "path";
|
|
34772
35382
|
import { input as input2 } from "@inquirer/prompts";
|
|
34773
35383
|
import { Command as Command7 } from "commander";
|
|
34774
35384
|
async function resolveGoalsDir() {
|
|
@@ -34781,7 +35391,7 @@ async function resolveGoalsDir() {
|
|
|
34781
35391
|
} finally {
|
|
34782
35392
|
await db?.close();
|
|
34783
35393
|
}
|
|
34784
|
-
return goalsDir ?
|
|
35394
|
+
return goalsDir ? resolve8(goalsDir) : resolve8("goals");
|
|
34785
35395
|
}
|
|
34786
35396
|
var goalCommand = new Command7("goal").description(
|
|
34787
35397
|
"Manage learning goals (markdown files)"
|
|
@@ -34791,7 +35401,7 @@ goalCommand.command("list").description("List all goals").option(
|
|
|
34791
35401
|
"Filter by status (active, completed, paused, abandoned)"
|
|
34792
35402
|
).option("--tree", "Show goals as a tree with parent/child relationships").option("--json", "Output as JSON").action(async (opts) => {
|
|
34793
35403
|
const goalsDir = await resolveGoalsDir();
|
|
34794
|
-
if (!
|
|
35404
|
+
if (!existsSync24(goalsDir)) {
|
|
34795
35405
|
console.error(`Goals directory not found: ${goalsDir}`);
|
|
34796
35406
|
console.error(
|
|
34797
35407
|
"Set it with: zam settings set personal.goals_dir /path/to/goals"
|
|
@@ -34892,8 +35502,8 @@ ${"\u2500".repeat(50)}`);
|
|
|
34892
35502
|
});
|
|
34893
35503
|
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
35504
|
const goalsDir = await resolveGoalsDir();
|
|
34895
|
-
if (!
|
|
34896
|
-
|
|
35505
|
+
if (!existsSync24(goalsDir)) {
|
|
35506
|
+
mkdirSync17(goalsDir, { recursive: true });
|
|
34897
35507
|
}
|
|
34898
35508
|
let slug = opts.slug;
|
|
34899
35509
|
let title = opts.title;
|
|
@@ -34953,9 +35563,9 @@ goalCommand.command("status <slug> <status>").description("Update a goal's statu
|
|
|
34953
35563
|
|
|
34954
35564
|
// src/cli/commands/init.ts
|
|
34955
35565
|
init_kernel();
|
|
34956
|
-
import { existsSync as
|
|
35566
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync18, writeFileSync as writeFileSync15 } from "fs";
|
|
34957
35567
|
import { homedir as homedir15 } from "os";
|
|
34958
|
-
import { join as
|
|
35568
|
+
import { join as join26, resolve as resolve9 } from "path";
|
|
34959
35569
|
import { confirm, input as input3 } from "@inquirer/prompts";
|
|
34960
35570
|
import { Command as Command8 } from "commander";
|
|
34961
35571
|
var HOME2 = homedir15();
|
|
@@ -34963,12 +35573,12 @@ function printLine(char = "\u2550", len = 60, color = "\x1B[36m") {
|
|
|
34963
35573
|
console.log(`${color}${char.repeat(len)}\x1B[0m`);
|
|
34964
35574
|
}
|
|
34965
35575
|
function bootstrapSandboxWorkspace(workspaceDir) {
|
|
34966
|
-
|
|
34967
|
-
|
|
34968
|
-
|
|
34969
|
-
const worldviewFile =
|
|
34970
|
-
if (!
|
|
34971
|
-
|
|
35576
|
+
mkdirSync18(join26(workspaceDir, "beliefs"), { recursive: true });
|
|
35577
|
+
mkdirSync18(join26(workspaceDir, "goals"), { recursive: true });
|
|
35578
|
+
mkdirSync18(join26(workspaceDir, "skills"), { recursive: true });
|
|
35579
|
+
const worldviewFile = join26(workspaceDir, "beliefs", "worldview.md");
|
|
35580
|
+
if (!existsSync25(worldviewFile)) {
|
|
35581
|
+
writeFileSync15(
|
|
34972
35582
|
worldviewFile,
|
|
34973
35583
|
`# Personal Worldview
|
|
34974
35584
|
|
|
@@ -34980,9 +35590,9 @@ Here, I declare the core concepts and principles I want to master.
|
|
|
34980
35590
|
"utf8"
|
|
34981
35591
|
);
|
|
34982
35592
|
}
|
|
34983
|
-
const goalsFile =
|
|
34984
|
-
if (!
|
|
34985
|
-
|
|
35593
|
+
const goalsFile = join26(workspaceDir, "goals", "goals.md");
|
|
35594
|
+
if (!existsSync25(goalsFile)) {
|
|
35595
|
+
writeFileSync15(
|
|
34986
35596
|
goalsFile,
|
|
34987
35597
|
`# Personal Goals
|
|
34988
35598
|
|
|
@@ -35004,8 +35614,8 @@ var initCommand = new Command8("init").description("Launch the guided interactiv
|
|
|
35004
35614
|
);
|
|
35005
35615
|
printLine();
|
|
35006
35616
|
console.log("\n\x1B[1m[1/5] Setting up Local Workspace Sandbox\x1B[0m");
|
|
35007
|
-
const defaultWorkspace =
|
|
35008
|
-
const workspacePath =
|
|
35617
|
+
const defaultWorkspace = join26(HOME2, "Documents", "zam");
|
|
35618
|
+
const workspacePath = resolve9(
|
|
35009
35619
|
await input3({
|
|
35010
35620
|
message: "Choose your ZAM workspace directory:",
|
|
35011
35621
|
default: defaultWorkspace
|
|
@@ -36206,7 +36816,7 @@ observerCommand.command("revoke <process>").description("Remove a process from o
|
|
|
36206
36816
|
|
|
36207
36817
|
// src/cli/commands/profile.ts
|
|
36208
36818
|
init_kernel();
|
|
36209
|
-
import { dirname as
|
|
36819
|
+
import { dirname as dirname11, resolve as resolve10 } from "path";
|
|
36210
36820
|
import { Command as Command13 } from "commander";
|
|
36211
36821
|
var C2 = {
|
|
36212
36822
|
reset: "\x1B[0m",
|
|
@@ -36234,7 +36844,7 @@ var profileCommand = new Command13("profile").description("Show or change this m
|
|
|
36234
36844
|
if (opts.mode) setInstallMode(opts.mode);
|
|
36235
36845
|
db = await openDatabaseWithSync({ initialize: true });
|
|
36236
36846
|
if (opts.dir) {
|
|
36237
|
-
await activateWorkspacePath(db,
|
|
36847
|
+
await activateWorkspacePath(db, resolve10(opts.dir), {
|
|
36238
36848
|
kind: "personal",
|
|
36239
36849
|
label: "Personal"
|
|
36240
36850
|
});
|
|
@@ -36247,7 +36857,7 @@ var profileCommand = new Command13("profile").description("Show or change this m
|
|
|
36247
36857
|
mode: getInstallMode(),
|
|
36248
36858
|
personalDir,
|
|
36249
36859
|
syncProvider: detectSyncProvider(personalDir),
|
|
36250
|
-
dataDir:
|
|
36860
|
+
dataDir: dirname11(dbPath),
|
|
36251
36861
|
dbPath
|
|
36252
36862
|
};
|
|
36253
36863
|
if (opts.json) {
|
|
@@ -36636,7 +37246,7 @@ ${"\u2550".repeat(50)}`);
|
|
|
36636
37246
|
|
|
36637
37247
|
// src/cli/commands/session.ts
|
|
36638
37248
|
init_kernel();
|
|
36639
|
-
import { readFileSync as
|
|
37249
|
+
import { readFileSync as readFileSync19 } from "fs";
|
|
36640
37250
|
import { input as input6, select as select2 } from "@inquirer/prompts";
|
|
36641
37251
|
import { Command as Command16 } from "commander";
|
|
36642
37252
|
var sessionCommand = new Command16("session").description(
|
|
@@ -36827,7 +37437,7 @@ function loadPatternFile(path) {
|
|
|
36827
37437
|
if (!path) return [];
|
|
36828
37438
|
let parsed;
|
|
36829
37439
|
try {
|
|
36830
|
-
parsed = JSON.parse(
|
|
37440
|
+
parsed = JSON.parse(readFileSync19(path, "utf-8"));
|
|
36831
37441
|
} catch (err) {
|
|
36832
37442
|
throw new Error(
|
|
36833
37443
|
`Cannot read synthesis patterns from ${path}: ${err.message}`
|
|
@@ -37018,7 +37628,7 @@ sessionCommand.command("end").description("End a session and show summary").requ
|
|
|
37018
37628
|
|
|
37019
37629
|
// src/cli/commands/settings.ts
|
|
37020
37630
|
init_kernel();
|
|
37021
|
-
import { existsSync as
|
|
37631
|
+
import { existsSync as existsSync26 } from "fs";
|
|
37022
37632
|
import { Command as Command17 } from "commander";
|
|
37023
37633
|
var settingsCommand = new Command17("settings").description(
|
|
37024
37634
|
"Manage user settings"
|
|
@@ -37187,7 +37797,7 @@ settingsCommand.command("repos").description("Show or set Personal, Team, and Or
|
|
|
37187
37797
|
console.log("\nValidation:");
|
|
37188
37798
|
for (const [name, path] of Object.entries(paths)) {
|
|
37189
37799
|
if (path) {
|
|
37190
|
-
const exists =
|
|
37800
|
+
const exists = existsSync26(path);
|
|
37191
37801
|
console.log(
|
|
37192
37802
|
` ${name.padEnd(9)}: ${exists ? "\x1B[32m\u2713 Valid folder\x1B[0m" : "\x1B[31m\u2717 Directory does not exist\x1B[0m"}`
|
|
37193
37803
|
);
|
|
@@ -37200,7 +37810,7 @@ settingsCommand.command("repos").description("Show or set Personal, Team, and Or
|
|
|
37200
37810
|
|
|
37201
37811
|
// src/cli/commands/setup.ts
|
|
37202
37812
|
init_kernel();
|
|
37203
|
-
import { resolve as
|
|
37813
|
+
import { resolve as resolve11 } from "path";
|
|
37204
37814
|
import { Command as Command18 } from "commander";
|
|
37205
37815
|
function formatDatabaseInitTarget(target) {
|
|
37206
37816
|
switch (target.kind) {
|
|
@@ -37300,7 +37910,7 @@ var setupCommand = new Command18("setup").description(
|
|
|
37300
37910
|
console.error(`Error: ${err.message}`);
|
|
37301
37911
|
process.exit(1);
|
|
37302
37912
|
}
|
|
37303
|
-
const target =
|
|
37913
|
+
const target = resolve11(opts.target ?? process.cwd());
|
|
37304
37914
|
const updateExistingInstructions = Boolean(opts.target) || opts.force;
|
|
37305
37915
|
console.log(
|
|
37306
37916
|
`Setting up ZAM in ${target}${opts.dryRun ? " (dry run)" : ""}
|
|
@@ -37425,8 +38035,8 @@ skillCommand.command("add").description("Register a new agent skill").requiredOp
|
|
|
37425
38035
|
|
|
37426
38036
|
// src/cli/commands/snapshot.ts
|
|
37427
38037
|
init_kernel();
|
|
37428
|
-
import { existsSync as
|
|
37429
|
-
import { dirname as
|
|
38038
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync19, readFileSync as readFileSync20, writeFileSync as writeFileSync16 } from "fs";
|
|
38039
|
+
import { dirname as dirname12, join as join27 } from "path";
|
|
37430
38040
|
import { Command as Command20 } from "commander";
|
|
37431
38041
|
function defaultOutName() {
|
|
37432
38042
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+Z$/, "").replace(/:/g, "-");
|
|
@@ -37455,12 +38065,12 @@ var exportCmd = new Command20("export").description("Write a portable SQL-text s
|
|
|
37455
38065
|
process.stdout.write(snapshot);
|
|
37456
38066
|
return;
|
|
37457
38067
|
}
|
|
37458
|
-
const out = opts.out ??
|
|
37459
|
-
const dir =
|
|
37460
|
-
if (dir && dir !== "." && !
|
|
37461
|
-
|
|
38068
|
+
const out = opts.out ?? join27(personalDir, "snapshots", defaultOutName());
|
|
38069
|
+
const dir = dirname12(out);
|
|
38070
|
+
if (dir && dir !== "." && !existsSync27(dir)) {
|
|
38071
|
+
mkdirSync19(dir, { recursive: true });
|
|
37462
38072
|
}
|
|
37463
|
-
|
|
38073
|
+
writeFileSync16(out, snapshot, "utf-8");
|
|
37464
38074
|
console.log(`Snapshot written: ${out}`);
|
|
37465
38075
|
console.log(
|
|
37466
38076
|
` ${total} row(s)${nonEmpty.length ? ` \u2014 ${nonEmpty.join(", ")}` : ""}`
|
|
@@ -37474,11 +38084,11 @@ var exportCmd = new Command20("export").description("Write a portable SQL-text s
|
|
|
37474
38084
|
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
38085
|
let db;
|
|
37476
38086
|
try {
|
|
37477
|
-
if (!
|
|
38087
|
+
if (!existsSync27(file)) {
|
|
37478
38088
|
console.error(`Error: Snapshot file not found: ${file}`);
|
|
37479
38089
|
process.exit(1);
|
|
37480
38090
|
}
|
|
37481
|
-
const snapshot =
|
|
38091
|
+
const snapshot = readFileSync20(file, "utf-8");
|
|
37482
38092
|
db = await openDatabaseWithSync({ initialize: true });
|
|
37483
38093
|
const result = await importSnapshot(db, snapshot, { force: opts.force });
|
|
37484
38094
|
await db.close();
|
|
@@ -37496,11 +38106,11 @@ var importCmd = new Command20("import").description("Restore a snapshot into the
|
|
|
37496
38106
|
});
|
|
37497
38107
|
var verifyCmd = new Command20("verify").description("Check a snapshot's manifest and checksum without importing").argument("<file>", "Snapshot file to verify").action((file) => {
|
|
37498
38108
|
try {
|
|
37499
|
-
if (!
|
|
38109
|
+
if (!existsSync27(file)) {
|
|
37500
38110
|
console.error(`Error: Snapshot file not found: ${file}`);
|
|
37501
38111
|
process.exit(1);
|
|
37502
38112
|
}
|
|
37503
|
-
const manifest = verifySnapshot(
|
|
38113
|
+
const manifest = verifySnapshot(readFileSync20(file, "utf-8"));
|
|
37504
38114
|
const { total, nonEmpty } = summarize(manifest.tables);
|
|
37505
38115
|
console.log(`Valid snapshot (format v${manifest.version})`);
|
|
37506
38116
|
console.log(` created: ${manifest.createdAt}`);
|
|
@@ -38049,10 +38659,10 @@ tokenCommand.command("reembed").description("Backfill or refresh semantic-search
|
|
|
38049
38659
|
// src/cli/commands/ui.ts
|
|
38050
38660
|
init_kernel();
|
|
38051
38661
|
import { spawn as spawn3, spawnSync } from "child_process";
|
|
38052
|
-
import { existsSync as
|
|
38662
|
+
import { existsSync as existsSync28 } from "fs";
|
|
38053
38663
|
import { homedir as homedir16 } from "os";
|
|
38054
|
-
import { dirname as
|
|
38055
|
-
import { fileURLToPath as
|
|
38664
|
+
import { dirname as dirname13, join as join28 } from "path";
|
|
38665
|
+
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
38056
38666
|
import { Command as Command23 } from "commander";
|
|
38057
38667
|
var C3 = {
|
|
38058
38668
|
reset: "\x1B[0m",
|
|
@@ -38063,14 +38673,14 @@ var C3 = {
|
|
|
38063
38673
|
dim: "\x1B[2m"
|
|
38064
38674
|
};
|
|
38065
38675
|
function findDesktopDir() {
|
|
38066
|
-
const starts = [process.cwd(),
|
|
38676
|
+
const starts = [process.cwd(), dirname13(fileURLToPath7(import.meta.url))];
|
|
38067
38677
|
for (const start of starts) {
|
|
38068
38678
|
let dir = start;
|
|
38069
38679
|
for (let i = 0; i < 10; i++) {
|
|
38070
|
-
if (
|
|
38071
|
-
return
|
|
38680
|
+
if (existsSync28(join28(dir, "desktop", "src-tauri", "tauri.conf.json"))) {
|
|
38681
|
+
return join28(dir, "desktop");
|
|
38072
38682
|
}
|
|
38073
|
-
const parent =
|
|
38683
|
+
const parent = dirname13(dir);
|
|
38074
38684
|
if (parent === dir) break;
|
|
38075
38685
|
dir = parent;
|
|
38076
38686
|
}
|
|
@@ -38078,30 +38688,30 @@ function findDesktopDir() {
|
|
|
38078
38688
|
return null;
|
|
38079
38689
|
}
|
|
38080
38690
|
function findBuiltApp(desktopDir) {
|
|
38081
|
-
const releaseDir =
|
|
38691
|
+
const releaseDir = join28(desktopDir, "src-tauri", "target", "release");
|
|
38082
38692
|
if (process.platform === "win32") {
|
|
38083
38693
|
for (const name of ["ZAM.exe", "zam.exe", "zam-desktop.exe"]) {
|
|
38084
|
-
const p =
|
|
38085
|
-
if (
|
|
38694
|
+
const p = join28(releaseDir, name);
|
|
38695
|
+
if (existsSync28(p)) return p;
|
|
38086
38696
|
}
|
|
38087
38697
|
} else if (process.platform === "darwin") {
|
|
38088
|
-
const app =
|
|
38089
|
-
if (
|
|
38698
|
+
const app = join28(releaseDir, "bundle", "macos", "ZAM.app");
|
|
38699
|
+
if (existsSync28(app)) return app;
|
|
38090
38700
|
} else {
|
|
38091
38701
|
for (const name of ["zam", "ZAM", "zam-desktop"]) {
|
|
38092
|
-
const p =
|
|
38093
|
-
if (
|
|
38702
|
+
const p = join28(releaseDir, name);
|
|
38703
|
+
if (existsSync28(p)) return p;
|
|
38094
38704
|
}
|
|
38095
38705
|
}
|
|
38096
38706
|
return null;
|
|
38097
38707
|
}
|
|
38098
38708
|
function findInstalledApp() {
|
|
38099
38709
|
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 &&
|
|
38710
|
+
process.env.LOCALAPPDATA && join28(process.env.LOCALAPPDATA, "Programs", "ZAM", "ZAM.exe"),
|
|
38711
|
+
process.env.ProgramFiles && join28(process.env.ProgramFiles, "ZAM", "ZAM.exe"),
|
|
38712
|
+
process.env["ProgramFiles(x86)"] && join28(process.env["ProgramFiles(x86)"], "ZAM", "ZAM.exe")
|
|
38713
|
+
] : process.platform === "darwin" ? ["/Applications/ZAM.app", join28(homedir16(), "Applications", "ZAM.app")] : ["/opt/ZAM/zam", "/usr/bin/zam-desktop"];
|
|
38714
|
+
return candidates.find((candidate) => candidate && existsSync28(candidate)) || null;
|
|
38105
38715
|
}
|
|
38106
38716
|
function runNpm(args, opts) {
|
|
38107
38717
|
const res = spawnSync("npm", args, {
|
|
@@ -38112,7 +38722,7 @@ function runNpm(args, opts) {
|
|
|
38112
38722
|
return res.status ?? 1;
|
|
38113
38723
|
}
|
|
38114
38724
|
function ensureDesktopDeps(desktopDir) {
|
|
38115
|
-
if (
|
|
38725
|
+
if (existsSync28(join28(desktopDir, "node_modules"))) return true;
|
|
38116
38726
|
console.log(
|
|
38117
38727
|
`${C3.cyan}Installing desktop dependencies (one-time)...${C3.reset}`
|
|
38118
38728
|
);
|
|
@@ -38138,13 +38748,13 @@ function requireRust() {
|
|
|
38138
38748
|
function hasMsvcBuildTools() {
|
|
38139
38749
|
if (process.platform !== "win32") return true;
|
|
38140
38750
|
const pf86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
|
38141
|
-
const vswhere =
|
|
38751
|
+
const vswhere = join28(
|
|
38142
38752
|
pf86,
|
|
38143
38753
|
"Microsoft Visual Studio",
|
|
38144
38754
|
"Installer",
|
|
38145
38755
|
"vswhere.exe"
|
|
38146
38756
|
);
|
|
38147
|
-
if (!
|
|
38757
|
+
if (!existsSync28(vswhere)) return false;
|
|
38148
38758
|
const res = spawnSync(
|
|
38149
38759
|
vswhere,
|
|
38150
38760
|
[
|
|
@@ -38176,7 +38786,7 @@ function requireMsvcOnWindows() {
|
|
|
38176
38786
|
return false;
|
|
38177
38787
|
}
|
|
38178
38788
|
function warnIfCliMissing(repoRoot) {
|
|
38179
|
-
if (!
|
|
38789
|
+
if (!existsSync28(join28(repoRoot, "dist", "cli", "index.js"))) {
|
|
38180
38790
|
console.warn(
|
|
38181
38791
|
`${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
38792
|
);
|
|
@@ -38249,7 +38859,7 @@ var uiCommand = new Command23("ui").description("Launch the ZAM Desktop GUI (Act
|
|
|
38249
38859
|
);
|
|
38250
38860
|
process.exit(1);
|
|
38251
38861
|
}
|
|
38252
|
-
const repoRoot =
|
|
38862
|
+
const repoRoot = dirname13(desktopDir);
|
|
38253
38863
|
if (opts.build) {
|
|
38254
38864
|
if (!requireRust()) process.exit(1);
|
|
38255
38865
|
if (!requireMsvcOnWindows()) process.exit(1);
|
|
@@ -38260,7 +38870,7 @@ var uiCommand = new Command23("ui").description("Launch the ZAM Desktop GUI (Act
|
|
|
38260
38870
|
);
|
|
38261
38871
|
const code = runNpm(["run", "tauri", "build"], { cwd: desktopDir });
|
|
38262
38872
|
if (code === 0) {
|
|
38263
|
-
const bundle =
|
|
38873
|
+
const bundle = join28(
|
|
38264
38874
|
desktopDir,
|
|
38265
38875
|
"src-tauri",
|
|
38266
38876
|
"target",
|
|
@@ -38300,7 +38910,7 @@ ${C3.green}\u2713 Done. Installer is in:${C3.reset}
|
|
|
38300
38910
|
);
|
|
38301
38911
|
process.exit(1);
|
|
38302
38912
|
}
|
|
38303
|
-
createShortcuts(shortcutTarget,
|
|
38913
|
+
createShortcuts(shortcutTarget, dirname13(shortcutTarget));
|
|
38304
38914
|
return;
|
|
38305
38915
|
}
|
|
38306
38916
|
if (builtApp) {
|
|
@@ -38338,9 +38948,9 @@ ${C3.dim}After that, 'zam ui' launches it directly.${C3.reset}`
|
|
|
38338
38948
|
// src/cli/commands/update.ts
|
|
38339
38949
|
init_kernel();
|
|
38340
38950
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
38341
|
-
import { existsSync as
|
|
38342
|
-
import { dirname as
|
|
38343
|
-
import { fileURLToPath as
|
|
38951
|
+
import { existsSync as existsSync29, readFileSync as readFileSync21, realpathSync as realpathSync3 } from "fs";
|
|
38952
|
+
import { dirname as dirname14, join as join29 } from "path";
|
|
38953
|
+
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
38344
38954
|
import { confirm as confirm3 } from "@inquirer/prompts";
|
|
38345
38955
|
import { Command as Command24 } from "commander";
|
|
38346
38956
|
var CHANNELS = [
|
|
@@ -38361,7 +38971,7 @@ var C4 = {
|
|
|
38361
38971
|
function versionAt(dir) {
|
|
38362
38972
|
try {
|
|
38363
38973
|
const pkg2 = JSON.parse(
|
|
38364
|
-
|
|
38974
|
+
readFileSync21(join29(dir, "package.json"), "utf-8")
|
|
38365
38975
|
);
|
|
38366
38976
|
return pkg2.version ?? "unknown";
|
|
38367
38977
|
} catch {
|
|
@@ -38419,14 +39029,14 @@ var checkCmd = new Command24("check").description("Check whether a newer ZAM has
|
|
|
38419
39029
|
}
|
|
38420
39030
|
);
|
|
38421
39031
|
function findSourceRepo() {
|
|
38422
|
-
let dir =
|
|
38423
|
-
let parent =
|
|
39032
|
+
let dir = realpathSync3(dirname14(fileURLToPath8(import.meta.url)));
|
|
39033
|
+
let parent = dirname14(dir);
|
|
38424
39034
|
while (parent !== dir) {
|
|
38425
|
-
if (
|
|
39035
|
+
if (existsSync29(join29(dir, ".git"))) return dir;
|
|
38426
39036
|
dir = parent;
|
|
38427
|
-
parent =
|
|
39037
|
+
parent = dirname14(dir);
|
|
38428
39038
|
}
|
|
38429
|
-
return
|
|
39039
|
+
return existsSync29(join29(dir, ".git")) ? dir : null;
|
|
38430
39040
|
}
|
|
38431
39041
|
function runGit(cwd, args, capture) {
|
|
38432
39042
|
const res = spawnSync2("git", args, {
|
|
@@ -38447,7 +39057,7 @@ function runNpm2(args, cwd) {
|
|
|
38447
39057
|
function smokeTestBuild(src) {
|
|
38448
39058
|
const res = spawnSync2(
|
|
38449
39059
|
process.execPath,
|
|
38450
|
-
[
|
|
39060
|
+
[join29(src, "dist", "cli", "index.js"), "--version"],
|
|
38451
39061
|
{
|
|
38452
39062
|
cwd: src,
|
|
38453
39063
|
encoding: "utf8",
|
|
@@ -38527,7 +39137,7 @@ Fix it manually in ${src} (try \`npm ci && npm run build\`, and check your Node
|
|
|
38527
39137
|
console.log(`${C4.dim}\u2192 zam setup --force${C4.reset}`);
|
|
38528
39138
|
const setup = spawnSync2(
|
|
38529
39139
|
process.execPath,
|
|
38530
|
-
[
|
|
39140
|
+
[join29(src, "dist", "cli", "index.js"), "setup", "--force"],
|
|
38531
39141
|
{ cwd: process.cwd(), stdio: "inherit" }
|
|
38532
39142
|
);
|
|
38533
39143
|
if (setup.status !== 0) {
|
|
@@ -38640,9 +39250,9 @@ var whoamiCommand = new Command25("whoami").description("Show or set the default
|
|
|
38640
39250
|
// src/cli/commands/workspace.ts
|
|
38641
39251
|
init_kernel();
|
|
38642
39252
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
38643
|
-
import { existsSync as
|
|
39253
|
+
import { existsSync as existsSync30, writeFileSync as writeFileSync17 } from "fs";
|
|
38644
39254
|
import { homedir as homedir17 } from "os";
|
|
38645
|
-
import { join as
|
|
39255
|
+
import { join as join30, resolve as resolve12 } from "path";
|
|
38646
39256
|
import { confirm as confirm4, input as input7 } from "@inquirer/prompts";
|
|
38647
39257
|
import { Command as Command26 } from "commander";
|
|
38648
39258
|
function runGit2(cwd, args) {
|
|
@@ -38726,7 +39336,7 @@ workspaceCommand.command("list").description("List configured ZAM workspaces").o
|
|
|
38726
39336
|
const workspaces = getConfiguredWorkspaces();
|
|
38727
39337
|
const agents = parseSetupAgents();
|
|
38728
39338
|
const linkHealth = Object.fromEntries(
|
|
38729
|
-
workspaces.filter((workspace) =>
|
|
39339
|
+
workspaces.filter((workspace) => existsSync30(workspace.path)).map((workspace) => [
|
|
38730
39340
|
workspace.id,
|
|
38731
39341
|
summarizeSkillLinkHealth(inspectSkillLinks(workspace.path, agents))
|
|
38732
39342
|
])
|
|
@@ -38768,8 +39378,8 @@ workspaceCommand.command("add <id>").description("Register an existing directory
|
|
|
38768
39378
|
`Source-control provider (${WORKSPACE_SOURCE_CONTROLS.join(" | ")})`
|
|
38769
39379
|
).option("--scopes <list>", "Comma-separated knowledge scopes").option("--default-agent <id>", "Default agent harness for this workspace").action((id, opts) => {
|
|
38770
39380
|
try {
|
|
38771
|
-
const path =
|
|
38772
|
-
if (!
|
|
39381
|
+
const path = resolve12(String(opts.path));
|
|
39382
|
+
if (!existsSync30(path)) {
|
|
38773
39383
|
console.error(`Workspace path does not exist: ${path}`);
|
|
38774
39384
|
process.exit(1);
|
|
38775
39385
|
}
|
|
@@ -38863,7 +39473,7 @@ workspaceCommand.command("publish").description("Publish your local workspace sa
|
|
|
38863
39473
|
);
|
|
38864
39474
|
process.exit(1);
|
|
38865
39475
|
}
|
|
38866
|
-
if (!
|
|
39476
|
+
if (!existsSync30(workspaceDir)) {
|
|
38867
39477
|
console.error(
|
|
38868
39478
|
`\x1B[31m\u2717 Workspace directory does not exist: ${workspaceDir}\x1B[0m`
|
|
38869
39479
|
);
|
|
@@ -38877,15 +39487,15 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
|
|
|
38877
39487
|
);
|
|
38878
39488
|
process.exit(1);
|
|
38879
39489
|
}
|
|
38880
|
-
const gitignorePath =
|
|
38881
|
-
if (!
|
|
38882
|
-
|
|
39490
|
+
const gitignorePath = join30(workspaceDir, ".gitignore");
|
|
39491
|
+
if (!existsSync30(gitignorePath)) {
|
|
39492
|
+
writeFileSync17(
|
|
38883
39493
|
gitignorePath,
|
|
38884
39494
|
"node_modules/\n.agent/\n.agents/\n.claude/\n.gemini/\n.goose/\n*.log\n",
|
|
38885
39495
|
"utf8"
|
|
38886
39496
|
);
|
|
38887
39497
|
}
|
|
38888
|
-
const hasGitRepo =
|
|
39498
|
+
const hasGitRepo = existsSync30(join30(workspaceDir, ".git"));
|
|
38889
39499
|
if (!hasGitRepo) {
|
|
38890
39500
|
console.log("Initializing local Git repository...");
|
|
38891
39501
|
runGit2(workspaceDir, ["init", "-b", "main"]);
|
|
@@ -38972,7 +39582,7 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
|
|
|
38972
39582
|
}
|
|
38973
39583
|
});
|
|
38974
39584
|
workspaceCommand.command("data-dir").description("Print the ZAM data directory (database, credentials, config)").option("--json", "Output as JSON").action((opts) => {
|
|
38975
|
-
const dir =
|
|
39585
|
+
const dir = join30(homedir17(), ".zam");
|
|
38976
39586
|
console.log(opts.json ? JSON.stringify({ dataDir: dir }) : dir);
|
|
38977
39587
|
});
|
|
38978
39588
|
workspaceCommand.command("backup").description("Back up the local ZAM database into your workspace").option(
|
|
@@ -39013,9 +39623,9 @@ workspaceCommand.command("backup").description("Back up the local ZAM database i
|
|
|
39013
39623
|
});
|
|
39014
39624
|
|
|
39015
39625
|
// src/cli/app.ts
|
|
39016
|
-
var __dirname =
|
|
39626
|
+
var __dirname = dirname15(fileURLToPath9(import.meta.url));
|
|
39017
39627
|
var pkg = JSON.parse(
|
|
39018
|
-
|
|
39628
|
+
readFileSync22(join31(__dirname, "..", "..", "package.json"), "utf-8")
|
|
39019
39629
|
);
|
|
39020
39630
|
var program = new Command27();
|
|
39021
39631
|
program.name("zam").description(
|