zam-core 0.12.0 → 0.14.0

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