zam-core 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/app.js +828 -218
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/commands/mcp.js +712 -234
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/copilot-extension/extension.mjs +35 -0
- package/dist/copilot-extension/host.bundle.js +1 -1
- package/dist/copilot-extension/manifest.json +1 -1
- package/dist/copilot-extension/mcp-client.bundle.mjs +1 -1
- package/dist/index.d.ts +39 -1
- package/dist/index.js +79 -4
- package/dist/index.js.map +1 -1
- package/dist/ui/graph-panel.html +80 -40
- package/dist/ui/okf-panel.html +146 -21
- package/dist/vscode-extension/ZAM_Companion_0.15.0.vsix +0 -0
- package/dist/vscode-extension/extension.cjs +19 -18
- package/dist/vscode-extension/host.bundle.js +3 -2
- package/dist/vscode-extension/manifest.json +2 -2
- package/package.json +1 -1
- package/dist/vscode-extension/ZAM_Companion_0.13.0.vsix +0 -0
package/dist/cli/commands/mcp.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,
|
|
@@ -9165,21 +9240,41 @@ var init_bundle = __esm({
|
|
|
9165
9240
|
var io_exports = {};
|
|
9166
9241
|
__export(io_exports, {
|
|
9167
9242
|
DEFAULT_BUNDLE_DIR: () => DEFAULT_BUNDLE_DIR,
|
|
9243
|
+
collectSourceLinkBases: () => collectSourceLinkBases,
|
|
9168
9244
|
findRepoRoot: () => findRepoRoot,
|
|
9169
9245
|
loadBundle: () => loadBundle,
|
|
9170
9246
|
resolveArticlePath: () => resolveArticlePath,
|
|
9247
|
+
resolveBundleDirFromRoots: () => resolveBundleDirFromRoots,
|
|
9171
9248
|
resolveCitationPath: () => resolveCitationPath,
|
|
9172
9249
|
upsertArticle: () => upsertArticle
|
|
9173
9250
|
});
|
|
9174
9251
|
import {
|
|
9175
|
-
existsSync as
|
|
9176
|
-
mkdirSync as
|
|
9177
|
-
readdirSync as
|
|
9178
|
-
readFileSync as
|
|
9179
|
-
realpathSync
|
|
9180
|
-
writeFileSync as
|
|
9252
|
+
existsSync as existsSync13,
|
|
9253
|
+
mkdirSync as mkdirSync9,
|
|
9254
|
+
readdirSync as readdirSync2,
|
|
9255
|
+
readFileSync as readFileSync11,
|
|
9256
|
+
realpathSync,
|
|
9257
|
+
writeFileSync as writeFileSync6
|
|
9181
9258
|
} from "fs";
|
|
9182
|
-
import { dirname as
|
|
9259
|
+
import { dirname as dirname6, isAbsolute, join as join13, relative, resolve as resolve4, sep } from "path";
|
|
9260
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
9261
|
+
function resolveBundleDirFromRoots(rootUris, fallback) {
|
|
9262
|
+
const dirs = [];
|
|
9263
|
+
for (const uri of rootUris) {
|
|
9264
|
+
if (!uri?.startsWith("file:")) continue;
|
|
9265
|
+
try {
|
|
9266
|
+
dirs.push(join13(fileURLToPath3(uri), DEFAULT_BUNDLE_DIR));
|
|
9267
|
+
} catch {
|
|
9268
|
+
}
|
|
9269
|
+
}
|
|
9270
|
+
return dirs.find((dir) => existsSync13(dir)) ?? dirs[0] ?? fallback;
|
|
9271
|
+
}
|
|
9272
|
+
function collectSourceLinkBases(dir) {
|
|
9273
|
+
const bundle = loadBundle(dir);
|
|
9274
|
+
return bundle.catalog.map(
|
|
9275
|
+
(entry) => entry.resource ?? resolveArticlePath(dir, entry.file)
|
|
9276
|
+
);
|
|
9277
|
+
}
|
|
9183
9278
|
function resolveArticlePath(dir, file) {
|
|
9184
9279
|
if (file.includes("/") || file.includes("\\") || file.includes("..")) {
|
|
9185
9280
|
throw new Error(`invalid article file name: ${file}`);
|
|
@@ -9187,19 +9282,19 @@ function resolveArticlePath(dir, file) {
|
|
|
9187
9282
|
if (isReservedFile(file)) {
|
|
9188
9283
|
throw new Error(`refusing to address reserved file: ${file}`);
|
|
9189
9284
|
}
|
|
9190
|
-
return
|
|
9285
|
+
return join13(resolve4(dir), file);
|
|
9191
9286
|
}
|
|
9192
9287
|
function findRepoRoot(startDir) {
|
|
9193
|
-
let dir =
|
|
9288
|
+
let dir = resolve4(startDir);
|
|
9194
9289
|
for (; ; ) {
|
|
9195
|
-
if (
|
|
9196
|
-
const parent =
|
|
9197
|
-
if (parent === dir) return
|
|
9290
|
+
if (existsSync13(join13(dir, ".git"))) return dir;
|
|
9291
|
+
const parent = dirname6(dir);
|
|
9292
|
+
if (parent === dir) return resolve4(startDir, "..");
|
|
9198
9293
|
dir = parent;
|
|
9199
9294
|
}
|
|
9200
9295
|
}
|
|
9201
9296
|
function resolveCitationPath(bundleDir, target) {
|
|
9202
|
-
if (
|
|
9297
|
+
if (isAbsolute(target)) {
|
|
9203
9298
|
throw new Error(
|
|
9204
9299
|
`invalid citation target: absolute paths are not allowed (${target})`
|
|
9205
9300
|
);
|
|
@@ -9210,26 +9305,26 @@ function resolveCitationPath(bundleDir, target) {
|
|
|
9210
9305
|
);
|
|
9211
9306
|
}
|
|
9212
9307
|
const root = findRepoRoot(bundleDir);
|
|
9213
|
-
const resolved =
|
|
9308
|
+
const resolved = resolve4(bundleDir, target);
|
|
9214
9309
|
assertContained(root, resolved, target);
|
|
9215
|
-
if (
|
|
9216
|
-
assertContained(
|
|
9310
|
+
if (existsSync13(resolved)) {
|
|
9311
|
+
assertContained(realpathSync(root), realpathSync(resolved), target);
|
|
9217
9312
|
}
|
|
9218
9313
|
return resolved;
|
|
9219
9314
|
}
|
|
9220
9315
|
function assertContained(root, candidate, target) {
|
|
9221
9316
|
const rel = relative(root, candidate);
|
|
9222
|
-
if (rel === ".." || rel.startsWith(`..${sep}`) ||
|
|
9317
|
+
if (rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
|
|
9223
9318
|
throw new Error(
|
|
9224
9319
|
`invalid citation target: resolves outside the repository root (${target})`
|
|
9225
9320
|
);
|
|
9226
9321
|
}
|
|
9227
9322
|
}
|
|
9228
9323
|
function loadBundle(dir) {
|
|
9229
|
-
const root =
|
|
9324
|
+
const root = resolve4(dir);
|
|
9230
9325
|
let entries;
|
|
9231
9326
|
try {
|
|
9232
|
-
entries =
|
|
9327
|
+
entries = readdirSync2(root).filter(
|
|
9233
9328
|
(name) => name.endsWith(".md") && !isReservedFile(name)
|
|
9234
9329
|
);
|
|
9235
9330
|
} catch {
|
|
@@ -9237,7 +9332,7 @@ function loadBundle(dir) {
|
|
|
9237
9332
|
}
|
|
9238
9333
|
const articles = entries.sort().map((file) => ({
|
|
9239
9334
|
file,
|
|
9240
|
-
markdown:
|
|
9335
|
+
markdown: readFileSync11(join13(root, file), "utf8")
|
|
9241
9336
|
}));
|
|
9242
9337
|
const problems = articles.flatMap(
|
|
9243
9338
|
({ file, markdown }) => validateArticle(file, markdown).problems
|
|
@@ -9259,25 +9354,25 @@ function upsertArticle(dir, file, markdown, today = (/* @__PURE__ */ new Date())
|
|
|
9259
9354
|
const target = resolveArticlePath(dir, file);
|
|
9260
9355
|
const validation = validateArticle(file, markdown);
|
|
9261
9356
|
if (!validation.ok) return { validation };
|
|
9262
|
-
const root =
|
|
9263
|
-
|
|
9357
|
+
const root = resolve4(dir);
|
|
9358
|
+
mkdirSync9(root, { recursive: true });
|
|
9264
9359
|
let created = true;
|
|
9265
9360
|
try {
|
|
9266
|
-
|
|
9361
|
+
readFileSync11(target, "utf8");
|
|
9267
9362
|
created = false;
|
|
9268
9363
|
} catch {
|
|
9269
9364
|
}
|
|
9270
|
-
|
|
9365
|
+
writeFileSync6(target, markdown, "utf8");
|
|
9271
9366
|
const bundle = loadBundle(root);
|
|
9272
|
-
|
|
9367
|
+
writeFileSync6(join13(root, "index.md"), renderIndex(bundle.catalog), "utf8");
|
|
9273
9368
|
let log = "";
|
|
9274
9369
|
try {
|
|
9275
|
-
log =
|
|
9370
|
+
log = readFileSync11(join13(root, "log.md"), "utf8");
|
|
9276
9371
|
} catch {
|
|
9277
9372
|
}
|
|
9278
9373
|
const entry = bundle.catalog.find((e) => e.file === file);
|
|
9279
|
-
|
|
9280
|
-
|
|
9374
|
+
writeFileSync6(
|
|
9375
|
+
join13(root, "log.md"),
|
|
9281
9376
|
appendLog(
|
|
9282
9377
|
log,
|
|
9283
9378
|
today,
|
|
@@ -9296,11 +9391,68 @@ var init_io = __esm({
|
|
|
9296
9391
|
}
|
|
9297
9392
|
});
|
|
9298
9393
|
|
|
9394
|
+
// src/cli/okf-focus.ts
|
|
9395
|
+
var okf_focus_exports = {};
|
|
9396
|
+
__export(okf_focus_exports, {
|
|
9397
|
+
getOkfFocusPath: () => getOkfFocusPath,
|
|
9398
|
+
readOkfFocus: () => readOkfFocus,
|
|
9399
|
+
writeOkfFocus: () => writeOkfFocus
|
|
9400
|
+
});
|
|
9401
|
+
import { mkdir as mkdir2, readFile as readFile2, rename as rename2, writeFile as writeFile2 } from "fs/promises";
|
|
9402
|
+
import { homedir as homedir16 } from "os";
|
|
9403
|
+
import { dirname as dirname12, join as join25 } from "path";
|
|
9404
|
+
function getOkfFocusPath(home = homedir16()) {
|
|
9405
|
+
return join25(home, ".zam", "okf-focus.json");
|
|
9406
|
+
}
|
|
9407
|
+
function resolvePath(explicit) {
|
|
9408
|
+
return explicit ?? process.env.ZAM_OKF_FOCUS_PATH ?? getOkfFocusPath();
|
|
9409
|
+
}
|
|
9410
|
+
async function writeOkfFocus(file, bundleDir, opts = {}) {
|
|
9411
|
+
if (file.includes("/") || file.includes("\\") || !file.endsWith(".md")) {
|
|
9412
|
+
throw new Error(`invalid article file name: ${file}`);
|
|
9413
|
+
}
|
|
9414
|
+
const path = resolvePath(opts.path);
|
|
9415
|
+
const focus = {
|
|
9416
|
+
version: 1,
|
|
9417
|
+
file,
|
|
9418
|
+
...bundleDir ? { bundleDir } : {},
|
|
9419
|
+
updatedAt: (opts.now?.() ?? /* @__PURE__ */ new Date()).toISOString()
|
|
9420
|
+
};
|
|
9421
|
+
await mkdir2(dirname12(path), { recursive: true });
|
|
9422
|
+
const tmp = `${path}.tmp`;
|
|
9423
|
+
await writeFile2(tmp, `${JSON.stringify(focus, null, 2)}
|
|
9424
|
+
`, "utf8");
|
|
9425
|
+
await rename2(tmp, path);
|
|
9426
|
+
return focus;
|
|
9427
|
+
}
|
|
9428
|
+
async function readOkfFocus(opts = {}) {
|
|
9429
|
+
try {
|
|
9430
|
+
const raw = await readFile2(resolvePath(opts.path), "utf8");
|
|
9431
|
+
const parsed = JSON.parse(raw);
|
|
9432
|
+
if (parsed.version !== 1 || typeof parsed.file !== "string" || typeof parsed.updatedAt !== "string") {
|
|
9433
|
+
return null;
|
|
9434
|
+
}
|
|
9435
|
+
return {
|
|
9436
|
+
version: 1,
|
|
9437
|
+
file: parsed.file,
|
|
9438
|
+
...typeof parsed.bundleDir === "string" ? { bundleDir: parsed.bundleDir } : {},
|
|
9439
|
+
updatedAt: parsed.updatedAt
|
|
9440
|
+
};
|
|
9441
|
+
} catch {
|
|
9442
|
+
return null;
|
|
9443
|
+
}
|
|
9444
|
+
}
|
|
9445
|
+
var init_okf_focus = __esm({
|
|
9446
|
+
"src/cli/okf-focus.ts"() {
|
|
9447
|
+
"use strict";
|
|
9448
|
+
}
|
|
9449
|
+
});
|
|
9450
|
+
|
|
9299
9451
|
// src/cli/commands/mcp.ts
|
|
9300
9452
|
init_kernel();
|
|
9301
9453
|
import { existsSync as existsSync22, readFileSync as readFileSync19 } from "fs";
|
|
9302
|
-
import { dirname as
|
|
9303
|
-
import { fileURLToPath as
|
|
9454
|
+
import { basename as basename6, dirname as dirname13, join as join26 } from "path";
|
|
9455
|
+
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
9304
9456
|
import {
|
|
9305
9457
|
RESOURCE_MIME_TYPE,
|
|
9306
9458
|
registerAppResource,
|
|
@@ -9513,8 +9665,8 @@ function buildCompanionContext(input) {
|
|
|
9513
9665
|
|
|
9514
9666
|
// src/cli/bridge-handlers.ts
|
|
9515
9667
|
init_kernel();
|
|
9516
|
-
import { mkdirSync as
|
|
9517
|
-
import { join as
|
|
9668
|
+
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync7 } from "fs";
|
|
9669
|
+
import { join as join14 } from "path";
|
|
9518
9670
|
|
|
9519
9671
|
// src/cli/knowledge-contexts.ts
|
|
9520
9672
|
init_kernel();
|
|
@@ -10397,6 +10549,161 @@ async function addToken(db, params) {
|
|
|
10397
10549
|
possible_duplicates: possibleDuplicates
|
|
10398
10550
|
};
|
|
10399
10551
|
}
|
|
10552
|
+
async function importOkfTokens(db, params) {
|
|
10553
|
+
const userId = await resolveHandlerUser(db, params.user);
|
|
10554
|
+
if (!params.file?.trim()) throw new Error("file must be non-empty");
|
|
10555
|
+
if (!Array.isArray(params.tokens) || params.tokens.length === 0) {
|
|
10556
|
+
throw new Error("tokens must be a non-empty array");
|
|
10557
|
+
}
|
|
10558
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10559
|
+
for (const input of params.tokens) {
|
|
10560
|
+
const slug = input.slug?.trim();
|
|
10561
|
+
if (!slug || !input.concept?.trim()) {
|
|
10562
|
+
throw new Error("every token needs a non-empty slug and concept");
|
|
10563
|
+
}
|
|
10564
|
+
if (seen.has(slug)) throw new Error(`duplicate token slug: ${slug}`);
|
|
10565
|
+
seen.add(slug);
|
|
10566
|
+
if (input.bloomLevel !== void 0 && (!Number.isInteger(input.bloomLevel) || input.bloomLevel < 1 || input.bloomLevel > 5)) {
|
|
10567
|
+
throw new Error(`bloomLevel must be 1-5 (token: ${slug})`);
|
|
10568
|
+
}
|
|
10569
|
+
}
|
|
10570
|
+
const { DEFAULT_BUNDLE_DIR: DEFAULT_BUNDLE_DIR2, resolveArticlePath: resolveArticlePath2 } = await Promise.resolve().then(() => (init_io(), io_exports));
|
|
10571
|
+
const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_bundle(), bundle_exports));
|
|
10572
|
+
const { readFileSync: readFileSync20 } = await import("fs");
|
|
10573
|
+
const bundleDir = params.bundleDir ?? DEFAULT_BUNDLE_DIR2;
|
|
10574
|
+
const articlePath = resolveArticlePath2(bundleDir, params.file);
|
|
10575
|
+
let markdown;
|
|
10576
|
+
try {
|
|
10577
|
+
markdown = readFileSync20(articlePath, "utf8");
|
|
10578
|
+
} catch {
|
|
10579
|
+
throw new Error(`Article not found: ${articlePath}`);
|
|
10580
|
+
}
|
|
10581
|
+
let resource;
|
|
10582
|
+
try {
|
|
10583
|
+
const { fields } = parseFrontmatter2(markdown);
|
|
10584
|
+
resource = typeof fields.resource === "string" ? fields.resource : void 0;
|
|
10585
|
+
} catch {
|
|
10586
|
+
resource = void 0;
|
|
10587
|
+
}
|
|
10588
|
+
const sourceBase = resource ?? articlePath;
|
|
10589
|
+
const sourceLinkFor = (anchor) => anchor?.trim() ? `${sourceBase}#${anchor.trim()}` : sourceBase;
|
|
10590
|
+
const created = [];
|
|
10591
|
+
const updated = [];
|
|
10592
|
+
const replaced = [];
|
|
10593
|
+
const maintenance = [];
|
|
10594
|
+
let cardsEnsured = 0;
|
|
10595
|
+
await db.transaction(async (tx) => {
|
|
10596
|
+
const inImport = /* @__PURE__ */ new Map();
|
|
10597
|
+
for (const input of params.tokens) {
|
|
10598
|
+
const mode = input.mode ?? "new";
|
|
10599
|
+
const existing = await getTokenBySlug(tx, input.slug);
|
|
10600
|
+
if (mode === "new") {
|
|
10601
|
+
if (existing) {
|
|
10602
|
+
throw new Error(
|
|
10603
|
+
`Token '${input.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.`
|
|
10604
|
+
);
|
|
10605
|
+
}
|
|
10606
|
+
const token2 = await createToken(tx, {
|
|
10607
|
+
slug: input.slug,
|
|
10608
|
+
title: input.title,
|
|
10609
|
+
concept: input.concept,
|
|
10610
|
+
domain: input.domain,
|
|
10611
|
+
bloom_level: input.bloomLevel ?? 1,
|
|
10612
|
+
source_link: sourceLinkFor(input.anchor),
|
|
10613
|
+
question: input.question ?? null,
|
|
10614
|
+
question_source: input.question ? "llm" : void 0
|
|
10615
|
+
});
|
|
10616
|
+
inImport.set(input.slug, token2);
|
|
10617
|
+
created.push(input.slug);
|
|
10618
|
+
} else {
|
|
10619
|
+
if (!existing) {
|
|
10620
|
+
throw new Error(
|
|
10621
|
+
`Token '${input.slug}' does not exist \u2014 mode "${mode}" requires an existing token.`
|
|
10622
|
+
);
|
|
10623
|
+
}
|
|
10624
|
+
const updates = {
|
|
10625
|
+
concept: input.concept,
|
|
10626
|
+
source_link: sourceLinkFor(input.anchor)
|
|
10627
|
+
};
|
|
10628
|
+
if (input.title !== void 0) updates.title = input.title;
|
|
10629
|
+
if (input.domain !== void 0) updates.domain = input.domain;
|
|
10630
|
+
if (input.bloomLevel !== void 0) {
|
|
10631
|
+
updates.bloom_level = input.bloomLevel;
|
|
10632
|
+
}
|
|
10633
|
+
if (input.question !== void 0) {
|
|
10634
|
+
updates.question = input.question;
|
|
10635
|
+
updates.question_source = input.question ? "llm" : void 0;
|
|
10636
|
+
}
|
|
10637
|
+
const token2 = await updateToken(tx, input.slug, updates);
|
|
10638
|
+
if (existing.maintenance_at) {
|
|
10639
|
+
await clearTokenMaintenance(tx, input.slug);
|
|
10640
|
+
}
|
|
10641
|
+
if (mode === "replace") {
|
|
10642
|
+
await resetCardsForToken(tx, token2.id);
|
|
10643
|
+
replaced.push(input.slug);
|
|
10644
|
+
} else {
|
|
10645
|
+
updated.push(input.slug);
|
|
10646
|
+
}
|
|
10647
|
+
inImport.set(input.slug, token2);
|
|
10648
|
+
}
|
|
10649
|
+
const ctxNames = parseKnowledgeContextNames(input.knowledgeContexts);
|
|
10650
|
+
const assigned = await resolveKnowledgeContexts(tx, ctxNames);
|
|
10651
|
+
const token = inImport.get(input.slug);
|
|
10652
|
+
if (token) {
|
|
10653
|
+
for (const context of assigned) {
|
|
10654
|
+
await assignTokenToContext(tx, token.id, context.id);
|
|
10655
|
+
}
|
|
10656
|
+
}
|
|
10657
|
+
}
|
|
10658
|
+
for (const input of params.tokens) {
|
|
10659
|
+
const token = inImport.get(input.slug);
|
|
10660
|
+
if (!token) continue;
|
|
10661
|
+
const prereqSlugs = [
|
|
10662
|
+
...new Set(
|
|
10663
|
+
(input.prerequisites ?? []).map((s) => s.trim()).filter(Boolean)
|
|
10664
|
+
)
|
|
10665
|
+
];
|
|
10666
|
+
for (const prereqSlug of prereqSlugs) {
|
|
10667
|
+
const target = inImport.get(prereqSlug) ?? await getTokenBySlug(tx, prereqSlug);
|
|
10668
|
+
if (!target) {
|
|
10669
|
+
throw new Error(
|
|
10670
|
+
`Prerequisite token not found: ${prereqSlug} (for '${input.slug}')`
|
|
10671
|
+
);
|
|
10672
|
+
}
|
|
10673
|
+
await addPrerequisite(tx, token.id, target.id);
|
|
10674
|
+
}
|
|
10675
|
+
}
|
|
10676
|
+
for (const token of inImport.values()) {
|
|
10677
|
+
await ensureCard(tx, token.id, userId);
|
|
10678
|
+
cardsEnsured++;
|
|
10679
|
+
}
|
|
10680
|
+
const prior = await getTokensBySourceLinkBase(tx, sourceBase);
|
|
10681
|
+
for (const token of prior) {
|
|
10682
|
+
if (!inImport.has(token.slug)) {
|
|
10683
|
+
await setTokenMaintenance(
|
|
10684
|
+
tx,
|
|
10685
|
+
token.slug,
|
|
10686
|
+
`absent from re-import of ${params.file}`
|
|
10687
|
+
);
|
|
10688
|
+
maintenance.push(token.slug);
|
|
10689
|
+
}
|
|
10690
|
+
}
|
|
10691
|
+
});
|
|
10692
|
+
try {
|
|
10693
|
+
await ensureTokenEmbeddings(db, { limit: 16 });
|
|
10694
|
+
} catch {
|
|
10695
|
+
}
|
|
10696
|
+
return {
|
|
10697
|
+
success: true,
|
|
10698
|
+
user: userId,
|
|
10699
|
+
article: { file: params.file, source_link: sourceBase },
|
|
10700
|
+
created,
|
|
10701
|
+
updated,
|
|
10702
|
+
replaced,
|
|
10703
|
+
maintenance,
|
|
10704
|
+
cards: cardsEnsured
|
|
10705
|
+
};
|
|
10706
|
+
}
|
|
10400
10707
|
async function findTokens2(db, params) {
|
|
10401
10708
|
const userId = await resolveHandlerUser(db, params.user);
|
|
10402
10709
|
if (!params.context.trim()) {
|
|
@@ -10721,11 +11028,11 @@ async function backupCreate(db, params) {
|
|
|
10721
11028
|
const targetDir = params.dir || (await ensureActiveWorkspace(db)).path;
|
|
10722
11029
|
const snapshot = await exportSnapshot(db);
|
|
10723
11030
|
const manifest = verifySnapshot(snapshot);
|
|
10724
|
-
const backupDir =
|
|
10725
|
-
|
|
11031
|
+
const backupDir = join14(targetDir, "zam-backups");
|
|
11032
|
+
mkdirSync10(backupDir, { recursive: true });
|
|
10726
11033
|
const stamp = manifest.createdAt.replace(/[:.]/g, "-");
|
|
10727
|
-
const path =
|
|
10728
|
-
|
|
11034
|
+
const path = join14(backupDir, `zam-snapshot-${stamp}.sql`);
|
|
11035
|
+
writeFileSync7(path, snapshot, "utf-8");
|
|
10729
11036
|
return {
|
|
10730
11037
|
ok: true,
|
|
10731
11038
|
path,
|
|
@@ -10750,27 +11057,27 @@ init_kernel();
|
|
|
10750
11057
|
|
|
10751
11058
|
// src/cli/agent-connect.ts
|
|
10752
11059
|
init_kernel();
|
|
10753
|
-
import { mkdirSync as
|
|
11060
|
+
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync11 } from "fs";
|
|
10754
11061
|
import { homedir as homedir12 } from "os";
|
|
10755
|
-
import { dirname as
|
|
11062
|
+
import { dirname as dirname9 } from "path";
|
|
10756
11063
|
|
|
10757
11064
|
// src/cli/agent-harness.ts
|
|
10758
11065
|
import { spawn as spawn2 } from "child_process";
|
|
10759
|
-
import { existsSync as
|
|
11066
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
|
|
10760
11067
|
import { homedir as homedir9 } from "os";
|
|
10761
|
-
import { join as
|
|
11068
|
+
import { join as join16 } from "path";
|
|
10762
11069
|
|
|
10763
11070
|
// src/cli/terminal-open.ts
|
|
10764
11071
|
import { execFileSync as execFileSync2, execSync as execSync4 } from "child_process";
|
|
10765
11072
|
import {
|
|
10766
11073
|
accessSync,
|
|
10767
11074
|
constants,
|
|
10768
|
-
existsSync as
|
|
11075
|
+
existsSync as existsSync14,
|
|
10769
11076
|
unlinkSync as unlinkSync2,
|
|
10770
|
-
writeFileSync as
|
|
11077
|
+
writeFileSync as writeFileSync8
|
|
10771
11078
|
} from "fs";
|
|
10772
11079
|
import { tmpdir } from "os";
|
|
10773
|
-
import { basename as basename3, delimiter, extname, isAbsolute, join as
|
|
11080
|
+
import { basename as basename3, delimiter, extname, isAbsolute as isAbsolute2, join as join15 } from "path";
|
|
10774
11081
|
function isPowerShellShell(shell) {
|
|
10775
11082
|
return shell === "pwsh" || shell === "powershell";
|
|
10776
11083
|
}
|
|
@@ -10812,7 +11119,7 @@ function stripSurroundingQuotes(command) {
|
|
|
10812
11119
|
return trimmed;
|
|
10813
11120
|
}
|
|
10814
11121
|
function executableExists(path) {
|
|
10815
|
-
if (!
|
|
11122
|
+
if (!existsSync14(path)) return false;
|
|
10816
11123
|
if (process.platform === "win32") return true;
|
|
10817
11124
|
try {
|
|
10818
11125
|
accessSync(path, constants.X_OK);
|
|
@@ -10828,7 +11135,7 @@ function windowsExecutableNames(command) {
|
|
|
10828
11135
|
return extensions.map((ext) => `${command}${ext}`);
|
|
10829
11136
|
}
|
|
10830
11137
|
function hasDirectoryPart(command) {
|
|
10831
|
-
return
|
|
11138
|
+
return isAbsolute2(command) || command.includes("/") || command.includes("\\");
|
|
10832
11139
|
}
|
|
10833
11140
|
function findExecutable(command) {
|
|
10834
11141
|
const normalized = stripSurroundingQuotes(command);
|
|
@@ -10843,7 +11150,7 @@ function findExecutable(command) {
|
|
|
10843
11150
|
const pathEntries = (process.env.PATH ?? "").split(delimiter).map((entry) => entry.trim()).filter(Boolean);
|
|
10844
11151
|
for (const entry of pathEntries) {
|
|
10845
11152
|
for (const name of windowsExecutableNames(normalized)) {
|
|
10846
|
-
const candidate =
|
|
11153
|
+
const candidate = join15(entry, name);
|
|
10847
11154
|
if (executableExists(candidate)) matches.push(candidate);
|
|
10848
11155
|
}
|
|
10849
11156
|
}
|
|
@@ -10881,9 +11188,9 @@ end tell` : `tell application "Terminal"
|
|
|
10881
11188
|
activate
|
|
10882
11189
|
do script "${escaped}"
|
|
10883
11190
|
end tell`;
|
|
10884
|
-
const tmpFile =
|
|
11191
|
+
const tmpFile = join15(tmpdir(), `zam-terminal-${label}.scpt`);
|
|
10885
11192
|
try {
|
|
10886
|
-
|
|
11193
|
+
writeFileSync8(tmpFile, appleScript);
|
|
10887
11194
|
execSync4(`osascript ${JSON.stringify(tmpFile)}`, { stdio: "ignore" });
|
|
10888
11195
|
if (!silent) {
|
|
10889
11196
|
console.log(
|
|
@@ -10964,7 +11271,7 @@ function openTerminalWindow(opts) {
|
|
|
10964
11271
|
// src/cli/agent-harness.ts
|
|
10965
11272
|
var ANTIGRAVITY_IDE_CANDIDATE_PATHS = {
|
|
10966
11273
|
darwin: [
|
|
10967
|
-
|
|
11274
|
+
join16(
|
|
10968
11275
|
homedir9(),
|
|
10969
11276
|
".antigravity-ide",
|
|
10970
11277
|
"antigravity-ide",
|
|
@@ -10985,7 +11292,7 @@ var AGENT_HARNESSES = [
|
|
|
10985
11292
|
command: "cursor",
|
|
10986
11293
|
candidatePaths: {
|
|
10987
11294
|
win32: [
|
|
10988
|
-
|
|
11295
|
+
join16(homedir9(), "AppData", "Local", "Programs", "cursor", "Cursor.exe")
|
|
10989
11296
|
],
|
|
10990
11297
|
darwin: ["/Applications/Cursor.app/Contents/MacOS/Cursor"]
|
|
10991
11298
|
}
|
|
@@ -11010,7 +11317,7 @@ function getHarness(id) {
|
|
|
11010
11317
|
}
|
|
11011
11318
|
function resolveAntigravityIdeExecutable(deps = {}) {
|
|
11012
11319
|
const find = deps.find ?? findExecutable;
|
|
11013
|
-
const exists = deps.exists ??
|
|
11320
|
+
const exists = deps.exists ?? existsSync15;
|
|
11014
11321
|
const platform = deps.platform ?? process.platform;
|
|
11015
11322
|
const found = find("antigravity-ide");
|
|
11016
11323
|
if (found) return found;
|
|
@@ -11018,7 +11325,7 @@ function resolveAntigravityIdeExecutable(deps = {}) {
|
|
|
11018
11325
|
}
|
|
11019
11326
|
function resolveHarnessExecutable(harness, overrideCommand, deps = {}) {
|
|
11020
11327
|
const find = deps.find ?? findExecutable;
|
|
11021
|
-
const exists = deps.exists ??
|
|
11328
|
+
const exists = deps.exists ?? existsSync15;
|
|
11022
11329
|
const platform = deps.platform ?? process.platform;
|
|
11023
11330
|
const found = find(overrideCommand || harness.command);
|
|
11024
11331
|
if (found) return found;
|
|
@@ -11078,18 +11385,18 @@ function detectInstalledConnectHarnesses(options = {}) {
|
|
|
11078
11385
|
const home = options.home ?? homedir9();
|
|
11079
11386
|
const platform = options.platform ?? process.platform;
|
|
11080
11387
|
const find = options.find ?? findExecutable;
|
|
11081
|
-
const exists = options.exists ??
|
|
11388
|
+
const exists = options.exists ?? existsSync15;
|
|
11082
11389
|
const detected = [];
|
|
11083
11390
|
const hasCommandOrPath = (command, paths = []) => Boolean(find(command)) || paths.some((path) => exists(path));
|
|
11084
11391
|
if (hasCommandOrPath("codex", [
|
|
11085
|
-
|
|
11086
|
-
...platform === "darwin" ? ["/Applications/Codex.app"] : platform === "win32" ? [
|
|
11392
|
+
join16(home, ".codex"),
|
|
11393
|
+
...platform === "darwin" ? ["/Applications/Codex.app"] : platform === "win32" ? [join16(home, "AppData", "Local", "Programs", "Codex")] : []
|
|
11087
11394
|
])) {
|
|
11088
11395
|
detected.push("codex");
|
|
11089
11396
|
}
|
|
11090
11397
|
const vscodePaths = platform === "darwin" ? [
|
|
11091
11398
|
"/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code",
|
|
11092
|
-
|
|
11399
|
+
join16(
|
|
11093
11400
|
home,
|
|
11094
11401
|
"Applications",
|
|
11095
11402
|
"Visual Studio Code.app",
|
|
@@ -11100,7 +11407,7 @@ function detectInstalledConnectHarnesses(options = {}) {
|
|
|
11100
11407
|
"code"
|
|
11101
11408
|
)
|
|
11102
11409
|
] : platform === "win32" ? [
|
|
11103
|
-
|
|
11410
|
+
join16(
|
|
11104
11411
|
home,
|
|
11105
11412
|
"AppData",
|
|
11106
11413
|
"Local",
|
|
@@ -11111,23 +11418,23 @@ function detectInstalledConnectHarnesses(options = {}) {
|
|
|
11111
11418
|
)
|
|
11112
11419
|
] : ["/usr/bin/code", "/usr/local/bin/code", "/snap/bin/code"];
|
|
11113
11420
|
if (hasCommandOrPath("code", vscodePaths)) detected.push("vscode");
|
|
11114
|
-
const copilotHome = options.copilotHome ??
|
|
11421
|
+
const copilotHome = options.copilotHome ?? join16(home, ".copilot");
|
|
11115
11422
|
if (hasCommandOrPath("copilot", [
|
|
11116
11423
|
copilotHome,
|
|
11117
11424
|
...platform === "darwin" ? ["/Applications/GitHub Copilot.app"] : []
|
|
11118
11425
|
])) {
|
|
11119
11426
|
detected.push("copilot");
|
|
11120
11427
|
}
|
|
11121
|
-
if (hasCommandOrPath("opencode", [
|
|
11428
|
+
if (hasCommandOrPath("opencode", [join16(home, ".config", "opencode")])) {
|
|
11122
11429
|
detected.push("opencode");
|
|
11123
11430
|
}
|
|
11124
|
-
if (hasCommandOrPath("goose", [
|
|
11431
|
+
if (hasCommandOrPath("goose", [join16(home, ".config", "goose")])) {
|
|
11125
11432
|
detected.push("goose");
|
|
11126
11433
|
}
|
|
11127
11434
|
if (hasCommandOrPath("antigravity", [
|
|
11128
|
-
|
|
11435
|
+
join16(home, ".gemini", "config"),
|
|
11129
11436
|
...platform === "darwin" ? [
|
|
11130
|
-
|
|
11437
|
+
join16(
|
|
11131
11438
|
home,
|
|
11132
11439
|
".antigravity-ide",
|
|
11133
11440
|
"antigravity-ide",
|
|
@@ -11140,7 +11447,7 @@ function detectInstalledConnectHarnesses(options = {}) {
|
|
|
11140
11447
|
]) || find("antigravity-ide")) {
|
|
11141
11448
|
detected.push("antigravity");
|
|
11142
11449
|
}
|
|
11143
|
-
const claudeDesktopPath = platform === "darwin" ? "/Applications/Claude.app" : platform === "win32" ? exists(
|
|
11450
|
+
const claudeDesktopPath = platform === "darwin" ? "/Applications/Claude.app" : platform === "win32" ? exists(join16(home, "AppData", "Local", "AnthropicClaude")) ? join16(home, "AppData", "Local", "AnthropicClaude") : exists(join16(home, "AppData", "Local", "Claude")) ? join16(home, "AppData", "Local", "Claude") : join16(home, "AppData", "Roaming", "Claude") : join16(home, ".config", "Claude");
|
|
11144
11451
|
if (exists(claudeDesktopPath)) detected.push("claude-desktop");
|
|
11145
11452
|
return detected;
|
|
11146
11453
|
}
|
|
@@ -11173,11 +11480,11 @@ function connectHarnessMcp(harnessId, opts) {
|
|
|
11173
11480
|
return false;
|
|
11174
11481
|
}
|
|
11175
11482
|
}
|
|
11176
|
-
return
|
|
11483
|
+
return existsSync15(p);
|
|
11177
11484
|
};
|
|
11178
11485
|
const read = (p) => {
|
|
11179
11486
|
if (opts.readFile) return opts.readFile(p);
|
|
11180
|
-
return
|
|
11487
|
+
return readFileSync12(p, "utf-8");
|
|
11181
11488
|
};
|
|
11182
11489
|
let targetPath = "";
|
|
11183
11490
|
let content = "";
|
|
@@ -11209,32 +11516,32 @@ function connectHarnessMcp(harnessId, opts) {
|
|
|
11209
11516
|
return JSON.stringify(existing, null, 2);
|
|
11210
11517
|
};
|
|
11211
11518
|
if (harnessId === "claude-code") {
|
|
11212
|
-
targetPath =
|
|
11519
|
+
targetPath = join16(opts.cwd, ".mcp.json");
|
|
11213
11520
|
hint = "Claude Code will prompt you to approve the 'zam' MCP server on next launch.";
|
|
11214
11521
|
content = mergeMcpServersJson(targetPath);
|
|
11215
11522
|
} else if (harnessId === "claude-desktop") {
|
|
11216
11523
|
const platform = opts.platform ?? process.platform;
|
|
11217
|
-
targetPath = platform === "win32" ?
|
|
11524
|
+
targetPath = platform === "win32" ? join16(
|
|
11218
11525
|
opts.home,
|
|
11219
11526
|
"AppData",
|
|
11220
11527
|
"Roaming",
|
|
11221
11528
|
"Claude",
|
|
11222
11529
|
"claude_desktop_config.json"
|
|
11223
|
-
) : platform === "darwin" ?
|
|
11530
|
+
) : platform === "darwin" ? join16(
|
|
11224
11531
|
opts.home,
|
|
11225
11532
|
"Library",
|
|
11226
11533
|
"Application Support",
|
|
11227
11534
|
"Claude",
|
|
11228
11535
|
"claude_desktop_config.json"
|
|
11229
|
-
) :
|
|
11536
|
+
) : join16(opts.home, ".config", "Claude", "claude_desktop_config.json");
|
|
11230
11537
|
hint = "Restart Claude Desktop to load the 'zam' MCP server; MCP Apps panels render inline in the chat.";
|
|
11231
11538
|
content = mergeMcpServersJson(targetPath);
|
|
11232
11539
|
} else if (harnessId === "antigravity") {
|
|
11233
|
-
targetPath =
|
|
11540
|
+
targetPath = join16(opts.home, ".gemini", "config", "mcp_config.json");
|
|
11234
11541
|
hint = "Shared config read by Antigravity CLI and IDE (2.0+); older IDE builds read ~/.gemini/antigravity/mcp_config.json instead. Refresh Installed MCP Servers; the first tool call may still require approval.";
|
|
11235
11542
|
content = mergeMcpServersJson(targetPath);
|
|
11236
11543
|
} else if (harnessId === "opencode") {
|
|
11237
|
-
targetPath =
|
|
11544
|
+
targetPath = join16(opts.home, ".config", "opencode", "opencode.json");
|
|
11238
11545
|
hint = "OpenCode will load the enabled 'zam' MCP server on next launch.";
|
|
11239
11546
|
let existing = {};
|
|
11240
11547
|
if (exists(targetPath)) {
|
|
@@ -11262,7 +11569,7 @@ function connectHarnessMcp(harnessId, opts) {
|
|
|
11262
11569
|
existing.mcp = servers;
|
|
11263
11570
|
content = JSON.stringify(existing, null, 2);
|
|
11264
11571
|
} else if (harnessId === "codex") {
|
|
11265
|
-
targetPath =
|
|
11572
|
+
targetPath = join16(opts.home, ".codex", "config.toml");
|
|
11266
11573
|
hint = "Codex will prompt for tool execution approvals or respect the TOML approval modes.";
|
|
11267
11574
|
let existingStr = "";
|
|
11268
11575
|
if (exists(targetPath)) {
|
|
@@ -11289,14 +11596,14 @@ ${block}` : block;
|
|
|
11289
11596
|
}
|
|
11290
11597
|
} else if (harnessId === "vscode") {
|
|
11291
11598
|
const platform = opts.platform ?? process.platform;
|
|
11292
|
-
targetPath = platform === "win32" ?
|
|
11599
|
+
targetPath = platform === "win32" ? join16(opts.home, "AppData", "Roaming", "Code", "User", "mcp.json") : platform === "darwin" ? join16(
|
|
11293
11600
|
opts.home,
|
|
11294
11601
|
"Library",
|
|
11295
11602
|
"Application Support",
|
|
11296
11603
|
"Code",
|
|
11297
11604
|
"User",
|
|
11298
11605
|
"mcp.json"
|
|
11299
|
-
) :
|
|
11606
|
+
) : join16(opts.home, ".config", "Code", "User", "mcp.json");
|
|
11300
11607
|
hint = "Reload VS Code after setup. ZAM Companion stays separate from the Codex chat and can be moved to any panel or sidebar.";
|
|
11301
11608
|
let existing = {};
|
|
11302
11609
|
if (exists(targetPath)) {
|
|
@@ -11318,7 +11625,7 @@ ${block}` : block;
|
|
|
11318
11625
|
if (!existing.inputs) existing.inputs = [];
|
|
11319
11626
|
content = JSON.stringify(existing, null, 2);
|
|
11320
11627
|
} else if (harnessId === "goose") {
|
|
11321
|
-
targetPath =
|
|
11628
|
+
targetPath = join16(opts.home, ".config", "goose", "config.yaml");
|
|
11322
11629
|
hint = "goose will load the 'zam' extension on next session start. Run 'goose configure' to manage extensions.";
|
|
11323
11630
|
const isJs = opts.zamPath.endsWith(".js");
|
|
11324
11631
|
const cmdStr = isJs ? process.execPath : opts.zamPath;
|
|
@@ -11357,8 +11664,8 @@ ${zamExtension}
|
|
|
11357
11664
|
`;
|
|
11358
11665
|
}
|
|
11359
11666
|
} else if (harnessId === "copilot") {
|
|
11360
|
-
targetPath =
|
|
11361
|
-
opts.copilotHome ??
|
|
11667
|
+
targetPath = join16(
|
|
11668
|
+
opts.copilotHome ?? join16(opts.home, ".copilot"),
|
|
11362
11669
|
"mcp-config.json"
|
|
11363
11670
|
);
|
|
11364
11671
|
hint = "Restart GitHub Copilot or start a new session to load the 'zam' MCP server and its focused Recall, Graph, and Settings canvases.";
|
|
@@ -11400,15 +11707,15 @@ ${zamExtension}
|
|
|
11400
11707
|
|
|
11401
11708
|
// src/cli/copilot-extension.ts
|
|
11402
11709
|
import {
|
|
11403
|
-
existsSync as
|
|
11710
|
+
existsSync as existsSync16,
|
|
11404
11711
|
lstatSync,
|
|
11405
|
-
mkdirSync as
|
|
11406
|
-
readFileSync as
|
|
11407
|
-
writeFileSync as
|
|
11712
|
+
mkdirSync as mkdirSync11,
|
|
11713
|
+
readFileSync as readFileSync13,
|
|
11714
|
+
writeFileSync as writeFileSync9
|
|
11408
11715
|
} from "fs";
|
|
11409
11716
|
import { homedir as homedir10 } from "os";
|
|
11410
|
-
import { dirname as
|
|
11411
|
-
import { fileURLToPath as
|
|
11717
|
+
import { dirname as dirname7, join as join17, resolve as resolve5 } from "path";
|
|
11718
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
11412
11719
|
var EXTENSION_NAME = "zam-mcp-apps";
|
|
11413
11720
|
var EXTENSION_FILES = [
|
|
11414
11721
|
"extension.mjs",
|
|
@@ -11417,18 +11724,18 @@ var EXTENSION_FILES = [
|
|
|
11417
11724
|
"manifest.json"
|
|
11418
11725
|
];
|
|
11419
11726
|
var packageRoot = [
|
|
11420
|
-
|
|
11421
|
-
|
|
11422
|
-
].find((candidate) =>
|
|
11727
|
+
fileURLToPath4(new URL("../..", import.meta.url)),
|
|
11728
|
+
fileURLToPath4(new URL("../../..", import.meta.url))
|
|
11729
|
+
].find((candidate) => existsSync16(join17(candidate, "package.json"))) ?? fileURLToPath4(new URL("../..", import.meta.url));
|
|
11423
11730
|
function defaultCliEntry() {
|
|
11424
|
-
return
|
|
11731
|
+
return join17(dirname7(fileURLToPath4(import.meta.url)), "index.js");
|
|
11425
11732
|
}
|
|
11426
11733
|
function resolveCopilotHome(home = homedir10(), configuredHome = process.env.COPILOT_HOME) {
|
|
11427
|
-
return configuredHome?.trim() ?
|
|
11734
|
+
return configuredHome?.trim() ? resolve5(configuredHome) : join17(home, ".copilot");
|
|
11428
11735
|
}
|
|
11429
11736
|
function resolveCopilotZamLaunch(zamPath, options = {}) {
|
|
11430
11737
|
const cliEntry = options.cliEntry ?? defaultCliEntry();
|
|
11431
|
-
if (
|
|
11738
|
+
if (existsSync16(cliEntry)) {
|
|
11432
11739
|
return {
|
|
11433
11740
|
command: options.nodePath ?? process.execPath,
|
|
11434
11741
|
args: [cliEntry, "mcp"]
|
|
@@ -11440,20 +11747,20 @@ function resolveCopilotZamLaunch(zamPath, options = {}) {
|
|
|
11440
11747
|
};
|
|
11441
11748
|
}
|
|
11442
11749
|
function planCopilotExtensionInstall(options) {
|
|
11443
|
-
const sourceDir = options.assetsDir ??
|
|
11750
|
+
const sourceDir = options.assetsDir ?? join17(packageRoot, "dist", "copilot-extension");
|
|
11444
11751
|
for (const file of EXTENSION_FILES) {
|
|
11445
|
-
if (!
|
|
11752
|
+
if (!existsSync16(join17(sourceDir, file))) {
|
|
11446
11753
|
throw new Error(
|
|
11447
|
-
`Copilot MCP Apps asset is missing: ${
|
|
11754
|
+
`Copilot MCP Apps asset is missing: ${join17(sourceDir, file)}. Run \`npm run build\` and retry.`
|
|
11448
11755
|
);
|
|
11449
11756
|
}
|
|
11450
11757
|
}
|
|
11451
11758
|
const manifest = JSON.parse(
|
|
11452
|
-
|
|
11759
|
+
readFileSync13(join17(sourceDir, "manifest.json"), "utf8")
|
|
11453
11760
|
);
|
|
11454
11761
|
if (manifest.name !== EXTENSION_NAME || typeof manifest.version !== "string" || !manifest.version) {
|
|
11455
11762
|
throw new Error(
|
|
11456
|
-
`Invalid Copilot MCP Apps manifest: ${
|
|
11763
|
+
`Invalid Copilot MCP Apps manifest: ${join17(sourceDir, "manifest.json")}`
|
|
11457
11764
|
);
|
|
11458
11765
|
}
|
|
11459
11766
|
const copilotHome = resolveCopilotHome(
|
|
@@ -11462,15 +11769,15 @@ function planCopilotExtensionInstall(options) {
|
|
|
11462
11769
|
);
|
|
11463
11770
|
return {
|
|
11464
11771
|
sourceDir,
|
|
11465
|
-
destinationDir:
|
|
11772
|
+
destinationDir: join17(copilotHome, "extensions", EXTENSION_NAME),
|
|
11466
11773
|
launch: resolveCopilotZamLaunch(options.zamPath, options),
|
|
11467
11774
|
files: EXTENSION_FILES
|
|
11468
11775
|
};
|
|
11469
11776
|
}
|
|
11470
11777
|
function writeIfChanged(path, content) {
|
|
11471
11778
|
const next = Buffer.isBuffer(content) ? content : Buffer.from(content);
|
|
11472
|
-
if (
|
|
11473
|
-
|
|
11779
|
+
if (existsSync16(path) && readFileSync13(path).equals(next)) return false;
|
|
11780
|
+
writeFileSync9(path, next);
|
|
11474
11781
|
return true;
|
|
11475
11782
|
}
|
|
11476
11783
|
function installCopilotExtension(options) {
|
|
@@ -11478,18 +11785,18 @@ function installCopilotExtension(options) {
|
|
|
11478
11785
|
if (options.dryRun) {
|
|
11479
11786
|
return { ...plan, action: "planned", changedFiles: [] };
|
|
11480
11787
|
}
|
|
11481
|
-
const destinationExisted =
|
|
11788
|
+
const destinationExisted = existsSync16(plan.destinationDir);
|
|
11482
11789
|
if (destinationExisted && lstatSync(plan.destinationDir).isSymbolicLink()) {
|
|
11483
11790
|
throw new Error(
|
|
11484
11791
|
`Refusing to replace symlinked Copilot extension directory: ${plan.destinationDir}`
|
|
11485
11792
|
);
|
|
11486
11793
|
}
|
|
11487
|
-
|
|
11794
|
+
mkdirSync11(plan.destinationDir, { recursive: true });
|
|
11488
11795
|
const changedFiles = [];
|
|
11489
11796
|
for (const file of plan.files) {
|
|
11490
|
-
const source =
|
|
11491
|
-
const destination =
|
|
11492
|
-
if (writeIfChanged(destination,
|
|
11797
|
+
const source = join17(plan.sourceDir, file);
|
|
11798
|
+
const destination = join17(plan.destinationDir, file);
|
|
11799
|
+
if (writeIfChanged(destination, readFileSync13(source))) {
|
|
11493
11800
|
changedFiles.push(file);
|
|
11494
11801
|
}
|
|
11495
11802
|
}
|
|
@@ -11503,7 +11810,7 @@ function installCopilotExtension(options) {
|
|
|
11503
11810
|
2
|
|
11504
11811
|
)}
|
|
11505
11812
|
`;
|
|
11506
|
-
if (writeIfChanged(
|
|
11813
|
+
if (writeIfChanged(join17(plan.destinationDir, "launch.json"), launchContent)) {
|
|
11507
11814
|
changedFiles.push("launch.json");
|
|
11508
11815
|
}
|
|
11509
11816
|
return {
|
|
@@ -11515,24 +11822,24 @@ function installCopilotExtension(options) {
|
|
|
11515
11822
|
|
|
11516
11823
|
// src/cli/vscode-extension.ts
|
|
11517
11824
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
11518
|
-
import { existsSync as
|
|
11825
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync12, readFileSync as readFileSync14, writeFileSync as writeFileSync10 } from "fs";
|
|
11519
11826
|
import { homedir as homedir11 } from "os";
|
|
11520
|
-
import { dirname as
|
|
11521
|
-
import { fileURLToPath as
|
|
11827
|
+
import { dirname as dirname8, join as join18 } from "path";
|
|
11828
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
11522
11829
|
var packageRoot2 = [
|
|
11523
|
-
|
|
11524
|
-
|
|
11525
|
-
].find((candidate) =>
|
|
11830
|
+
fileURLToPath5(new URL("../..", import.meta.url)),
|
|
11831
|
+
fileURLToPath5(new URL("../../..", import.meta.url))
|
|
11832
|
+
].find((candidate) => existsSync17(join18(candidate, "package.json"))) ?? fileURLToPath5(new URL("../..", import.meta.url));
|
|
11526
11833
|
function resolveVscodeExecutable(options = {}) {
|
|
11527
11834
|
const home = options.home ?? homedir11();
|
|
11528
11835
|
const platform = options.platform ?? process.platform;
|
|
11529
11836
|
const find = options.find ?? findExecutable;
|
|
11530
|
-
const exists = options.exists ??
|
|
11837
|
+
const exists = options.exists ?? existsSync17;
|
|
11531
11838
|
const found = find("code");
|
|
11532
11839
|
if (found) return found;
|
|
11533
11840
|
const candidates = platform === "darwin" ? [
|
|
11534
11841
|
"/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code",
|
|
11535
|
-
|
|
11842
|
+
join18(
|
|
11536
11843
|
home,
|
|
11537
11844
|
"Applications",
|
|
11538
11845
|
"Visual Studio Code.app",
|
|
@@ -11543,7 +11850,7 @@ function resolveVscodeExecutable(options = {}) {
|
|
|
11543
11850
|
"code"
|
|
11544
11851
|
)
|
|
11545
11852
|
] : platform === "win32" ? [
|
|
11546
|
-
|
|
11853
|
+
join18(
|
|
11547
11854
|
home,
|
|
11548
11855
|
"AppData",
|
|
11549
11856
|
"Local",
|
|
@@ -11563,7 +11870,7 @@ function buildVscodeCliInvocation(command, args, platform = process.platform) {
|
|
|
11563
11870
|
}
|
|
11564
11871
|
function packageVersion() {
|
|
11565
11872
|
const parsed = JSON.parse(
|
|
11566
|
-
|
|
11873
|
+
readFileSync14(join18(packageRoot2, "package.json"), "utf8")
|
|
11567
11874
|
);
|
|
11568
11875
|
if (typeof parsed.version !== "string" || !parsed.version) {
|
|
11569
11876
|
throw new Error("Cannot determine the ZAM package version");
|
|
@@ -11573,9 +11880,9 @@ function packageVersion() {
|
|
|
11573
11880
|
function planVscodeExtensionInstall(options) {
|
|
11574
11881
|
const home = options.home ?? homedir11();
|
|
11575
11882
|
const version = options.version ?? packageVersion();
|
|
11576
|
-
const assetsDir = options.assetsDir ??
|
|
11577
|
-
const vsixPath =
|
|
11578
|
-
if (!
|
|
11883
|
+
const assetsDir = options.assetsDir ?? join18(packageRoot2, "dist", "vscode-extension");
|
|
11884
|
+
const vsixPath = join18(assetsDir, `ZAM_Companion_${version}.vsix`);
|
|
11885
|
+
if (!existsSync17(vsixPath)) {
|
|
11579
11886
|
throw new Error(
|
|
11580
11887
|
`ZAM Companion VSIX asset is missing: ${vsixPath}. Run \`npm run build\` and retry.`
|
|
11581
11888
|
);
|
|
@@ -11595,7 +11902,7 @@ function planVscodeExtensionInstall(options) {
|
|
|
11595
11902
|
version,
|
|
11596
11903
|
vsixPath,
|
|
11597
11904
|
codePath,
|
|
11598
|
-
launchConfigPath:
|
|
11905
|
+
launchConfigPath: join18(home, ".zam", "vscode-launch.json"),
|
|
11599
11906
|
launch: { command: options.zamPath, args: ["mcp"] }
|
|
11600
11907
|
};
|
|
11601
11908
|
}
|
|
@@ -11631,8 +11938,8 @@ function installVscodeExtension(options) {
|
|
|
11631
11938
|
2
|
|
11632
11939
|
)}
|
|
11633
11940
|
`;
|
|
11634
|
-
const existed =
|
|
11635
|
-
const changed = !existed ||
|
|
11941
|
+
const existed = existsSync17(plan.launchConfigPath);
|
|
11942
|
+
const changed = !existed || readFileSync14(plan.launchConfigPath, "utf8") !== launchContent;
|
|
11636
11943
|
const run = options.run ?? ((command, args) => {
|
|
11637
11944
|
const invocation = buildVscodeCliInvocation(command, args);
|
|
11638
11945
|
execFileSync3(invocation.command, invocation.args, {
|
|
@@ -11654,8 +11961,8 @@ function installVscodeExtension(options) {
|
|
|
11654
11961
|
}
|
|
11655
11962
|
run(plan.codePath, ["--install-extension", plan.vsixPath, "--force"]);
|
|
11656
11963
|
if (changed) {
|
|
11657
|
-
|
|
11658
|
-
|
|
11964
|
+
mkdirSync12(dirname8(plan.launchConfigPath), { recursive: true });
|
|
11965
|
+
writeFileSync10(plan.launchConfigPath, launchContent, "utf8");
|
|
11659
11966
|
}
|
|
11660
11967
|
return {
|
|
11661
11968
|
...plan,
|
|
@@ -11715,8 +12022,8 @@ function resolveDeps(deps) {
|
|
|
11715
12022
|
detect: deps.detect ?? (() => detectInstalledConnectHarnesses({ home, copilotHome })),
|
|
11716
12023
|
connectMcp: deps.connectMcp ?? connectHarnessMcp,
|
|
11717
12024
|
writeConfig: deps.writeConfig ?? ((path, content) => {
|
|
11718
|
-
|
|
11719
|
-
|
|
12025
|
+
mkdirSync13(dirname9(path), { recursive: true });
|
|
12026
|
+
writeFileSync11(path, content, "utf-8");
|
|
11720
12027
|
}),
|
|
11721
12028
|
installCopilot: deps.installCopilot ?? installCopilotExtension,
|
|
11722
12029
|
installVscode: deps.installVscode ?? installVscodeExtension,
|
|
@@ -11862,9 +12169,9 @@ function inspectConnectHarnesses(deps = {}) {
|
|
|
11862
12169
|
init_kernel();
|
|
11863
12170
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
11864
12171
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
11865
|
-
import { existsSync as
|
|
12172
|
+
import { existsSync as existsSync21, readdirSync as readdirSync3, readFileSync as readFileSync18, rmSync as rmSync3 } from "fs";
|
|
11866
12173
|
import { homedir as homedir14, tmpdir as tmpdir3 } from "os";
|
|
11867
|
-
import { join as
|
|
12174
|
+
import { join as join23, resolve as resolve7 } from "path";
|
|
11868
12175
|
import { Command } from "commander";
|
|
11869
12176
|
import { ulid as ulid10 } from "ulid";
|
|
11870
12177
|
|
|
@@ -11944,7 +12251,7 @@ async function readWebLink(url) {
|
|
|
11944
12251
|
redirect: "manual",
|
|
11945
12252
|
signal: controller.signal,
|
|
11946
12253
|
headers: {
|
|
11947
|
-
"User-Agent": "ZAM-Content-Studio/0.
|
|
12254
|
+
"User-Agent": "ZAM-Content-Studio/0.15.0"
|
|
11948
12255
|
}
|
|
11949
12256
|
});
|
|
11950
12257
|
if (res.status >= 300 && res.status < 400) {
|
|
@@ -12011,16 +12318,16 @@ async function readImageOCR(db, imagePath) {
|
|
|
12011
12318
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
12012
12319
|
import {
|
|
12013
12320
|
chmodSync as chmodSync2,
|
|
12014
|
-
existsSync as
|
|
12015
|
-
mkdirSync as
|
|
12016
|
-
readFileSync as
|
|
12017
|
-
writeFileSync as
|
|
12321
|
+
existsSync as existsSync18,
|
|
12322
|
+
mkdirSync as mkdirSync14,
|
|
12323
|
+
readFileSync as readFileSync15,
|
|
12324
|
+
writeFileSync as writeFileSync12
|
|
12018
12325
|
} from "fs";
|
|
12019
12326
|
import { homedir as homedir13 } from "os";
|
|
12020
|
-
import { delimiter as delimiter2, join as
|
|
12327
|
+
import { delimiter as delimiter2, join as join19, resolve as resolve6 } from "path";
|
|
12021
12328
|
var BUILT_CLI_PATTERN = /[\\/]dist[\\/]cli[\\/]index\.js$/;
|
|
12022
12329
|
function normalizeForCompare(path, platform) {
|
|
12023
|
-
const resolved =
|
|
12330
|
+
const resolved = resolve6(path);
|
|
12024
12331
|
return platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
12025
12332
|
}
|
|
12026
12333
|
function windowsShimContent(nodePath, cliPath) {
|
|
@@ -12060,14 +12367,14 @@ function ensureWindowsUserPath(binDir) {
|
|
|
12060
12367
|
return output.endsWith("updated");
|
|
12061
12368
|
}
|
|
12062
12369
|
function ensureUnixUserPath(home, platform) {
|
|
12063
|
-
const profile =
|
|
12064
|
-
const existing =
|
|
12370
|
+
const profile = join19(home, platform === "darwin" ? ".zprofile" : ".profile");
|
|
12371
|
+
const existing = existsSync18(profile) ? readFileSync15(profile, "utf8") : "";
|
|
12065
12372
|
if (existing.includes(".zam/bin")) return false;
|
|
12066
12373
|
const block = `
|
|
12067
12374
|
# Added by ZAM: keep the zam CLI on PATH
|
|
12068
12375
|
export PATH="$HOME/.zam/bin:$PATH"
|
|
12069
12376
|
`;
|
|
12070
|
-
|
|
12377
|
+
writeFileSync12(profile, existing + block, "utf8");
|
|
12071
12378
|
return true;
|
|
12072
12379
|
}
|
|
12073
12380
|
function installCliShim(options = {}) {
|
|
@@ -12075,10 +12382,10 @@ function installCliShim(options = {}) {
|
|
|
12075
12382
|
const platform = options.platform ?? process.platform;
|
|
12076
12383
|
const env = options.env ?? process.env;
|
|
12077
12384
|
const find = options.find ?? findExecutable;
|
|
12078
|
-
const nodePath =
|
|
12079
|
-
const cliPath =
|
|
12080
|
-
const binDir =
|
|
12081
|
-
const shimPath =
|
|
12385
|
+
const nodePath = resolve6(options.nodePath ?? process.execPath);
|
|
12386
|
+
const cliPath = resolve6(options.cliPath ?? process.argv[1] ?? "");
|
|
12387
|
+
const binDir = join19(home, ".zam", "bin");
|
|
12388
|
+
const shimPath = join19(binDir, platform === "win32" ? "zam.cmd" : "zam");
|
|
12082
12389
|
const report = {
|
|
12083
12390
|
status: "ok",
|
|
12084
12391
|
binDir,
|
|
@@ -12089,7 +12396,7 @@ function installCliShim(options = {}) {
|
|
|
12089
12396
|
pathUpdated: false,
|
|
12090
12397
|
needsNewTerminal: false
|
|
12091
12398
|
};
|
|
12092
|
-
if (!BUILT_CLI_PATTERN.test(cliPath) || !
|
|
12399
|
+
if (!BUILT_CLI_PATTERN.test(cliPath) || !existsSync18(cliPath)) {
|
|
12093
12400
|
report.status = "skipped";
|
|
12094
12401
|
report.detail = `Not running from a built CLI entry (${cliPath}); nothing to link.`;
|
|
12095
12402
|
return report;
|
|
@@ -12103,11 +12410,11 @@ function installCliShim(options = {}) {
|
|
|
12103
12410
|
return report;
|
|
12104
12411
|
}
|
|
12105
12412
|
const content = platform === "win32" ? windowsShimContent(nodePath, cliPath) : unixShimContent(nodePath, cliPath);
|
|
12106
|
-
const existed =
|
|
12107
|
-
const changed = !existed ||
|
|
12413
|
+
const existed = existsSync18(shimPath);
|
|
12414
|
+
const changed = !existed || readFileSync15(shimPath, "utf8") !== content;
|
|
12108
12415
|
if (changed) {
|
|
12109
|
-
|
|
12110
|
-
|
|
12416
|
+
mkdirSync14(binDir, { recursive: true });
|
|
12417
|
+
writeFileSync12(shimPath, content, "utf8");
|
|
12111
12418
|
if (platform !== "win32") chmodSync2(shimPath, 493);
|
|
12112
12419
|
}
|
|
12113
12420
|
report.status = !existed ? "installed" : changed ? "refreshed" : "ok";
|
|
@@ -28816,40 +29123,40 @@ function getCurriculumProvider(id) {
|
|
|
28816
29123
|
|
|
28817
29124
|
// src/cli/install-repair.ts
|
|
28818
29125
|
init_kernel();
|
|
28819
|
-
import { existsSync as
|
|
29126
|
+
import { existsSync as existsSync20 } from "fs";
|
|
28820
29127
|
|
|
28821
29128
|
// src/cli/provisioning/index.ts
|
|
28822
29129
|
import {
|
|
28823
|
-
existsSync as
|
|
29130
|
+
existsSync as existsSync19,
|
|
28824
29131
|
lstatSync as lstatSync2,
|
|
28825
|
-
mkdirSync as
|
|
28826
|
-
readFileSync as
|
|
28827
|
-
realpathSync,
|
|
29132
|
+
mkdirSync as mkdirSync15,
|
|
29133
|
+
readFileSync as readFileSync16,
|
|
29134
|
+
realpathSync as realpathSync2,
|
|
28828
29135
|
rmSync as rmSync2,
|
|
28829
29136
|
symlinkSync,
|
|
28830
|
-
writeFileSync as
|
|
29137
|
+
writeFileSync as writeFileSync13
|
|
28831
29138
|
} from "fs";
|
|
28832
|
-
import { basename as basename4, dirname as
|
|
28833
|
-
import { fileURLToPath as
|
|
29139
|
+
import { basename as basename4, dirname as dirname10, join as join20 } from "path";
|
|
29140
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
28834
29141
|
var packageRoot3 = [
|
|
28835
|
-
|
|
28836
|
-
|
|
28837
|
-
].find((candidate) =>
|
|
29142
|
+
fileURLToPath6(new URL("../..", import.meta.url)),
|
|
29143
|
+
fileURLToPath6(new URL("../../..", import.meta.url))
|
|
29144
|
+
].find((candidate) => existsSync19(join20(candidate, "package.json"))) ?? fileURLToPath6(new URL("../..", import.meta.url));
|
|
28838
29145
|
var ALL_SETUP_AGENTS = ["claude", "copilot", "codex", "agent"];
|
|
28839
29146
|
var SKILL_PAIRS = [
|
|
28840
29147
|
{
|
|
28841
|
-
from:
|
|
28842
|
-
to:
|
|
29148
|
+
from: join20(packageRoot3, ".claude", "skills", "zam", "SKILL.md"),
|
|
29149
|
+
to: join20(".claude", "skills", "zam", "SKILL.md"),
|
|
28843
29150
|
agents: ["claude", "copilot"]
|
|
28844
29151
|
},
|
|
28845
29152
|
{
|
|
28846
|
-
from:
|
|
28847
|
-
to:
|
|
29153
|
+
from: join20(packageRoot3, ".agent", "skills", "zam", "SKILL.md"),
|
|
29154
|
+
to: join20(".agent", "skills", "zam", "SKILL.md"),
|
|
28848
29155
|
agents: ["agent"]
|
|
28849
29156
|
},
|
|
28850
29157
|
{
|
|
28851
|
-
from:
|
|
28852
|
-
to:
|
|
29158
|
+
from: join20(packageRoot3, ".agents", "skills", "zam", "SKILL.md"),
|
|
29159
|
+
to: join20(".agents", "skills", "zam", "SKILL.md"),
|
|
28853
29160
|
agents: ["codex"]
|
|
28854
29161
|
}
|
|
28855
29162
|
];
|
|
@@ -28893,7 +29200,7 @@ function isSymbolicLink(path) {
|
|
|
28893
29200
|
}
|
|
28894
29201
|
}
|
|
28895
29202
|
function comparableRealPath(path) {
|
|
28896
|
-
const real =
|
|
29203
|
+
const real = realpathSync2(path);
|
|
28897
29204
|
return process.platform === "win32" ? real.toLowerCase() : real;
|
|
28898
29205
|
}
|
|
28899
29206
|
function pathsResolveToSameDirectory(sourceDir, destinationDir) {
|
|
@@ -28909,15 +29216,15 @@ function linkPointsTo(sourceDir, destinationDir) {
|
|
|
28909
29216
|
function isZamSkillCopy(destinationDir) {
|
|
28910
29217
|
try {
|
|
28911
29218
|
if (!lstatSync2(destinationDir).isDirectory()) return false;
|
|
28912
|
-
const skillFile =
|
|
28913
|
-
if (!
|
|
28914
|
-
return /^name:\s*zam\s*$/m.test(
|
|
29219
|
+
const skillFile = join20(destinationDir, "SKILL.md");
|
|
29220
|
+
if (!existsSync19(skillFile)) return false;
|
|
29221
|
+
return /^name:\s*zam\s*$/m.test(readFileSync16(skillFile, "utf8"));
|
|
28915
29222
|
} catch {
|
|
28916
29223
|
return false;
|
|
28917
29224
|
}
|
|
28918
29225
|
}
|
|
28919
29226
|
function classifySkillDestination(sourceDir, destinationDir) {
|
|
28920
|
-
if (!
|
|
29227
|
+
if (!existsSync19(sourceDir)) return "source-missing";
|
|
28921
29228
|
if (!isSymbolicLink(destinationDir) && pathsResolveToSameDirectory(sourceDir, destinationDir)) {
|
|
28922
29229
|
return "source-directory";
|
|
28923
29230
|
}
|
|
@@ -28934,9 +29241,9 @@ function wireSkills(cwd = process.cwd(), agents = parseSetupAgents(), opts = {})
|
|
|
28934
29241
|
};
|
|
28935
29242
|
for (const { from, to, agents: pairAgents } of SKILL_PAIRS) {
|
|
28936
29243
|
if (!pairAgents.some((agent) => agents.has(agent))) continue;
|
|
28937
|
-
const sourceDir =
|
|
28938
|
-
const destinationDir =
|
|
28939
|
-
const label =
|
|
29244
|
+
const sourceDir = dirname10(from);
|
|
29245
|
+
const destinationDir = dirname10(join20(cwd, to));
|
|
29246
|
+
const label = dirname10(to);
|
|
28940
29247
|
const state = classifySkillDestination(sourceDir, destinationDir);
|
|
28941
29248
|
if (state === "source-missing") {
|
|
28942
29249
|
if (!opts.quiet) {
|
|
@@ -28993,7 +29300,7 @@ function wireSkills(cwd = process.cwd(), agents = parseSetupAgents(), opts = {})
|
|
|
28993
29300
|
if (destinationExists) {
|
|
28994
29301
|
rmSync2(destinationDir, { recursive: true, force: true });
|
|
28995
29302
|
}
|
|
28996
|
-
|
|
29303
|
+
mkdirSync15(dirname10(destinationDir), { recursive: true });
|
|
28997
29304
|
symlinkSync(
|
|
28998
29305
|
sourceDir,
|
|
28999
29306
|
destinationDir,
|
|
@@ -29009,8 +29316,8 @@ function inspectSkillLinks(cwd = process.cwd(), agents = parseSetupAgents()) {
|
|
|
29009
29316
|
const results = [];
|
|
29010
29317
|
for (const { from, to, agents: pairAgents } of SKILL_PAIRS) {
|
|
29011
29318
|
if (!pairAgents.some((agent) => agents.has(agent))) continue;
|
|
29012
|
-
const sourceDir =
|
|
29013
|
-
const destinationDir =
|
|
29319
|
+
const sourceDir = dirname10(from);
|
|
29320
|
+
const destinationDir = dirname10(join20(cwd, to));
|
|
29014
29321
|
results.push({
|
|
29015
29322
|
agents: pairAgents,
|
|
29016
29323
|
source: sourceDir,
|
|
@@ -29041,7 +29348,7 @@ function defaultRepairWorkspaces() {
|
|
|
29041
29348
|
try {
|
|
29042
29349
|
const agents = parseSetupAgents();
|
|
29043
29350
|
for (const workspace of getConfiguredWorkspaces()) {
|
|
29044
|
-
if (!
|
|
29351
|
+
if (!existsSync20(workspace.path)) {
|
|
29045
29352
|
summary.missing += 1;
|
|
29046
29353
|
continue;
|
|
29047
29354
|
}
|
|
@@ -29234,9 +29541,9 @@ init_client();
|
|
|
29234
29541
|
init_kernel();
|
|
29235
29542
|
init_client();
|
|
29236
29543
|
import { randomBytes } from "crypto";
|
|
29237
|
-
import { readFileSync as
|
|
29544
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
29238
29545
|
import { tmpdir as tmpdir2 } from "os";
|
|
29239
|
-
import { basename as basename5, join as
|
|
29546
|
+
import { basename as basename5, join as join21 } from "path";
|
|
29240
29547
|
var LANGUAGE_NAMES2 = {
|
|
29241
29548
|
en: "English",
|
|
29242
29549
|
de: "German",
|
|
@@ -29274,7 +29581,7 @@ async function observeUiSnapshotViaLLM(db, input) {
|
|
|
29274
29581
|
if (isVideo) {
|
|
29275
29582
|
const { mkdirSync: mkdirSync17, readdirSync: readdirSync4, rmSync: rmSync4 } = await import("fs");
|
|
29276
29583
|
const { execSync: execSync5 } = await import("child_process");
|
|
29277
|
-
const tempDir =
|
|
29584
|
+
const tempDir = join21(
|
|
29278
29585
|
tmpdir2(),
|
|
29279
29586
|
`zam-frames-${randomBytes(4).toString("hex")}`
|
|
29280
29587
|
);
|
|
@@ -29307,7 +29614,7 @@ async function observeUiSnapshotViaLLM(db, input) {
|
|
|
29307
29614
|
}
|
|
29308
29615
|
}
|
|
29309
29616
|
for (const file of sampledFiles) {
|
|
29310
|
-
const bytes =
|
|
29617
|
+
const bytes = readFileSync17(join21(tempDir, file));
|
|
29311
29618
|
imageUrls.push(`data:image/png;base64,${bytes.toString("base64")}`);
|
|
29312
29619
|
}
|
|
29313
29620
|
} finally {
|
|
@@ -29317,7 +29624,7 @@ async function observeUiSnapshotViaLLM(db, input) {
|
|
|
29317
29624
|
}
|
|
29318
29625
|
}
|
|
29319
29626
|
} else {
|
|
29320
|
-
const imageBytes =
|
|
29627
|
+
const imageBytes = readFileSync17(input.imagePath);
|
|
29321
29628
|
const ext = input.imagePath.split(".").pop()?.toLowerCase();
|
|
29322
29629
|
const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : "image/png";
|
|
29323
29630
|
imageUrls.push(`data:${mime};base64,${imageBytes.toString("base64")}`);
|
|
@@ -29826,13 +30133,13 @@ async function resolveUser(opts, db, resolveOpts) {
|
|
|
29826
30133
|
}
|
|
29827
30134
|
|
|
29828
30135
|
// src/cli/workspaces/backup.ts
|
|
29829
|
-
import { mkdirSync as
|
|
29830
|
-
import { join as
|
|
30136
|
+
import { mkdirSync as mkdirSync16 } from "fs";
|
|
30137
|
+
import { join as join22 } from "path";
|
|
29831
30138
|
async function backupDatabaseTo(db, targetDir) {
|
|
29832
|
-
const backupDir =
|
|
29833
|
-
|
|
30139
|
+
const backupDir = join22(targetDir, "zam-backups");
|
|
30140
|
+
mkdirSync16(backupDir, { recursive: true });
|
|
29834
30141
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
29835
|
-
const dest =
|
|
30142
|
+
const dest = join22(backupDir, `zam-${stamp}.db`);
|
|
29836
30143
|
await db.exec(`VACUUM INTO '${dest.replace(/'/g, "''")}'`);
|
|
29837
30144
|
return dest;
|
|
29838
30145
|
}
|
|
@@ -29966,20 +30273,20 @@ bridgeCommand.command("workspace-info").description("Report the workspace dir, i
|
|
|
29966
30273
|
activeWorkspace,
|
|
29967
30274
|
workspaceDir: activeWorkspace.path,
|
|
29968
30275
|
defaultWorkspaceDir: defaultWorkspaceDir(),
|
|
29969
|
-
dataDir:
|
|
30276
|
+
dataDir: join23(homedir14(), ".zam")
|
|
29970
30277
|
});
|
|
29971
30278
|
});
|
|
29972
30279
|
});
|
|
29973
30280
|
function provisionConfiguredWorkspaces() {
|
|
29974
30281
|
return getConfiguredWorkspaces().flatMap(
|
|
29975
|
-
(workspace) =>
|
|
30282
|
+
(workspace) => existsSync21(workspace.path) ? wireSkills(workspace.path, parseSetupAgents(), { quiet: true }) : []
|
|
29976
30283
|
);
|
|
29977
30284
|
}
|
|
29978
30285
|
function buildWorkspaceLinkHealth(workspaces) {
|
|
29979
30286
|
const agents = parseSetupAgents();
|
|
29980
30287
|
const map = {};
|
|
29981
30288
|
for (const workspace of workspaces) {
|
|
29982
|
-
if (!
|
|
30289
|
+
if (!existsSync21(workspace.path)) continue;
|
|
29983
30290
|
const links = inspectSkillLinks(workspace.path, agents);
|
|
29984
30291
|
map[workspace.id] = {
|
|
29985
30292
|
health: summarizeSkillLinkHealth(links),
|
|
@@ -30009,7 +30316,7 @@ bridgeCommand.command("workspace-list").description("List configured ZAM workspa
|
|
|
30009
30316
|
activeWorkspace,
|
|
30010
30317
|
workspaceDir: activeWorkspace.path,
|
|
30011
30318
|
defaultWorkspaceDir: defaultWorkspaceDir(),
|
|
30012
|
-
dataDir:
|
|
30319
|
+
dataDir: join23(homedir14(), ".zam"),
|
|
30013
30320
|
linkHealth: buildWorkspaceLinkHealth(workspaces)
|
|
30014
30321
|
});
|
|
30015
30322
|
});
|
|
@@ -30024,7 +30331,7 @@ bridgeCommand.command("workspace-repair-links").description(
|
|
|
30024
30331
|
if (!id) jsonError("A non-empty --id is required");
|
|
30025
30332
|
const workspace = getConfiguredWorkspaces().find((item) => item.id === id);
|
|
30026
30333
|
if (!workspace) jsonError(`Workspace "${id}" is not configured`);
|
|
30027
|
-
if (!
|
|
30334
|
+
if (!existsSync21(workspace.path)) {
|
|
30028
30335
|
jsonError(`Workspace path does not exist: ${workspace.path}`);
|
|
30029
30336
|
}
|
|
30030
30337
|
const agents = parseSetupAgents(opts.agents);
|
|
@@ -30055,8 +30362,8 @@ function parseBridgeWorkspaceKind(value) {
|
|
|
30055
30362
|
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) => {
|
|
30056
30363
|
const raw = String(opts.path ?? "").trim();
|
|
30057
30364
|
if (!raw) jsonError("A non-empty --path is required");
|
|
30058
|
-
const path =
|
|
30059
|
-
if (!
|
|
30365
|
+
const path = resolve7(raw);
|
|
30366
|
+
if (!existsSync21(path)) jsonError(`Workspace path does not exist: ${path}`);
|
|
30060
30367
|
const id = opts.id ? String(opts.id).trim() : void 0;
|
|
30061
30368
|
if (opts.id && !id) jsonError("A non-empty --id is required");
|
|
30062
30369
|
const kind = parseBridgeWorkspaceKind(opts.kind);
|
|
@@ -30100,8 +30407,8 @@ bridgeCommand.command("workspace-remove").description("Unregister a ZAM workspac
|
|
|
30100
30407
|
bridgeCommand.command("set-workspace-dir").description("Set the personal workspace directory (JSON)").requiredOption("--dir <path>", "Path to the workspace directory").action(async (opts) => {
|
|
30101
30408
|
const raw = String(opts.dir ?? "").trim();
|
|
30102
30409
|
if (!raw) jsonError("A non-empty --dir is required");
|
|
30103
|
-
const dir =
|
|
30104
|
-
if (!
|
|
30410
|
+
const dir = resolve7(raw);
|
|
30411
|
+
if (!existsSync21(dir)) jsonError(`Workspace path does not exist: ${dir}`);
|
|
30105
30412
|
const skillLinks = wireSkills(dir, parseSetupAgents(), { quiet: true });
|
|
30106
30413
|
await withOptionalDb2(async (db) => {
|
|
30107
30414
|
const workspace = await activateWorkspacePath(db, dir);
|
|
@@ -30164,7 +30471,7 @@ bridgeCommand.command("agent-open").description("Launch an agent harness in the
|
|
|
30164
30471
|
jsonError(`Workspace is not configured: ${opts.workspace}`);
|
|
30165
30472
|
}
|
|
30166
30473
|
const activeWorkspace = await ensureActiveWorkspace(db);
|
|
30167
|
-
const workspace = opts.dir ?
|
|
30474
|
+
const workspace = opts.dir ? existsSync21(opts.dir) ? opts.dir : homedir14() : existingWorkspaceDirOrHome(configuredWorkspace ?? activeWorkspace);
|
|
30168
30475
|
launchHarness(harness, {
|
|
30169
30476
|
executable,
|
|
30170
30477
|
workspace,
|
|
@@ -30440,6 +30747,52 @@ bridgeCommand.command("add-token").description("Create a token + card from JSON
|
|
|
30440
30747
|
jsonError(err.message);
|
|
30441
30748
|
}
|
|
30442
30749
|
});
|
|
30750
|
+
bridgeCommand.command("okf-import").description(
|
|
30751
|
+
"Record an agent's decomposition of an OKF article as learning tokens (JSON stdin, ADR 2026-07-18)"
|
|
30752
|
+
).option("--user <id>", "User ID (default: whoami)").action(async (opts) => {
|
|
30753
|
+
try {
|
|
30754
|
+
let raw;
|
|
30755
|
+
if (isServeMode) {
|
|
30756
|
+
raw = serveStdinPayload ?? "";
|
|
30757
|
+
} else {
|
|
30758
|
+
const chunks = [];
|
|
30759
|
+
for await (const chunk of process.stdin) {
|
|
30760
|
+
chunks.push(chunk);
|
|
30761
|
+
}
|
|
30762
|
+
raw = Buffer.concat(chunks).toString("utf-8").trim();
|
|
30763
|
+
}
|
|
30764
|
+
if (!raw) {
|
|
30765
|
+
jsonError(
|
|
30766
|
+
"No input received on stdin. Pipe JSON: { file, bundle_dir?, tokens: [...] }"
|
|
30767
|
+
);
|
|
30768
|
+
}
|
|
30769
|
+
let data;
|
|
30770
|
+
try {
|
|
30771
|
+
data = JSON.parse(raw);
|
|
30772
|
+
} catch {
|
|
30773
|
+
jsonError("Invalid JSON input");
|
|
30774
|
+
}
|
|
30775
|
+
if (!data?.file || !Array.isArray(data?.tokens)) {
|
|
30776
|
+
jsonError("JSON must include 'file' and a 'tokens' array");
|
|
30777
|
+
}
|
|
30778
|
+
await withDb2(async (db) => {
|
|
30779
|
+
try {
|
|
30780
|
+
const userId = await resolveUser(opts, db, { json: true });
|
|
30781
|
+
const result = await importOkfTokens(db, {
|
|
30782
|
+
user: userId,
|
|
30783
|
+
bundleDir: data.bundle_dir,
|
|
30784
|
+
file: data.file,
|
|
30785
|
+
tokens: data.tokens
|
|
30786
|
+
});
|
|
30787
|
+
jsonOut(result);
|
|
30788
|
+
} catch (err) {
|
|
30789
|
+
jsonError(err.message);
|
|
30790
|
+
}
|
|
30791
|
+
});
|
|
30792
|
+
} catch (err) {
|
|
30793
|
+
jsonError(err.message);
|
|
30794
|
+
}
|
|
30795
|
+
});
|
|
30443
30796
|
bridgeCommand.command("relevant-tokens").description("Find tokens relevant to a given context").option("--user <id>", "User ID (default: whoami)").action(async (opts) => {
|
|
30444
30797
|
try {
|
|
30445
30798
|
let raw;
|
|
@@ -30536,10 +30889,10 @@ bridgeCommand.command("discover-skills").description(
|
|
|
30536
30889
|
"20"
|
|
30537
30890
|
).action(async (opts) => {
|
|
30538
30891
|
try {
|
|
30539
|
-
const monitorDir =
|
|
30892
|
+
const monitorDir = join23(homedir14(), ".zam", "monitor");
|
|
30540
30893
|
let files;
|
|
30541
30894
|
try {
|
|
30542
|
-
files =
|
|
30895
|
+
files = readdirSync3(monitorDir).filter((f) => f.endsWith(".jsonl"));
|
|
30543
30896
|
} catch {
|
|
30544
30897
|
jsonOut({ proposals: [], message: "No monitor logs found." });
|
|
30545
30898
|
return;
|
|
@@ -30549,7 +30902,7 @@ bridgeCommand.command("discover-skills").description(
|
|
|
30549
30902
|
return;
|
|
30550
30903
|
}
|
|
30551
30904
|
const limit = Number(opts.limit);
|
|
30552
|
-
const sorted = files.map((f) => ({ name: f, path:
|
|
30905
|
+
const sorted = files.map((f) => ({ name: f, path: join23(monitorDir, f) })).sort((a, b) => b.name.localeCompare(a.name)).slice(0, limit);
|
|
30553
30906
|
const sessionCommands = /* @__PURE__ */ new Map();
|
|
30554
30907
|
for (const file of sorted) {
|
|
30555
30908
|
const sessionId = file.name.replace(".jsonl", "");
|
|
@@ -31051,7 +31404,7 @@ bridgeCommand.command("capture-ui").description("Capture a screenshot for agent-
|
|
|
31051
31404
|
return;
|
|
31052
31405
|
}
|
|
31053
31406
|
}
|
|
31054
|
-
const outputPath = opts.image ?? opts.output ??
|
|
31407
|
+
const outputPath = opts.image ?? opts.output ?? join23(tmpdir3(), `zam-capture-${randomBytes2(4).toString("hex")}.png`);
|
|
31055
31408
|
const captureResult = isProvided ? { method: "provided", target: null } : captureScreenshot(outputPath, opts.hwnd, opts.processName);
|
|
31056
31409
|
if (!isProvided) {
|
|
31057
31410
|
const post = decidePostCapture(policy, {
|
|
@@ -31079,7 +31432,7 @@ bridgeCommand.command("capture-ui").description("Capture a screenshot for agent-
|
|
|
31079
31432
|
return;
|
|
31080
31433
|
}
|
|
31081
31434
|
}
|
|
31082
|
-
const imageBytes =
|
|
31435
|
+
const imageBytes = readFileSync18(outputPath);
|
|
31083
31436
|
const base64 = imageBytes.toString("base64");
|
|
31084
31437
|
jsonOut({
|
|
31085
31438
|
sessionId: opts.session ?? null,
|
|
@@ -31106,9 +31459,9 @@ bridgeCommand.command("start-recording").description("Start screen recording in
|
|
|
31106
31459
|
return;
|
|
31107
31460
|
}
|
|
31108
31461
|
const sessionId = opts.session;
|
|
31109
|
-
const statePath =
|
|
31462
|
+
const statePath = join23(tmpdir3(), `zam-recording-${sessionId}.json`);
|
|
31110
31463
|
const defaultExt = platform === "win32" ? ".mkv" : ".mov";
|
|
31111
|
-
const outputPath = opts.output ??
|
|
31464
|
+
const outputPath = opts.output ?? join23(tmpdir3(), `zam-recording-${sessionId}${defaultExt}`);
|
|
31112
31465
|
const { existsSync: existsSync23, writeFileSync: writeFileSync14, openSync, closeSync } = await import("fs");
|
|
31113
31466
|
if (existsSync23(statePath)) {
|
|
31114
31467
|
jsonOut({
|
|
@@ -31118,7 +31471,7 @@ bridgeCommand.command("start-recording").description("Start screen recording in
|
|
|
31118
31471
|
});
|
|
31119
31472
|
return;
|
|
31120
31473
|
}
|
|
31121
|
-
const logPath =
|
|
31474
|
+
const logPath = join23(tmpdir3(), `zam-recording-${sessionId}.log`);
|
|
31122
31475
|
let logFd;
|
|
31123
31476
|
try {
|
|
31124
31477
|
logFd = openSync(logPath, "w");
|
|
@@ -31200,7 +31553,7 @@ bridgeCommand.command("stop-recording").description(
|
|
|
31200
31553
|
return;
|
|
31201
31554
|
}
|
|
31202
31555
|
const sessionId = opts.session;
|
|
31203
|
-
const statePath =
|
|
31556
|
+
const statePath = join23(tmpdir3(), `zam-recording-${sessionId}.json`);
|
|
31204
31557
|
const { existsSync: existsSync23, readFileSync: readFileSync20, rmSync: rmSync4 } = await import("fs");
|
|
31205
31558
|
if (!existsSync23(statePath)) {
|
|
31206
31559
|
jsonOut({
|
|
@@ -31669,7 +32022,7 @@ bridgeCommand.command("cloud-model-hint").description("Suggest a cloud model for
|
|
|
31669
32022
|
});
|
|
31670
32023
|
bridgeCommand.command("local-llm-hints").description("Detect installed local LLM servers and suggest defaults (JSON)").action(() => {
|
|
31671
32024
|
const profile = getSystemProfile();
|
|
31672
|
-
const flmInstalled = hasCommand("flm") ||
|
|
32025
|
+
const flmInstalled = hasCommand("flm") || existsSync21("C:\\Program Files\\flm\\flm.exe");
|
|
31673
32026
|
const ollamaInstalled = isOllamaInstalled();
|
|
31674
32027
|
const runners = [
|
|
31675
32028
|
{ id: "flm", label: "FastFlowLM", installed: flmInstalled },
|
|
@@ -32043,7 +32396,12 @@ bridgeCommand.command("list-tokens").description(
|
|
|
32043
32396
|
).option("--domain <domain>", "Filter by exact domain").option(
|
|
32044
32397
|
"--domain-prefix <prefix>",
|
|
32045
32398
|
"Filter by domain prefix (e.g. company-team) \u2014 uses / separator for hierarchy"
|
|
32046
|
-
).option("--knowledge-context <context>", "Filter by knowledge context").
|
|
32399
|
+
).option("--knowledge-context <context>", "Filter by knowledge context").option(
|
|
32400
|
+
"--source-link-base <base>",
|
|
32401
|
+
"Filter by source-link base (exact or '<base>#<anchor>', as written by OKF imports); repeatable",
|
|
32402
|
+
(value, previous) => [...previous, value],
|
|
32403
|
+
[]
|
|
32404
|
+
).action(async (opts) => {
|
|
32047
32405
|
await withDb2(async (db) => {
|
|
32048
32406
|
const userId = opts.user ? await resolveUser(opts, db, { json: true }) : void 0;
|
|
32049
32407
|
const listOpts = {};
|
|
@@ -32051,6 +32409,8 @@ bridgeCommand.command("list-tokens").description(
|
|
|
32051
32409
|
if (opts.domainPrefix) listOpts.domainPrefix = opts.domainPrefix;
|
|
32052
32410
|
if (opts.knowledgeContext)
|
|
32053
32411
|
listOpts.knowledgeContext = opts.knowledgeContext;
|
|
32412
|
+
if (opts.sourceLinkBase.length)
|
|
32413
|
+
listOpts.sourceLinkBases = opts.sourceLinkBase;
|
|
32054
32414
|
const tokens = await listTokens(
|
|
32055
32415
|
db,
|
|
32056
32416
|
Object.keys(listOpts).length ? listOpts : void 0
|
|
@@ -33804,13 +34164,13 @@ async function writeCompanionContext(db, request, options = {}) {
|
|
|
33804
34164
|
// src/cli/ui-intent.ts
|
|
33805
34165
|
import { mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
33806
34166
|
import { homedir as homedir15 } from "os";
|
|
33807
|
-
import { dirname as
|
|
34167
|
+
import { dirname as dirname11, join as join24 } from "path";
|
|
33808
34168
|
import { ulid as ulid11 } from "ulid";
|
|
33809
34169
|
function getUiIntentPath(home = homedir15()) {
|
|
33810
|
-
return
|
|
34170
|
+
return join24(home, ".zam", "ui-intent.json");
|
|
33811
34171
|
}
|
|
33812
34172
|
function getUiHostRegistrationPath(home = homedir15()) {
|
|
33813
|
-
return
|
|
34173
|
+
return join24(home, ".zam", "vscode-host.json");
|
|
33814
34174
|
}
|
|
33815
34175
|
function compactStringInput(input) {
|
|
33816
34176
|
return Object.fromEntries(
|
|
@@ -33829,8 +34189,8 @@ async function writeUiIntent(app, input = {}, opts = {}) {
|
|
|
33829
34189
|
input: compactStringInput(input),
|
|
33830
34190
|
createdAt: (opts.now ?? (() => /* @__PURE__ */ new Date()))().toISOString()
|
|
33831
34191
|
};
|
|
33832
|
-
const tempPath =
|
|
33833
|
-
await mkdir(
|
|
34192
|
+
const tempPath = join24(dirname11(path), `.ui-intent-${process.pid}-${id}.tmp`);
|
|
34193
|
+
await mkdir(dirname11(path), { recursive: true });
|
|
33834
34194
|
await writeFile(tempPath, `${JSON.stringify(intent, null, 2)}
|
|
33835
34195
|
`, "utf8");
|
|
33836
34196
|
await rename(tempPath, path);
|
|
@@ -33863,10 +34223,10 @@ async function publishUiIntent(app, input = {}, opts = {}) {
|
|
|
33863
34223
|
}
|
|
33864
34224
|
|
|
33865
34225
|
// src/cli/commands/mcp.ts
|
|
33866
|
-
var __dirname =
|
|
33867
|
-
var pkgPath =
|
|
34226
|
+
var __dirname = dirname13(fileURLToPath7(import.meta.url));
|
|
34227
|
+
var pkgPath = join26(__dirname, "..", "..", "package.json");
|
|
33868
34228
|
if (!existsSync22(pkgPath)) {
|
|
33869
|
-
pkgPath =
|
|
34229
|
+
pkgPath = join26(__dirname, "..", "..", "..", "package.json");
|
|
33870
34230
|
}
|
|
33871
34231
|
var pkg = JSON.parse(readFileSync19(pkgPath, "utf-8"));
|
|
33872
34232
|
var STUDIO_RESOURCE_URI = "ui://zam/studio";
|
|
@@ -33896,9 +34256,9 @@ var STUDIO_BRIDGE_ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
33896
34256
|
function loadPanelHtml(fileName, placeholderTitle) {
|
|
33897
34257
|
const candidates = [
|
|
33898
34258
|
// dist/cli/commands/mcp.js → dist/ui/
|
|
33899
|
-
|
|
34259
|
+
join26(__dirname, "..", "..", "ui", fileName),
|
|
33900
34260
|
// src/cli/commands/mcp.ts via tsx → <repo>/dist/ui/
|
|
33901
|
-
|
|
34261
|
+
join26(__dirname, "..", "..", "..", "dist", "ui", fileName)
|
|
33902
34262
|
];
|
|
33903
34263
|
for (const candidate of candidates) {
|
|
33904
34264
|
if (existsSync22(candidate)) {
|
|
@@ -34467,7 +34827,7 @@ function createMcpServer(db) {
|
|
|
34467
34827
|
"zam_show_graph",
|
|
34468
34828
|
{
|
|
34469
34829
|
title: "Open ZAM knowledge graph",
|
|
34470
|
-
description: "Open the ZAM 2D knowledge-graph card, centered on a token's direct prerequisites and dependents. Pass `focus` (a token slug) when the conversation already names one;
|
|
34830
|
+
description: "Open the ZAM 2D knowledge-graph card, centered on a token's direct prerequisites and dependents. Pass `focus` (a token slug) when the conversation already names one; without it the card offers scope selectors and defaults to tokens anchored in the current repo's OKF knowledge base.",
|
|
34471
34831
|
inputSchema: {
|
|
34472
34832
|
focus: z.string().optional().describe("Token slug to center the graph on"),
|
|
34473
34833
|
user: z.string().optional().describe("User ID")
|
|
@@ -34489,6 +34849,16 @@ function createMcpServer(db) {
|
|
|
34489
34849
|
{ clientSamplingCapable: getClientSamplingCapable() }
|
|
34490
34850
|
);
|
|
34491
34851
|
const userId = opening.context.user.currentId ?? null;
|
|
34852
|
+
let repoScope;
|
|
34853
|
+
try {
|
|
34854
|
+
const { collectSourceLinkBases: collectSourceLinkBases2, findRepoRoot: findRepoRoot2 } = await Promise.resolve().then(() => (init_io(), io_exports));
|
|
34855
|
+
const bundleDir = await resolveOkfBundleDir();
|
|
34856
|
+
const bases = collectSourceLinkBases2(bundleDir);
|
|
34857
|
+
if (bases.length > 0) {
|
|
34858
|
+
repoScope = { label: basename6(findRepoRoot2(bundleDir)), bases };
|
|
34859
|
+
}
|
|
34860
|
+
} catch {
|
|
34861
|
+
}
|
|
34492
34862
|
await publishUiIntent("graph", { user, focus });
|
|
34493
34863
|
return {
|
|
34494
34864
|
graph: "zam",
|
|
@@ -34496,6 +34866,7 @@ function createMcpServer(db) {
|
|
|
34496
34866
|
version: pkg.version,
|
|
34497
34867
|
user: userId,
|
|
34498
34868
|
companionContext: opening.context,
|
|
34869
|
+
...repoScope ? { repoScope } : {},
|
|
34499
34870
|
...opening.degraded ? { companionContextDegraded: true } : {}
|
|
34500
34871
|
};
|
|
34501
34872
|
})
|
|
@@ -34667,7 +35038,24 @@ function createMcpServer(db) {
|
|
|
34667
35038
|
}
|
|
34668
35039
|
)
|
|
34669
35040
|
);
|
|
34670
|
-
const okfBundleDirSchema = z.string().optional().describe(
|
|
35041
|
+
const okfBundleDirSchema = z.string().optional().describe(
|
|
35042
|
+
"Bundle directory (default: docs/okf under the client's workspace root, falling back to the server cwd)"
|
|
35043
|
+
);
|
|
35044
|
+
async function resolveOkfBundleDir(explicit) {
|
|
35045
|
+
const { DEFAULT_BUNDLE_DIR: DEFAULT_BUNDLE_DIR2, resolveBundleDirFromRoots: resolveBundleDirFromRoots2 } = await Promise.resolve().then(() => (init_io(), io_exports));
|
|
35046
|
+
if (explicit) return explicit;
|
|
35047
|
+
try {
|
|
35048
|
+
if (server.server.getClientCapabilities()?.roots) {
|
|
35049
|
+
const { roots } = await server.server.listRoots();
|
|
35050
|
+
return resolveBundleDirFromRoots2(
|
|
35051
|
+
(roots ?? []).map((root) => root.uri),
|
|
35052
|
+
DEFAULT_BUNDLE_DIR2
|
|
35053
|
+
);
|
|
35054
|
+
}
|
|
35055
|
+
} catch {
|
|
35056
|
+
}
|
|
35057
|
+
return DEFAULT_BUNDLE_DIR2;
|
|
35058
|
+
}
|
|
34671
35059
|
server.registerTool(
|
|
34672
35060
|
"zam_okf_catalog",
|
|
34673
35061
|
{
|
|
@@ -34685,13 +35073,13 @@ function createMcpServer(db) {
|
|
|
34685
35073
|
},
|
|
34686
35074
|
wrapHandler(
|
|
34687
35075
|
async (params) => {
|
|
34688
|
-
const {
|
|
34689
|
-
const bundle = loadBundle2(params.bundle_dir
|
|
35076
|
+
const { loadBundle: loadBundle2 } = await Promise.resolve().then(() => (init_io(), io_exports));
|
|
35077
|
+
const bundle = loadBundle2(await resolveOkfBundleDir(params.bundle_dir));
|
|
34690
35078
|
let log;
|
|
34691
35079
|
if (params.include_log) {
|
|
34692
35080
|
const { readFileSync: readFileSync20 } = await import("fs");
|
|
34693
35081
|
try {
|
|
34694
|
-
log = readFileSync20(
|
|
35082
|
+
log = readFileSync20(join26(bundle.dir, "log.md"), "utf8");
|
|
34695
35083
|
} catch {
|
|
34696
35084
|
log = "";
|
|
34697
35085
|
}
|
|
@@ -34720,10 +35108,10 @@ function createMcpServer(db) {
|
|
|
34720
35108
|
},
|
|
34721
35109
|
wrapHandler(async (params) => {
|
|
34722
35110
|
const { readFileSync: readFileSync20 } = await import("fs");
|
|
34723
|
-
const {
|
|
35111
|
+
const { resolveArticlePath: resolveArticlePath2 } = await Promise.resolve().then(() => (init_io(), io_exports));
|
|
34724
35112
|
const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_bundle(), bundle_exports));
|
|
34725
35113
|
const path = resolveArticlePath2(
|
|
34726
|
-
params.bundle_dir
|
|
35114
|
+
await resolveOkfBundleDir(params.bundle_dir),
|
|
34727
35115
|
params.file
|
|
34728
35116
|
);
|
|
34729
35117
|
const markdown = readFileSync20(path, "utf8");
|
|
@@ -34750,9 +35138,9 @@ function createMcpServer(db) {
|
|
|
34750
35138
|
},
|
|
34751
35139
|
wrapHandler(
|
|
34752
35140
|
async (params) => {
|
|
34753
|
-
const {
|
|
35141
|
+
const { upsertArticle: upsertArticle2 } = await Promise.resolve().then(() => (init_io(), io_exports));
|
|
34754
35142
|
const result = upsertArticle2(
|
|
34755
|
-
params.bundle_dir
|
|
35143
|
+
await resolveOkfBundleDir(params.bundle_dir),
|
|
34756
35144
|
params.file,
|
|
34757
35145
|
params.markdown
|
|
34758
35146
|
);
|
|
@@ -34763,6 +35151,96 @@ function createMcpServer(db) {
|
|
|
34763
35151
|
}
|
|
34764
35152
|
)
|
|
34765
35153
|
);
|
|
35154
|
+
server.registerTool(
|
|
35155
|
+
"zam_okf_import",
|
|
35156
|
+
{
|
|
35157
|
+
description: "Record YOUR finished decomposition of one OKF article as learning tokens + cards (ADR 2026-07-18). Decomposition is your job, not this tool's \u2014 before calling: (1) read the FULL article (zam_okf_read); (2) extract the concepts a practitioner must produce FROM MEMORY \u2014 recall-speed knowledge only; facts one would look up stay in the article; (3) one atomic concept per token, with a JUDGED bloom level and a JUDGED domain per token (reuse existing domains where they fit); (4) arrange a prerequisite DAG from foundational to dependent (in-import slugs and existing tokens both allowed); (5) check for existing tokens first (zam_find_tokens) and link them as prerequisites instead of duplicating. Multiple tokens are the expected outcome for real articles. On RE-import classify each token via mode: 'new' adds, 'update' refreshes content and KEEPS learning state, 'replace' means the concept changed \u2014 content refreshed and learning state RESET to the beginning. Previously imported tokens you do not confirm are moved to maintenance (kept, unscheduled) \u2014 never deleted. The write is atomic; every token gets a card for the importing user.",
|
|
35158
|
+
inputSchema: {
|
|
35159
|
+
user: z.string().optional().describe("User ID to create cards for"),
|
|
35160
|
+
bundle_dir: okfBundleDirSchema,
|
|
35161
|
+
file: z.string().describe("Article file name inside the bundle (kebab-case .md)"),
|
|
35162
|
+
tokens: z.array(
|
|
35163
|
+
z.object({
|
|
35164
|
+
slug: z.string().describe("Unique kebab-case token slug"),
|
|
35165
|
+
title: z.string().optional().describe("Display title"),
|
|
35166
|
+
concept: z.string().describe("The memorable concept, stated precisely"),
|
|
35167
|
+
bloomLevel: z.number().int().min(1).max(5).optional().describe("Judged Bloom level (1-5)"),
|
|
35168
|
+
domain: z.string().optional().describe("Judged domain (reuse existing domains)"),
|
|
35169
|
+
anchor: z.string().optional().describe(
|
|
35170
|
+
"Heading anchor in the article; appended to the source link"
|
|
35171
|
+
),
|
|
35172
|
+
prerequisites: z.array(z.string()).optional().describe(
|
|
35173
|
+
"Slugs this token requires \u2014 in-import or existing tokens"
|
|
35174
|
+
),
|
|
35175
|
+
knowledgeContexts: z.array(z.string()).optional().describe("Knowledge contexts to assign"),
|
|
35176
|
+
question: z.string().nullable().optional().describe("Optional pre-defined review question"),
|
|
35177
|
+
mode: z.enum(["new", "update", "replace"]).optional().describe(
|
|
35178
|
+
"Re-import classification: new (default) | update (keep learning state) | replace (concept changed, reset learning state)"
|
|
35179
|
+
)
|
|
35180
|
+
})
|
|
35181
|
+
).min(1).describe("Your finished decomposition")
|
|
35182
|
+
},
|
|
35183
|
+
annotations: {
|
|
35184
|
+
...commonAnnotations
|
|
35185
|
+
}
|
|
35186
|
+
},
|
|
35187
|
+
wrapHandler(
|
|
35188
|
+
async (params) => {
|
|
35189
|
+
return await importOkfTokens(db, {
|
|
35190
|
+
user: await getUserId(params.user),
|
|
35191
|
+
bundleDir: await resolveOkfBundleDir(params.bundle_dir),
|
|
35192
|
+
file: params.file,
|
|
35193
|
+
tokens: params.tokens
|
|
35194
|
+
});
|
|
35195
|
+
}
|
|
35196
|
+
)
|
|
35197
|
+
);
|
|
35198
|
+
registerAppTool(
|
|
35199
|
+
server,
|
|
35200
|
+
"zam_okf_focus",
|
|
35201
|
+
{
|
|
35202
|
+
description: "App-only: record which OKF article the visualizer panel currently shows, so chat agents can resolve 'the currently focused article'. Not intended for direct model use.",
|
|
35203
|
+
inputSchema: {
|
|
35204
|
+
file: z.string().describe("Article file name inside the bundle (kebab-case .md)"),
|
|
35205
|
+
bundle_dir: z.string().optional().describe("Absolute bundle directory the panel is browsing")
|
|
35206
|
+
},
|
|
35207
|
+
annotations: {
|
|
35208
|
+
...commonAnnotations,
|
|
35209
|
+
destructiveHint: false,
|
|
35210
|
+
idempotentHint: true
|
|
35211
|
+
},
|
|
35212
|
+
_meta: {
|
|
35213
|
+
ui: { visibility: ["app"] }
|
|
35214
|
+
}
|
|
35215
|
+
},
|
|
35216
|
+
wrapHandler(async (params) => {
|
|
35217
|
+
const { writeOkfFocus: writeOkfFocus2 } = await Promise.resolve().then(() => (init_okf_focus(), okf_focus_exports));
|
|
35218
|
+
const focus = await writeOkfFocus2(params.file, params.bundle_dir);
|
|
35219
|
+
return { recorded: { file: focus.file, updatedAt: focus.updatedAt } };
|
|
35220
|
+
})
|
|
35221
|
+
);
|
|
35222
|
+
server.registerTool(
|
|
35223
|
+
"zam_okf_focused",
|
|
35224
|
+
{
|
|
35225
|
+
description: "The OKF article currently focused in the user's visualizer panel \u2014 resolve requests like 'import this okf', 'import the currently focused article', or 'the open article'. Returns {focused: {file, bundle_dir?, updatedAt} | null}; check updatedAt and confirm the article by name with the user if it looks stale. To import, read the article (zam_okf_read) and follow the zam_okf_import contract.",
|
|
35226
|
+
inputSchema: {},
|
|
35227
|
+
annotations: {
|
|
35228
|
+
...commonAnnotations,
|
|
35229
|
+
readOnlyHint: true
|
|
35230
|
+
}
|
|
35231
|
+
},
|
|
35232
|
+
wrapHandler(async () => {
|
|
35233
|
+
const { readOkfFocus: readOkfFocus2 } = await Promise.resolve().then(() => (init_okf_focus(), okf_focus_exports));
|
|
35234
|
+
const focus = await readOkfFocus2();
|
|
35235
|
+
return {
|
|
35236
|
+
focused: focus ? {
|
|
35237
|
+
file: focus.file,
|
|
35238
|
+
...focus.bundleDir ? { bundle_dir: focus.bundleDir } : {},
|
|
35239
|
+
updatedAt: focus.updatedAt
|
|
35240
|
+
} : null
|
|
35241
|
+
};
|
|
35242
|
+
})
|
|
35243
|
+
);
|
|
34766
35244
|
server.registerTool(
|
|
34767
35245
|
"zam_okf_read_citation",
|
|
34768
35246
|
{
|
|
@@ -34781,8 +35259,8 @@ function createMcpServer(db) {
|
|
|
34781
35259
|
wrapHandler(async (params) => {
|
|
34782
35260
|
const { readFileSync: readFileSync20 } = await import("fs");
|
|
34783
35261
|
const { relative: relative2, sep: sep2 } = await import("path");
|
|
34784
|
-
const {
|
|
34785
|
-
const bundleDir = params.bundle_dir
|
|
35262
|
+
const { findRepoRoot: findRepoRoot2, resolveCitationPath: resolveCitationPath2 } = await Promise.resolve().then(() => (init_io(), io_exports));
|
|
35263
|
+
const bundleDir = await resolveOkfBundleDir(params.bundle_dir);
|
|
34786
35264
|
const path = resolveCitationPath2(bundleDir, params.target);
|
|
34787
35265
|
const content = readFileSync20(path, "utf8");
|
|
34788
35266
|
const root = findRepoRoot2(bundleDir);
|
|
@@ -34815,11 +35293,11 @@ function createMcpServer(db) {
|
|
|
34815
35293
|
getNativeClientInfo(),
|
|
34816
35294
|
{ clientSamplingCapable: getClientSamplingCapable() }
|
|
34817
35295
|
);
|
|
34818
|
-
await
|
|
34819
|
-
const { DEFAULT_BUNDLE_DIR: DEFAULT_BUNDLE_DIR2, loadBundle: loadBundle2 } = await Promise.resolve().then(() => (init_io(), io_exports));
|
|
35296
|
+
const { loadBundle: loadBundle2 } = await Promise.resolve().then(() => (init_io(), io_exports));
|
|
34820
35297
|
const { resolve: resolve8 } = await import("path");
|
|
34821
|
-
const requestedDir = bundle_dir
|
|
35298
|
+
const requestedDir = await resolveOkfBundleDir(bundle_dir);
|
|
34822
35299
|
let resolvedBundleDir = resolve8(requestedDir);
|
|
35300
|
+
await publishUiIntent("okf", { bundle_dir: resolvedBundleDir });
|
|
34823
35301
|
let catalog = [];
|
|
34824
35302
|
let problems = [];
|
|
34825
35303
|
let log = "";
|
|
@@ -34831,13 +35309,13 @@ function createMcpServer(db) {
|
|
|
34831
35309
|
problems = bundle.problems;
|
|
34832
35310
|
const { readFileSync: readFileSync20 } = await import("fs");
|
|
34833
35311
|
try {
|
|
34834
|
-
log = readFileSync20(
|
|
35312
|
+
log = readFileSync20(join26(bundle.dir, "log.md"), "utf8");
|
|
34835
35313
|
} catch {
|
|
34836
35314
|
log = "";
|
|
34837
35315
|
}
|
|
34838
35316
|
try {
|
|
34839
35317
|
const { parseFrontmatter: parseFrontmatter2 } = await Promise.resolve().then(() => (init_bundle(), bundle_exports));
|
|
34840
|
-
const indexRaw = readFileSync20(
|
|
35318
|
+
const indexRaw = readFileSync20(join26(bundle.dir, "index.md"), "utf8");
|
|
34841
35319
|
const { fields } = parseFrontmatter2(indexRaw);
|
|
34842
35320
|
okfVersion = typeof fields.okf_version === "string" ? fields.okf_version : null;
|
|
34843
35321
|
} catch {
|