zam-core 0.9.3 → 0.10.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 +520 -369
- package/dist/cli/app.js.map +1 -1
- package/dist/cli/commands/mcp.js +8376 -108
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +44 -7
- package/dist/index.js.map +1 -1
- package/dist/ui/graph-panel.html +385 -0
- package/dist/ui/recall-panel.html +403 -0
- package/dist/ui/settings-panel.html +422 -0
- package/dist/ui/studio-panel.html +1059 -0
- package/package.json +6 -2
package/dist/cli/app.js
CHANGED
|
@@ -585,7 +585,12 @@ CREATE TABLE IF NOT EXISTS tokens (
|
|
|
585
585
|
deprecated_at TEXT,
|
|
586
586
|
question TEXT,
|
|
587
587
|
provider TEXT,
|
|
588
|
-
topic_id TEXT
|
|
588
|
+
topic_id TEXT,
|
|
589
|
+
-- Who authored the current question ('manual' | 'llm' | 'template');
|
|
590
|
+
-- validated in code, not via CHECK. Column default is 'llm' so unlabeled
|
|
591
|
+
-- writes (pre-M013 rows, old snapshot restores) count as LLM-era content;
|
|
592
|
+
-- createToken() defaults to 'manual' for API callers instead.
|
|
593
|
+
question_source TEXT NOT NULL DEFAULT 'llm'
|
|
589
594
|
);
|
|
590
595
|
|
|
591
596
|
-- Prerequisite dependency graph: "to learn A, first know B"
|
|
@@ -1109,6 +1114,11 @@ async function runMigrations(db) {
|
|
|
1109
1114
|
await db.exec(`
|
|
1110
1115
|
CREATE INDEX IF NOT EXISTS idx_token_contexts_context ON token_contexts(context_id)
|
|
1111
1116
|
`);
|
|
1117
|
+
if (tokenCols.length > 0 && !tokenCols.some((c) => c.name === "question_source")) {
|
|
1118
|
+
await db.exec(
|
|
1119
|
+
`ALTER TABLE tokens ADD COLUMN question_source TEXT NOT NULL DEFAULT 'llm'`
|
|
1120
|
+
);
|
|
1121
|
+
}
|
|
1112
1122
|
}
|
|
1113
1123
|
var DEFAULT_DB_DIR, DEFAULT_DB_PATH, require2;
|
|
1114
1124
|
var init_connection = __esm({
|
|
@@ -1269,7 +1279,11 @@ var init_snapshot = __esm({
|
|
|
1269
1279
|
"review_logs",
|
|
1270
1280
|
"session_syntheses",
|
|
1271
1281
|
"user_config",
|
|
1272
|
-
"agent_skills"
|
|
1282
|
+
"agent_skills",
|
|
1283
|
+
"sources",
|
|
1284
|
+
"token_sources",
|
|
1285
|
+
"contexts",
|
|
1286
|
+
"token_contexts"
|
|
1273
1287
|
];
|
|
1274
1288
|
}
|
|
1275
1289
|
});
|
|
@@ -1754,6 +1768,13 @@ var init_knowledge_context = __esm({
|
|
|
1754
1768
|
|
|
1755
1769
|
// src/kernel/models/token.ts
|
|
1756
1770
|
import { ulid as ulid4 } from "ulid";
|
|
1771
|
+
function validateQuestionSource(value) {
|
|
1772
|
+
if (!VALID_QUESTION_SOURCES.includes(value)) {
|
|
1773
|
+
throw new Error(
|
|
1774
|
+
`Invalid question_source: ${value} (expected one of ${VALID_QUESTION_SOURCES.join(", ")})`
|
|
1775
|
+
);
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1757
1778
|
async function createToken(db, input8) {
|
|
1758
1779
|
const id = ulid4();
|
|
1759
1780
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1762,9 +1783,11 @@ async function createToken(db, input8) {
|
|
|
1762
1783
|
throw new Error(`bloom_level must be between 1 and 5, got ${bloom}`);
|
|
1763
1784
|
}
|
|
1764
1785
|
const title = input8.title ?? "";
|
|
1786
|
+
const questionSource = input8.question_source ?? "manual";
|
|
1787
|
+
validateQuestionSource(questionSource);
|
|
1765
1788
|
await db.prepare(`
|
|
1766
|
-
INSERT INTO tokens (id, slug, title, concept, domain, bloom_level, context, symbiosis_mode, source_link, question, provider, topic_id, created_at, updated_at)
|
|
1767
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1789
|
+
INSERT INTO tokens (id, slug, title, concept, domain, bloom_level, context, symbiosis_mode, source_link, question, question_source, provider, topic_id, created_at, updated_at)
|
|
1790
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1768
1791
|
`).run(
|
|
1769
1792
|
id,
|
|
1770
1793
|
input8.slug,
|
|
@@ -1776,6 +1799,7 @@ async function createToken(db, input8) {
|
|
|
1776
1799
|
input8.symbiosis_mode ?? null,
|
|
1777
1800
|
input8.source_link ?? null,
|
|
1778
1801
|
input8.question ?? null,
|
|
1802
|
+
questionSource,
|
|
1779
1803
|
input8.provider ?? null,
|
|
1780
1804
|
input8.topic_id ?? null,
|
|
1781
1805
|
now,
|
|
@@ -1864,6 +1888,12 @@ async function updateToken(db, slug, updates) {
|
|
|
1864
1888
|
fields.push("question = ?");
|
|
1865
1889
|
values.push(updates.question);
|
|
1866
1890
|
}
|
|
1891
|
+
const questionSource = updates.question_source ?? (updates.question !== void 0 ? "manual" : void 0);
|
|
1892
|
+
if (questionSource !== void 0) {
|
|
1893
|
+
validateQuestionSource(questionSource);
|
|
1894
|
+
fields.push("question_source = ?");
|
|
1895
|
+
values.push(questionSource);
|
|
1896
|
+
}
|
|
1867
1897
|
if (updates.provider !== void 0) {
|
|
1868
1898
|
fields.push("provider = ?");
|
|
1869
1899
|
values.push(updates.provider);
|
|
@@ -2192,6 +2222,9 @@ async function importCurriculumCards(db, userId, cards) {
|
|
|
2192
2222
|
symbiosis_mode: symbiosisMode,
|
|
2193
2223
|
source_link: card.source_link || null,
|
|
2194
2224
|
question: card.question || null,
|
|
2225
|
+
// Curriculum cards are LLM-extracted; their questions are LLM
|
|
2226
|
+
// inventions, not human-authored content.
|
|
2227
|
+
question_source: "llm",
|
|
2195
2228
|
provider: card.provider || null,
|
|
2196
2229
|
topic_id: card.topic_id || null
|
|
2197
2230
|
});
|
|
@@ -2515,10 +2548,12 @@ async function assertPrerequisiteDoesNotCreateCycle(db, tokenId, prerequisiteId)
|
|
|
2515
2548
|
throw new Error("Cannot add prerequisite: would create a cycle");
|
|
2516
2549
|
}
|
|
2517
2550
|
}
|
|
2551
|
+
var VALID_QUESTION_SOURCES;
|
|
2518
2552
|
var init_token = __esm({
|
|
2519
2553
|
"src/kernel/models/token.ts"() {
|
|
2520
2554
|
"use strict";
|
|
2521
2555
|
init_card();
|
|
2556
|
+
VALID_QUESTION_SOURCES = ["manual", "llm", "template"];
|
|
2522
2557
|
}
|
|
2523
2558
|
});
|
|
2524
2559
|
|
|
@@ -5142,7 +5177,8 @@ async function buildReviewQueue(db, options) {
|
|
|
5142
5177
|
c.state AS state,
|
|
5143
5178
|
c.due_at AS due_at,
|
|
5144
5179
|
t.source_link AS source_link,
|
|
5145
|
-
t.question AS question
|
|
5180
|
+
t.question AS question,
|
|
5181
|
+
t.question_source AS question_source
|
|
5146
5182
|
FROM cards c
|
|
5147
5183
|
JOIN tokens t ON t.id = c.token_id
|
|
5148
5184
|
WHERE c.user_id = ?
|
|
@@ -5172,7 +5208,8 @@ async function buildReviewQueue(db, options) {
|
|
|
5172
5208
|
c.state AS state,
|
|
5173
5209
|
c.due_at AS due_at,
|
|
5174
5210
|
t.source_link AS source_link,
|
|
5175
|
-
t.question AS question
|
|
5211
|
+
t.question AS question,
|
|
5212
|
+
t.question_source AS question_source
|
|
5176
5213
|
FROM cards c
|
|
5177
5214
|
JOIN tokens t ON t.id = c.token_id
|
|
5178
5215
|
WHERE c.user_id = ?
|
|
@@ -5241,7 +5278,8 @@ function rowToItem(row) {
|
|
|
5241
5278
|
state: row.state,
|
|
5242
5279
|
dueAt: row.due_at,
|
|
5243
5280
|
sourceLink: row.source_link,
|
|
5244
|
-
question: row.question
|
|
5281
|
+
question: row.question,
|
|
5282
|
+
questionSource: row.question_source
|
|
5245
5283
|
};
|
|
5246
5284
|
}
|
|
5247
5285
|
function intersperseNew(reviews, newCards, interval) {
|
|
@@ -6770,9 +6808,9 @@ var init_kernel = __esm({
|
|
|
6770
6808
|
});
|
|
6771
6809
|
|
|
6772
6810
|
// src/cli/app.ts
|
|
6773
|
-
import { readFileSync as
|
|
6774
|
-
import { dirname as
|
|
6775
|
-
import { fileURLToPath as
|
|
6811
|
+
import { readFileSync as readFileSync18 } from "fs";
|
|
6812
|
+
import { dirname as dirname12, join as join27 } from "path";
|
|
6813
|
+
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
6776
6814
|
import { Command as Command27 } from "commander";
|
|
6777
6815
|
|
|
6778
6816
|
// src/cli/commands/agent.ts
|
|
@@ -7118,12 +7156,10 @@ function connectHarnessMcp(harnessId, opts) {
|
|
|
7118
7156
|
let content = "";
|
|
7119
7157
|
let alreadyConfigured = false;
|
|
7120
7158
|
let hint = "";
|
|
7121
|
-
|
|
7122
|
-
targetPath = join12(opts.cwd, ".mcp.json");
|
|
7123
|
-
hint = "Claude Code will prompt you to approve the 'zam' MCP server on next launch.";
|
|
7159
|
+
const mergeMcpServersJson = (path) => {
|
|
7124
7160
|
let existing = {};
|
|
7125
|
-
if (exists(
|
|
7126
|
-
existing = parseMcpJsonConfig(
|
|
7161
|
+
if (exists(path)) {
|
|
7162
|
+
existing = parseMcpJsonConfig(path, read(path));
|
|
7127
7163
|
}
|
|
7128
7164
|
if (!existing.mcpServers) {
|
|
7129
7165
|
existing.mcpServers = {};
|
|
@@ -7132,7 +7168,29 @@ function connectHarnessMcp(harnessId, opts) {
|
|
|
7132
7168
|
command: opts.zamPath,
|
|
7133
7169
|
args: ["mcp"]
|
|
7134
7170
|
};
|
|
7135
|
-
|
|
7171
|
+
return JSON.stringify(existing, null, 2);
|
|
7172
|
+
};
|
|
7173
|
+
if (harnessId === "claude-code") {
|
|
7174
|
+
targetPath = join12(opts.cwd, ".mcp.json");
|
|
7175
|
+
hint = "Claude Code will prompt you to approve the 'zam' MCP server on next launch.";
|
|
7176
|
+
content = mergeMcpServersJson(targetPath);
|
|
7177
|
+
} else if (harnessId === "claude-desktop") {
|
|
7178
|
+
const platform = opts.platform ?? process.platform;
|
|
7179
|
+
targetPath = platform === "win32" ? join12(
|
|
7180
|
+
opts.home,
|
|
7181
|
+
"AppData",
|
|
7182
|
+
"Roaming",
|
|
7183
|
+
"Claude",
|
|
7184
|
+
"claude_desktop_config.json"
|
|
7185
|
+
) : platform === "darwin" ? join12(
|
|
7186
|
+
opts.home,
|
|
7187
|
+
"Library",
|
|
7188
|
+
"Application Support",
|
|
7189
|
+
"Claude",
|
|
7190
|
+
"claude_desktop_config.json"
|
|
7191
|
+
) : join12(opts.home, ".config", "Claude", "claude_desktop_config.json");
|
|
7192
|
+
hint = "Restart Claude Desktop to load the 'zam' MCP server; MCP Apps panels render inline in the chat.";
|
|
7193
|
+
content = mergeMcpServersJson(targetPath);
|
|
7136
7194
|
} else if (harnessId === "antigravity") {
|
|
7137
7195
|
targetPath = join12(opts.home, ".gemini", "config", "mcp_config.json");
|
|
7138
7196
|
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.";
|
|
@@ -7265,6 +7323,7 @@ var C = {
|
|
|
7265
7323
|
var SUPPORTED_AGENTS = ["opencode"];
|
|
7266
7324
|
var CONNECT_HARNESSES = [
|
|
7267
7325
|
"claude-code",
|
|
7326
|
+
"claude-desktop",
|
|
7268
7327
|
"antigravity",
|
|
7269
7328
|
"codex",
|
|
7270
7329
|
"opencode",
|
|
@@ -7383,7 +7442,7 @@ var openCmd = new Command("open").description("Open an agent harness in the work
|
|
|
7383
7442
|
});
|
|
7384
7443
|
var connectCmd = new Command("connect").description("Configure the ZAM MCP server for an agent harness").argument(
|
|
7385
7444
|
"<harness>",
|
|
7386
|
-
"Harness to connect: claude-code | antigravity | codex | opencode | goose | copilot"
|
|
7445
|
+
"Harness to connect: claude-code | claude-desktop | antigravity | codex | opencode | goose | copilot"
|
|
7387
7446
|
).option(
|
|
7388
7447
|
"--print",
|
|
7389
7448
|
"Print configuration changes instead of writing them to disk"
|
|
@@ -7446,9 +7505,9 @@ var agentCommand = new Command("agent").description("Provision and inspect the a
|
|
|
7446
7505
|
init_kernel();
|
|
7447
7506
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
7448
7507
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
7449
|
-
import { existsSync as existsSync17, readdirSync as readdirSync2, readFileSync as
|
|
7508
|
+
import { existsSync as existsSync17, readdirSync as readdirSync2, readFileSync as readFileSync14, rmSync as rmSync3 } from "fs";
|
|
7450
7509
|
import { homedir as homedir11, tmpdir as tmpdir3 } from "os";
|
|
7451
|
-
import { join as
|
|
7510
|
+
import { join as join20, resolve as resolve4 } from "path";
|
|
7452
7511
|
import { Command as Command2 } from "commander";
|
|
7453
7512
|
import { ulid as ulid9 } from "ulid";
|
|
7454
7513
|
|
|
@@ -7720,6 +7779,9 @@ async function generateQuestionViaLLM(db, input8) {
|
|
|
7720
7779
|
const bloom = input8.bloomLevel >= 1 && input8.bloomLevel <= 5 ? input8.bloomLevel : 1;
|
|
7721
7780
|
const verb = BLOOM_VERBS2[bloom];
|
|
7722
7781
|
const langName = LANGUAGE_NAMES[cfg.locale] || "English";
|
|
7782
|
+
const existingQuestion = input8.existingQuestion?.trim();
|
|
7783
|
+
const variationGuideline = existingQuestion ? `
|
|
7784
|
+
5. A canonical question for this token already exists. Generate a fresh VARIATION of it: test the same knowledge, but with different wording or from a different angle, so the learner cannot memorize the exact phrasing. Never repeat the canonical question verbatim.` : "";
|
|
7723
7785
|
const systemPrompt = `You are ZAM, a highly precise agentic skills trainer.
|
|
7724
7786
|
Your task is to generate a single, clear, conceptual active-recall question (flashcard front) in ${langName} for a knowledge token.
|
|
7725
7787
|
|
|
@@ -7727,12 +7789,13 @@ Guidelines:
|
|
|
7727
7789
|
1. The question MUST match the Bloom level: ${verb} (Level ${bloom}).
|
|
7728
7790
|
2. CRITICAL: The question MUST NOT contain or reveal the concept text itself! The concept is the answer (flashcard back) that the learner needs to recall.
|
|
7729
7791
|
3. Keep the question concise, highly specific, and clear. Avoid generic prompts like "What is the concept of..." if possible, and ask about the core mechanism, function, or purpose of the slug/concept without giving the answer away.
|
|
7730
|
-
4. Output ONLY the raw question text in ${langName}. Do not include any preamble, headers, markdown fences, or conversational filler
|
|
7792
|
+
4. Output ONLY the raw question text in ${langName}. Do not include any preamble, headers, markdown fences, or conversational filler.${variationGuideline}`;
|
|
7731
7793
|
const userPrompt = `Domain: ${input8.domain}
|
|
7732
7794
|
Slug: ${input8.slug}
|
|
7733
7795
|
Concept to Recall (DO NOT REVEAL IN QUESTION): ${input8.concept}
|
|
7734
7796
|
Context: ${input8.context || "(none)"}
|
|
7735
|
-
${
|
|
7797
|
+
${existingQuestion ? `Canonical Question (vary, do not repeat verbatim): ${existingQuestion}
|
|
7798
|
+
` : ""}${input8.sourceLinkContent ? `Source Reference:
|
|
7736
7799
|
${input8.sourceLinkContent}` : ""}
|
|
7737
7800
|
|
|
7738
7801
|
Active-Recall Question:`;
|
|
@@ -8831,7 +8894,8 @@ async function fetchWithInteractiveTimeout(url, options = {}) {
|
|
|
8831
8894
|
}
|
|
8832
8895
|
async function ensureHighQualityQuestion(db, token) {
|
|
8833
8896
|
const { enabled } = await getLlmConfig(db);
|
|
8834
|
-
|
|
8897
|
+
const variationEnabled = await getSetting(db, "llm.dynamic_questions") !== "false";
|
|
8898
|
+
if (enabled && variationEnabled) {
|
|
8835
8899
|
try {
|
|
8836
8900
|
let sourceLinkContent = null;
|
|
8837
8901
|
if (token.sourceLink) {
|
|
@@ -8847,10 +8911,10 @@ async function ensureHighQualityQuestion(db, token) {
|
|
|
8847
8911
|
concept: token.concept,
|
|
8848
8912
|
domain: token.domain,
|
|
8849
8913
|
bloomLevel: token.bloomLevel,
|
|
8850
|
-
sourceLinkContent
|
|
8914
|
+
sourceLinkContent,
|
|
8915
|
+
existingQuestion: token.question
|
|
8851
8916
|
});
|
|
8852
8917
|
if (generated.text.trim().length > 0) {
|
|
8853
|
-
await updateToken(db, token.slug, { question: generated.text });
|
|
8854
8918
|
return {
|
|
8855
8919
|
question: generated.text,
|
|
8856
8920
|
source: "llm",
|
|
@@ -9058,7 +9122,7 @@ async function readWebLink(url) {
|
|
|
9058
9122
|
redirect: "manual",
|
|
9059
9123
|
signal: controller.signal,
|
|
9060
9124
|
headers: {
|
|
9061
|
-
"User-Agent": "ZAM-Content-Studio/0.
|
|
9125
|
+
"User-Agent": "ZAM-Content-Studio/0.10.0"
|
|
9062
9126
|
}
|
|
9063
9127
|
});
|
|
9064
9128
|
if (res.status >= 300 && res.status < 400) {
|
|
@@ -9123,6 +9187,8 @@ async function readImageOCR(db, imagePath) {
|
|
|
9123
9187
|
|
|
9124
9188
|
// src/cli/bridge-handlers.ts
|
|
9125
9189
|
init_kernel();
|
|
9190
|
+
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync8 } from "fs";
|
|
9191
|
+
import { join as join16 } from "path";
|
|
9126
9192
|
|
|
9127
9193
|
// src/cli/knowledge-contexts.ts
|
|
9128
9194
|
init_kernel();
|
|
@@ -9368,6 +9434,164 @@ async function resolveSuggestMinSimilarity(db) {
|
|
|
9368
9434
|
return Number.isFinite(parsed) && parsed > 0 && parsed <= 1 ? parsed : 0.45;
|
|
9369
9435
|
}
|
|
9370
9436
|
|
|
9437
|
+
// src/cli/update/latest-version.ts
|
|
9438
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
9439
|
+
import { dirname as dirname6, join as join14 } from "path";
|
|
9440
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9441
|
+
var GITHUB_REPO = "zam-os/zam";
|
|
9442
|
+
function currentVersion() {
|
|
9443
|
+
const here = dirname6(fileURLToPath2(import.meta.url));
|
|
9444
|
+
for (const up of ["..", "../..", "../../.."]) {
|
|
9445
|
+
try {
|
|
9446
|
+
const pkg2 = JSON.parse(
|
|
9447
|
+
readFileSync11(join14(here, up, "package.json"), "utf-8")
|
|
9448
|
+
);
|
|
9449
|
+
if (pkg2.version) return pkg2.version;
|
|
9450
|
+
} catch {
|
|
9451
|
+
}
|
|
9452
|
+
}
|
|
9453
|
+
return "0.0.0";
|
|
9454
|
+
}
|
|
9455
|
+
async function fetchLatestVersion(repo) {
|
|
9456
|
+
const res = await fetch(
|
|
9457
|
+
`https://api.github.com/repos/${repo}/releases/latest`,
|
|
9458
|
+
{
|
|
9459
|
+
headers: {
|
|
9460
|
+
Accept: "application/vnd.github+json",
|
|
9461
|
+
"User-Agent": "zam-cli"
|
|
9462
|
+
}
|
|
9463
|
+
}
|
|
9464
|
+
);
|
|
9465
|
+
if (!res.ok) {
|
|
9466
|
+
throw new Error(
|
|
9467
|
+
`Could not reach the release server (HTTP ${res.status}). Pass --latest <version> to check offline.`
|
|
9468
|
+
);
|
|
9469
|
+
}
|
|
9470
|
+
const data = await res.json();
|
|
9471
|
+
if (!data.tag_name) throw new Error("No published release found yet.");
|
|
9472
|
+
return data.tag_name;
|
|
9473
|
+
}
|
|
9474
|
+
|
|
9475
|
+
// src/cli/workspaces/active.ts
|
|
9476
|
+
init_kernel();
|
|
9477
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync9 } from "fs";
|
|
9478
|
+
import { homedir as homedir10 } from "os";
|
|
9479
|
+
import { basename as basename3, join as join15, resolve as resolve3 } from "path";
|
|
9480
|
+
var LEGACY_WORKSPACE_DIR_KEY = "personal.workspace_dir";
|
|
9481
|
+
var DEFAULT_WORKSPACE_ID = "personal";
|
|
9482
|
+
function defaultWorkspaceDir() {
|
|
9483
|
+
return join15(homedir10(), "Documents", "zam");
|
|
9484
|
+
}
|
|
9485
|
+
function normalizeWorkspacePath(path) {
|
|
9486
|
+
const resolved = resolve3(path);
|
|
9487
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
9488
|
+
}
|
|
9489
|
+
function sameWorkspacePath(left, right) {
|
|
9490
|
+
return normalizeWorkspacePath(left) === normalizeWorkspacePath(right);
|
|
9491
|
+
}
|
|
9492
|
+
function labelFromPath(path) {
|
|
9493
|
+
return basename3(path) || "ZAM";
|
|
9494
|
+
}
|
|
9495
|
+
function workspaceIdFromPath(dir) {
|
|
9496
|
+
const base = basename3(dir).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
9497
|
+
const prefix = base || "workspace";
|
|
9498
|
+
const existing = new Set(getConfiguredWorkspaces().map((item) => item.id));
|
|
9499
|
+
if (!existing.has(prefix)) return prefix;
|
|
9500
|
+
let index = 2;
|
|
9501
|
+
while (existing.has(`${prefix}-${index}`)) index++;
|
|
9502
|
+
return `${prefix}-${index}`;
|
|
9503
|
+
}
|
|
9504
|
+
function findWorkspaceByPath(dir) {
|
|
9505
|
+
return getConfiguredWorkspaces().find(
|
|
9506
|
+
(workspace) => sameWorkspacePath(workspace.path, dir)
|
|
9507
|
+
);
|
|
9508
|
+
}
|
|
9509
|
+
async function clearLegacyWorkspaceDir(db) {
|
|
9510
|
+
await deleteSetting(db, LEGACY_WORKSPACE_DIR_KEY);
|
|
9511
|
+
}
|
|
9512
|
+
async function migrateLegacyWorkspaceDir(db) {
|
|
9513
|
+
const legacyDir = await getSetting(db, LEGACY_WORKSPACE_DIR_KEY);
|
|
9514
|
+
if (!legacyDir) return void 0;
|
|
9515
|
+
const path = resolve3(legacyDir);
|
|
9516
|
+
const existing = findWorkspaceByPath(path);
|
|
9517
|
+
const migrated = existing ?? {
|
|
9518
|
+
id: getConfiguredWorkspaces().some(
|
|
9519
|
+
(workspace) => workspace.id === DEFAULT_WORKSPACE_ID
|
|
9520
|
+
) ? workspaceIdFromPath(path) : DEFAULT_WORKSPACE_ID,
|
|
9521
|
+
label: labelFromPath(path),
|
|
9522
|
+
kind: "personal",
|
|
9523
|
+
path
|
|
9524
|
+
};
|
|
9525
|
+
if (!existing) {
|
|
9526
|
+
upsertConfiguredWorkspace(migrated);
|
|
9527
|
+
}
|
|
9528
|
+
if (!getActiveWorkspace()) {
|
|
9529
|
+
setActiveWorkspaceId(migrated.id);
|
|
9530
|
+
}
|
|
9531
|
+
await clearLegacyWorkspaceDir(db);
|
|
9532
|
+
return migrated;
|
|
9533
|
+
}
|
|
9534
|
+
async function ensureActiveWorkspace(db) {
|
|
9535
|
+
await migrateLegacyWorkspaceDir(db);
|
|
9536
|
+
const active = getActiveWorkspace();
|
|
9537
|
+
if (active) {
|
|
9538
|
+
mkdirSync9(active.path, { recursive: true });
|
|
9539
|
+
return active;
|
|
9540
|
+
}
|
|
9541
|
+
const configured = getConfiguredWorkspaces()[0];
|
|
9542
|
+
if (configured) {
|
|
9543
|
+
setActiveWorkspaceId(configured.id);
|
|
9544
|
+
mkdirSync9(configured.path, { recursive: true });
|
|
9545
|
+
return configured;
|
|
9546
|
+
}
|
|
9547
|
+
const workspace = {
|
|
9548
|
+
id: DEFAULT_WORKSPACE_ID,
|
|
9549
|
+
label: "Personal",
|
|
9550
|
+
kind: "personal",
|
|
9551
|
+
path: defaultWorkspaceDir()
|
|
9552
|
+
};
|
|
9553
|
+
mkdirSync9(workspace.path, { recursive: true });
|
|
9554
|
+
upsertConfiguredWorkspace(workspace);
|
|
9555
|
+
setActiveWorkspaceId(workspace.id);
|
|
9556
|
+
await clearLegacyWorkspaceDir(db);
|
|
9557
|
+
return workspace;
|
|
9558
|
+
}
|
|
9559
|
+
async function activateWorkspace(db, workspace) {
|
|
9560
|
+
upsertConfiguredWorkspace(workspace);
|
|
9561
|
+
setActiveWorkspaceId(workspace.id);
|
|
9562
|
+
await clearLegacyWorkspaceDir(db);
|
|
9563
|
+
return workspace;
|
|
9564
|
+
}
|
|
9565
|
+
async function activateWorkspacePath(db, dir, opts = {}) {
|
|
9566
|
+
await migrateLegacyWorkspaceDir(db);
|
|
9567
|
+
const path = resolve3(dir);
|
|
9568
|
+
const existing = opts.id ? void 0 : findWorkspaceByPath(path);
|
|
9569
|
+
if (existing) {
|
|
9570
|
+
setActiveWorkspaceId(existing.id);
|
|
9571
|
+
await clearLegacyWorkspaceDir(db);
|
|
9572
|
+
return existing;
|
|
9573
|
+
}
|
|
9574
|
+
const workspace = {
|
|
9575
|
+
id: opts.id || workspaceIdFromPath(path),
|
|
9576
|
+
label: opts.label || labelFromPath(path),
|
|
9577
|
+
kind: opts.kind || "personal",
|
|
9578
|
+
path
|
|
9579
|
+
};
|
|
9580
|
+
return activateWorkspace(db, workspace);
|
|
9581
|
+
}
|
|
9582
|
+
async function removeWorkspaceAndResolveActive(db, id) {
|
|
9583
|
+
removeConfiguredWorkspace(id);
|
|
9584
|
+
await clearLegacyWorkspaceDir(db);
|
|
9585
|
+
const activeWorkspace = await ensureActiveWorkspace(db);
|
|
9586
|
+
return {
|
|
9587
|
+
activeWorkspace,
|
|
9588
|
+
workspaces: getConfiguredWorkspaces()
|
|
9589
|
+
};
|
|
9590
|
+
}
|
|
9591
|
+
function existingWorkspaceDirOrHome(workspace) {
|
|
9592
|
+
return existsSync15(workspace.path) ? workspace.path : homedir10();
|
|
9593
|
+
}
|
|
9594
|
+
|
|
9371
9595
|
// src/cli/bridge-handlers.ts
|
|
9372
9596
|
var BLOOM_VERBS3 = {
|
|
9373
9597
|
1: "Remember",
|
|
@@ -9803,7 +10027,10 @@ async function addToken(db, params) {
|
|
|
9803
10027
|
context: params.context,
|
|
9804
10028
|
symbiosis_mode: params.symbiosisMode,
|
|
9805
10029
|
source_link: params.sourceLink ?? null,
|
|
9806
|
-
question: params.question ?? null
|
|
10030
|
+
question: params.question ?? null,
|
|
10031
|
+
// Bridge/MCP callers are agents: their questions are LLM-authored and
|
|
10032
|
+
// stay refreshable. Humans author questions via the token CLI instead.
|
|
10033
|
+
question_source: params.question ? "llm" : void 0
|
|
9807
10034
|
});
|
|
9808
10035
|
for (const context of assignedContexts) {
|
|
9809
10036
|
await assignTokenToContext(db, token.id, context.id);
|
|
@@ -10130,6 +10357,33 @@ async function sessionOpen(db, params) {
|
|
|
10130
10357
|
relevant: findTokensResult
|
|
10131
10358
|
};
|
|
10132
10359
|
}
|
|
10360
|
+
async function backupCreate(db, params) {
|
|
10361
|
+
const targetDir = params.dir || (await ensureActiveWorkspace(db)).path;
|
|
10362
|
+
const snapshot = await exportSnapshot(db);
|
|
10363
|
+
const manifest = verifySnapshot(snapshot);
|
|
10364
|
+
const backupDir = join16(targetDir, "zam-backups");
|
|
10365
|
+
mkdirSync10(backupDir, { recursive: true });
|
|
10366
|
+
const stamp = manifest.createdAt.replace(/[:.]/g, "-");
|
|
10367
|
+
const path = join16(backupDir, `zam-snapshot-${stamp}.sql`);
|
|
10368
|
+
writeFileSync8(path, snapshot, "utf-8");
|
|
10369
|
+
return {
|
|
10370
|
+
ok: true,
|
|
10371
|
+
path,
|
|
10372
|
+
createdAt: manifest.createdAt,
|
|
10373
|
+
checksum: manifest.checksum,
|
|
10374
|
+
tables: manifest.tables
|
|
10375
|
+
};
|
|
10376
|
+
}
|
|
10377
|
+
async function updateCheck(params) {
|
|
10378
|
+
const current = currentVersion();
|
|
10379
|
+
const latest = params.latest ?? await fetchLatestVersion(GITHUB_REPO);
|
|
10380
|
+
const channel = params.channel ?? getInstallChannel();
|
|
10381
|
+
return decideUpdate({
|
|
10382
|
+
currentVersion: current,
|
|
10383
|
+
latestVersion: latest,
|
|
10384
|
+
channel
|
|
10385
|
+
});
|
|
10386
|
+
}
|
|
10133
10387
|
|
|
10134
10388
|
// src/cli/curriculum/breadcrumb.ts
|
|
10135
10389
|
init_kernel();
|
|
@@ -12796,9 +13050,9 @@ function getCurriculumProvider(id) {
|
|
|
12796
13050
|
// src/cli/llm/vision.ts
|
|
12797
13051
|
init_kernel();
|
|
12798
13052
|
import { randomBytes } from "crypto";
|
|
12799
|
-
import { readFileSync as
|
|
13053
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
12800
13054
|
import { tmpdir as tmpdir2 } from "os";
|
|
12801
|
-
import { basename as
|
|
13055
|
+
import { basename as basename4, join as join17 } from "path";
|
|
12802
13056
|
var LANGUAGE_NAMES2 = {
|
|
12803
13057
|
en: "English",
|
|
12804
13058
|
de: "German",
|
|
@@ -12834,13 +13088,13 @@ async function observeUiSnapshotViaLLM(db, input8) {
|
|
|
12834
13088
|
const imageUrls = [];
|
|
12835
13089
|
const isVideo = /\.(mp4|mov|m4v|avi|mkv|webm)$/i.test(input8.imagePath);
|
|
12836
13090
|
if (isVideo) {
|
|
12837
|
-
const { mkdirSync:
|
|
13091
|
+
const { mkdirSync: mkdirSync16, readdirSync: readdirSync3, rmSync: rmSync4 } = await import("fs");
|
|
12838
13092
|
const { execSync: execSync6 } = await import("child_process");
|
|
12839
|
-
const tempDir =
|
|
13093
|
+
const tempDir = join17(
|
|
12840
13094
|
tmpdir2(),
|
|
12841
13095
|
`zam-frames-${randomBytes(4).toString("hex")}`
|
|
12842
13096
|
);
|
|
12843
|
-
|
|
13097
|
+
mkdirSync16(tempDir, { recursive: true });
|
|
12844
13098
|
try {
|
|
12845
13099
|
execSync6(
|
|
12846
13100
|
`ffmpeg -i "${input8.imagePath}" -vf "fps=1/3,scale=1280:-1" -vsync vfr "${tempDir}/frame_%03d.png"`,
|
|
@@ -12869,7 +13123,7 @@ async function observeUiSnapshotViaLLM(db, input8) {
|
|
|
12869
13123
|
}
|
|
12870
13124
|
}
|
|
12871
13125
|
for (const file of sampledFiles) {
|
|
12872
|
-
const bytes =
|
|
13126
|
+
const bytes = readFileSync12(join17(tempDir, file));
|
|
12873
13127
|
imageUrls.push(`data:image/png;base64,${bytes.toString("base64")}`);
|
|
12874
13128
|
}
|
|
12875
13129
|
} finally {
|
|
@@ -12879,7 +13133,7 @@ async function observeUiSnapshotViaLLM(db, input8) {
|
|
|
12879
13133
|
}
|
|
12880
13134
|
}
|
|
12881
13135
|
} else {
|
|
12882
|
-
const imageBytes =
|
|
13136
|
+
const imageBytes = readFileSync12(input8.imagePath);
|
|
12883
13137
|
const ext = input8.imagePath.split(".").pop()?.toLowerCase();
|
|
12884
13138
|
const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : "image/png";
|
|
12885
13139
|
imageUrls.push(`data:${mime};base64,${imageBytes.toString("base64")}`);
|
|
@@ -13117,7 +13371,7 @@ function buildReport(input8, draft) {
|
|
|
13117
13371
|
evidence: [
|
|
13118
13372
|
{
|
|
13119
13373
|
type: "keyframe",
|
|
13120
|
-
ref: input8.evidenceRef ??
|
|
13374
|
+
ref: input8.evidenceRef ?? basename4(input8.imagePath),
|
|
13121
13375
|
redacted: input8.redacted ?? false
|
|
13122
13376
|
}
|
|
13123
13377
|
],
|
|
@@ -13139,7 +13393,7 @@ function uncertainReport(input8, summary) {
|
|
|
13139
13393
|
evidence: [
|
|
13140
13394
|
{
|
|
13141
13395
|
type: "keyframe",
|
|
13142
|
-
ref: input8.evidenceRef ??
|
|
13396
|
+
ref: input8.evidenceRef ?? basename4(input8.imagePath),
|
|
13143
13397
|
redacted: input8.redacted ?? false
|
|
13144
13398
|
}
|
|
13145
13399
|
],
|
|
@@ -13348,36 +13602,36 @@ async function withProviderScope(machine, action) {
|
|
|
13348
13602
|
|
|
13349
13603
|
// src/cli/provisioning/index.ts
|
|
13350
13604
|
import {
|
|
13351
|
-
existsSync as
|
|
13605
|
+
existsSync as existsSync16,
|
|
13352
13606
|
lstatSync,
|
|
13353
|
-
mkdirSync as
|
|
13354
|
-
readFileSync as
|
|
13607
|
+
mkdirSync as mkdirSync11,
|
|
13608
|
+
readFileSync as readFileSync13,
|
|
13355
13609
|
realpathSync,
|
|
13356
13610
|
rmSync as rmSync2,
|
|
13357
13611
|
symlinkSync,
|
|
13358
|
-
writeFileSync as
|
|
13612
|
+
writeFileSync as writeFileSync9
|
|
13359
13613
|
} from "fs";
|
|
13360
|
-
import { basename as
|
|
13361
|
-
import { fileURLToPath as
|
|
13614
|
+
import { basename as basename5, dirname as dirname7, join as join18 } from "path";
|
|
13615
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
13362
13616
|
var packageRoot = [
|
|
13363
|
-
|
|
13364
|
-
|
|
13365
|
-
].find((candidate) =>
|
|
13617
|
+
fileURLToPath3(new URL("../..", import.meta.url)),
|
|
13618
|
+
fileURLToPath3(new URL("../../..", import.meta.url))
|
|
13619
|
+
].find((candidate) => existsSync16(join18(candidate, "package.json"))) ?? fileURLToPath3(new URL("../..", import.meta.url));
|
|
13366
13620
|
var ALL_SETUP_AGENTS = ["claude", "copilot", "codex", "agent"];
|
|
13367
13621
|
var SKILL_PAIRS = [
|
|
13368
13622
|
{
|
|
13369
|
-
from:
|
|
13370
|
-
to:
|
|
13623
|
+
from: join18(packageRoot, ".claude", "skills", "zam", "SKILL.md"),
|
|
13624
|
+
to: join18(".claude", "skills", "zam", "SKILL.md"),
|
|
13371
13625
|
agents: ["claude", "copilot"]
|
|
13372
13626
|
},
|
|
13373
13627
|
{
|
|
13374
|
-
from:
|
|
13375
|
-
to:
|
|
13628
|
+
from: join18(packageRoot, ".agent", "skills", "zam", "SKILL.md"),
|
|
13629
|
+
to: join18(".agent", "skills", "zam", "SKILL.md"),
|
|
13376
13630
|
agents: ["agent"]
|
|
13377
13631
|
},
|
|
13378
13632
|
{
|
|
13379
|
-
from:
|
|
13380
|
-
to:
|
|
13633
|
+
from: join18(packageRoot, ".agents", "skills", "zam", "SKILL.md"),
|
|
13634
|
+
to: join18(".agents", "skills", "zam", "SKILL.md"),
|
|
13381
13635
|
agents: ["codex"]
|
|
13382
13636
|
}
|
|
13383
13637
|
];
|
|
@@ -13437,15 +13691,15 @@ function linkPointsTo(sourceDir, destinationDir) {
|
|
|
13437
13691
|
function isZamSkillCopy(destinationDir) {
|
|
13438
13692
|
try {
|
|
13439
13693
|
if (!lstatSync(destinationDir).isDirectory()) return false;
|
|
13440
|
-
const skillFile =
|
|
13441
|
-
if (!
|
|
13442
|
-
return /^name:\s*zam\s*$/m.test(
|
|
13694
|
+
const skillFile = join18(destinationDir, "SKILL.md");
|
|
13695
|
+
if (!existsSync16(skillFile)) return false;
|
|
13696
|
+
return /^name:\s*zam\s*$/m.test(readFileSync13(skillFile, "utf8"));
|
|
13443
13697
|
} catch {
|
|
13444
13698
|
return false;
|
|
13445
13699
|
}
|
|
13446
13700
|
}
|
|
13447
13701
|
function classifySkillDestination(sourceDir, destinationDir) {
|
|
13448
|
-
if (!
|
|
13702
|
+
if (!existsSync16(sourceDir)) return "source-missing";
|
|
13449
13703
|
if (!isSymbolicLink(destinationDir) && pathsResolveToSameDirectory(sourceDir, destinationDir)) {
|
|
13450
13704
|
return "source-directory";
|
|
13451
13705
|
}
|
|
@@ -13462,9 +13716,9 @@ function wireSkills(cwd = process.cwd(), agents = parseSetupAgents(), opts = {})
|
|
|
13462
13716
|
};
|
|
13463
13717
|
for (const { from, to, agents: pairAgents } of SKILL_PAIRS) {
|
|
13464
13718
|
if (!pairAgents.some((agent) => agents.has(agent))) continue;
|
|
13465
|
-
const sourceDir =
|
|
13466
|
-
const destinationDir =
|
|
13467
|
-
const label =
|
|
13719
|
+
const sourceDir = dirname7(from);
|
|
13720
|
+
const destinationDir = dirname7(join18(cwd, to));
|
|
13721
|
+
const label = dirname7(to);
|
|
13468
13722
|
const state = classifySkillDestination(sourceDir, destinationDir);
|
|
13469
13723
|
if (state === "source-missing") {
|
|
13470
13724
|
if (!opts.quiet) {
|
|
@@ -13521,7 +13775,7 @@ function wireSkills(cwd = process.cwd(), agents = parseSetupAgents(), opts = {})
|
|
|
13521
13775
|
if (destinationExists) {
|
|
13522
13776
|
rmSync2(destinationDir, { recursive: true, force: true });
|
|
13523
13777
|
}
|
|
13524
|
-
|
|
13778
|
+
mkdirSync11(dirname7(destinationDir), { recursive: true });
|
|
13525
13779
|
symlinkSync(
|
|
13526
13780
|
sourceDir,
|
|
13527
13781
|
destinationDir,
|
|
@@ -13537,8 +13791,8 @@ function inspectSkillLinks(cwd = process.cwd(), agents = parseSetupAgents()) {
|
|
|
13537
13791
|
const results = [];
|
|
13538
13792
|
for (const { from, to, agents: pairAgents } of SKILL_PAIRS) {
|
|
13539
13793
|
if (!pairAgents.some((agent) => agents.has(agent))) continue;
|
|
13540
|
-
const sourceDir =
|
|
13541
|
-
const destinationDir =
|
|
13794
|
+
const sourceDir = dirname7(from);
|
|
13795
|
+
const destinationDir = dirname7(join18(cwd, to));
|
|
13542
13796
|
results.push({
|
|
13543
13797
|
agents: pairAgents,
|
|
13544
13798
|
source: sourceDir,
|
|
@@ -13564,15 +13818,15 @@ function upsertMarkedBlock(dest, blockBody, dryRun) {
|
|
|
13564
13818
|
const block = `${ZAM_BLOCK_START}
|
|
13565
13819
|
${blockBody.trim()}
|
|
13566
13820
|
${ZAM_BLOCK_END}`;
|
|
13567
|
-
if (!
|
|
13821
|
+
if (!existsSync16(dest)) {
|
|
13568
13822
|
if (!dryRun) {
|
|
13569
|
-
|
|
13570
|
-
|
|
13823
|
+
mkdirSync11(dirname7(dest), { recursive: true });
|
|
13824
|
+
writeFileSync9(dest, `${block}
|
|
13571
13825
|
`, "utf8");
|
|
13572
13826
|
}
|
|
13573
13827
|
return "write";
|
|
13574
13828
|
}
|
|
13575
|
-
const existing =
|
|
13829
|
+
const existing = readFileSync13(dest, "utf8");
|
|
13576
13830
|
if (existing.includes(block)) return "skip";
|
|
13577
13831
|
const start = existing.indexOf(ZAM_BLOCK_START);
|
|
13578
13832
|
const end = existing.indexOf(ZAM_BLOCK_END);
|
|
@@ -13580,7 +13834,7 @@ ${ZAM_BLOCK_END}`;
|
|
|
13580
13834
|
|
|
13581
13835
|
${block}
|
|
13582
13836
|
`;
|
|
13583
|
-
if (!dryRun)
|
|
13837
|
+
if (!dryRun) writeFileSync9(dest, next, "utf8");
|
|
13584
13838
|
return start >= 0 && end > start ? "update" : "write";
|
|
13585
13839
|
}
|
|
13586
13840
|
function logInstructionAction(action, label, dryRun) {
|
|
@@ -13592,8 +13846,8 @@ function logInstructionAction(action, label, dryRun) {
|
|
|
13592
13846
|
}
|
|
13593
13847
|
function writeClaudeMd(skipClaudeMd, cwd = process.cwd(), opts = {}) {
|
|
13594
13848
|
if (skipClaudeMd) return;
|
|
13595
|
-
const dest =
|
|
13596
|
-
if (
|
|
13849
|
+
const dest = join18(cwd, "CLAUDE.md");
|
|
13850
|
+
if (existsSync16(dest)) {
|
|
13597
13851
|
if (!opts.updateExisting) {
|
|
13598
13852
|
console.log(` skip CLAUDE.md (already present)`);
|
|
13599
13853
|
return;
|
|
@@ -13612,7 +13866,7 @@ ZAM is available in this repository. Use the \`zam\` skill in Claude Code to tur
|
|
|
13612
13866
|
logInstructionAction(action, "CLAUDE.md", Boolean(opts.dryRun));
|
|
13613
13867
|
return;
|
|
13614
13868
|
}
|
|
13615
|
-
const name =
|
|
13869
|
+
const name = basename5(cwd);
|
|
13616
13870
|
const content = `# ZAM Personal Kernel \u2014 ${name}
|
|
13617
13871
|
|
|
13618
13872
|
This is a ZAM personal instance. ZAM builds lasting skills through spaced
|
|
@@ -13636,14 +13890,14 @@ Use \`zam connector setup turso\` to store cloud credentials in
|
|
|
13636
13890
|
if (opts.dryRun) {
|
|
13637
13891
|
console.log(` would write CLAUDE.md`);
|
|
13638
13892
|
} else {
|
|
13639
|
-
|
|
13893
|
+
writeFileSync9(dest, content, "utf8");
|
|
13640
13894
|
console.log(` write CLAUDE.md`);
|
|
13641
13895
|
}
|
|
13642
13896
|
}
|
|
13643
13897
|
function writeAgentsMd(skipAgentsMd, cwd = process.cwd(), opts = {}) {
|
|
13644
13898
|
if (skipAgentsMd) return;
|
|
13645
|
-
const dest =
|
|
13646
|
-
if (
|
|
13899
|
+
const dest = join18(cwd, "AGENTS.md");
|
|
13900
|
+
if (existsSync16(dest)) {
|
|
13647
13901
|
if (!opts.updateExisting) {
|
|
13648
13902
|
console.log(` skip AGENTS.md (already present)`);
|
|
13649
13903
|
return;
|
|
@@ -13662,7 +13916,7 @@ ZAM is available in this repository. Select the \`zam\` skill through \`/skills\
|
|
|
13662
13916
|
logInstructionAction(action, "AGENTS.md", Boolean(opts.dryRun));
|
|
13663
13917
|
return;
|
|
13664
13918
|
}
|
|
13665
|
-
const name =
|
|
13919
|
+
const name = basename5(cwd);
|
|
13666
13920
|
const content = `# ZAM Personal Kernel - ${name}
|
|
13667
13921
|
|
|
13668
13922
|
This is a ZAM personal instance. ZAM builds lasting skills through spaced
|
|
@@ -13693,12 +13947,12 @@ Codex discovers repository skills under \`.agents/skills/\`. Run
|
|
|
13693
13947
|
if (opts.dryRun) {
|
|
13694
13948
|
console.log(` would write AGENTS.md`);
|
|
13695
13949
|
} else {
|
|
13696
|
-
|
|
13950
|
+
writeFileSync9(dest, content, "utf8");
|
|
13697
13951
|
console.log(` write AGENTS.md`);
|
|
13698
13952
|
}
|
|
13699
13953
|
}
|
|
13700
13954
|
function writeCopilotInstructions(cwd = process.cwd(), opts = {}) {
|
|
13701
|
-
const dest =
|
|
13955
|
+
const dest = join18(cwd, ".github", "copilot-instructions.md");
|
|
13702
13956
|
const action = upsertMarkedBlock(
|
|
13703
13957
|
dest,
|
|
13704
13958
|
`## ZAM learning sessions
|
|
@@ -13738,134 +13992,14 @@ async function resolveUser(opts, db, resolveOpts) {
|
|
|
13738
13992
|
process.exit(1);
|
|
13739
13993
|
}
|
|
13740
13994
|
|
|
13741
|
-
// src/cli/workspaces/active.ts
|
|
13742
|
-
init_kernel();
|
|
13743
|
-
import { existsSync as existsSync16, mkdirSync as mkdirSync10 } from "fs";
|
|
13744
|
-
import { homedir as homedir10 } from "os";
|
|
13745
|
-
import { basename as basename5, join as join16, resolve as resolve3 } from "path";
|
|
13746
|
-
var LEGACY_WORKSPACE_DIR_KEY = "personal.workspace_dir";
|
|
13747
|
-
var DEFAULT_WORKSPACE_ID = "personal";
|
|
13748
|
-
function defaultWorkspaceDir() {
|
|
13749
|
-
return join16(homedir10(), "Documents", "zam");
|
|
13750
|
-
}
|
|
13751
|
-
function normalizeWorkspacePath(path) {
|
|
13752
|
-
const resolved = resolve3(path);
|
|
13753
|
-
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
13754
|
-
}
|
|
13755
|
-
function sameWorkspacePath(left, right) {
|
|
13756
|
-
return normalizeWorkspacePath(left) === normalizeWorkspacePath(right);
|
|
13757
|
-
}
|
|
13758
|
-
function labelFromPath(path) {
|
|
13759
|
-
return basename5(path) || "ZAM";
|
|
13760
|
-
}
|
|
13761
|
-
function workspaceIdFromPath(dir) {
|
|
13762
|
-
const base = basename5(dir).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
13763
|
-
const prefix = base || "workspace";
|
|
13764
|
-
const existing = new Set(getConfiguredWorkspaces().map((item) => item.id));
|
|
13765
|
-
if (!existing.has(prefix)) return prefix;
|
|
13766
|
-
let index = 2;
|
|
13767
|
-
while (existing.has(`${prefix}-${index}`)) index++;
|
|
13768
|
-
return `${prefix}-${index}`;
|
|
13769
|
-
}
|
|
13770
|
-
function findWorkspaceByPath(dir) {
|
|
13771
|
-
return getConfiguredWorkspaces().find(
|
|
13772
|
-
(workspace) => sameWorkspacePath(workspace.path, dir)
|
|
13773
|
-
);
|
|
13774
|
-
}
|
|
13775
|
-
async function clearLegacyWorkspaceDir(db) {
|
|
13776
|
-
await deleteSetting(db, LEGACY_WORKSPACE_DIR_KEY);
|
|
13777
|
-
}
|
|
13778
|
-
async function migrateLegacyWorkspaceDir(db) {
|
|
13779
|
-
const legacyDir = await getSetting(db, LEGACY_WORKSPACE_DIR_KEY);
|
|
13780
|
-
if (!legacyDir) return void 0;
|
|
13781
|
-
const path = resolve3(legacyDir);
|
|
13782
|
-
const existing = findWorkspaceByPath(path);
|
|
13783
|
-
const migrated = existing ?? {
|
|
13784
|
-
id: getConfiguredWorkspaces().some(
|
|
13785
|
-
(workspace) => workspace.id === DEFAULT_WORKSPACE_ID
|
|
13786
|
-
) ? workspaceIdFromPath(path) : DEFAULT_WORKSPACE_ID,
|
|
13787
|
-
label: labelFromPath(path),
|
|
13788
|
-
kind: "personal",
|
|
13789
|
-
path
|
|
13790
|
-
};
|
|
13791
|
-
if (!existing) {
|
|
13792
|
-
upsertConfiguredWorkspace(migrated);
|
|
13793
|
-
}
|
|
13794
|
-
if (!getActiveWorkspace()) {
|
|
13795
|
-
setActiveWorkspaceId(migrated.id);
|
|
13796
|
-
}
|
|
13797
|
-
await clearLegacyWorkspaceDir(db);
|
|
13798
|
-
return migrated;
|
|
13799
|
-
}
|
|
13800
|
-
async function ensureActiveWorkspace(db) {
|
|
13801
|
-
await migrateLegacyWorkspaceDir(db);
|
|
13802
|
-
const active = getActiveWorkspace();
|
|
13803
|
-
if (active) {
|
|
13804
|
-
mkdirSync10(active.path, { recursive: true });
|
|
13805
|
-
return active;
|
|
13806
|
-
}
|
|
13807
|
-
const configured = getConfiguredWorkspaces()[0];
|
|
13808
|
-
if (configured) {
|
|
13809
|
-
setActiveWorkspaceId(configured.id);
|
|
13810
|
-
mkdirSync10(configured.path, { recursive: true });
|
|
13811
|
-
return configured;
|
|
13812
|
-
}
|
|
13813
|
-
const workspace = {
|
|
13814
|
-
id: DEFAULT_WORKSPACE_ID,
|
|
13815
|
-
label: "Personal",
|
|
13816
|
-
kind: "personal",
|
|
13817
|
-
path: defaultWorkspaceDir()
|
|
13818
|
-
};
|
|
13819
|
-
mkdirSync10(workspace.path, { recursive: true });
|
|
13820
|
-
upsertConfiguredWorkspace(workspace);
|
|
13821
|
-
setActiveWorkspaceId(workspace.id);
|
|
13822
|
-
await clearLegacyWorkspaceDir(db);
|
|
13823
|
-
return workspace;
|
|
13824
|
-
}
|
|
13825
|
-
async function activateWorkspace(db, workspace) {
|
|
13826
|
-
upsertConfiguredWorkspace(workspace);
|
|
13827
|
-
setActiveWorkspaceId(workspace.id);
|
|
13828
|
-
await clearLegacyWorkspaceDir(db);
|
|
13829
|
-
return workspace;
|
|
13830
|
-
}
|
|
13831
|
-
async function activateWorkspacePath(db, dir, opts = {}) {
|
|
13832
|
-
await migrateLegacyWorkspaceDir(db);
|
|
13833
|
-
const path = resolve3(dir);
|
|
13834
|
-
const existing = opts.id ? void 0 : findWorkspaceByPath(path);
|
|
13835
|
-
if (existing) {
|
|
13836
|
-
setActiveWorkspaceId(existing.id);
|
|
13837
|
-
await clearLegacyWorkspaceDir(db);
|
|
13838
|
-
return existing;
|
|
13839
|
-
}
|
|
13840
|
-
const workspace = {
|
|
13841
|
-
id: opts.id || workspaceIdFromPath(path),
|
|
13842
|
-
label: opts.label || labelFromPath(path),
|
|
13843
|
-
kind: opts.kind || "personal",
|
|
13844
|
-
path
|
|
13845
|
-
};
|
|
13846
|
-
return activateWorkspace(db, workspace);
|
|
13847
|
-
}
|
|
13848
|
-
async function removeWorkspaceAndResolveActive(db, id) {
|
|
13849
|
-
removeConfiguredWorkspace(id);
|
|
13850
|
-
await clearLegacyWorkspaceDir(db);
|
|
13851
|
-
const activeWorkspace = await ensureActiveWorkspace(db);
|
|
13852
|
-
return {
|
|
13853
|
-
activeWorkspace,
|
|
13854
|
-
workspaces: getConfiguredWorkspaces()
|
|
13855
|
-
};
|
|
13856
|
-
}
|
|
13857
|
-
function existingWorkspaceDirOrHome(workspace) {
|
|
13858
|
-
return existsSync16(workspace.path) ? workspace.path : homedir10();
|
|
13859
|
-
}
|
|
13860
|
-
|
|
13861
13995
|
// src/cli/workspaces/backup.ts
|
|
13862
|
-
import { mkdirSync as
|
|
13863
|
-
import { join as
|
|
13996
|
+
import { mkdirSync as mkdirSync12 } from "fs";
|
|
13997
|
+
import { join as join19 } from "path";
|
|
13864
13998
|
async function backupDatabaseTo(db, targetDir) {
|
|
13865
|
-
const backupDir =
|
|
13866
|
-
|
|
13999
|
+
const backupDir = join19(targetDir, "zam-backups");
|
|
14000
|
+
mkdirSync12(backupDir, { recursive: true });
|
|
13867
14001
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
13868
|
-
const dest =
|
|
14002
|
+
const dest = join19(backupDir, `zam-${stamp}.db`);
|
|
13869
14003
|
await db.exec(`VACUUM INTO '${dest.replace(/'/g, "''")}'`);
|
|
13870
14004
|
return dest;
|
|
13871
14005
|
}
|
|
@@ -13959,6 +14093,35 @@ bridgeCommand.command("backup-db").description("Back up the local database into
|
|
|
13959
14093
|
jsonOut2({ ok: true, path });
|
|
13960
14094
|
});
|
|
13961
14095
|
});
|
|
14096
|
+
bridgeCommand.command("backup-create").description(
|
|
14097
|
+
"Create a portable SQL snapshot backup (kernel exportSnapshot), distinct from backup-db's VACUUM copy (JSON)"
|
|
14098
|
+
).option(
|
|
14099
|
+
"--dir <path>",
|
|
14100
|
+
"Target directory (default: workspace dir, else ~/Documents/zam)"
|
|
14101
|
+
).action(async (opts) => {
|
|
14102
|
+
await withDb2(async (db) => {
|
|
14103
|
+
try {
|
|
14104
|
+
const result = await backupCreate(db, { dir: opts.dir });
|
|
14105
|
+
jsonOut2(result);
|
|
14106
|
+
} catch (err) {
|
|
14107
|
+
jsonError(err.message);
|
|
14108
|
+
}
|
|
14109
|
+
});
|
|
14110
|
+
});
|
|
14111
|
+
bridgeCommand.command("update-check").description("Check whether a newer ZAM release is available (JSON)").option(
|
|
14112
|
+
"--latest <version>",
|
|
14113
|
+
"Compare against this version instead of fetching (offline/deterministic checks)"
|
|
14114
|
+
).option("--channel <channel>", "Override the detected install channel").action(async (opts) => {
|
|
14115
|
+
try {
|
|
14116
|
+
const result = await updateCheck({
|
|
14117
|
+
latest: opts.latest,
|
|
14118
|
+
channel: opts.channel
|
|
14119
|
+
});
|
|
14120
|
+
jsonOut2(result);
|
|
14121
|
+
} catch (err) {
|
|
14122
|
+
jsonError(err.message);
|
|
14123
|
+
}
|
|
14124
|
+
});
|
|
13962
14125
|
bridgeCommand.command("workspace-info").description("Report the workspace dir, its default, and the data dir (JSON)").action(async () => {
|
|
13963
14126
|
await withDb2(async (db) => {
|
|
13964
14127
|
const activeWorkspace = await ensureActiveWorkspace(db);
|
|
@@ -13967,7 +14130,7 @@ bridgeCommand.command("workspace-info").description("Report the workspace dir, i
|
|
|
13967
14130
|
activeWorkspace,
|
|
13968
14131
|
workspaceDir: activeWorkspace.path,
|
|
13969
14132
|
defaultWorkspaceDir: defaultWorkspaceDir(),
|
|
13970
|
-
dataDir:
|
|
14133
|
+
dataDir: join20(homedir11(), ".zam")
|
|
13971
14134
|
});
|
|
13972
14135
|
});
|
|
13973
14136
|
});
|
|
@@ -14010,7 +14173,7 @@ bridgeCommand.command("workspace-list").description("List configured ZAM workspa
|
|
|
14010
14173
|
activeWorkspace,
|
|
14011
14174
|
workspaceDir: activeWorkspace.path,
|
|
14012
14175
|
defaultWorkspaceDir: defaultWorkspaceDir(),
|
|
14013
|
-
dataDir:
|
|
14176
|
+
dataDir: join20(homedir11(), ".zam"),
|
|
14014
14177
|
linkHealth: buildWorkspaceLinkHealth(workspaces)
|
|
14015
14178
|
});
|
|
14016
14179
|
});
|
|
@@ -14537,7 +14700,7 @@ bridgeCommand.command("discover-skills").description(
|
|
|
14537
14700
|
"20"
|
|
14538
14701
|
).action(async (opts) => {
|
|
14539
14702
|
try {
|
|
14540
|
-
const monitorDir =
|
|
14703
|
+
const monitorDir = join20(homedir11(), ".zam", "monitor");
|
|
14541
14704
|
let files;
|
|
14542
14705
|
try {
|
|
14543
14706
|
files = readdirSync2(monitorDir).filter((f) => f.endsWith(".jsonl"));
|
|
@@ -14550,7 +14713,7 @@ bridgeCommand.command("discover-skills").description(
|
|
|
14550
14713
|
return;
|
|
14551
14714
|
}
|
|
14552
14715
|
const limit = Number(opts.limit);
|
|
14553
|
-
const sorted = files.map((f) => ({ name: f, path:
|
|
14716
|
+
const sorted = files.map((f) => ({ name: f, path: join20(monitorDir, f) })).sort((a, b) => b.name.localeCompare(a.name)).slice(0, limit);
|
|
14554
14717
|
const sessionCommands = /* @__PURE__ */ new Map();
|
|
14555
14718
|
for (const file of sorted) {
|
|
14556
14719
|
const sessionId = file.name.replace(".jsonl", "");
|
|
@@ -15052,7 +15215,7 @@ bridgeCommand.command("capture-ui").description("Capture a screenshot for agent-
|
|
|
15052
15215
|
return;
|
|
15053
15216
|
}
|
|
15054
15217
|
}
|
|
15055
|
-
const outputPath = opts.image ?? opts.output ??
|
|
15218
|
+
const outputPath = opts.image ?? opts.output ?? join20(tmpdir3(), `zam-capture-${randomBytes2(4).toString("hex")}.png`);
|
|
15056
15219
|
const captureResult = isProvided ? { method: "provided", target: null } : captureScreenshot(outputPath, opts.hwnd, opts.processName);
|
|
15057
15220
|
if (!isProvided) {
|
|
15058
15221
|
const post = decidePostCapture(policy, {
|
|
@@ -15080,7 +15243,7 @@ bridgeCommand.command("capture-ui").description("Capture a screenshot for agent-
|
|
|
15080
15243
|
return;
|
|
15081
15244
|
}
|
|
15082
15245
|
}
|
|
15083
|
-
const imageBytes =
|
|
15246
|
+
const imageBytes = readFileSync14(outputPath);
|
|
15084
15247
|
const base64 = imageBytes.toString("base64");
|
|
15085
15248
|
jsonOut2({
|
|
15086
15249
|
sessionId: opts.session ?? null,
|
|
@@ -15107,10 +15270,10 @@ bridgeCommand.command("start-recording").description("Start screen recording in
|
|
|
15107
15270
|
return;
|
|
15108
15271
|
}
|
|
15109
15272
|
const sessionId = opts.session;
|
|
15110
|
-
const statePath =
|
|
15273
|
+
const statePath = join20(tmpdir3(), `zam-recording-${sessionId}.json`);
|
|
15111
15274
|
const defaultExt = platform === "win32" ? ".mkv" : ".mov";
|
|
15112
|
-
const outputPath = opts.output ??
|
|
15113
|
-
const { existsSync: existsSync26, writeFileSync:
|
|
15275
|
+
const outputPath = opts.output ?? join20(tmpdir3(), `zam-recording-${sessionId}${defaultExt}`);
|
|
15276
|
+
const { existsSync: existsSync26, writeFileSync: writeFileSync14, openSync, closeSync } = await import("fs");
|
|
15114
15277
|
if (existsSync26(statePath)) {
|
|
15115
15278
|
jsonOut2({
|
|
15116
15279
|
sessionId,
|
|
@@ -15119,7 +15282,7 @@ bridgeCommand.command("start-recording").description("Start screen recording in
|
|
|
15119
15282
|
});
|
|
15120
15283
|
return;
|
|
15121
15284
|
}
|
|
15122
|
-
const logPath =
|
|
15285
|
+
const logPath = join20(tmpdir3(), `zam-recording-${sessionId}.log`);
|
|
15123
15286
|
let logFd;
|
|
15124
15287
|
try {
|
|
15125
15288
|
logFd = openSync(logPath, "w");
|
|
@@ -15165,7 +15328,7 @@ bridgeCommand.command("start-recording").description("Start screen recording in
|
|
|
15165
15328
|
}
|
|
15166
15329
|
child.unref();
|
|
15167
15330
|
if (child.pid) {
|
|
15168
|
-
|
|
15331
|
+
writeFileSync14(
|
|
15169
15332
|
statePath,
|
|
15170
15333
|
JSON.stringify({
|
|
15171
15334
|
pid: child.pid,
|
|
@@ -15201,8 +15364,8 @@ bridgeCommand.command("stop-recording").description(
|
|
|
15201
15364
|
return;
|
|
15202
15365
|
}
|
|
15203
15366
|
const sessionId = opts.session;
|
|
15204
|
-
const statePath =
|
|
15205
|
-
const { existsSync: existsSync26, readFileSync:
|
|
15367
|
+
const statePath = join20(tmpdir3(), `zam-recording-${sessionId}.json`);
|
|
15368
|
+
const { existsSync: existsSync26, readFileSync: readFileSync19, rmSync: rmSync4 } = await import("fs");
|
|
15206
15369
|
if (!existsSync26(statePath)) {
|
|
15207
15370
|
jsonOut2({
|
|
15208
15371
|
sessionId,
|
|
@@ -15211,7 +15374,7 @@ bridgeCommand.command("stop-recording").description(
|
|
|
15211
15374
|
});
|
|
15212
15375
|
return;
|
|
15213
15376
|
}
|
|
15214
|
-
const state = JSON.parse(
|
|
15377
|
+
const state = JSON.parse(readFileSync19(statePath, "utf8"));
|
|
15215
15378
|
const { pid, outputPath } = state;
|
|
15216
15379
|
try {
|
|
15217
15380
|
process.kill(pid, "SIGINT");
|
|
@@ -16599,6 +16762,79 @@ bridgeCommand.command("set-active-knowledge-context").description("Set the activ
|
|
|
16599
16762
|
jsonOut2({ success: true, activeContext: context.name });
|
|
16600
16763
|
});
|
|
16601
16764
|
});
|
|
16765
|
+
var commanderConfiguredForJsonExecution = false;
|
|
16766
|
+
function ensureCommanderThrowsInsteadOfExiting() {
|
|
16767
|
+
if (commanderConfiguredForJsonExecution) return;
|
|
16768
|
+
bridgeCommand.exitOverride();
|
|
16769
|
+
for (const sub of bridgeCommand.commands) {
|
|
16770
|
+
sub.exitOverride();
|
|
16771
|
+
}
|
|
16772
|
+
commanderConfiguredForJsonExecution = true;
|
|
16773
|
+
}
|
|
16774
|
+
async function runBridgeCommandOnce(cmd, args) {
|
|
16775
|
+
ensureCommanderThrowsInsteadOfExiting();
|
|
16776
|
+
let outputBuffer = "";
|
|
16777
|
+
const captureOutput = (str) => {
|
|
16778
|
+
outputBuffer += str;
|
|
16779
|
+
};
|
|
16780
|
+
bridgeCommand.configureOutput({
|
|
16781
|
+
writeOut: captureOutput,
|
|
16782
|
+
writeErr: captureOutput
|
|
16783
|
+
});
|
|
16784
|
+
for (const sub of bridgeCommand.commands) {
|
|
16785
|
+
sub.configureOutput({ writeOut: captureOutput, writeErr: captureOutput });
|
|
16786
|
+
}
|
|
16787
|
+
const originalLog = console.log;
|
|
16788
|
+
const originalError = console.error;
|
|
16789
|
+
console.log = (...logArgs) => {
|
|
16790
|
+
outputBuffer += `${logArgs.map((a) => typeof a === "object" ? JSON.stringify(a) : String(a)).join(" ")}
|
|
16791
|
+
`;
|
|
16792
|
+
};
|
|
16793
|
+
console.error = (...logArgs) => {
|
|
16794
|
+
outputBuffer += `${logArgs.map((a) => typeof a === "object" ? JSON.stringify(a) : String(a)).join(" ")}
|
|
16795
|
+
`;
|
|
16796
|
+
};
|
|
16797
|
+
const wasServeMode = isServeMode;
|
|
16798
|
+
isServeMode = true;
|
|
16799
|
+
try {
|
|
16800
|
+
try {
|
|
16801
|
+
await bridgeCommand.parseAsync(["node", "bridge", cmd, ...args]);
|
|
16802
|
+
} catch (err) {
|
|
16803
|
+
let message;
|
|
16804
|
+
if (err instanceof Error && err.message.startsWith('{"error":')) {
|
|
16805
|
+
try {
|
|
16806
|
+
message = JSON.parse(err.message).error;
|
|
16807
|
+
} catch {
|
|
16808
|
+
message = err.message;
|
|
16809
|
+
}
|
|
16810
|
+
} else if (err?.code?.startsWith("commander.")) {
|
|
16811
|
+
message = outputBuffer.trim() || err.message;
|
|
16812
|
+
} else {
|
|
16813
|
+
message = err.message || String(err);
|
|
16814
|
+
}
|
|
16815
|
+
throw new Error(message);
|
|
16816
|
+
}
|
|
16817
|
+
} finally {
|
|
16818
|
+
console.log = originalLog;
|
|
16819
|
+
console.error = originalError;
|
|
16820
|
+
isServeMode = wasServeMode;
|
|
16821
|
+
}
|
|
16822
|
+
const trimmed = outputBuffer.trim();
|
|
16823
|
+
try {
|
|
16824
|
+
return JSON.parse(trimmed);
|
|
16825
|
+
} catch {
|
|
16826
|
+
return trimmed;
|
|
16827
|
+
}
|
|
16828
|
+
}
|
|
16829
|
+
var bridgeExecutionQueue = Promise.resolve();
|
|
16830
|
+
function executeBridgeCommandJson(cmd, args) {
|
|
16831
|
+
const run = bridgeExecutionQueue.then(() => runBridgeCommandOnce(cmd, args));
|
|
16832
|
+
bridgeExecutionQueue = run.then(
|
|
16833
|
+
() => void 0,
|
|
16834
|
+
() => void 0
|
|
16835
|
+
);
|
|
16836
|
+
return run;
|
|
16837
|
+
}
|
|
16602
16838
|
bridgeCommand.command("serve").description("Start the persistent JSON-RPC stdin/stdout server").option("--stdin", "Use stdin/stdout for communication").action(async (_opts) => {
|
|
16603
16839
|
isServeMode = true;
|
|
16604
16840
|
const {
|
|
@@ -16621,25 +16857,7 @@ bridgeCommand.command("serve").description("Start the persistent JSON-RPC stdin/
|
|
|
16621
16857
|
logDiag(
|
|
16622
16858
|
`serve start | homedir=${nodeOs.homedir()} | USERPROFILE=${process.env.USERPROFILE ?? ""} | HOME=${process.env.HOME ?? ""} | cwd=${process.cwd()}`
|
|
16623
16859
|
);
|
|
16624
|
-
bridgeCommand.exitOverride();
|
|
16625
|
-
for (const cmd of bridgeCommand.commands) {
|
|
16626
|
-
cmd.exitOverride();
|
|
16627
|
-
}
|
|
16628
|
-
let outputBuffer = "";
|
|
16629
|
-
const outputOpts = {
|
|
16630
|
-
writeOut: (str) => {
|
|
16631
|
-
outputBuffer += str;
|
|
16632
|
-
},
|
|
16633
|
-
writeErr: (str) => {
|
|
16634
|
-
outputBuffer += str;
|
|
16635
|
-
}
|
|
16636
|
-
};
|
|
16637
|
-
bridgeCommand.configureOutput(outputOpts);
|
|
16638
|
-
for (const cmd of bridgeCommand.commands) {
|
|
16639
|
-
cmd.configureOutput(outputOpts);
|
|
16640
|
-
}
|
|
16641
16860
|
const processRequest = async (line) => {
|
|
16642
|
-
outputBuffer = "";
|
|
16643
16861
|
let requestId = null;
|
|
16644
16862
|
try {
|
|
16645
16863
|
const req = JSON.parse(line);
|
|
@@ -16659,49 +16877,15 @@ bridgeCommand.command("serve").description("Start the persistent JSON-RPC stdin/
|
|
|
16659
16877
|
error: "Missing 'cmd' field"
|
|
16660
16878
|
});
|
|
16661
16879
|
}
|
|
16662
|
-
const originalLog = console.log;
|
|
16663
|
-
const originalError = console.error;
|
|
16664
|
-
console.log = (...logArgs) => {
|
|
16665
|
-
outputBuffer += `${logArgs.map((a) => typeof a === "object" ? JSON.stringify(a) : String(a)).join(" ")}
|
|
16666
|
-
`;
|
|
16667
|
-
};
|
|
16668
|
-
console.error = (...logArgs) => {
|
|
16669
|
-
outputBuffer += `${logArgs.map((a) => typeof a === "object" ? JSON.stringify(a) : String(a)).join(" ")}
|
|
16670
|
-
`;
|
|
16671
|
-
};
|
|
16672
16880
|
try {
|
|
16673
|
-
await
|
|
16881
|
+
const result = await executeBridgeCommandJson(cmd, args);
|
|
16882
|
+
return JSON.stringify({ id: requestId, result });
|
|
16674
16883
|
} catch (err) {
|
|
16675
|
-
if (err instanceof Error && err.message.startsWith('{"error":')) {
|
|
16676
|
-
try {
|
|
16677
|
-
const parsed = JSON.parse(err.message);
|
|
16678
|
-
return JSON.stringify({ id: requestId, error: parsed.error });
|
|
16679
|
-
} catch {
|
|
16680
|
-
return JSON.stringify({ id: requestId, error: err.message });
|
|
16681
|
-
}
|
|
16682
|
-
}
|
|
16683
|
-
if (err.code?.startsWith("commander.")) {
|
|
16684
|
-
return JSON.stringify({
|
|
16685
|
-
id: requestId,
|
|
16686
|
-
error: outputBuffer.trim() || err.message
|
|
16687
|
-
});
|
|
16688
|
-
}
|
|
16689
16884
|
return JSON.stringify({
|
|
16690
16885
|
id: requestId,
|
|
16691
16886
|
error: err.message || String(err)
|
|
16692
16887
|
});
|
|
16693
|
-
} finally {
|
|
16694
|
-
console.log = originalLog;
|
|
16695
|
-
console.error = originalError;
|
|
16696
|
-
}
|
|
16697
|
-
let result;
|
|
16698
|
-
const trimmed = outputBuffer.trim();
|
|
16699
|
-
try {
|
|
16700
|
-
result = JSON.parse(trimmed);
|
|
16701
|
-
} catch {
|
|
16702
|
-
result = trimmed;
|
|
16703
16888
|
}
|
|
16704
|
-
return JSON.stringify({ id: requestId, result });
|
|
16705
16889
|
} catch (err) {
|
|
16706
16890
|
return JSON.stringify({
|
|
16707
16891
|
id: requestId,
|
|
@@ -18006,26 +18190,26 @@ var doctorCommand = new Command5("doctor").description(
|
|
|
18006
18190
|
// src/cli/commands/git-sync.ts
|
|
18007
18191
|
init_kernel();
|
|
18008
18192
|
import { execSync as execSync5 } from "child_process";
|
|
18009
|
-
import { chmodSync as chmodSync2, existsSync as existsSync18, writeFileSync as
|
|
18010
|
-
import { join as
|
|
18193
|
+
import { chmodSync as chmodSync2, existsSync as existsSync18, writeFileSync as writeFileSync10 } from "fs";
|
|
18194
|
+
import { join as join21 } from "path";
|
|
18011
18195
|
import { Command as Command6 } from "commander";
|
|
18012
18196
|
function installHook2() {
|
|
18013
|
-
const gitDir =
|
|
18197
|
+
const gitDir = join21(process.cwd(), ".git");
|
|
18014
18198
|
if (!existsSync18(gitDir)) {
|
|
18015
18199
|
console.error(
|
|
18016
18200
|
"Error: Current directory is not the root of a Git repository."
|
|
18017
18201
|
);
|
|
18018
18202
|
process.exit(1);
|
|
18019
18203
|
}
|
|
18020
|
-
const hooksDir =
|
|
18021
|
-
const hookPath =
|
|
18204
|
+
const hooksDir = join21(gitDir, "hooks");
|
|
18205
|
+
const hookPath = join21(hooksDir, "post-commit");
|
|
18022
18206
|
const hookContent = `#!/bin/sh
|
|
18023
18207
|
# ZAM Spaced Repetition Auto-Stale Hook
|
|
18024
18208
|
# Triggered automatically on git commits to decay modified concept cards.
|
|
18025
18209
|
zam git-sync --commit HEAD --quiet
|
|
18026
18210
|
`;
|
|
18027
18211
|
try {
|
|
18028
|
-
|
|
18212
|
+
writeFileSync10(hookPath, hookContent, { encoding: "utf-8", flag: "w" });
|
|
18029
18213
|
try {
|
|
18030
18214
|
chmodSync2(hookPath, "755");
|
|
18031
18215
|
} catch (_e) {
|
|
@@ -18128,7 +18312,7 @@ ZAM Auto-Stale Complete: Scanned ${changedFiles.length} file(s).`
|
|
|
18128
18312
|
|
|
18129
18313
|
// src/cli/commands/goal.ts
|
|
18130
18314
|
init_kernel();
|
|
18131
|
-
import { existsSync as existsSync19, mkdirSync as
|
|
18315
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync13 } from "fs";
|
|
18132
18316
|
import { resolve as resolve5 } from "path";
|
|
18133
18317
|
import { input as input2 } from "@inquirer/prompts";
|
|
18134
18318
|
import { Command as Command7 } from "commander";
|
|
@@ -18254,7 +18438,7 @@ ${"\u2500".repeat(50)}`);
|
|
|
18254
18438
|
goalCommand.command("create").description("Create a new goal").option("--slug <slug>", "Goal slug (used as filename)").option("--title <title>", "Goal title").option("--parent <slug>", "Parent goal slug").option("--description <text>", "Goal description").option("--json", "Output as JSON").action(async (opts) => {
|
|
18255
18439
|
const goalsDir = await resolveGoalsDir();
|
|
18256
18440
|
if (!existsSync19(goalsDir)) {
|
|
18257
|
-
|
|
18441
|
+
mkdirSync13(goalsDir, { recursive: true });
|
|
18258
18442
|
}
|
|
18259
18443
|
let slug = opts.slug;
|
|
18260
18444
|
let title = opts.title;
|
|
@@ -18314,9 +18498,9 @@ goalCommand.command("status <slug> <status>").description("Update a goal's statu
|
|
|
18314
18498
|
|
|
18315
18499
|
// src/cli/commands/init.ts
|
|
18316
18500
|
init_kernel();
|
|
18317
|
-
import { existsSync as existsSync20, mkdirSync as
|
|
18501
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync14, writeFileSync as writeFileSync11 } from "fs";
|
|
18318
18502
|
import { homedir as homedir12 } from "os";
|
|
18319
|
-
import { join as
|
|
18503
|
+
import { join as join22, resolve as resolve6 } from "path";
|
|
18320
18504
|
import { confirm, input as input3 } from "@inquirer/prompts";
|
|
18321
18505
|
import { Command as Command8 } from "commander";
|
|
18322
18506
|
var HOME2 = homedir12();
|
|
@@ -18324,12 +18508,12 @@ function printLine(char = "\u2550", len = 60, color = "\x1B[36m") {
|
|
|
18324
18508
|
console.log(`${color}${char.repeat(len)}\x1B[0m`);
|
|
18325
18509
|
}
|
|
18326
18510
|
function bootstrapSandboxWorkspace(workspaceDir) {
|
|
18327
|
-
|
|
18328
|
-
|
|
18329
|
-
|
|
18330
|
-
const worldviewFile =
|
|
18511
|
+
mkdirSync14(join22(workspaceDir, "beliefs"), { recursive: true });
|
|
18512
|
+
mkdirSync14(join22(workspaceDir, "goals"), { recursive: true });
|
|
18513
|
+
mkdirSync14(join22(workspaceDir, "skills"), { recursive: true });
|
|
18514
|
+
const worldviewFile = join22(workspaceDir, "beliefs", "worldview.md");
|
|
18331
18515
|
if (!existsSync20(worldviewFile)) {
|
|
18332
|
-
|
|
18516
|
+
writeFileSync11(
|
|
18333
18517
|
worldviewFile,
|
|
18334
18518
|
`# Personal Worldview
|
|
18335
18519
|
|
|
@@ -18341,9 +18525,9 @@ Here, I declare the core concepts and principles I want to master.
|
|
|
18341
18525
|
"utf8"
|
|
18342
18526
|
);
|
|
18343
18527
|
}
|
|
18344
|
-
const goalsFile =
|
|
18528
|
+
const goalsFile = join22(workspaceDir, "goals", "goals.md");
|
|
18345
18529
|
if (!existsSync20(goalsFile)) {
|
|
18346
|
-
|
|
18530
|
+
writeFileSync11(
|
|
18347
18531
|
goalsFile,
|
|
18348
18532
|
`# Personal Goals
|
|
18349
18533
|
|
|
@@ -18365,7 +18549,7 @@ var initCommand = new Command8("init").description("Launch the guided interactiv
|
|
|
18365
18549
|
);
|
|
18366
18550
|
printLine();
|
|
18367
18551
|
console.log("\n\x1B[1m[1/5] Setting up Local Workspace Sandbox\x1B[0m");
|
|
18368
|
-
const defaultWorkspace =
|
|
18552
|
+
const defaultWorkspace = join22(HOME2, "Documents", "zam");
|
|
18369
18553
|
const workspacePath = resolve6(
|
|
18370
18554
|
await input3({
|
|
18371
18555
|
message: "Choose your ZAM workspace directory:",
|
|
@@ -19567,7 +19751,7 @@ observerCommand.command("revoke <process>").description("Remove a process from o
|
|
|
19567
19751
|
|
|
19568
19752
|
// src/cli/commands/profile.ts
|
|
19569
19753
|
init_kernel();
|
|
19570
|
-
import { dirname as
|
|
19754
|
+
import { dirname as dirname8, resolve as resolve7 } from "path";
|
|
19571
19755
|
import { Command as Command13 } from "commander";
|
|
19572
19756
|
var C2 = {
|
|
19573
19757
|
reset: "\x1B[0m",
|
|
@@ -19608,7 +19792,7 @@ var profileCommand = new Command13("profile").description("Show or change this m
|
|
|
19608
19792
|
mode: getInstallMode(),
|
|
19609
19793
|
personalDir,
|
|
19610
19794
|
syncProvider: detectSyncProvider(personalDir),
|
|
19611
|
-
dataDir:
|
|
19795
|
+
dataDir: dirname8(dbPath),
|
|
19612
19796
|
dbPath
|
|
19613
19797
|
};
|
|
19614
19798
|
if (opts.json) {
|
|
@@ -19997,7 +20181,7 @@ ${"\u2550".repeat(50)}`);
|
|
|
19997
20181
|
|
|
19998
20182
|
// src/cli/commands/session.ts
|
|
19999
20183
|
init_kernel();
|
|
20000
|
-
import { readFileSync as
|
|
20184
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
20001
20185
|
import { input as input6, select as select2 } from "@inquirer/prompts";
|
|
20002
20186
|
import { Command as Command16 } from "commander";
|
|
20003
20187
|
var sessionCommand = new Command16("session").description(
|
|
@@ -20188,7 +20372,7 @@ function loadPatternFile(path) {
|
|
|
20188
20372
|
if (!path) return [];
|
|
20189
20373
|
let parsed;
|
|
20190
20374
|
try {
|
|
20191
|
-
parsed = JSON.parse(
|
|
20375
|
+
parsed = JSON.parse(readFileSync15(path, "utf-8"));
|
|
20192
20376
|
} catch (err) {
|
|
20193
20377
|
throw new Error(
|
|
20194
20378
|
`Cannot read synthesis patterns from ${path}: ${err.message}`
|
|
@@ -20741,8 +20925,8 @@ skillCommand.command("add").description("Register a new agent skill").requiredOp
|
|
|
20741
20925
|
|
|
20742
20926
|
// src/cli/commands/snapshot.ts
|
|
20743
20927
|
init_kernel();
|
|
20744
|
-
import { existsSync as existsSync22, mkdirSync as
|
|
20745
|
-
import { dirname as
|
|
20928
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync15, readFileSync as readFileSync16, writeFileSync as writeFileSync12 } from "fs";
|
|
20929
|
+
import { dirname as dirname9, join as join23 } from "path";
|
|
20746
20930
|
import { Command as Command20 } from "commander";
|
|
20747
20931
|
function defaultOutName() {
|
|
20748
20932
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+Z$/, "").replace(/:/g, "-");
|
|
@@ -20771,12 +20955,12 @@ var exportCmd = new Command20("export").description("Write a portable SQL-text s
|
|
|
20771
20955
|
process.stdout.write(snapshot);
|
|
20772
20956
|
return;
|
|
20773
20957
|
}
|
|
20774
|
-
const out = opts.out ??
|
|
20775
|
-
const dir =
|
|
20958
|
+
const out = opts.out ?? join23(personalDir, "snapshots", defaultOutName());
|
|
20959
|
+
const dir = dirname9(out);
|
|
20776
20960
|
if (dir && dir !== "." && !existsSync22(dir)) {
|
|
20777
|
-
|
|
20961
|
+
mkdirSync15(dir, { recursive: true });
|
|
20778
20962
|
}
|
|
20779
|
-
|
|
20963
|
+
writeFileSync12(out, snapshot, "utf-8");
|
|
20780
20964
|
console.log(`Snapshot written: ${out}`);
|
|
20781
20965
|
console.log(
|
|
20782
20966
|
` ${total} row(s)${nonEmpty.length ? ` \u2014 ${nonEmpty.join(", ")}` : ""}`
|
|
@@ -20794,7 +20978,7 @@ var importCmd = new Command20("import").description("Restore a snapshot into the
|
|
|
20794
20978
|
console.error(`Error: Snapshot file not found: ${file}`);
|
|
20795
20979
|
process.exit(1);
|
|
20796
20980
|
}
|
|
20797
|
-
const snapshot =
|
|
20981
|
+
const snapshot = readFileSync16(file, "utf-8");
|
|
20798
20982
|
db = await openDatabaseWithSync({ initialize: true });
|
|
20799
20983
|
const result = await importSnapshot(db, snapshot, { force: opts.force });
|
|
20800
20984
|
await db.close();
|
|
@@ -20816,7 +21000,7 @@ var verifyCmd = new Command20("verify").description("Check a snapshot's manifest
|
|
|
20816
21000
|
console.error(`Error: Snapshot file not found: ${file}`);
|
|
20817
21001
|
process.exit(1);
|
|
20818
21002
|
}
|
|
20819
|
-
const manifest = verifySnapshot(
|
|
21003
|
+
const manifest = verifySnapshot(readFileSync16(file, "utf-8"));
|
|
20820
21004
|
const { total, nonEmpty } = summarize(manifest.tables);
|
|
20821
21005
|
console.log(`Valid snapshot (format v${manifest.version})`);
|
|
20822
21006
|
console.log(` created: ${manifest.createdAt}`);
|
|
@@ -21367,8 +21551,8 @@ init_kernel();
|
|
|
21367
21551
|
import { spawn as spawn3, spawnSync } from "child_process";
|
|
21368
21552
|
import { existsSync as existsSync23 } from "fs";
|
|
21369
21553
|
import { homedir as homedir13 } from "os";
|
|
21370
|
-
import { dirname as
|
|
21371
|
-
import { fileURLToPath as
|
|
21554
|
+
import { dirname as dirname10, join as join24 } from "path";
|
|
21555
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
21372
21556
|
import { Command as Command23 } from "commander";
|
|
21373
21557
|
var C3 = {
|
|
21374
21558
|
reset: "\x1B[0m",
|
|
@@ -21379,14 +21563,14 @@ var C3 = {
|
|
|
21379
21563
|
dim: "\x1B[2m"
|
|
21380
21564
|
};
|
|
21381
21565
|
function findDesktopDir() {
|
|
21382
|
-
const starts = [process.cwd(),
|
|
21566
|
+
const starts = [process.cwd(), dirname10(fileURLToPath4(import.meta.url))];
|
|
21383
21567
|
for (const start of starts) {
|
|
21384
21568
|
let dir = start;
|
|
21385
21569
|
for (let i = 0; i < 10; i++) {
|
|
21386
|
-
if (existsSync23(
|
|
21387
|
-
return
|
|
21570
|
+
if (existsSync23(join24(dir, "desktop", "src-tauri", "tauri.conf.json"))) {
|
|
21571
|
+
return join24(dir, "desktop");
|
|
21388
21572
|
}
|
|
21389
|
-
const parent =
|
|
21573
|
+
const parent = dirname10(dir);
|
|
21390
21574
|
if (parent === dir) break;
|
|
21391
21575
|
dir = parent;
|
|
21392
21576
|
}
|
|
@@ -21394,18 +21578,18 @@ function findDesktopDir() {
|
|
|
21394
21578
|
return null;
|
|
21395
21579
|
}
|
|
21396
21580
|
function findBuiltApp(desktopDir) {
|
|
21397
|
-
const releaseDir =
|
|
21581
|
+
const releaseDir = join24(desktopDir, "src-tauri", "target", "release");
|
|
21398
21582
|
if (process.platform === "win32") {
|
|
21399
21583
|
for (const name of ["ZAM.exe", "zam.exe", "zam-desktop.exe"]) {
|
|
21400
|
-
const p =
|
|
21584
|
+
const p = join24(releaseDir, name);
|
|
21401
21585
|
if (existsSync23(p)) return p;
|
|
21402
21586
|
}
|
|
21403
21587
|
} else if (process.platform === "darwin") {
|
|
21404
|
-
const app =
|
|
21588
|
+
const app = join24(releaseDir, "bundle", "macos", "ZAM.app");
|
|
21405
21589
|
if (existsSync23(app)) return app;
|
|
21406
21590
|
} else {
|
|
21407
21591
|
for (const name of ["zam", "ZAM", "zam-desktop"]) {
|
|
21408
|
-
const p =
|
|
21592
|
+
const p = join24(releaseDir, name);
|
|
21409
21593
|
if (existsSync23(p)) return p;
|
|
21410
21594
|
}
|
|
21411
21595
|
}
|
|
@@ -21413,10 +21597,10 @@ function findBuiltApp(desktopDir) {
|
|
|
21413
21597
|
}
|
|
21414
21598
|
function findInstalledApp() {
|
|
21415
21599
|
const candidates = process.platform === "win32" ? [
|
|
21416
|
-
process.env.LOCALAPPDATA &&
|
|
21417
|
-
process.env.ProgramFiles &&
|
|
21418
|
-
process.env["ProgramFiles(x86)"] &&
|
|
21419
|
-
] : process.platform === "darwin" ? ["/Applications/ZAM.app",
|
|
21600
|
+
process.env.LOCALAPPDATA && join24(process.env.LOCALAPPDATA, "Programs", "ZAM", "ZAM.exe"),
|
|
21601
|
+
process.env.ProgramFiles && join24(process.env.ProgramFiles, "ZAM", "ZAM.exe"),
|
|
21602
|
+
process.env["ProgramFiles(x86)"] && join24(process.env["ProgramFiles(x86)"], "ZAM", "ZAM.exe")
|
|
21603
|
+
] : process.platform === "darwin" ? ["/Applications/ZAM.app", join24(homedir13(), "Applications", "ZAM.app")] : ["/opt/ZAM/zam", "/usr/bin/zam-desktop"];
|
|
21420
21604
|
return candidates.find((candidate) => candidate && existsSync23(candidate)) || null;
|
|
21421
21605
|
}
|
|
21422
21606
|
function runNpm(args, opts) {
|
|
@@ -21428,7 +21612,7 @@ function runNpm(args, opts) {
|
|
|
21428
21612
|
return res.status ?? 1;
|
|
21429
21613
|
}
|
|
21430
21614
|
function ensureDesktopDeps(desktopDir) {
|
|
21431
|
-
if (existsSync23(
|
|
21615
|
+
if (existsSync23(join24(desktopDir, "node_modules"))) return true;
|
|
21432
21616
|
console.log(
|
|
21433
21617
|
`${C3.cyan}Installing desktop dependencies (one-time)...${C3.reset}`
|
|
21434
21618
|
);
|
|
@@ -21454,7 +21638,7 @@ function requireRust() {
|
|
|
21454
21638
|
function hasMsvcBuildTools() {
|
|
21455
21639
|
if (process.platform !== "win32") return true;
|
|
21456
21640
|
const pf86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
|
21457
|
-
const vswhere =
|
|
21641
|
+
const vswhere = join24(
|
|
21458
21642
|
pf86,
|
|
21459
21643
|
"Microsoft Visual Studio",
|
|
21460
21644
|
"Installer",
|
|
@@ -21492,7 +21676,7 @@ function requireMsvcOnWindows() {
|
|
|
21492
21676
|
return false;
|
|
21493
21677
|
}
|
|
21494
21678
|
function warnIfCliMissing(repoRoot) {
|
|
21495
|
-
if (!existsSync23(
|
|
21679
|
+
if (!existsSync23(join24(repoRoot, "dist", "cli", "index.js"))) {
|
|
21496
21680
|
console.warn(
|
|
21497
21681
|
`${C3.yellow}\u26A0 CLI build not found (dist/cli/index.js). The GUI bridge needs it \u2014 run 'npm run build' at the repo root.${C3.reset}`
|
|
21498
21682
|
);
|
|
@@ -21565,7 +21749,7 @@ var uiCommand = new Command23("ui").description("Launch the ZAM Desktop GUI (Act
|
|
|
21565
21749
|
);
|
|
21566
21750
|
process.exit(1);
|
|
21567
21751
|
}
|
|
21568
|
-
const repoRoot =
|
|
21752
|
+
const repoRoot = dirname10(desktopDir);
|
|
21569
21753
|
if (opts.build) {
|
|
21570
21754
|
if (!requireRust()) process.exit(1);
|
|
21571
21755
|
if (!requireMsvcOnWindows()) process.exit(1);
|
|
@@ -21576,7 +21760,7 @@ var uiCommand = new Command23("ui").description("Launch the ZAM Desktop GUI (Act
|
|
|
21576
21760
|
);
|
|
21577
21761
|
const code = runNpm(["run", "tauri", "build"], { cwd: desktopDir });
|
|
21578
21762
|
if (code === 0) {
|
|
21579
|
-
const bundle =
|
|
21763
|
+
const bundle = join24(
|
|
21580
21764
|
desktopDir,
|
|
21581
21765
|
"src-tauri",
|
|
21582
21766
|
"target",
|
|
@@ -21616,7 +21800,7 @@ ${C3.green}\u2713 Done. Installer is in:${C3.reset}
|
|
|
21616
21800
|
);
|
|
21617
21801
|
process.exit(1);
|
|
21618
21802
|
}
|
|
21619
|
-
createShortcuts(shortcutTarget,
|
|
21803
|
+
createShortcuts(shortcutTarget, dirname10(shortcutTarget));
|
|
21620
21804
|
return;
|
|
21621
21805
|
}
|
|
21622
21806
|
if (builtApp) {
|
|
@@ -21654,12 +21838,11 @@ ${C3.dim}After that, 'zam ui' launches it directly.${C3.reset}`
|
|
|
21654
21838
|
// src/cli/commands/update.ts
|
|
21655
21839
|
init_kernel();
|
|
21656
21840
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
21657
|
-
import { existsSync as existsSync24, readFileSync as
|
|
21658
|
-
import { dirname as
|
|
21659
|
-
import { fileURLToPath as
|
|
21841
|
+
import { existsSync as existsSync24, readFileSync as readFileSync17, realpathSync as realpathSync2 } from "fs";
|
|
21842
|
+
import { dirname as dirname11, join as join25 } from "path";
|
|
21843
|
+
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
21660
21844
|
import { confirm as confirm3 } from "@inquirer/prompts";
|
|
21661
21845
|
import { Command as Command24 } from "commander";
|
|
21662
|
-
var GITHUB_REPO = "zam-os/zam";
|
|
21663
21846
|
var CHANNELS = [
|
|
21664
21847
|
"developer",
|
|
21665
21848
|
"direct",
|
|
@@ -21675,48 +21858,16 @@ var C4 = {
|
|
|
21675
21858
|
red: "\x1B[31m",
|
|
21676
21859
|
yellow: "\x1B[33m"
|
|
21677
21860
|
};
|
|
21678
|
-
function currentVersion() {
|
|
21679
|
-
const here = dirname10(fileURLToPath4(import.meta.url));
|
|
21680
|
-
for (const up of ["..", "../..", "../../.."]) {
|
|
21681
|
-
try {
|
|
21682
|
-
const pkg2 = JSON.parse(
|
|
21683
|
-
readFileSync16(join23(here, up, "package.json"), "utf-8")
|
|
21684
|
-
);
|
|
21685
|
-
if (pkg2.version) return pkg2.version;
|
|
21686
|
-
} catch {
|
|
21687
|
-
}
|
|
21688
|
-
}
|
|
21689
|
-
return "0.0.0";
|
|
21690
|
-
}
|
|
21691
21861
|
function versionAt(dir) {
|
|
21692
21862
|
try {
|
|
21693
21863
|
const pkg2 = JSON.parse(
|
|
21694
|
-
|
|
21864
|
+
readFileSync17(join25(dir, "package.json"), "utf-8")
|
|
21695
21865
|
);
|
|
21696
21866
|
return pkg2.version ?? "unknown";
|
|
21697
21867
|
} catch {
|
|
21698
21868
|
return "unknown";
|
|
21699
21869
|
}
|
|
21700
21870
|
}
|
|
21701
|
-
async function fetchLatestVersion(repo) {
|
|
21702
|
-
const res = await fetch(
|
|
21703
|
-
`https://api.github.com/repos/${repo}/releases/latest`,
|
|
21704
|
-
{
|
|
21705
|
-
headers: {
|
|
21706
|
-
Accept: "application/vnd.github+json",
|
|
21707
|
-
"User-Agent": "zam-cli"
|
|
21708
|
-
}
|
|
21709
|
-
}
|
|
21710
|
-
);
|
|
21711
|
-
if (!res.ok) {
|
|
21712
|
-
throw new Error(
|
|
21713
|
-
`Could not reach the release server (HTTP ${res.status}). Pass --latest <version> to check offline.`
|
|
21714
|
-
);
|
|
21715
|
-
}
|
|
21716
|
-
const data = await res.json();
|
|
21717
|
-
if (!data.tag_name) throw new Error("No published release found yet.");
|
|
21718
|
-
return data.tag_name;
|
|
21719
|
-
}
|
|
21720
21871
|
function render2(decision) {
|
|
21721
21872
|
if (!decision.updateAvailable) {
|
|
21722
21873
|
console.log(
|
|
@@ -21768,14 +21919,14 @@ var checkCmd = new Command24("check").description("Check whether a newer ZAM has
|
|
|
21768
21919
|
}
|
|
21769
21920
|
);
|
|
21770
21921
|
function findSourceRepo() {
|
|
21771
|
-
let dir = realpathSync2(
|
|
21772
|
-
let parent =
|
|
21922
|
+
let dir = realpathSync2(dirname11(fileURLToPath5(import.meta.url)));
|
|
21923
|
+
let parent = dirname11(dir);
|
|
21773
21924
|
while (parent !== dir) {
|
|
21774
|
-
if (existsSync24(
|
|
21925
|
+
if (existsSync24(join25(dir, ".git"))) return dir;
|
|
21775
21926
|
dir = parent;
|
|
21776
|
-
parent =
|
|
21927
|
+
parent = dirname11(dir);
|
|
21777
21928
|
}
|
|
21778
|
-
return existsSync24(
|
|
21929
|
+
return existsSync24(join25(dir, ".git")) ? dir : null;
|
|
21779
21930
|
}
|
|
21780
21931
|
function runGit(cwd, args, capture) {
|
|
21781
21932
|
const res = spawnSync2("git", args, {
|
|
@@ -21796,7 +21947,7 @@ function runNpm2(args, cwd) {
|
|
|
21796
21947
|
function smokeTestBuild(src) {
|
|
21797
21948
|
const res = spawnSync2(
|
|
21798
21949
|
process.execPath,
|
|
21799
|
-
[
|
|
21950
|
+
[join25(src, "dist", "cli", "index.js"), "--version"],
|
|
21800
21951
|
{
|
|
21801
21952
|
cwd: src,
|
|
21802
21953
|
encoding: "utf8",
|
|
@@ -21876,7 +22027,7 @@ Fix it manually in ${src} (try \`npm ci && npm run build\`, and check your Node
|
|
|
21876
22027
|
console.log(`${C4.dim}\u2192 zam setup --force${C4.reset}`);
|
|
21877
22028
|
const setup = spawnSync2(
|
|
21878
22029
|
process.execPath,
|
|
21879
|
-
[
|
|
22030
|
+
[join25(src, "dist", "cli", "index.js"), "setup", "--force"],
|
|
21880
22031
|
{ cwd: process.cwd(), stdio: "inherit" }
|
|
21881
22032
|
);
|
|
21882
22033
|
if (setup.status !== 0) {
|
|
@@ -21989,9 +22140,9 @@ var whoamiCommand = new Command25("whoami").description("Show or set the default
|
|
|
21989
22140
|
// src/cli/commands/workspace.ts
|
|
21990
22141
|
init_kernel();
|
|
21991
22142
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
21992
|
-
import { existsSync as existsSync25, writeFileSync as
|
|
22143
|
+
import { existsSync as existsSync25, writeFileSync as writeFileSync13 } from "fs";
|
|
21993
22144
|
import { homedir as homedir14 } from "os";
|
|
21994
|
-
import { join as
|
|
22145
|
+
import { join as join26, resolve as resolve9 } from "path";
|
|
21995
22146
|
import { confirm as confirm4, input as input7 } from "@inquirer/prompts";
|
|
21996
22147
|
import { Command as Command26 } from "commander";
|
|
21997
22148
|
function runGit2(cwd, args) {
|
|
@@ -22226,15 +22377,15 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
|
|
|
22226
22377
|
);
|
|
22227
22378
|
process.exit(1);
|
|
22228
22379
|
}
|
|
22229
|
-
const gitignorePath =
|
|
22380
|
+
const gitignorePath = join26(workspaceDir, ".gitignore");
|
|
22230
22381
|
if (!existsSync25(gitignorePath)) {
|
|
22231
|
-
|
|
22382
|
+
writeFileSync13(
|
|
22232
22383
|
gitignorePath,
|
|
22233
22384
|
"node_modules/\n.agent/\n.agents/\n.claude/\n.gemini/\n.goose/\n*.log\n",
|
|
22234
22385
|
"utf8"
|
|
22235
22386
|
);
|
|
22236
22387
|
}
|
|
22237
|
-
const hasGitRepo = existsSync25(
|
|
22388
|
+
const hasGitRepo = existsSync25(join26(workspaceDir, ".git"));
|
|
22238
22389
|
if (!hasGitRepo) {
|
|
22239
22390
|
console.log("Initializing local Git repository...");
|
|
22240
22391
|
runGit2(workspaceDir, ["init", "-b", "main"]);
|
|
@@ -22321,7 +22472,7 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
|
|
|
22321
22472
|
}
|
|
22322
22473
|
});
|
|
22323
22474
|
workspaceCommand.command("data-dir").description("Print the ZAM data directory (database, credentials, config)").option("--json", "Output as JSON").action((opts) => {
|
|
22324
|
-
const dir =
|
|
22475
|
+
const dir = join26(homedir14(), ".zam");
|
|
22325
22476
|
console.log(opts.json ? JSON.stringify({ dataDir: dir }) : dir);
|
|
22326
22477
|
});
|
|
22327
22478
|
workspaceCommand.command("backup").description("Back up the local ZAM database into your workspace").option(
|
|
@@ -22362,9 +22513,9 @@ workspaceCommand.command("backup").description("Back up the local ZAM database i
|
|
|
22362
22513
|
});
|
|
22363
22514
|
|
|
22364
22515
|
// src/cli/app.ts
|
|
22365
|
-
var __dirname =
|
|
22516
|
+
var __dirname = dirname12(fileURLToPath6(import.meta.url));
|
|
22366
22517
|
var pkg = JSON.parse(
|
|
22367
|
-
|
|
22518
|
+
readFileSync18(join27(__dirname, "..", "..", "package.json"), "utf-8")
|
|
22368
22519
|
);
|
|
22369
22520
|
var program = new Command27();
|
|
22370
22521
|
program.name("zam").description(
|