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 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 readFileSync21 } from "fs";
7264
- import { dirname as dirname14, join as join30 } from "path";
7265
- import { fileURLToPath as fileURLToPath8 } from "url";
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 existsSync21, readdirSync as readdirSync2, readFileSync as readFileSync17, rmSync as rmSync3 } from "fs";
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 join23, resolve as resolve6 } from "path";
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((resolve12) => setTimeout(resolve12, 1e3));
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((resolve12) => setTimeout(resolve12, 500));
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((resolve12) => {
10075
- timeoutId = setTimeout(() => resolve12("timeout"), timeoutMs);
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.13.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 mkdirSync12, writeFileSync as writeFileSync10 } from "fs";
10408
- import { join as join18 } from "path";
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 = join18(targetDir, "zam-backups");
11584
- mkdirSync12(backupDir, { recursive: true });
12140
+ const backupDir = join19(targetDir, "zam-backups");
12141
+ mkdirSync13(backupDir, { recursive: true });
11585
12142
  const stamp = manifest.createdAt.replace(/[:.]/g, "-");
11586
- const path = join18(backupDir, `zam-snapshot-${stamp}.sql`);
11587
- writeFileSync10(path, snapshot, "utf-8");
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 existsSync18,
11612
- mkdirSync as mkdirSync13,
11613
- readFileSync as readFileSync14,
11614
- writeFileSync as writeFileSync11
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 join19, resolve as resolve5 } from "path";
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 = resolve5(path);
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 = join19(home, platform === "darwin" ? ".zprofile" : ".profile");
11661
- const existing = existsSync18(profile) ? readFileSync14(profile, "utf8") : "";
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
- writeFileSync11(profile, existing + block, "utf8");
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 = resolve5(options.nodePath ?? process.execPath);
11676
- const cliPath = resolve5(options.cliPath ?? process.argv[1] ?? "");
11677
- const binDir = join19(home, ".zam", "bin");
11678
- const shimPath = join19(binDir, platform === "win32" ? "zam.cmd" : "zam");
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) || !existsSync18(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 = existsSync18(shimPath);
11704
- const changed = !existed || readFileSync14(shimPath, "utf8") !== content;
12260
+ const existed = existsSync19(shimPath);
12261
+ const changed = !existed || readFileSync15(shimPath, "utf8") !== content;
11705
12262
  if (changed) {
11706
- mkdirSync13(binDir, { recursive: true });
11707
- writeFileSync11(shimPath, content, "utf8");
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 existsSync20 } from "fs";
28973
+ import { existsSync as existsSync21 } from "fs";
28417
28974
 
28418
28975
  // src/cli/provisioning/index.ts
28419
28976
  import {
28420
- existsSync as existsSync19,
28977
+ existsSync as existsSync20,
28421
28978
  lstatSync as lstatSync2,
28422
- mkdirSync as mkdirSync14,
28423
- readFileSync as readFileSync15,
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 writeFileSync12
28984
+ writeFileSync as writeFileSync13
28428
28985
  } from "fs";
28429
- import { basename as basename4, dirname as dirname9, join as join20 } from "path";
28430
- import { fileURLToPath as fileURLToPath5 } from "url";
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
- fileURLToPath5(new URL("../..", import.meta.url)),
28433
- fileURLToPath5(new URL("../../..", import.meta.url))
28434
- ].find((candidate) => existsSync19(join20(candidate, "package.json"))) ?? fileURLToPath5(new URL("../..", import.meta.url));
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: join20(packageRoot3, ".claude", "skills", "zam", "SKILL.md"),
28439
- to: join20(".claude", "skills", "zam", "SKILL.md"),
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: join20(packageRoot3, ".agent", "skills", "zam", "SKILL.md"),
28444
- to: join20(".agent", "skills", "zam", "SKILL.md"),
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: join20(packageRoot3, ".agents", "skills", "zam", "SKILL.md"),
28449
- to: join20(".agents", "skills", "zam", "SKILL.md"),
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 = realpathSync(path);
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 = join20(destinationDir, "SKILL.md");
28510
- if (!existsSync19(skillFile)) return false;
28511
- return /^name:\s*zam\s*$/m.test(readFileSync15(skillFile, "utf8"));
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 (!existsSync19(sourceDir)) return "source-missing";
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 = dirname9(from);
28535
- const destinationDir = dirname9(join20(cwd, to));
28536
- const label = dirname9(to);
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
- mkdirSync14(dirname9(destinationDir), { recursive: true });
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 = dirname9(from);
28610
- const destinationDir = dirname9(join20(cwd, to));
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 (!existsSync19(dest)) {
29193
+ if (!existsSync20(dest)) {
28637
29194
  if (!dryRun) {
28638
- mkdirSync14(dirname9(dest), { recursive: true });
28639
- writeFileSync12(dest, `${block}
29195
+ mkdirSync15(dirname10(dest), { recursive: true });
29196
+ writeFileSync13(dest, `${block}
28640
29197
  `, "utf8");
28641
29198
  }
28642
29199
  return "write";
28643
29200
  }
28644
- const existing = readFileSync15(dest, "utf8");
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) writeFileSync12(dest, next, "utf8");
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 = join20(cwd, "CLAUDE.md");
28665
- if (existsSync19(dest)) {
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
- writeFileSync12(dest, content, "utf8");
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 = join20(cwd, "AGENTS.md");
28715
- if (existsSync19(dest)) {
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
- writeFileSync12(dest, content, "utf8");
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 = join20(cwd, ".github", "copilot-instructions.md");
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 (!existsSync20(workspace.path)) {
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 readFileSync16 } from "fs";
29543
+ import { readFileSync as readFileSync17 } from "fs";
28987
29544
  import { tmpdir as tmpdir2 } from "os";
28988
- import { basename as basename5, join as join21 } from "path";
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: mkdirSync19, readdirSync: readdirSync3, rmSync: rmSync4 } = await import("fs");
29581
+ const { mkdirSync: mkdirSync20, readdirSync: readdirSync4, rmSync: rmSync4 } = await import("fs");
29025
29582
  const { execSync: execSync6 } = await import("child_process");
29026
- const tempDir = join21(
29583
+ const tempDir = join22(
29027
29584
  tmpdir2(),
29028
29585
  `zam-frames-${randomBytes(4).toString("hex")}`
29029
29586
  );
29030
- mkdirSync19(tempDir, { recursive: true });
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 = readdirSync3(tempDir).filter((f) => f.endsWith(".png")).sort();
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 = readdirSync3(tempDir).filter((f) => f.endsWith(".png")).sort();
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 = readFileSync16(join21(tempDir, file));
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 = readFileSync16(input8.imagePath);
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 mkdirSync15 } from "fs";
29581
- import { join as join22 } from "path";
30137
+ import { mkdirSync as mkdirSync16 } from "fs";
30138
+ import { join as join23 } from "path";
29582
30139
  async function backupDatabaseTo(db, targetDir) {
29583
- const backupDir = join22(targetDir, "zam-backups");
29584
- mkdirSync15(backupDir, { recursive: true });
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 = join22(backupDir, `zam-${stamp}.db`);
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: join23(homedir14(), ".zam")
30277
+ dataDir: join24(homedir14(), ".zam")
29721
30278
  });
29722
30279
  });
29723
30280
  });
29724
30281
  function provisionConfiguredWorkspaces() {
29725
30282
  return getConfiguredWorkspaces().flatMap(
29726
- (workspace) => existsSync21(workspace.path) ? wireSkills(workspace.path, parseSetupAgents(), { quiet: true }) : []
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 (!existsSync21(workspace.path)) continue;
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: join23(homedir14(), ".zam"),
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 (!existsSync21(workspace.path)) {
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 = resolve6(raw);
29810
- if (!existsSync21(path)) jsonError(`Workspace path does not exist: ${path}`);
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 = resolve6(raw);
29855
- if (!existsSync21(dir)) jsonError(`Workspace path does not exist: ${dir}`);
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 ? existsSync21(opts.dir) ? opts.dir : homedir14() : existingWorkspaceDirOrHome(configuredWorkspace ?? activeWorkspace);
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 = join23(homedir14(), ".zam", "monitor");
30893
+ const monitorDir = join24(homedir14(), ".zam", "monitor");
30291
30894
  let files;
30292
30895
  try {
30293
- files = readdirSync2(monitorDir).filter((f) => f.endsWith(".jsonl"));
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: join23(monitorDir, f) })).sort((a, b) => b.name.localeCompare(a.name)).slice(0, limit);
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 ?? join23(tmpdir3(), `zam-capture-${randomBytes2(4).toString("hex")}.png`);
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 = readFileSync17(outputPath);
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 = join23(tmpdir3(), `zam-recording-${sessionId}.json`);
31463
+ const statePath = join24(tmpdir3(), `zam-recording-${sessionId}.json`);
30861
31464
  const defaultExt = platform === "win32" ? ".mkv" : ".mov";
30862
- const outputPath = opts.output ?? join23(tmpdir3(), `zam-recording-${sessionId}${defaultExt}`);
30863
- const { existsSync: existsSync30, writeFileSync: writeFileSync17, openSync, closeSync } = await import("fs");
30864
- if (existsSync30(statePath)) {
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 = join23(tmpdir3(), `zam-recording-${sessionId}.log`);
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
- writeFileSync17(
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 = join23(tmpdir3(), `zam-recording-${sessionId}.json`);
30955
- const { existsSync: existsSync30, readFileSync: readFileSync22, rmSync: rmSync4 } = await import("fs");
30956
- if (!existsSync30(statePath)) {
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(readFileSync22(statePath, "utf8"));
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((resolve12) => setTimeout(resolve12, 250));
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 (!existsSync30(outputPath)) {
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") || existsSync21("C:\\Program Files\\flm\\flm.exe");
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").action(async (opts) => {
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 existsSync22, writeFileSync as writeFileSync13 } from "fs";
34649
- import { join as join24 } from "path";
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 = join24(process.cwd(), ".git");
34653
- if (!existsSync22(gitDir)) {
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 = join24(gitDir, "hooks");
34660
- const hookPath = join24(hooksDir, "post-commit");
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
- writeFileSync13(hookPath, hookContent, { encoding: "utf-8", flag: "w" });
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 existsSync23, mkdirSync as mkdirSync16 } from "fs";
34771
- import { resolve as resolve7 } from "path";
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 ? resolve7(goalsDir) : resolve7("goals");
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 (!existsSync23(goalsDir)) {
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 (!existsSync23(goalsDir)) {
34896
- mkdirSync16(goalsDir, { recursive: true });
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 existsSync24, mkdirSync as mkdirSync17, writeFileSync as writeFileSync14 } from "fs";
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 join25, resolve as resolve8 } from "path";
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
- mkdirSync17(join25(workspaceDir, "beliefs"), { recursive: true });
34967
- mkdirSync17(join25(workspaceDir, "goals"), { recursive: true });
34968
- mkdirSync17(join25(workspaceDir, "skills"), { recursive: true });
34969
- const worldviewFile = join25(workspaceDir, "beliefs", "worldview.md");
34970
- if (!existsSync24(worldviewFile)) {
34971
- writeFileSync14(
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 = join25(workspaceDir, "goals", "goals.md");
34984
- if (!existsSync24(goalsFile)) {
34985
- writeFileSync14(
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 = join25(HOME2, "Documents", "zam");
35008
- const workspacePath = resolve8(
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 dirname10, resolve as resolve9 } from "path";
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, resolve9(opts.dir), {
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: dirname10(dbPath),
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 readFileSync18 } from "fs";
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(readFileSync18(path, "utf-8"));
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 existsSync25 } from "fs";
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 = existsSync25(path);
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 resolve10 } from "path";
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 = resolve10(opts.target ?? process.cwd());
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 existsSync26, mkdirSync as mkdirSync18, readFileSync as readFileSync19, writeFileSync as writeFileSync15 } from "fs";
37429
- import { dirname as dirname11, join as join26 } from "path";
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 ?? join26(personalDir, "snapshots", defaultOutName());
37459
- const dir = dirname11(out);
37460
- if (dir && dir !== "." && !existsSync26(dir)) {
37461
- mkdirSync18(dir, { recursive: true });
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
- writeFileSync15(out, snapshot, "utf-8");
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 (!existsSync26(file)) {
38087
+ if (!existsSync27(file)) {
37478
38088
  console.error(`Error: Snapshot file not found: ${file}`);
37479
38089
  process.exit(1);
37480
38090
  }
37481
- const snapshot = readFileSync19(file, "utf-8");
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 (!existsSync26(file)) {
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(readFileSync19(file, "utf-8"));
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 existsSync27 } from "fs";
38662
+ import { existsSync as existsSync28 } from "fs";
38053
38663
  import { homedir as homedir16 } from "os";
38054
- import { dirname as dirname12, join as join27 } from "path";
38055
- import { fileURLToPath as fileURLToPath6 } from "url";
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(), dirname12(fileURLToPath6(import.meta.url))];
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 (existsSync27(join27(dir, "desktop", "src-tauri", "tauri.conf.json"))) {
38071
- return join27(dir, "desktop");
38680
+ if (existsSync28(join28(dir, "desktop", "src-tauri", "tauri.conf.json"))) {
38681
+ return join28(dir, "desktop");
38072
38682
  }
38073
- const parent = dirname12(dir);
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 = join27(desktopDir, "src-tauri", "target", "release");
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 = join27(releaseDir, name);
38085
- if (existsSync27(p)) return p;
38694
+ const p = join28(releaseDir, name);
38695
+ if (existsSync28(p)) return p;
38086
38696
  }
38087
38697
  } else if (process.platform === "darwin") {
38088
- const app = join27(releaseDir, "bundle", "macos", "ZAM.app");
38089
- if (existsSync27(app)) return app;
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 = join27(releaseDir, name);
38093
- if (existsSync27(p)) return p;
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 && join27(process.env.LOCALAPPDATA, "Programs", "ZAM", "ZAM.exe"),
38101
- process.env.ProgramFiles && join27(process.env.ProgramFiles, "ZAM", "ZAM.exe"),
38102
- process.env["ProgramFiles(x86)"] && join27(process.env["ProgramFiles(x86)"], "ZAM", "ZAM.exe")
38103
- ] : process.platform === "darwin" ? ["/Applications/ZAM.app", join27(homedir16(), "Applications", "ZAM.app")] : ["/opt/ZAM/zam", "/usr/bin/zam-desktop"];
38104
- return candidates.find((candidate) => candidate && existsSync27(candidate)) || null;
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 (existsSync27(join27(desktopDir, "node_modules"))) return true;
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 = join27(
38751
+ const vswhere = join28(
38142
38752
  pf86,
38143
38753
  "Microsoft Visual Studio",
38144
38754
  "Installer",
38145
38755
  "vswhere.exe"
38146
38756
  );
38147
- if (!existsSync27(vswhere)) return false;
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 (!existsSync27(join27(repoRoot, "dist", "cli", "index.js"))) {
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 = dirname12(desktopDir);
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 = join27(
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, dirname12(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 existsSync28, readFileSync as readFileSync20, realpathSync as realpathSync2 } from "fs";
38342
- import { dirname as dirname13, join as join28 } from "path";
38343
- import { fileURLToPath as fileURLToPath7 } from "url";
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
- readFileSync20(join28(dir, "package.json"), "utf-8")
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 = realpathSync2(dirname13(fileURLToPath7(import.meta.url)));
38423
- let parent = dirname13(dir);
39032
+ let dir = realpathSync3(dirname14(fileURLToPath8(import.meta.url)));
39033
+ let parent = dirname14(dir);
38424
39034
  while (parent !== dir) {
38425
- if (existsSync28(join28(dir, ".git"))) return dir;
39035
+ if (existsSync29(join29(dir, ".git"))) return dir;
38426
39036
  dir = parent;
38427
- parent = dirname13(dir);
39037
+ parent = dirname14(dir);
38428
39038
  }
38429
- return existsSync28(join28(dir, ".git")) ? dir : null;
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
- [join28(src, "dist", "cli", "index.js"), "--version"],
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
- [join28(src, "dist", "cli", "index.js"), "setup", "--force"],
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 existsSync29, writeFileSync as writeFileSync16 } from "fs";
39253
+ import { existsSync as existsSync30, writeFileSync as writeFileSync17 } from "fs";
38644
39254
  import { homedir as homedir17 } from "os";
38645
- import { join as join29, resolve as resolve11 } from "path";
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) => existsSync29(workspace.path)).map((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 = resolve11(String(opts.path));
38772
- if (!existsSync29(path)) {
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 (!existsSync29(workspaceDir)) {
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 = join29(workspaceDir, ".gitignore");
38881
- if (!existsSync29(gitignorePath)) {
38882
- writeFileSync16(
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 = existsSync29(join29(workspaceDir, ".git"));
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 = join29(homedir17(), ".zam");
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 = dirname14(fileURLToPath8(import.meta.url));
39626
+ var __dirname = dirname15(fileURLToPath9(import.meta.url));
39017
39627
  var pkg = JSON.parse(
39018
- readFileSync21(join30(__dirname, "..", "..", "package.json"), "utf-8")
39628
+ readFileSync22(join31(__dirname, "..", "..", "package.json"), "utf-8")
39019
39629
  );
39020
39630
  var program = new Command27();
39021
39631
  program.name("zam").description(