zam-core 0.3.11 → 0.3.13
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/index.js +925 -208
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +77 -2
- package/dist/index.js +366 -82
- package/dist/index.js.map +1 -1
- package/package.json +9 -2
- package/templates/personal/beliefs/README.md +1 -1
- package/templates/personal/foundations/README.md +19 -0
package/dist/cli/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
|
-
import { readFileSync as
|
|
5
|
-
import { dirname as dirname10, join as
|
|
4
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
5
|
+
import { dirname as dirname10, join as join21 } from "path";
|
|
6
6
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
7
7
|
import { Command as Command23 } from "commander";
|
|
8
8
|
|
|
9
9
|
// src/cli/commands/agent.ts
|
|
10
|
-
import { existsSync as
|
|
11
|
-
import { join as
|
|
10
|
+
import { existsSync as existsSync11 } from "fs";
|
|
11
|
+
import { join as join10 } from "path";
|
|
12
12
|
import { Command } from "commander";
|
|
13
13
|
|
|
14
14
|
// src/kernel/analytics/stats.ts
|
|
@@ -1360,8 +1360,17 @@ async function deleteCardForUser(db, tokenId, userId) {
|
|
|
1360
1360
|
await db.prepare("DELETE FROM cards WHERE id = ?").run(card.id);
|
|
1361
1361
|
return { card, impact };
|
|
1362
1362
|
}
|
|
1363
|
-
async function getDueCards(db, userId, now) {
|
|
1363
|
+
async function getDueCards(db, userId, now, domain) {
|
|
1364
1364
|
const cutoff = now ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1365
|
+
if (domain) {
|
|
1366
|
+
return await db.prepare(
|
|
1367
|
+
`SELECT c.*, t.slug, t.concept, t.domain, t.bloom_level
|
|
1368
|
+
FROM cards c
|
|
1369
|
+
JOIN tokens t ON t.id = c.token_id
|
|
1370
|
+
WHERE c.user_id = ? AND c.blocked = 0 AND c.due_at <= ? AND t.domain = ?
|
|
1371
|
+
ORDER BY t.bloom_level ASC, c.due_at ASC`
|
|
1372
|
+
).all(userId, cutoff, domain);
|
|
1373
|
+
}
|
|
1365
1374
|
return await db.prepare(
|
|
1366
1375
|
`SELECT c.*, t.slug, t.concept, t.domain, t.bloom_level
|
|
1367
1376
|
FROM cards c
|
|
@@ -1891,11 +1900,11 @@ function analyzeObservation(commands, tokenPatterns) {
|
|
|
1891
1900
|
const ratings = [];
|
|
1892
1901
|
for (const tp of tokenPatterns) {
|
|
1893
1902
|
const matchIndices = [];
|
|
1894
|
-
const
|
|
1903
|
+
const matchedTexts2 = [];
|
|
1895
1904
|
for (let i = 0; i < commands.length; i++) {
|
|
1896
1905
|
if (matchesToken(commands[i].command, tp.patterns)) {
|
|
1897
1906
|
matchIndices.push(i);
|
|
1898
|
-
|
|
1907
|
+
matchedTexts2.push(commands[i].command);
|
|
1899
1908
|
matchedSet.add(i);
|
|
1900
1909
|
}
|
|
1901
1910
|
}
|
|
@@ -1985,7 +1994,7 @@ function analyzeObservation(commands, tokenPatterns) {
|
|
|
1985
1994
|
medianGapMs,
|
|
1986
1995
|
thinkingGapMs
|
|
1987
1996
|
},
|
|
1988
|
-
matchedCommandTexts:
|
|
1997
|
+
matchedCommandTexts: matchedTexts2
|
|
1989
1998
|
});
|
|
1990
1999
|
}
|
|
1991
2000
|
const unmatchedCommands = [];
|
|
@@ -2347,6 +2356,240 @@ async function unblockReady(db, userId) {
|
|
|
2347
2356
|
return { unblocked };
|
|
2348
2357
|
}
|
|
2349
2358
|
|
|
2359
|
+
// src/kernel/observation/ui-observer-io.ts
|
|
2360
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5 } from "fs";
|
|
2361
|
+
import { homedir as homedir4 } from "os";
|
|
2362
|
+
import { join as join5 } from "path";
|
|
2363
|
+
|
|
2364
|
+
// src/kernel/observation/ui-observer.ts
|
|
2365
|
+
var UI_OBSERVATION_PROTOCOL_VERSION = 1;
|
|
2366
|
+
var OBSERVATION_KINDS = /* @__PURE__ */ new Set([
|
|
2367
|
+
"progress",
|
|
2368
|
+
"step-completed",
|
|
2369
|
+
"error",
|
|
2370
|
+
"help-seeking",
|
|
2371
|
+
"uncertain",
|
|
2372
|
+
"privacy-pause",
|
|
2373
|
+
"heartbeat"
|
|
2374
|
+
]);
|
|
2375
|
+
var ACTION_TYPES = /* @__PURE__ */ new Set([
|
|
2376
|
+
"click",
|
|
2377
|
+
"shortcut",
|
|
2378
|
+
"typing",
|
|
2379
|
+
"scroll",
|
|
2380
|
+
"window-change"
|
|
2381
|
+
]);
|
|
2382
|
+
var EVIDENCE_TYPES = /* @__PURE__ */ new Set([
|
|
2383
|
+
"uia",
|
|
2384
|
+
"keyframe",
|
|
2385
|
+
"clip",
|
|
2386
|
+
"window"
|
|
2387
|
+
]);
|
|
2388
|
+
function isUiObservationReport(value) {
|
|
2389
|
+
if (!isRecord(value)) return false;
|
|
2390
|
+
if (value.version !== UI_OBSERVATION_PROTOCOL_VERSION) return false;
|
|
2391
|
+
if (!isNonEmptyString(value.sessionId)) return false;
|
|
2392
|
+
if (!isNonNegativeInteger(value.sequence)) return false;
|
|
2393
|
+
if (!isNonEmptyString(value.observedFrom)) return false;
|
|
2394
|
+
if (!isNonEmptyString(value.observedTo)) return false;
|
|
2395
|
+
if (typeof value.kind !== "string" || !OBSERVATION_KINDS.has(value.kind)) {
|
|
2396
|
+
return false;
|
|
2397
|
+
}
|
|
2398
|
+
if (!isApplication(value.application)) return false;
|
|
2399
|
+
if (!isNonEmptyString(value.summary)) return false;
|
|
2400
|
+
if (!Array.isArray(value.actions) || !value.actions.every(isObservedAction)) {
|
|
2401
|
+
return false;
|
|
2402
|
+
}
|
|
2403
|
+
if (!Array.isArray(value.evidence) || !value.evidence.every(isEvidenceRef)) {
|
|
2404
|
+
return false;
|
|
2405
|
+
}
|
|
2406
|
+
if (!Array.isArray(value.candidateTokens) || !value.candidateTokens.every(isCandidateToken)) {
|
|
2407
|
+
return false;
|
|
2408
|
+
}
|
|
2409
|
+
return isConfidence(value.confidence);
|
|
2410
|
+
}
|
|
2411
|
+
function parseUiObservationLog(jsonl) {
|
|
2412
|
+
const reports = [];
|
|
2413
|
+
for (const line of jsonl.split("\n")) {
|
|
2414
|
+
const trimmed = line.trim();
|
|
2415
|
+
if (!trimmed) continue;
|
|
2416
|
+
try {
|
|
2417
|
+
const value = JSON.parse(trimmed);
|
|
2418
|
+
if (isUiObservationReport(value)) {
|
|
2419
|
+
reports.push(value);
|
|
2420
|
+
}
|
|
2421
|
+
} catch {
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
return reports.sort((left, right) => left.sequence - right.sequence);
|
|
2425
|
+
}
|
|
2426
|
+
function isApplication(value) {
|
|
2427
|
+
if (!isRecord(value) || !isNonEmptyString(value.processName)) return false;
|
|
2428
|
+
if (value.processId !== void 0 && !isNonNegativeInteger(value.processId)) {
|
|
2429
|
+
return false;
|
|
2430
|
+
}
|
|
2431
|
+
return value.windowTitle === void 0 || typeof value.windowTitle === "string";
|
|
2432
|
+
}
|
|
2433
|
+
function isObservedAction(value) {
|
|
2434
|
+
if (!isRecord(value) || typeof value.type !== "string") return false;
|
|
2435
|
+
if (!ACTION_TYPES.has(value.type)) return false;
|
|
2436
|
+
if (value.target !== void 0 && typeof value.target !== "string") {
|
|
2437
|
+
return false;
|
|
2438
|
+
}
|
|
2439
|
+
return value.result === void 0 || typeof value.result === "string";
|
|
2440
|
+
}
|
|
2441
|
+
function isEvidenceRef(value) {
|
|
2442
|
+
if (!isRecord(value) || typeof value.type !== "string") return false;
|
|
2443
|
+
return EVIDENCE_TYPES.has(value.type) && isNonEmptyString(value.ref) && typeof value.redacted === "boolean";
|
|
2444
|
+
}
|
|
2445
|
+
function isCandidateToken(value) {
|
|
2446
|
+
return isRecord(value) && isNonEmptyString(value.slug) && isConfidence(value.confidence) && isNonEmptyString(value.rationale);
|
|
2447
|
+
}
|
|
2448
|
+
function isRecord(value) {
|
|
2449
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2450
|
+
}
|
|
2451
|
+
function isNonEmptyString(value) {
|
|
2452
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
2453
|
+
}
|
|
2454
|
+
function isNonNegativeInteger(value) {
|
|
2455
|
+
return Number.isInteger(value) && Number(value) >= 0;
|
|
2456
|
+
}
|
|
2457
|
+
function isConfidence(value) {
|
|
2458
|
+
return typeof value === "number" && value >= 0 && value <= 1;
|
|
2459
|
+
}
|
|
2460
|
+
|
|
2461
|
+
// src/kernel/observation/ui-observer-io.ts
|
|
2462
|
+
var DEFAULT_UI_OBSERVER_DIR = join5(homedir4(), ".zam", "observer");
|
|
2463
|
+
function getUiObserverDir() {
|
|
2464
|
+
return process.env.ZAM_OBSERVER_DIR || DEFAULT_UI_OBSERVER_DIR;
|
|
2465
|
+
}
|
|
2466
|
+
function getUiObservationPath(sessionId) {
|
|
2467
|
+
if (!/^[A-Za-z0-9_-]+$/.test(sessionId)) {
|
|
2468
|
+
throw new Error(`Invalid observer session ID: ${sessionId}`);
|
|
2469
|
+
}
|
|
2470
|
+
return join5(getUiObserverDir(), `${sessionId}.reports.jsonl`);
|
|
2471
|
+
}
|
|
2472
|
+
function ensureUiObserverDir() {
|
|
2473
|
+
const dir = getUiObserverDir();
|
|
2474
|
+
if (!existsSync5(dir)) {
|
|
2475
|
+
mkdirSync4(dir, { recursive: true, mode: 448 });
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
function uiObservationLogExists(sessionId) {
|
|
2479
|
+
return existsSync5(getUiObservationPath(sessionId));
|
|
2480
|
+
}
|
|
2481
|
+
function readUiObservationLog(sessionId) {
|
|
2482
|
+
const path = getUiObservationPath(sessionId);
|
|
2483
|
+
if (!existsSync5(path)) return [];
|
|
2484
|
+
return parseUiObservationLog(readFileSync5(path, "utf8"));
|
|
2485
|
+
}
|
|
2486
|
+
function appendUiObservationReport(report) {
|
|
2487
|
+
if (!isUiObservationReport(report)) {
|
|
2488
|
+
throw new Error("Invalid UI observation report");
|
|
2489
|
+
}
|
|
2490
|
+
ensureUiObserverDir();
|
|
2491
|
+
appendFileSync2(
|
|
2492
|
+
getUiObservationPath(report.sessionId),
|
|
2493
|
+
`${JSON.stringify(report)}
|
|
2494
|
+
`,
|
|
2495
|
+
{ encoding: "utf8", mode: 384 }
|
|
2496
|
+
);
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
// src/kernel/observation/ui-observer-synthesis.ts
|
|
2500
|
+
var MIN_UI_CONFIDENCE = 0.6;
|
|
2501
|
+
function kindToRating(kind) {
|
|
2502
|
+
switch (kind) {
|
|
2503
|
+
case "step-completed":
|
|
2504
|
+
return 4;
|
|
2505
|
+
case "progress":
|
|
2506
|
+
return 3;
|
|
2507
|
+
case "error":
|
|
2508
|
+
case "help-seeking":
|
|
2509
|
+
return 2;
|
|
2510
|
+
default:
|
|
2511
|
+
return null;
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
function toSynthesisConfidence(confidence) {
|
|
2515
|
+
return confidence >= 0.85 ? "high" : "medium";
|
|
2516
|
+
}
|
|
2517
|
+
function confidenceRank(confidence) {
|
|
2518
|
+
return confidence === "high" ? 2 : 1;
|
|
2519
|
+
}
|
|
2520
|
+
function buildEvidence(report) {
|
|
2521
|
+
return {
|
|
2522
|
+
matchedCommands: report.actions.length,
|
|
2523
|
+
helpSeeking: report.kind === "help-seeking",
|
|
2524
|
+
errorCount: report.kind === "error" ? 1 : 0,
|
|
2525
|
+
selfCorrections: 0,
|
|
2526
|
+
medianGapMs: null,
|
|
2527
|
+
thinkingGapMs: null
|
|
2528
|
+
};
|
|
2529
|
+
}
|
|
2530
|
+
function matchedTexts(report) {
|
|
2531
|
+
const texts = [report.summary];
|
|
2532
|
+
for (const action of report.actions) {
|
|
2533
|
+
const label = [action.type, action.target, action.result].filter(Boolean).join(" ");
|
|
2534
|
+
if (label) texts.push(label);
|
|
2535
|
+
}
|
|
2536
|
+
return texts;
|
|
2537
|
+
}
|
|
2538
|
+
function buildUiSynthesisCandidates(reports, tokens, applied, minConfidence) {
|
|
2539
|
+
const minRank = confidenceRank(minConfidence);
|
|
2540
|
+
const bestBySlug = /* @__PURE__ */ new Map();
|
|
2541
|
+
let skippedLowConfidence = 0;
|
|
2542
|
+
for (const report of reports) {
|
|
2543
|
+
if (report.confidence < MIN_UI_CONFIDENCE) continue;
|
|
2544
|
+
const fallbackRating = kindToRating(report.kind);
|
|
2545
|
+
const candidates2 = report.candidateTokens.length > 0 ? report.candidateTokens.map((candidate) => ({
|
|
2546
|
+
slug: candidate.slug,
|
|
2547
|
+
confidence: candidate.confidence,
|
|
2548
|
+
rating: fallbackRating
|
|
2549
|
+
})) : [];
|
|
2550
|
+
for (const candidate of candidates2) {
|
|
2551
|
+
const token = tokens.get(candidate.slug);
|
|
2552
|
+
if (!token || applied.has(token.id)) continue;
|
|
2553
|
+
const synthesisConfidence = toSynthesisConfidence(candidate.confidence);
|
|
2554
|
+
if (confidenceRank(synthesisConfidence) < minRank) {
|
|
2555
|
+
skippedLowConfidence++;
|
|
2556
|
+
continue;
|
|
2557
|
+
}
|
|
2558
|
+
const inferredRating = candidate.rating ?? 3;
|
|
2559
|
+
const existing = bestBySlug.get(candidate.slug);
|
|
2560
|
+
if (existing && existing.reportConfidence >= candidate.confidence) {
|
|
2561
|
+
continue;
|
|
2562
|
+
}
|
|
2563
|
+
bestBySlug.set(candidate.slug, {
|
|
2564
|
+
token,
|
|
2565
|
+
inferredRating,
|
|
2566
|
+
confidence: synthesisConfidence,
|
|
2567
|
+
evidence: buildEvidence(report),
|
|
2568
|
+
matchedCommandTexts: matchedTexts(report),
|
|
2569
|
+
reportConfidence: candidate.confidence
|
|
2570
|
+
});
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
const candidates = [...bestBySlug.values()].sort((left, right) => right.reportConfidence - left.reportConfidence).map((entry) => ({
|
|
2574
|
+
tokenId: entry.token.id,
|
|
2575
|
+
tokenSlug: entry.token.slug,
|
|
2576
|
+
concept: entry.token.concept,
|
|
2577
|
+
domain: entry.token.domain,
|
|
2578
|
+
inferredRating: entry.inferredRating,
|
|
2579
|
+
confidence: entry.confidence,
|
|
2580
|
+
evidence: entry.evidence,
|
|
2581
|
+
matchedCommandTexts: entry.matchedCommandTexts
|
|
2582
|
+
}));
|
|
2583
|
+
return { candidates, skippedLowConfidence };
|
|
2584
|
+
}
|
|
2585
|
+
function uiObservationTimeSpan(reports) {
|
|
2586
|
+
if (reports.length === 0) return null;
|
|
2587
|
+
const start = reports[0].observedFrom;
|
|
2588
|
+
const end = reports[reports.length - 1].observedTo;
|
|
2589
|
+
const durationMs = Math.max(0, Date.parse(end) - Date.parse(start));
|
|
2590
|
+
return { start, end, durationMs };
|
|
2591
|
+
}
|
|
2592
|
+
|
|
2350
2593
|
// src/kernel/observation/session-synthesis.ts
|
|
2351
2594
|
function parseSynthesisRow(row) {
|
|
2352
2595
|
return {
|
|
@@ -2354,7 +2597,7 @@ function parseSynthesisRow(row) {
|
|
|
2354
2597
|
evidence: JSON.parse(row.evidence)
|
|
2355
2598
|
};
|
|
2356
2599
|
}
|
|
2357
|
-
function
|
|
2600
|
+
function confidenceRank2(confidence) {
|
|
2358
2601
|
return confidence === "high" ? 2 : confidence === "medium" ? 1 : 0;
|
|
2359
2602
|
}
|
|
2360
2603
|
function normalizeSkillStep(step) {
|
|
@@ -2416,20 +2659,50 @@ async function prepareSessionSynthesis(db, input8) {
|
|
|
2416
2659
|
validPatterns.push(pattern);
|
|
2417
2660
|
tokens.set(pattern.slug, token);
|
|
2418
2661
|
}
|
|
2419
|
-
const commands = input8.commands ?? pairCommands(readMonitorLog(input8.sessionId));
|
|
2420
|
-
const analysis = analyzeObservation(commands, validPatterns);
|
|
2421
2662
|
const applied = new Set(
|
|
2422
2663
|
(await getSessionSynthesisRecords(db, input8.sessionId)).map(
|
|
2423
2664
|
(record) => record.token_id
|
|
2424
2665
|
)
|
|
2425
2666
|
);
|
|
2426
|
-
const
|
|
2667
|
+
const minConfidence = input8.minConfidence ?? "medium";
|
|
2668
|
+
if (session.execution_context === "ui") {
|
|
2669
|
+
const reports = readUiObservationLog(input8.sessionId);
|
|
2670
|
+
for (const report of reports) {
|
|
2671
|
+
for (const candidate of report.candidateTokens) {
|
|
2672
|
+
if (tokens.has(candidate.slug)) continue;
|
|
2673
|
+
const token = await getTokenBySlug(db, candidate.slug);
|
|
2674
|
+
if (token && !token.deprecated_at) {
|
|
2675
|
+
tokens.set(candidate.slug, token);
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
const { candidates: candidates2, skippedLowConfidence: skippedLowConfidence2 } = buildUiSynthesisCandidates(
|
|
2680
|
+
reports,
|
|
2681
|
+
tokens,
|
|
2682
|
+
applied,
|
|
2683
|
+
minConfidence
|
|
2684
|
+
);
|
|
2685
|
+
return {
|
|
2686
|
+
sessionId: session.id,
|
|
2687
|
+
userId: session.user_id,
|
|
2688
|
+
patternCount: validPatterns.length,
|
|
2689
|
+
commandCount: reports.length,
|
|
2690
|
+
alreadyApplied: applied.size,
|
|
2691
|
+
skippedLowConfidence: skippedLowConfidence2,
|
|
2692
|
+
candidates: candidates2,
|
|
2693
|
+
unmatchedCommands: [],
|
|
2694
|
+
timeSpan: uiObservationTimeSpan(reports)
|
|
2695
|
+
};
|
|
2696
|
+
}
|
|
2697
|
+
const commands = input8.commands ?? pairCommands(readMonitorLog(input8.sessionId));
|
|
2698
|
+
const analysis = analyzeObservation(commands, validPatterns);
|
|
2699
|
+
const minRank = confidenceRank2(minConfidence);
|
|
2427
2700
|
let skippedLowConfidence = 0;
|
|
2428
2701
|
const candidates = [];
|
|
2429
2702
|
for (const rating of analysis.ratings) {
|
|
2430
2703
|
const token = tokens.get(rating.tokenSlug);
|
|
2431
2704
|
if (!token || rating.rating == null || applied.has(token.id)) continue;
|
|
2432
|
-
if (
|
|
2705
|
+
if (confidenceRank2(rating.confidence) < minRank) {
|
|
2433
2706
|
skippedLowConfidence++;
|
|
2434
2707
|
continue;
|
|
2435
2708
|
}
|
|
@@ -3026,8 +3299,8 @@ function generatePrompt(input8) {
|
|
|
3026
3299
|
}
|
|
3027
3300
|
|
|
3028
3301
|
// src/kernel/recall/reference-resolver.ts
|
|
3029
|
-
import { existsSync as
|
|
3030
|
-
import { dirname as dirname3, join as
|
|
3302
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
3303
|
+
import { dirname as dirname3, join as join6, resolve } from "path";
|
|
3031
3304
|
var DEFAULT_REVIEW_CONTEXT_MAX_CHARS = 6e3;
|
|
3032
3305
|
function htmlToText(html) {
|
|
3033
3306
|
let content = html;
|
|
@@ -3088,11 +3361,11 @@ async function resolveReference(sourceLink) {
|
|
|
3088
3361
|
const filePath = anchorIndex2 !== -1 ? fullPathWithAnchor.slice(0, anchorIndex2) : fullPathWithAnchor;
|
|
3089
3362
|
const anchor2 = anchorIndex2 !== -1 ? fullPathWithAnchor.slice(anchorIndex2) : "";
|
|
3090
3363
|
const parentDir = dirname3(process.cwd());
|
|
3091
|
-
const localRepoPath =
|
|
3092
|
-
const localFilePath =
|
|
3093
|
-
if (
|
|
3364
|
+
const localRepoPath = join6(parentDir, repo);
|
|
3365
|
+
const localFilePath = join6(localRepoPath, filePath);
|
|
3366
|
+
if (existsSync6(localFilePath)) {
|
|
3094
3367
|
try {
|
|
3095
|
-
let fileContent =
|
|
3368
|
+
let fileContent = readFileSync6(localFilePath, "utf-8");
|
|
3096
3369
|
if (anchor2) {
|
|
3097
3370
|
fileContent = extractLines(fileContent, anchor2);
|
|
3098
3371
|
}
|
|
@@ -3146,9 +3419,9 @@ Link: ${cleaned}`,
|
|
|
3146
3419
|
const relativePath = anchorIndex !== -1 ? cleaned.slice(0, anchorIndex) : cleaned;
|
|
3147
3420
|
const anchor = anchorIndex !== -1 ? cleaned.slice(anchorIndex) : "";
|
|
3148
3421
|
const absolutePath = resolve(process.cwd(), relativePath);
|
|
3149
|
-
if (
|
|
3422
|
+
if (existsSync6(absolutePath)) {
|
|
3150
3423
|
try {
|
|
3151
|
-
let fileContent =
|
|
3424
|
+
let fileContent = readFileSync6(absolutePath, "utf-8");
|
|
3152
3425
|
if (anchor) {
|
|
3153
3426
|
fileContent = extractLines(fileContent, anchor);
|
|
3154
3427
|
}
|
|
@@ -3400,44 +3673,44 @@ function intersperseNew(reviews, newCards, interval) {
|
|
|
3400
3673
|
|
|
3401
3674
|
// src/kernel/system/hooks.ts
|
|
3402
3675
|
import {
|
|
3403
|
-
appendFileSync as
|
|
3676
|
+
appendFileSync as appendFileSync3,
|
|
3404
3677
|
copyFileSync,
|
|
3405
|
-
existsSync as
|
|
3406
|
-
mkdirSync as
|
|
3407
|
-
readFileSync as
|
|
3678
|
+
existsSync as existsSync7,
|
|
3679
|
+
mkdirSync as mkdirSync5,
|
|
3680
|
+
readFileSync as readFileSync7,
|
|
3408
3681
|
writeFileSync as writeFileSync3
|
|
3409
3682
|
} from "fs";
|
|
3410
|
-
import { homedir as
|
|
3411
|
-
import { join as
|
|
3683
|
+
import { homedir as homedir5 } from "os";
|
|
3684
|
+
import { join as join7 } from "path";
|
|
3412
3685
|
import { fileURLToPath } from "url";
|
|
3413
|
-
var HOME =
|
|
3686
|
+
var HOME = homedir5();
|
|
3414
3687
|
function getPackageSkillPath(agent = "default") {
|
|
3415
3688
|
const packageRoot2 = [
|
|
3416
3689
|
fileURLToPath(new URL("../../..", import.meta.url)),
|
|
3417
3690
|
fileURLToPath(new URL("../..", import.meta.url)),
|
|
3418
3691
|
fileURLToPath(new URL("..", import.meta.url))
|
|
3419
|
-
].find((candidate) =>
|
|
3692
|
+
].find((candidate) => existsSync7(join7(candidate, "package.json"))) ?? "";
|
|
3420
3693
|
if (!packageRoot2) return "";
|
|
3421
3694
|
if (agent === "codex") {
|
|
3422
|
-
const codexPath =
|
|
3423
|
-
if (
|
|
3695
|
+
const codexPath = join7(packageRoot2, ".agents", "skills", "zam", "SKILL.md");
|
|
3696
|
+
if (existsSync7(codexPath)) return codexPath;
|
|
3424
3697
|
return "";
|
|
3425
3698
|
}
|
|
3426
3699
|
if (agent === "claude") {
|
|
3427
|
-
const claudePath =
|
|
3700
|
+
const claudePath = join7(
|
|
3428
3701
|
packageRoot2,
|
|
3429
3702
|
".claude",
|
|
3430
3703
|
"skills",
|
|
3431
3704
|
"zam",
|
|
3432
3705
|
"SKILL.md"
|
|
3433
3706
|
);
|
|
3434
|
-
if (
|
|
3707
|
+
if (existsSync7(claudePath)) return claudePath;
|
|
3435
3708
|
return "";
|
|
3436
3709
|
}
|
|
3437
|
-
let path =
|
|
3438
|
-
if (
|
|
3439
|
-
path =
|
|
3440
|
-
if (
|
|
3710
|
+
let path = join7(packageRoot2, ".agent", "skills", "zam", "SKILL.md");
|
|
3711
|
+
if (existsSync7(path)) return path;
|
|
3712
|
+
path = join7(packageRoot2, ".claude", "skills", "zam", "SKILL.md");
|
|
3713
|
+
if (existsSync7(path)) return path;
|
|
3441
3714
|
return "";
|
|
3442
3715
|
}
|
|
3443
3716
|
function distributeGlobalSkills(home = HOME) {
|
|
@@ -3449,16 +3722,16 @@ function distributeGlobalSkills(home = HOME) {
|
|
|
3449
3722
|
console.warn("Could not find ZAM source SKILL.md in the package folder.");
|
|
3450
3723
|
return results;
|
|
3451
3724
|
}
|
|
3452
|
-
const claudeSkillsDir =
|
|
3725
|
+
const claudeSkillsDir = join7(home, ".claude", "skills", "zam");
|
|
3453
3726
|
try {
|
|
3454
3727
|
if (!claudeSourceSkill) {
|
|
3455
3728
|
throw new Error("Claude skill source not found");
|
|
3456
3729
|
}
|
|
3457
|
-
|
|
3458
|
-
copyFileSync(claudeSourceSkill,
|
|
3730
|
+
mkdirSync5(claudeSkillsDir, { recursive: true });
|
|
3731
|
+
copyFileSync(claudeSourceSkill, join7(claudeSkillsDir, "SKILL.md"));
|
|
3459
3732
|
results.push({
|
|
3460
3733
|
name: "Claude Code Global",
|
|
3461
|
-
path:
|
|
3734
|
+
path: join7(claudeSkillsDir, "SKILL.md"),
|
|
3462
3735
|
success: true
|
|
3463
3736
|
});
|
|
3464
3737
|
} catch (_err) {
|
|
@@ -3468,13 +3741,13 @@ function distributeGlobalSkills(home = HOME) {
|
|
|
3468
3741
|
success: false
|
|
3469
3742
|
});
|
|
3470
3743
|
}
|
|
3471
|
-
const geminiSkillsDir =
|
|
3744
|
+
const geminiSkillsDir = join7(home, ".gemini", "skills", "zam");
|
|
3472
3745
|
try {
|
|
3473
|
-
|
|
3474
|
-
copyFileSync(sourceSkill,
|
|
3746
|
+
mkdirSync5(geminiSkillsDir, { recursive: true });
|
|
3747
|
+
copyFileSync(sourceSkill, join7(geminiSkillsDir, "SKILL.md"));
|
|
3475
3748
|
results.push({
|
|
3476
3749
|
name: "Gemini CLI Global",
|
|
3477
|
-
path:
|
|
3750
|
+
path: join7(geminiSkillsDir, "SKILL.md"),
|
|
3478
3751
|
success: true
|
|
3479
3752
|
});
|
|
3480
3753
|
} catch (_err) {
|
|
@@ -3484,16 +3757,16 @@ function distributeGlobalSkills(home = HOME) {
|
|
|
3484
3757
|
success: false
|
|
3485
3758
|
});
|
|
3486
3759
|
}
|
|
3487
|
-
const codexSkillsDir =
|
|
3760
|
+
const codexSkillsDir = join7(home, ".agents", "skills", "zam");
|
|
3488
3761
|
try {
|
|
3489
3762
|
if (!codexSourceSkill) {
|
|
3490
3763
|
throw new Error("Codex skill source not found");
|
|
3491
3764
|
}
|
|
3492
|
-
|
|
3493
|
-
copyFileSync(codexSourceSkill,
|
|
3765
|
+
mkdirSync5(codexSkillsDir, { recursive: true });
|
|
3766
|
+
copyFileSync(codexSourceSkill, join7(codexSkillsDir, "SKILL.md"));
|
|
3494
3767
|
results.push({
|
|
3495
3768
|
name: "Codex Global",
|
|
3496
|
-
path:
|
|
3769
|
+
path: join7(codexSkillsDir, "SKILL.md"),
|
|
3497
3770
|
success: true
|
|
3498
3771
|
});
|
|
3499
3772
|
} catch (_err) {
|
|
@@ -3503,13 +3776,13 @@ function distributeGlobalSkills(home = HOME) {
|
|
|
3503
3776
|
success: false
|
|
3504
3777
|
});
|
|
3505
3778
|
}
|
|
3506
|
-
const gooseSkillsDir =
|
|
3779
|
+
const gooseSkillsDir = join7(home, ".goose", "skills", "zam");
|
|
3507
3780
|
try {
|
|
3508
|
-
|
|
3509
|
-
copyFileSync(sourceSkill,
|
|
3781
|
+
mkdirSync5(gooseSkillsDir, { recursive: true });
|
|
3782
|
+
copyFileSync(sourceSkill, join7(gooseSkillsDir, "SKILL.md"));
|
|
3510
3783
|
results.push({
|
|
3511
3784
|
name: "Goose Global",
|
|
3512
|
-
path:
|
|
3785
|
+
path: join7(gooseSkillsDir, "SKILL.md"),
|
|
3513
3786
|
success: true
|
|
3514
3787
|
});
|
|
3515
3788
|
} catch (_err) {
|
|
@@ -3553,14 +3826,14 @@ function Start-ZamMonitor {
|
|
|
3553
3826
|
`;
|
|
3554
3827
|
function installHook(file, hook, oldHook) {
|
|
3555
3828
|
try {
|
|
3556
|
-
const content =
|
|
3829
|
+
const content = existsSync7(file) ? readFileSync7(file, "utf8") : "";
|
|
3557
3830
|
if (content.includes(HOOK_MARKER)) {
|
|
3558
3831
|
return { success: true, alreadyHooked: true };
|
|
3559
3832
|
}
|
|
3560
3833
|
if (content.includes(oldHook.trim())) {
|
|
3561
3834
|
writeFileSync3(file, content.replace(oldHook.trim(), hook.trim()), "utf8");
|
|
3562
3835
|
} else {
|
|
3563
|
-
|
|
3836
|
+
appendFileSync3(file, hook);
|
|
3564
3837
|
}
|
|
3565
3838
|
return { success: true, alreadyHooked: false };
|
|
3566
3839
|
} catch {
|
|
@@ -3569,24 +3842,24 @@ function installHook(file, hook, oldHook) {
|
|
|
3569
3842
|
}
|
|
3570
3843
|
function injectShellHooks(home = HOME) {
|
|
3571
3844
|
const results = [];
|
|
3572
|
-
const zshrc =
|
|
3573
|
-
if (
|
|
3845
|
+
const zshrc = join7(home, ".zshrc");
|
|
3846
|
+
if (existsSync7(zshrc)) {
|
|
3574
3847
|
const status = installHook(zshrc, posixHook("zsh"), POSIX_OLD_HOOK);
|
|
3575
3848
|
results.push({ shell: "zsh", file: zshrc, ...status });
|
|
3576
3849
|
}
|
|
3577
|
-
const bashrc =
|
|
3578
|
-
if (
|
|
3850
|
+
const bashrc = join7(home, ".bashrc");
|
|
3851
|
+
if (existsSync7(bashrc)) {
|
|
3579
3852
|
const status = installHook(bashrc, posixHook("bash"), POSIX_OLD_HOOK);
|
|
3580
3853
|
results.push({ shell: "bash", file: bashrc, ...status });
|
|
3581
3854
|
}
|
|
3582
3855
|
const pwshDirs = [
|
|
3583
|
-
|
|
3584
|
-
|
|
3856
|
+
join7(home, "Documents", "PowerShell"),
|
|
3857
|
+
join7(home, "Documents", "WindowsPowerShell")
|
|
3585
3858
|
];
|
|
3586
3859
|
for (const dir of pwshDirs) {
|
|
3587
|
-
const profileFile =
|
|
3860
|
+
const profileFile = join7(dir, "Microsoft.PowerShell_profile.ps1");
|
|
3588
3861
|
try {
|
|
3589
|
-
|
|
3862
|
+
mkdirSync5(dir, { recursive: true });
|
|
3590
3863
|
const status = installHook(
|
|
3591
3864
|
profileFile,
|
|
3592
3865
|
POWERSHELL_HOOK,
|
|
@@ -3818,21 +4091,21 @@ function t(locale, key, params = {}) {
|
|
|
3818
4091
|
}
|
|
3819
4092
|
|
|
3820
4093
|
// src/kernel/system/install-config.ts
|
|
3821
|
-
import { existsSync as
|
|
3822
|
-
import { homedir as
|
|
3823
|
-
import { dirname as dirname4, join as
|
|
3824
|
-
var DEFAULT_CONFIG_PATH =
|
|
4094
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
|
|
4095
|
+
import { homedir as homedir6 } from "os";
|
|
4096
|
+
import { dirname as dirname4, join as join8 } from "path";
|
|
4097
|
+
var DEFAULT_CONFIG_PATH = join8(homedir6(), ".zam", "config.json");
|
|
3825
4098
|
function loadInstallConfig(path = DEFAULT_CONFIG_PATH) {
|
|
3826
|
-
if (!
|
|
4099
|
+
if (!existsSync8(path)) return {};
|
|
3827
4100
|
try {
|
|
3828
|
-
return JSON.parse(
|
|
4101
|
+
return JSON.parse(readFileSync8(path, "utf-8"));
|
|
3829
4102
|
} catch {
|
|
3830
4103
|
return {};
|
|
3831
4104
|
}
|
|
3832
4105
|
}
|
|
3833
4106
|
function saveInstallConfig(config, path = DEFAULT_CONFIG_PATH) {
|
|
3834
4107
|
const dir = dirname4(path);
|
|
3835
|
-
if (!
|
|
4108
|
+
if (!existsSync8(dir)) mkdirSync6(dir, { recursive: true });
|
|
3836
4109
|
writeFileSync4(path, `${JSON.stringify(config, null, 2)}
|
|
3837
4110
|
`, "utf-8");
|
|
3838
4111
|
}
|
|
@@ -3864,9 +4137,9 @@ function detectSyncProvider(dir) {
|
|
|
3864
4137
|
|
|
3865
4138
|
// src/kernel/system/installer.ts
|
|
3866
4139
|
import { execFileSync, execSync } from "child_process";
|
|
3867
|
-
import { existsSync as
|
|
3868
|
-
import { homedir as
|
|
3869
|
-
import { join as
|
|
4140
|
+
import { existsSync as existsSync9 } from "fs";
|
|
4141
|
+
import { homedir as homedir7 } from "os";
|
|
4142
|
+
import { join as join9 } from "path";
|
|
3870
4143
|
function hasCommand(cmd) {
|
|
3871
4144
|
try {
|
|
3872
4145
|
const checkCmd2 = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
|
|
@@ -3883,7 +4156,7 @@ function installFastFlowLM() {
|
|
|
3883
4156
|
message: "FastFlowLM is only supported on Windows."
|
|
3884
4157
|
};
|
|
3885
4158
|
}
|
|
3886
|
-
const hasFlm = hasCommand("flm") ||
|
|
4159
|
+
const hasFlm = hasCommand("flm") || existsSync9("C:\\Program Files\\flm\\flm.exe");
|
|
3887
4160
|
if (hasFlm) {
|
|
3888
4161
|
return { success: true, message: "FastFlowLM is already installed." };
|
|
3889
4162
|
}
|
|
@@ -3910,8 +4183,8 @@ function installFastFlowLM() {
|
|
|
3910
4183
|
function installOllama() {
|
|
3911
4184
|
const isMac = process.platform === "darwin";
|
|
3912
4185
|
const isWin = process.platform === "win32";
|
|
3913
|
-
const hasOllama = hasCommand("ollama") || isMac &&
|
|
3914
|
-
|
|
4186
|
+
const hasOllama = hasCommand("ollama") || isMac && existsSync9("/Applications/Ollama.app") || isWin && existsSync9(
|
|
4187
|
+
join9(homedir7(), "AppData", "Local", "Programs", "Ollama", "ollama.exe")
|
|
3915
4188
|
);
|
|
3916
4189
|
if (hasOllama) {
|
|
3917
4190
|
return { success: true, message: "Ollama is already installed." };
|
|
@@ -3971,8 +4244,8 @@ function installOllama() {
|
|
|
3971
4244
|
function resolveOllamaCommand() {
|
|
3972
4245
|
if (hasCommand("ollama")) return "ollama";
|
|
3973
4246
|
const candidates = process.platform === "win32" ? [
|
|
3974
|
-
|
|
3975
|
-
|
|
4247
|
+
join9(
|
|
4248
|
+
homedir7(),
|
|
3976
4249
|
"AppData",
|
|
3977
4250
|
"Local",
|
|
3978
4251
|
"Programs",
|
|
@@ -3980,7 +4253,7 @@ function resolveOllamaCommand() {
|
|
|
3980
4253
|
"ollama.exe"
|
|
3981
4254
|
)
|
|
3982
4255
|
] : process.platform === "darwin" ? ["/Applications/Ollama.app/Contents/Resources/ollama"] : [];
|
|
3983
|
-
return candidates.find((candidate) =>
|
|
4256
|
+
return candidates.find((candidate) => existsSync9(candidate));
|
|
3984
4257
|
}
|
|
3985
4258
|
function prepareLocalModel(runner, model) {
|
|
3986
4259
|
if (runner === "fastflowlm") {
|
|
@@ -4168,7 +4441,7 @@ function getSystemProfile() {
|
|
|
4168
4441
|
}
|
|
4169
4442
|
|
|
4170
4443
|
// src/kernel/system/repos.ts
|
|
4171
|
-
import { existsSync as
|
|
4444
|
+
import { existsSync as existsSync10 } from "fs";
|
|
4172
4445
|
import { resolve as resolve2 } from "path";
|
|
4173
4446
|
async function getRepoPaths(db) {
|
|
4174
4447
|
const personalSetting = await getSetting(db, "repo.personal") || await getSetting(db, "personal.workspace_dir");
|
|
@@ -4277,7 +4550,7 @@ var C = {
|
|
|
4277
4550
|
};
|
|
4278
4551
|
var SUPPORTED_AGENTS = ["opencode"];
|
|
4279
4552
|
function agentsMdPresent(cwd = process.cwd()) {
|
|
4280
|
-
return
|
|
4553
|
+
return existsSync11(join10(cwd, "AGENTS.md"));
|
|
4281
4554
|
}
|
|
4282
4555
|
function printStatus() {
|
|
4283
4556
|
const installed = hasCommand("opencode");
|
|
@@ -4319,13 +4592,13 @@ var agentCommand = new Command("agent").description("Provision and inspect the a
|
|
|
4319
4592
|
|
|
4320
4593
|
// src/cli/commands/bridge.ts
|
|
4321
4594
|
import { readdirSync as readdirSync2 } from "fs";
|
|
4322
|
-
import { homedir as
|
|
4323
|
-
import { join as
|
|
4595
|
+
import { homedir as homedir8 } from "os";
|
|
4596
|
+
import { join as join11 } from "path";
|
|
4324
4597
|
import { Command as Command2 } from "commander";
|
|
4325
4598
|
|
|
4326
4599
|
// src/cli/llm/client.ts
|
|
4327
4600
|
import { spawn } from "child_process";
|
|
4328
|
-
import { existsSync as
|
|
4601
|
+
import { existsSync as existsSync12 } from "fs";
|
|
4329
4602
|
var DEFAULT_LLM_URL = "http://localhost:8000/v1";
|
|
4330
4603
|
var DEFAULT_LLM_MODEL = "qwen3.5:4b";
|
|
4331
4604
|
var DEFAULT_LLM_API_KEY = "sk-none";
|
|
@@ -4338,6 +4611,16 @@ async function getLlmConfig(db) {
|
|
|
4338
4611
|
locale: await getSetting(db, "system.locale") || "en"
|
|
4339
4612
|
};
|
|
4340
4613
|
}
|
|
4614
|
+
async function getVisionConfig(db) {
|
|
4615
|
+
const base = await getLlmConfig(db);
|
|
4616
|
+
return {
|
|
4617
|
+
enabled: await getSetting(db, "llm.vision.enabled") === "true",
|
|
4618
|
+
url: await getSetting(db, "llm.vision.url") || base.url,
|
|
4619
|
+
model: await getSetting(db, "llm.vision.model") || base.model,
|
|
4620
|
+
apiKey: await getSetting(db, "llm.vision.api_key") || base.apiKey,
|
|
4621
|
+
locale: base.locale
|
|
4622
|
+
};
|
|
4623
|
+
}
|
|
4341
4624
|
var LANGUAGE_NAMES = {
|
|
4342
4625
|
en: "English",
|
|
4343
4626
|
de: "German",
|
|
@@ -4531,6 +4814,38 @@ async function getAvailableModels(url, apiKey = DEFAULT_LLM_API_KEY) {
|
|
|
4531
4814
|
return [];
|
|
4532
4815
|
}
|
|
4533
4816
|
}
|
|
4817
|
+
async function checkVisionReadiness(db) {
|
|
4818
|
+
const { enabled, url, model, apiKey } = await getVisionConfig(db);
|
|
4819
|
+
const visionModelSetting = await getSetting(db, "llm.vision.model");
|
|
4820
|
+
const visionModelExplicit = !!visionModelSetting;
|
|
4821
|
+
let online = false;
|
|
4822
|
+
let availableModels = [];
|
|
4823
|
+
let modelAvailable = false;
|
|
4824
|
+
if (enabled) {
|
|
4825
|
+
online = await isLlmOnline(url);
|
|
4826
|
+
if (online) {
|
|
4827
|
+
availableModels = await getAvailableModels(url, apiKey);
|
|
4828
|
+
modelAvailable = availableModels.length === 0 || availableModels.some(
|
|
4829
|
+
(candidate) => candidate.toLowerCase() === model.toLowerCase()
|
|
4830
|
+
);
|
|
4831
|
+
}
|
|
4832
|
+
}
|
|
4833
|
+
let warning;
|
|
4834
|
+
if (enabled && online && modelAvailable && !visionModelExplicit) {
|
|
4835
|
+
warning = `No explicit vision model configured (llm.vision.model). Falling back to base model "${model}", which may not support image input. Set a multimodal model: zam settings set llm.vision.model <model>`;
|
|
4836
|
+
}
|
|
4837
|
+
return {
|
|
4838
|
+
enabled,
|
|
4839
|
+
online,
|
|
4840
|
+
url,
|
|
4841
|
+
model,
|
|
4842
|
+
modelAvailable,
|
|
4843
|
+
availableModels,
|
|
4844
|
+
usable: enabled && online && modelAvailable,
|
|
4845
|
+
visionModelExplicit,
|
|
4846
|
+
warning
|
|
4847
|
+
};
|
|
4848
|
+
}
|
|
4534
4849
|
function detectRunner(url, model) {
|
|
4535
4850
|
let runner = "unknown";
|
|
4536
4851
|
let port = "8000";
|
|
@@ -4551,7 +4866,7 @@ function spawnLocalRunner(url, model) {
|
|
|
4551
4866
|
const { runner, port } = detectRunner(url, model);
|
|
4552
4867
|
try {
|
|
4553
4868
|
if (runner === "fastflowlm") {
|
|
4554
|
-
const flmExe =
|
|
4869
|
+
const flmExe = existsSync12("C:\\Program Files\\flm\\flm.exe") ? "C:\\Program Files\\flm\\flm.exe" : "flm";
|
|
4555
4870
|
if (!hasCommand("flm") && flmExe === "flm") return;
|
|
4556
4871
|
spawn(flmExe, ["serve", model, "--port", port], {
|
|
4557
4872
|
detached: true,
|
|
@@ -4572,7 +4887,7 @@ function spawnLocalRunner(url, model) {
|
|
|
4572
4887
|
async function startLocalRunner(url, model, locale) {
|
|
4573
4888
|
const { runner, port } = detectRunner(url, model);
|
|
4574
4889
|
if (runner === "fastflowlm") {
|
|
4575
|
-
const flmExe =
|
|
4890
|
+
const flmExe = existsSync12("C:\\Program Files\\flm\\flm.exe") ? "C:\\Program Files\\flm\\flm.exe" : "flm";
|
|
4576
4891
|
if (!hasCommand("flm") && flmExe === "flm") {
|
|
4577
4892
|
console.warn(
|
|
4578
4893
|
"\x1B[31m\u2717 FastFlowLM is configured but could not be found on the system.\x1B[0m"
|
|
@@ -4839,6 +5154,266 @@ async function ensureHighQualityQuestion(db, token) {
|
|
|
4839
5154
|
return null;
|
|
4840
5155
|
}
|
|
4841
5156
|
|
|
5157
|
+
// src/cli/llm/vision.ts
|
|
5158
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
5159
|
+
import { basename as basename2 } from "path";
|
|
5160
|
+
var LANGUAGE_NAMES2 = {
|
|
5161
|
+
en: "English",
|
|
5162
|
+
de: "German",
|
|
5163
|
+
es: "Spanish",
|
|
5164
|
+
fr: "French",
|
|
5165
|
+
pt: "Portuguese",
|
|
5166
|
+
zh: "Chinese",
|
|
5167
|
+
ja: "Japanese"
|
|
5168
|
+
};
|
|
5169
|
+
var OBSERVATION_KINDS2 = /* @__PURE__ */ new Set([
|
|
5170
|
+
"progress",
|
|
5171
|
+
"step-completed",
|
|
5172
|
+
"error",
|
|
5173
|
+
"help-seeking",
|
|
5174
|
+
"uncertain",
|
|
5175
|
+
"privacy-pause",
|
|
5176
|
+
"heartbeat"
|
|
5177
|
+
]);
|
|
5178
|
+
var ACTION_TYPES2 = /* @__PURE__ */ new Set([
|
|
5179
|
+
"click",
|
|
5180
|
+
"shortcut",
|
|
5181
|
+
"typing",
|
|
5182
|
+
"scroll",
|
|
5183
|
+
"window-change"
|
|
5184
|
+
]);
|
|
5185
|
+
async function observeUiSnapshotViaLLM(db, input8) {
|
|
5186
|
+
const cfg = await getVisionConfig(db);
|
|
5187
|
+
if (!cfg.enabled) {
|
|
5188
|
+
throw new Error(
|
|
5189
|
+
"Vision observation is disabled in settings (llm.vision.enabled)"
|
|
5190
|
+
);
|
|
5191
|
+
}
|
|
5192
|
+
const imageBytes = readFileSync9(input8.imagePath);
|
|
5193
|
+
const imageUrl = `data:image/png;base64,${imageBytes.toString("base64")}`;
|
|
5194
|
+
const model = input8.model ?? cfg.model;
|
|
5195
|
+
const content = await requestVisionDraft({
|
|
5196
|
+
url: cfg.url,
|
|
5197
|
+
apiKey: cfg.apiKey || DEFAULT_LLM_API_KEY,
|
|
5198
|
+
model,
|
|
5199
|
+
locale: cfg.locale,
|
|
5200
|
+
imageUrl,
|
|
5201
|
+
input: input8
|
|
5202
|
+
});
|
|
5203
|
+
let draft;
|
|
5204
|
+
try {
|
|
5205
|
+
draft = extractDraft(content);
|
|
5206
|
+
} catch {
|
|
5207
|
+
return uncertainReport(
|
|
5208
|
+
input8,
|
|
5209
|
+
"Vision model returned output that could not be parsed as JSON."
|
|
5210
|
+
);
|
|
5211
|
+
}
|
|
5212
|
+
const report = buildReport(input8, draft);
|
|
5213
|
+
if (!isUiObservationReport(report)) {
|
|
5214
|
+
return uncertainReport(
|
|
5215
|
+
input8,
|
|
5216
|
+
"Vision model returned an invalid UI report draft."
|
|
5217
|
+
);
|
|
5218
|
+
}
|
|
5219
|
+
return report;
|
|
5220
|
+
}
|
|
5221
|
+
async function requestVisionDraft(args) {
|
|
5222
|
+
const language = LANGUAGE_NAMES2[args.locale] ?? "English";
|
|
5223
|
+
const schema = `{
|
|
5224
|
+
"kind": "progress | step-completed | error | help-seeking | uncertain",
|
|
5225
|
+
"summary": "short factual UI summary in ${language}",
|
|
5226
|
+
"actions": [{"type": "click | shortcut | typing | scroll | window-change", "target": "optional UI target", "result": "optional visible result"}],
|
|
5227
|
+
"candidateTokens": [{"slug": "optional-kebab-case-skill-token", "confidence": 0.0, "rationale": "why this token may matter"}],
|
|
5228
|
+
"confidence": 0.0
|
|
5229
|
+
}`;
|
|
5230
|
+
const res = await fetchWithInteractiveTimeout(
|
|
5231
|
+
`${args.url}/chat/completions`,
|
|
5232
|
+
{
|
|
5233
|
+
method: "POST",
|
|
5234
|
+
headers: {
|
|
5235
|
+
"Content-Type": "application/json",
|
|
5236
|
+
Authorization: `Bearer ${args.apiKey}`
|
|
5237
|
+
},
|
|
5238
|
+
body: JSON.stringify({
|
|
5239
|
+
model: args.model,
|
|
5240
|
+
messages: [
|
|
5241
|
+
{
|
|
5242
|
+
role: "system",
|
|
5243
|
+
content: "You are ZAM's UI observer. Return only strict JSON. Do not include markdown, prose, or fields outside the requested schema."
|
|
5244
|
+
},
|
|
5245
|
+
{
|
|
5246
|
+
role: "user",
|
|
5247
|
+
content: [
|
|
5248
|
+
{
|
|
5249
|
+
type: "text",
|
|
5250
|
+
text: `Observe this Windows application snapshot for a learning session.
|
|
5251
|
+
Application process: ${args.input.application.processName}
|
|
5252
|
+
Window title: ${args.input.application.windowTitle ?? "(unknown)"}
|
|
5253
|
+
|
|
5254
|
+
Return this JSON draft only:
|
|
5255
|
+
${schema}`
|
|
5256
|
+
},
|
|
5257
|
+
{
|
|
5258
|
+
type: "image_url",
|
|
5259
|
+
image_url: { url: args.imageUrl }
|
|
5260
|
+
}
|
|
5261
|
+
]
|
|
5262
|
+
}
|
|
5263
|
+
],
|
|
5264
|
+
temperature: 0,
|
|
5265
|
+
max_tokens: args.input.maxTokens ?? 450
|
|
5266
|
+
}),
|
|
5267
|
+
locale: args.locale,
|
|
5268
|
+
hardTimeoutMs: args.input.hardTimeoutMs ?? 18e4
|
|
5269
|
+
}
|
|
5270
|
+
);
|
|
5271
|
+
if (!res.ok) {
|
|
5272
|
+
const errorText = await res.text().catch(() => "");
|
|
5273
|
+
if (errorText.includes("image") && (errorText.includes("not support") || errorText.includes("unsupported"))) {
|
|
5274
|
+
throw new Error(
|
|
5275
|
+
`Vision model "${args.model}" does not support image input. Set a multimodal model: zam settings set llm.vision.model <model>`
|
|
5276
|
+
);
|
|
5277
|
+
}
|
|
5278
|
+
throw new Error(
|
|
5279
|
+
`Vision LLM request failed: ${res.statusText} (${res.status}) - ${errorText}`
|
|
5280
|
+
);
|
|
5281
|
+
}
|
|
5282
|
+
const data = await res.json();
|
|
5283
|
+
if (data.error !== void 0) {
|
|
5284
|
+
const errorMsg = formatModelError(data.error);
|
|
5285
|
+
if (errorMsg.includes("image") && (errorMsg.includes("not support") || errorMsg.includes("unsupported"))) {
|
|
5286
|
+
throw new Error(
|
|
5287
|
+
`Vision model "${args.model}" does not support image input. Set a multimodal model: zam settings set llm.vision.model <model>`
|
|
5288
|
+
);
|
|
5289
|
+
}
|
|
5290
|
+
throw new Error(`Vision model failed: ${errorMsg}`);
|
|
5291
|
+
}
|
|
5292
|
+
const content = data.choices?.[0]?.message?.content;
|
|
5293
|
+
if (!content || typeof content !== "string") {
|
|
5294
|
+
throw new Error("Empty response from vision model");
|
|
5295
|
+
}
|
|
5296
|
+
return content.trim();
|
|
5297
|
+
}
|
|
5298
|
+
function extractDraft(content) {
|
|
5299
|
+
const fenced = content.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
5300
|
+
const candidate = (fenced?.[1] ?? content).trim();
|
|
5301
|
+
try {
|
|
5302
|
+
return JSON.parse(candidate);
|
|
5303
|
+
} catch {
|
|
5304
|
+
const start = candidate.indexOf("{");
|
|
5305
|
+
const end = candidate.lastIndexOf("}");
|
|
5306
|
+
if (start === -1 || end <= start) throw new Error("no JSON object found");
|
|
5307
|
+
return JSON.parse(
|
|
5308
|
+
candidate.slice(start, end + 1)
|
|
5309
|
+
);
|
|
5310
|
+
}
|
|
5311
|
+
}
|
|
5312
|
+
function buildReport(input8, draft) {
|
|
5313
|
+
return {
|
|
5314
|
+
version: UI_OBSERVATION_PROTOCOL_VERSION,
|
|
5315
|
+
sessionId: input8.sessionId,
|
|
5316
|
+
sequence: input8.sequence,
|
|
5317
|
+
observedFrom: input8.observedFrom,
|
|
5318
|
+
observedTo: input8.observedTo,
|
|
5319
|
+
kind: parseKind(draft.kind),
|
|
5320
|
+
application: input8.application,
|
|
5321
|
+
summary: parseSummary(draft.summary),
|
|
5322
|
+
actions: parseActions(draft.actions),
|
|
5323
|
+
evidence: [
|
|
5324
|
+
{
|
|
5325
|
+
type: "keyframe",
|
|
5326
|
+
ref: input8.evidenceRef ?? basename2(input8.imagePath),
|
|
5327
|
+
redacted: input8.redacted ?? false
|
|
5328
|
+
}
|
|
5329
|
+
],
|
|
5330
|
+
candidateTokens: parseCandidateTokens(draft.candidateTokens),
|
|
5331
|
+
confidence: parseConfidence(draft.confidence, 0.35)
|
|
5332
|
+
};
|
|
5333
|
+
}
|
|
5334
|
+
function uncertainReport(input8, summary) {
|
|
5335
|
+
return {
|
|
5336
|
+
version: UI_OBSERVATION_PROTOCOL_VERSION,
|
|
5337
|
+
sessionId: input8.sessionId,
|
|
5338
|
+
sequence: input8.sequence,
|
|
5339
|
+
observedFrom: input8.observedFrom,
|
|
5340
|
+
observedTo: input8.observedTo,
|
|
5341
|
+
kind: "uncertain",
|
|
5342
|
+
application: input8.application,
|
|
5343
|
+
summary,
|
|
5344
|
+
actions: [],
|
|
5345
|
+
evidence: [
|
|
5346
|
+
{
|
|
5347
|
+
type: "keyframe",
|
|
5348
|
+
ref: input8.evidenceRef ?? basename2(input8.imagePath),
|
|
5349
|
+
redacted: input8.redacted ?? false
|
|
5350
|
+
}
|
|
5351
|
+
],
|
|
5352
|
+
candidateTokens: [],
|
|
5353
|
+
confidence: 0.1
|
|
5354
|
+
};
|
|
5355
|
+
}
|
|
5356
|
+
function parseKind(value) {
|
|
5357
|
+
if (typeof value === "string" && OBSERVATION_KINDS2.has(value)) {
|
|
5358
|
+
return value;
|
|
5359
|
+
}
|
|
5360
|
+
return "uncertain";
|
|
5361
|
+
}
|
|
5362
|
+
function parseSummary(value) {
|
|
5363
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
5364
|
+
return value.trim();
|
|
5365
|
+
}
|
|
5366
|
+
return "Vision model did not provide a factual UI summary.";
|
|
5367
|
+
}
|
|
5368
|
+
function parseActions(value) {
|
|
5369
|
+
if (!Array.isArray(value)) return [];
|
|
5370
|
+
return value.flatMap((item) => {
|
|
5371
|
+
if (!isRecord2(item) || typeof item.type !== "string") return [];
|
|
5372
|
+
if (!ACTION_TYPES2.has(item.type)) return [];
|
|
5373
|
+
const action = { type: item.type };
|
|
5374
|
+
if (typeof item.target === "string" && item.target.trim()) {
|
|
5375
|
+
action.target = item.target.trim();
|
|
5376
|
+
}
|
|
5377
|
+
if (typeof item.result === "string" && item.result.trim()) {
|
|
5378
|
+
action.result = item.result.trim();
|
|
5379
|
+
}
|
|
5380
|
+
return [action];
|
|
5381
|
+
});
|
|
5382
|
+
}
|
|
5383
|
+
function parseCandidateTokens(value) {
|
|
5384
|
+
if (!Array.isArray(value)) return [];
|
|
5385
|
+
return value.flatMap((item) => {
|
|
5386
|
+
if (!isRecord2(item)) return [];
|
|
5387
|
+
if (typeof item.slug !== "string" || !/^[A-Za-z0-9._-]+$/.test(item.slug)) {
|
|
5388
|
+
return [];
|
|
5389
|
+
}
|
|
5390
|
+
if (typeof item.rationale !== "string" || !item.rationale.trim()) {
|
|
5391
|
+
return [];
|
|
5392
|
+
}
|
|
5393
|
+
return [
|
|
5394
|
+
{
|
|
5395
|
+
slug: item.slug,
|
|
5396
|
+
confidence: parseConfidence(item.confidence, 0.2),
|
|
5397
|
+
rationale: item.rationale.trim()
|
|
5398
|
+
}
|
|
5399
|
+
];
|
|
5400
|
+
});
|
|
5401
|
+
}
|
|
5402
|
+
function parseConfidence(value, fallback) {
|
|
5403
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
5404
|
+
return Math.max(0, Math.min(1, value));
|
|
5405
|
+
}
|
|
5406
|
+
function formatModelError(error) {
|
|
5407
|
+
if (typeof error === "string") return error;
|
|
5408
|
+
if (isRecord2(error) && typeof error.message === "string") {
|
|
5409
|
+
return error.message;
|
|
5410
|
+
}
|
|
5411
|
+
return JSON.stringify(error);
|
|
5412
|
+
}
|
|
5413
|
+
function isRecord2(value) {
|
|
5414
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5415
|
+
}
|
|
5416
|
+
|
|
4842
5417
|
// src/cli/commands/resolve-user.ts
|
|
4843
5418
|
async function ensureDefaultUser(db, preferredUserId) {
|
|
4844
5419
|
const stored = await getSetting(db, "user.id");
|
|
@@ -4892,6 +5467,13 @@ function jsonError(message) {
|
|
|
4892
5467
|
console.log(JSON.stringify({ error: message }, null, 2));
|
|
4893
5468
|
process.exit(1);
|
|
4894
5469
|
}
|
|
5470
|
+
function parseNonNegativeIntegerOption(name, value) {
|
|
5471
|
+
const parsed = Number(value);
|
|
5472
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
5473
|
+
jsonError(`${name} must be a non-negative integer`);
|
|
5474
|
+
}
|
|
5475
|
+
return parsed;
|
|
5476
|
+
}
|
|
4895
5477
|
async function withDb2(fn) {
|
|
4896
5478
|
await withDb(fn, jsonError);
|
|
4897
5479
|
}
|
|
@@ -4932,15 +5514,16 @@ function parseTokenUpdates(opts) {
|
|
|
4932
5514
|
var bridgeCommand = new Command2("bridge").description(
|
|
4933
5515
|
"Machine-readable JSON protocol for AI integration"
|
|
4934
5516
|
);
|
|
4935
|
-
bridgeCommand.command("check-due").description("Check due cards for a user (JSON)").option("--user <id>", "User ID (default: whoami)").action(async (opts) => {
|
|
5517
|
+
bridgeCommand.command("check-due").description("Check due cards for a user (JSON)").option("--user <id>", "User ID (default: whoami)").option("--domain <domain>", "Filter by knowledge domain").action(async (opts) => {
|
|
4936
5518
|
await withDb2(async (db) => {
|
|
4937
5519
|
const userId = await resolveUser(opts, db, { json: true });
|
|
4938
|
-
const dueCards = await getDueCards(db, userId);
|
|
5520
|
+
const dueCards = await getDueCards(db, userId, void 0, opts.domain);
|
|
4939
5521
|
const domains = [
|
|
4940
5522
|
...new Set(dueCards.map((c) => c.domain).filter(Boolean))
|
|
4941
5523
|
].sort();
|
|
4942
5524
|
jsonOut2({
|
|
4943
5525
|
userId,
|
|
5526
|
+
domain: opts.domain ?? null,
|
|
4944
5527
|
dueCount: dueCards.length,
|
|
4945
5528
|
domains,
|
|
4946
5529
|
cards: dueCards.map((c) => ({
|
|
@@ -5131,6 +5714,45 @@ bridgeCommand.command("get-skill").description("Get an agent skill by slug (JSON
|
|
|
5131
5714
|
});
|
|
5132
5715
|
});
|
|
5133
5716
|
});
|
|
5717
|
+
bridgeCommand.command("start-session").description("Start a ZAM learning session (JSON)").requiredOption("--task <task>", "Session task description").option(
|
|
5718
|
+
"--context <context>",
|
|
5719
|
+
"Execution context: shell | ui | reallife",
|
|
5720
|
+
"shell"
|
|
5721
|
+
).option("--user <id>", "User ID (default: whoami)").action(async (opts) => {
|
|
5722
|
+
const context = opts.context;
|
|
5723
|
+
if (!["shell", "ui", "reallife"].includes(context)) {
|
|
5724
|
+
jsonError("context must be shell, ui, or reallife");
|
|
5725
|
+
}
|
|
5726
|
+
await withDb2(async (db) => {
|
|
5727
|
+
const userId = await resolveUser(opts, db, { json: true });
|
|
5728
|
+
const session = await startSession(db, {
|
|
5729
|
+
user_id: userId,
|
|
5730
|
+
task: opts.task,
|
|
5731
|
+
execution_context: context
|
|
5732
|
+
});
|
|
5733
|
+
jsonOut2({
|
|
5734
|
+
id: session.id,
|
|
5735
|
+
userId: session.user_id,
|
|
5736
|
+
task: session.task,
|
|
5737
|
+
executionContext: session.execution_context,
|
|
5738
|
+
startedAt: session.started_at,
|
|
5739
|
+
completedAt: session.completed_at
|
|
5740
|
+
});
|
|
5741
|
+
});
|
|
5742
|
+
});
|
|
5743
|
+
bridgeCommand.command("end-session").description("Complete an active ZAM learning session (JSON)").requiredOption("--session <id>", "Session ID").action(async (opts) => {
|
|
5744
|
+
await withDb2(async (db) => {
|
|
5745
|
+
const session = await endSession(db, opts.session);
|
|
5746
|
+
jsonOut2({
|
|
5747
|
+
id: session.id,
|
|
5748
|
+
userId: session.user_id,
|
|
5749
|
+
task: session.task,
|
|
5750
|
+
executionContext: session.execution_context,
|
|
5751
|
+
startedAt: session.started_at,
|
|
5752
|
+
completedAt: session.completed_at
|
|
5753
|
+
});
|
|
5754
|
+
});
|
|
5755
|
+
});
|
|
5134
5756
|
bridgeCommand.command("get-monitor").description("Read monitor log for a session (JSON)").requiredOption("--session <id>", "Session ID").action((opts) => {
|
|
5135
5757
|
if (!monitorLogExists(opts.session)) {
|
|
5136
5758
|
jsonOut2({
|
|
@@ -5272,7 +5894,7 @@ bridgeCommand.command("discover-skills").description(
|
|
|
5272
5894
|
"20"
|
|
5273
5895
|
).action(async (opts) => {
|
|
5274
5896
|
try {
|
|
5275
|
-
const monitorDir =
|
|
5897
|
+
const monitorDir = join11(homedir8(), ".zam", "monitor");
|
|
5276
5898
|
let files;
|
|
5277
5899
|
try {
|
|
5278
5900
|
files = readdirSync2(monitorDir).filter((f) => f.endsWith(".jsonl"));
|
|
@@ -5285,7 +5907,7 @@ bridgeCommand.command("discover-skills").description(
|
|
|
5285
5907
|
return;
|
|
5286
5908
|
}
|
|
5287
5909
|
const limit = Number(opts.limit);
|
|
5288
|
-
const sorted = files.map((f) => ({ name: f, path:
|
|
5910
|
+
const sorted = files.map((f) => ({ name: f, path: join11(monitorDir, f) })).sort((a, b) => b.name.localeCompare(a.name)).slice(0, limit);
|
|
5289
5911
|
const sessionCommands = /* @__PURE__ */ new Map();
|
|
5290
5912
|
for (const file of sorted) {
|
|
5291
5913
|
const sessionId = file.name.replace(".jsonl", "");
|
|
@@ -5320,6 +5942,81 @@ bridgeCommand.command("discover-skills").description(
|
|
|
5320
5942
|
jsonError(err.message);
|
|
5321
5943
|
}
|
|
5322
5944
|
});
|
|
5945
|
+
bridgeCommand.command("observe-ui-watch").description(
|
|
5946
|
+
"Poll live UI observer watch reports for a ZAM learning session (JSON)"
|
|
5947
|
+
).requiredOption(
|
|
5948
|
+
"--session <id>",
|
|
5949
|
+
"ZAM session ID (also the observer log key)"
|
|
5950
|
+
).option("--after <n>", "Only return observations after this sequence").option("--limit <n>", "Maximum observations to return", "100").action(async (opts) => {
|
|
5951
|
+
await withDb2(async (db) => {
|
|
5952
|
+
const session = await db.prepare("SELECT id, execution_context FROM sessions WHERE id = ?").get(opts.session);
|
|
5953
|
+
if (!session) {
|
|
5954
|
+
jsonError(`Session not found: ${opts.session}`);
|
|
5955
|
+
}
|
|
5956
|
+
const after = opts.after === void 0 ? void 0 : parseNonNegativeIntegerOption("after", opts.after);
|
|
5957
|
+
const limit = parseNonNegativeIntegerOption("limit", opts.limit);
|
|
5958
|
+
const observations = readUiObservationLog(opts.session).filter((report) => after === void 0 || report.sequence > after).slice(0, limit);
|
|
5959
|
+
const last = observations[observations.length - 1];
|
|
5960
|
+
jsonOut2({
|
|
5961
|
+
sessionId: opts.session,
|
|
5962
|
+
executionContext: session.execution_context,
|
|
5963
|
+
observationSource: "ui",
|
|
5964
|
+
logExists: uiObservationLogExists(opts.session),
|
|
5965
|
+
after: after ?? null,
|
|
5966
|
+
count: observations.length,
|
|
5967
|
+
nextSequence: last?.sequence ?? after ?? null,
|
|
5968
|
+
observations
|
|
5969
|
+
});
|
|
5970
|
+
});
|
|
5971
|
+
});
|
|
5972
|
+
bridgeCommand.command("get-observations").description("Read UI observer reports for a session (JSON)").requiredOption("--session <id>", "Observer session ID").option("--after <n>", "Only return observations after this sequence").option("--limit <n>", "Maximum observations to return", "100").action((opts) => {
|
|
5973
|
+
try {
|
|
5974
|
+
const after = opts.after === void 0 ? void 0 : parseNonNegativeIntegerOption("after", opts.after);
|
|
5975
|
+
const limit = parseNonNegativeIntegerOption("limit", opts.limit);
|
|
5976
|
+
const observations = readUiObservationLog(opts.session).filter((report) => after === void 0 || report.sequence > after).slice(0, limit);
|
|
5977
|
+
const last = observations[observations.length - 1];
|
|
5978
|
+
jsonOut2({
|
|
5979
|
+
sessionId: opts.session,
|
|
5980
|
+
after: after ?? null,
|
|
5981
|
+
count: observations.length,
|
|
5982
|
+
nextSequence: last?.sequence ?? after ?? null,
|
|
5983
|
+
observations
|
|
5984
|
+
});
|
|
5985
|
+
} catch (err) {
|
|
5986
|
+
jsonError(err.message);
|
|
5987
|
+
}
|
|
5988
|
+
});
|
|
5989
|
+
bridgeCommand.command("observe-ui-snapshot").description(
|
|
5990
|
+
"Analyze a captured UI snapshot with the configured vision LLM (JSON)"
|
|
5991
|
+
).requiredOption("--session <id>", "Observer session ID").requiredOption("--sequence <n>", "Monotonic observation sequence number").requiredOption("--image <path>", "PNG snapshot path").requiredOption("--observed-from <iso>", "Observation window start time").requiredOption("--observed-to <iso>", "Observation window end time").requiredOption("--process-name <name>", "Observed application process name").option("--process-id <n>", "Observed application process ID").option("--window-title <title>", "Observed window title").option("--evidence-ref <ref>", "Evidence reference to put in the report").option("--model <model>", "Override configured LLM model for this request").option("--max-tokens <n>", "Model response token budget").option("--timeout <ms>", "Hard request timeout in milliseconds").option("--redacted", "Mark the snapshot evidence as redacted").option("--write-log", "Append the generated report to the session JSONL").action(async (opts) => {
|
|
5992
|
+
const sequence = parseNonNegativeIntegerOption("sequence", opts.sequence);
|
|
5993
|
+
const processId = opts.processId === void 0 ? void 0 : parseNonNegativeIntegerOption("process-id", opts.processId);
|
|
5994
|
+
const maxTokens = opts.maxTokens === void 0 ? void 0 : parseNonNegativeIntegerOption("max-tokens", opts.maxTokens);
|
|
5995
|
+
const hardTimeoutMs = opts.timeout === void 0 ? void 0 : parseNonNegativeIntegerOption("timeout", opts.timeout);
|
|
5996
|
+
await withDb2(async (db) => {
|
|
5997
|
+
const report = await observeUiSnapshotViaLLM(db, {
|
|
5998
|
+
sessionId: opts.session,
|
|
5999
|
+
sequence,
|
|
6000
|
+
observedFrom: opts.observedFrom,
|
|
6001
|
+
observedTo: opts.observedTo,
|
|
6002
|
+
imagePath: opts.image,
|
|
6003
|
+
application: {
|
|
6004
|
+
processName: opts.processName,
|
|
6005
|
+
processId,
|
|
6006
|
+
windowTitle: opts.windowTitle
|
|
6007
|
+
},
|
|
6008
|
+
evidenceRef: opts.evidenceRef,
|
|
6009
|
+
redacted: opts.redacted === true,
|
|
6010
|
+
model: opts.model,
|
|
6011
|
+
maxTokens,
|
|
6012
|
+
hardTimeoutMs
|
|
6013
|
+
});
|
|
6014
|
+
if (opts.writeLog === true) {
|
|
6015
|
+
appendUiObservationReport(report);
|
|
6016
|
+
}
|
|
6017
|
+
jsonOut2(report);
|
|
6018
|
+
});
|
|
6019
|
+
});
|
|
5323
6020
|
bridgeCommand.command("check-llm").description("Check if LLM is enabled and online (JSON)").action(async () => {
|
|
5324
6021
|
await withDb2(async (db) => {
|
|
5325
6022
|
const { enabled, url, model, apiKey } = await getLlmConfig(db);
|
|
@@ -5345,6 +6042,13 @@ bridgeCommand.command("check-llm").description("Check if LLM is enabled and onli
|
|
|
5345
6042
|
});
|
|
5346
6043
|
});
|
|
5347
6044
|
});
|
|
6045
|
+
bridgeCommand.command("check-vision").description(
|
|
6046
|
+
"Check if UI observer vision analysis is enabled and ready (JSON)"
|
|
6047
|
+
).action(async () => {
|
|
6048
|
+
await withDb2(async (db) => {
|
|
6049
|
+
jsonOut2(await checkVisionReadiness(db));
|
|
6050
|
+
});
|
|
6051
|
+
});
|
|
5348
6052
|
bridgeCommand.command("ensure-llm").description(
|
|
5349
6053
|
"Start the local LLM server if needed and report readiness (JSON)"
|
|
5350
6054
|
).option(
|
|
@@ -5532,7 +6236,7 @@ bridgeCommand.command("get-neighborhood").description(
|
|
|
5532
6236
|
bridgeCommand.command("serve").description("Start the persistent JSON-RPC stdin/stdout server").option("--stdin", "Use stdin/stdout for communication").action(async (_opts) => {
|
|
5533
6237
|
isServeMode = true;
|
|
5534
6238
|
const {
|
|
5535
|
-
appendFileSync:
|
|
6239
|
+
appendFileSync: appendFileSync4,
|
|
5536
6240
|
existsSync: fileExists,
|
|
5537
6241
|
mkdirSync: makeDir
|
|
5538
6242
|
} = await import("fs");
|
|
@@ -5543,7 +6247,7 @@ bridgeCommand.command("serve").description("Start the persistent JSON-RPC stdin/
|
|
|
5543
6247
|
const logDiag = (msg) => {
|
|
5544
6248
|
try {
|
|
5545
6249
|
if (!fileExists(logDir)) makeDir(logDir, { recursive: true });
|
|
5546
|
-
|
|
6250
|
+
appendFileSync4(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
5547
6251
|
`);
|
|
5548
6252
|
} catch {
|
|
5549
6253
|
}
|
|
@@ -5654,10 +6358,10 @@ import { Command as Command3 } from "commander";
|
|
|
5654
6358
|
var cardCommand = new Command3("card").description(
|
|
5655
6359
|
"Manage spaced-repetition cards"
|
|
5656
6360
|
);
|
|
5657
|
-
cardCommand.command("due").description("Show due tokens for a user").option("--user <id>", "User ID (default: whoami)").option("--json", "Output as JSON").option("--summary", "Show only counts per domain (no slugs or concepts)").action(async (opts) => {
|
|
6361
|
+
cardCommand.command("due").description("Show due tokens for a user").option("--user <id>", "User ID (default: whoami)").option("--domain <domain>", "Filter by knowledge domain").option("--json", "Output as JSON").option("--summary", "Show only counts per domain (no slugs or concepts)").action(async (opts) => {
|
|
5658
6362
|
await withDb(async (db) => {
|
|
5659
6363
|
const userId = await resolveUser(opts, db);
|
|
5660
|
-
const dueCards = await getDueCards(db, userId);
|
|
6364
|
+
const dueCards = await getDueCards(db, userId, void 0, opts.domain);
|
|
5661
6365
|
if (opts.json) {
|
|
5662
6366
|
console.log(JSON.stringify(dueCards, null, 2));
|
|
5663
6367
|
return;
|
|
@@ -5994,19 +6698,19 @@ async function setupTurso(urlArg, tokenArg, mode) {
|
|
|
5994
6698
|
|
|
5995
6699
|
// src/cli/commands/git-sync.ts
|
|
5996
6700
|
import { execSync as execSync4 } from "child_process";
|
|
5997
|
-
import { chmodSync as chmodSync2, existsSync as
|
|
5998
|
-
import { join as
|
|
6701
|
+
import { chmodSync as chmodSync2, existsSync as existsSync13, writeFileSync as writeFileSync5 } from "fs";
|
|
6702
|
+
import { join as join12 } from "path";
|
|
5999
6703
|
import { Command as Command5 } from "commander";
|
|
6000
6704
|
function installHook2() {
|
|
6001
|
-
const gitDir =
|
|
6002
|
-
if (!
|
|
6705
|
+
const gitDir = join12(process.cwd(), ".git");
|
|
6706
|
+
if (!existsSync13(gitDir)) {
|
|
6003
6707
|
console.error(
|
|
6004
6708
|
"Error: Current directory is not the root of a Git repository."
|
|
6005
6709
|
);
|
|
6006
6710
|
process.exit(1);
|
|
6007
6711
|
}
|
|
6008
|
-
const hooksDir =
|
|
6009
|
-
const hookPath =
|
|
6712
|
+
const hooksDir = join12(gitDir, "hooks");
|
|
6713
|
+
const hookPath = join12(hooksDir, "post-commit");
|
|
6010
6714
|
const hookContent = `#!/bin/sh
|
|
6011
6715
|
# ZAM Spaced Repetition Auto-Stale Hook
|
|
6012
6716
|
# Triggered automatically on git commits to decay modified concept cards.
|
|
@@ -6115,7 +6819,7 @@ ZAM Auto-Stale Complete: Scanned ${changedFiles.length} file(s).`
|
|
|
6115
6819
|
});
|
|
6116
6820
|
|
|
6117
6821
|
// src/cli/commands/goal.ts
|
|
6118
|
-
import { existsSync as
|
|
6822
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync7 } from "fs";
|
|
6119
6823
|
import { resolve as resolve3 } from "path";
|
|
6120
6824
|
import { input as input2 } from "@inquirer/prompts";
|
|
6121
6825
|
import { Command as Command6 } from "commander";
|
|
@@ -6139,7 +6843,7 @@ goalCommand.command("list").description("List all goals").option(
|
|
|
6139
6843
|
"Filter by status (active, completed, paused, abandoned)"
|
|
6140
6844
|
).option("--tree", "Show goals as a tree with parent/child relationships").option("--json", "Output as JSON").action(async (opts) => {
|
|
6141
6845
|
const goalsDir = await resolveGoalsDir();
|
|
6142
|
-
if (!
|
|
6846
|
+
if (!existsSync14(goalsDir)) {
|
|
6143
6847
|
console.error(`Goals directory not found: ${goalsDir}`);
|
|
6144
6848
|
console.error(
|
|
6145
6849
|
"Set it with: zam settings set personal.goals_dir /path/to/goals"
|
|
@@ -6240,8 +6944,8 @@ ${"\u2500".repeat(50)}`);
|
|
|
6240
6944
|
});
|
|
6241
6945
|
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) => {
|
|
6242
6946
|
const goalsDir = await resolveGoalsDir();
|
|
6243
|
-
if (!
|
|
6244
|
-
|
|
6947
|
+
if (!existsSync14(goalsDir)) {
|
|
6948
|
+
mkdirSync7(goalsDir, { recursive: true });
|
|
6245
6949
|
}
|
|
6246
6950
|
let slug = opts.slug;
|
|
6247
6951
|
let title = opts.title;
|
|
@@ -6300,21 +7004,21 @@ goalCommand.command("status <slug> <status>").description("Update a goal's statu
|
|
|
6300
7004
|
});
|
|
6301
7005
|
|
|
6302
7006
|
// src/cli/commands/init.ts
|
|
6303
|
-
import { existsSync as
|
|
6304
|
-
import { homedir as
|
|
6305
|
-
import { join as
|
|
7007
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
7008
|
+
import { homedir as homedir9 } from "os";
|
|
7009
|
+
import { join as join13 } from "path";
|
|
6306
7010
|
import { confirm, input as input3 } from "@inquirer/prompts";
|
|
6307
7011
|
import { Command as Command7 } from "commander";
|
|
6308
|
-
var HOME2 =
|
|
7012
|
+
var HOME2 = homedir9();
|
|
6309
7013
|
function printLine(char = "\u2550", len = 60, color = "\x1B[36m") {
|
|
6310
7014
|
console.log(`${color}${char.repeat(len)}\x1B[0m`);
|
|
6311
7015
|
}
|
|
6312
7016
|
function bootstrapSandboxWorkspace(workspaceDir) {
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
const worldviewFile =
|
|
6317
|
-
if (!
|
|
7017
|
+
mkdirSync8(join13(workspaceDir, "beliefs"), { recursive: true });
|
|
7018
|
+
mkdirSync8(join13(workspaceDir, "goals"), { recursive: true });
|
|
7019
|
+
mkdirSync8(join13(workspaceDir, "skills"), { recursive: true });
|
|
7020
|
+
const worldviewFile = join13(workspaceDir, "beliefs", "worldview.md");
|
|
7021
|
+
if (!existsSync15(worldviewFile)) {
|
|
6318
7022
|
writeFileSync6(
|
|
6319
7023
|
worldviewFile,
|
|
6320
7024
|
`# Personal Worldview
|
|
@@ -6327,8 +7031,8 @@ Here, I declare the core concepts and principles I want to master.
|
|
|
6327
7031
|
"utf8"
|
|
6328
7032
|
);
|
|
6329
7033
|
}
|
|
6330
|
-
const goalsFile =
|
|
6331
|
-
if (!
|
|
7034
|
+
const goalsFile = join13(workspaceDir, "goals", "goals.md");
|
|
7035
|
+
if (!existsSync15(goalsFile)) {
|
|
6332
7036
|
writeFileSync6(
|
|
6333
7037
|
goalsFile,
|
|
6334
7038
|
`# Personal Goals
|
|
@@ -6351,7 +7055,7 @@ var initCommand = new Command7("init").description("Launch the guided interactiv
|
|
|
6351
7055
|
);
|
|
6352
7056
|
printLine();
|
|
6353
7057
|
console.log("\n\x1B[1m[1/5] Setting up Local Workspace Sandbox\x1B[0m");
|
|
6354
|
-
const defaultWorkspace =
|
|
7058
|
+
const defaultWorkspace = join13(HOME2, "Documents", "zam");
|
|
6355
7059
|
const workspacePath = await input3({
|
|
6356
7060
|
message: "Choose your ZAM workspace directory:",
|
|
6357
7061
|
default: defaultWorkspace
|
|
@@ -7051,7 +7755,7 @@ ${"\u2550".repeat(50)}`);
|
|
|
7051
7755
|
import { execFileSync as execFileSync2, execSync as execSync5 } from "child_process";
|
|
7052
7756
|
import { unlinkSync, writeFileSync as writeFileSync7 } from "fs";
|
|
7053
7757
|
import { tmpdir } from "os";
|
|
7054
|
-
import { basename as
|
|
7758
|
+
import { basename as basename3, join as join14 } from "path";
|
|
7055
7759
|
import { Command as Command9 } from "commander";
|
|
7056
7760
|
function isPowerShellShell(shell) {
|
|
7057
7761
|
return shell === "pwsh" || shell === "powershell";
|
|
@@ -7073,7 +7777,7 @@ function detectShell() {
|
|
|
7073
7777
|
if (process.platform === "win32")
|
|
7074
7778
|
return findExecutable("pwsh.exe") ? "pwsh" : "powershell";
|
|
7075
7779
|
const shell = process.env.SHELL ?? "";
|
|
7076
|
-
return
|
|
7780
|
+
return basename3(shell) === "bash" ? "bash" : "zsh";
|
|
7077
7781
|
}
|
|
7078
7782
|
var monitorCommand = new Command9("monitor").description(
|
|
7079
7783
|
"Shell observation for real-time task monitoring"
|
|
@@ -7216,8 +7920,8 @@ function resolveZamInvocation(shell) {
|
|
|
7216
7920
|
if (installed) {
|
|
7217
7921
|
return isPowerShellShell(shell) ? `& ${psSingleQuoted2(installed)}` : installed;
|
|
7218
7922
|
}
|
|
7219
|
-
const projectRoot =
|
|
7220
|
-
const cliSource =
|
|
7923
|
+
const projectRoot = join14(import.meta.dirname, "..", "..", "..");
|
|
7924
|
+
const cliSource = join14(projectRoot, "src/cli/index.ts");
|
|
7221
7925
|
if (isPowerShellShell(shell)) {
|
|
7222
7926
|
return `& npx --prefix ${psSingleQuoted2(projectRoot)} tsx ${psSingleQuoted2(cliSource)}`;
|
|
7223
7927
|
}
|
|
@@ -7307,7 +8011,7 @@ end tell` : `tell application "Terminal"
|
|
|
7307
8011
|
activate
|
|
7308
8012
|
do script "${escaped}"
|
|
7309
8013
|
end tell`;
|
|
7310
|
-
const tmpFile =
|
|
8014
|
+
const tmpFile = join14(tmpdir(), `zam-monitor-${sessionId}.scpt`);
|
|
7311
8015
|
try {
|
|
7312
8016
|
writeFileSync7(tmpFile, appleScript);
|
|
7313
8017
|
execSync5(`osascript ${JSON.stringify(tmpFile)}`, { stdio: "ignore" });
|
|
@@ -7358,8 +8062,8 @@ Run this manually in a new PowerShell terminal:
|
|
|
7358
8062
|
}
|
|
7359
8063
|
|
|
7360
8064
|
// src/cli/commands/profile.ts
|
|
7361
|
-
import { homedir as
|
|
7362
|
-
import { dirname as dirname5, join as
|
|
8065
|
+
import { homedir as homedir10 } from "os";
|
|
8066
|
+
import { dirname as dirname5, join as join15, resolve as resolve4 } from "path";
|
|
7363
8067
|
import { Command as Command10 } from "commander";
|
|
7364
8068
|
var C2 = {
|
|
7365
8069
|
reset: "\x1B[0m",
|
|
@@ -7369,7 +8073,7 @@ var C2 = {
|
|
|
7369
8073
|
green: "\x1B[32m"
|
|
7370
8074
|
};
|
|
7371
8075
|
function defaultPersonalDir() {
|
|
7372
|
-
return
|
|
8076
|
+
return join15(homedir10(), "Documents", "zam");
|
|
7373
8077
|
}
|
|
7374
8078
|
function render(profile) {
|
|
7375
8079
|
const sync = profile.syncProvider ? `${C2.green}${profile.syncProvider}${C2.reset} ${C2.dim}(good for cross-device snapshots)${C2.reset}` : `${C2.dim}local folder (use a synced folder or snapshots to move between machines)${C2.reset}`;
|
|
@@ -7530,7 +8234,7 @@ ${"\u2550".repeat(50)}`);
|
|
|
7530
8234
|
});
|
|
7531
8235
|
|
|
7532
8236
|
// src/cli/commands/session.ts
|
|
7533
|
-
import { readFileSync as
|
|
8237
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
7534
8238
|
import { input as input6, select as select2 } from "@inquirer/prompts";
|
|
7535
8239
|
import { Command as Command12 } from "commander";
|
|
7536
8240
|
var sessionCommand = new Command12("session").description(
|
|
@@ -7711,7 +8415,7 @@ function loadPatternFile(path) {
|
|
|
7711
8415
|
if (!path) return [];
|
|
7712
8416
|
let parsed;
|
|
7713
8417
|
try {
|
|
7714
|
-
parsed = JSON.parse(
|
|
8418
|
+
parsed = JSON.parse(readFileSync10(path, "utf-8"));
|
|
7715
8419
|
} catch (err) {
|
|
7716
8420
|
throw new Error(
|
|
7717
8421
|
`Cannot read synthesis patterns from ${path}: ${err.message}`
|
|
@@ -7901,11 +8605,23 @@ sessionCommand.command("end").description("End a session and show summary").requ
|
|
|
7901
8605
|
});
|
|
7902
8606
|
|
|
7903
8607
|
// src/cli/commands/settings.ts
|
|
7904
|
-
import { existsSync as
|
|
8608
|
+
import { existsSync as existsSync16 } from "fs";
|
|
7905
8609
|
import { Command as Command13 } from "commander";
|
|
7906
8610
|
var settingsCommand = new Command13("settings").description(
|
|
7907
8611
|
"Manage user settings"
|
|
7908
8612
|
);
|
|
8613
|
+
var BOOLEAN_SETTING_KEYS = /* @__PURE__ */ new Set(["llm.enabled", "llm.vision.enabled"]);
|
|
8614
|
+
function normalizeSettingValue(key, value) {
|
|
8615
|
+
if (!BOOLEAN_SETTING_KEYS.has(key)) return value;
|
|
8616
|
+
const lower = value.toLowerCase();
|
|
8617
|
+
if (lower === "on" || lower === "enable" || lower === "enabled" || lower === "true") {
|
|
8618
|
+
return "true";
|
|
8619
|
+
}
|
|
8620
|
+
if (lower === "off" || lower === "disable" || lower === "disabled" || lower === "false") {
|
|
8621
|
+
return "false";
|
|
8622
|
+
}
|
|
8623
|
+
return value;
|
|
8624
|
+
}
|
|
7909
8625
|
settingsCommand.command("show").description("Show all settings").option("--json", "Output as JSON").action(async (opts) => {
|
|
7910
8626
|
await withDb(async (db) => {
|
|
7911
8627
|
if (opts.json) {
|
|
@@ -7943,15 +8659,7 @@ settingsCommand.command("get <key>").description("Get a single setting").option(
|
|
|
7943
8659
|
});
|
|
7944
8660
|
settingsCommand.command("set <key> <value>").description("Set a setting").option("--quiet", "Suppress output").action(async (key, value, opts) => {
|
|
7945
8661
|
await withDb(async (db) => {
|
|
7946
|
-
|
|
7947
|
-
if (key === "llm.enabled") {
|
|
7948
|
-
const lower = value.toLowerCase();
|
|
7949
|
-
if (lower === "on" || lower === "enable" || lower === "enabled" || lower === "true") {
|
|
7950
|
-
parsedVal = "true";
|
|
7951
|
-
} else if (lower === "off" || lower === "disable" || lower === "disabled" || lower === "false") {
|
|
7952
|
-
parsedVal = "false";
|
|
7953
|
-
}
|
|
7954
|
-
}
|
|
8662
|
+
const parsedVal = normalizeSettingValue(key, value);
|
|
7955
8663
|
await setSetting(db, key, parsedVal);
|
|
7956
8664
|
if (!opts.quiet) {
|
|
7957
8665
|
console.log(`Set ${key} = ${parsedVal}`);
|
|
@@ -8056,7 +8764,7 @@ settingsCommand.command("repos").description("Show or set Personal, Team, and Or
|
|
|
8056
8764
|
console.log("\nValidation:");
|
|
8057
8765
|
for (const [name, path] of Object.entries(paths)) {
|
|
8058
8766
|
if (path) {
|
|
8059
|
-
const exists =
|
|
8767
|
+
const exists = existsSync16(path);
|
|
8060
8768
|
console.log(
|
|
8061
8769
|
` ${name.padEnd(9)}: ${exists ? "\x1B[32m\u2713 Valid folder\x1B[0m" : "\x1B[31m\u2717 Directory does not exist\x1B[0m"}`
|
|
8062
8770
|
);
|
|
@@ -8068,41 +8776,41 @@ settingsCommand.command("repos").description("Show or set Personal, Team, and Or
|
|
|
8068
8776
|
});
|
|
8069
8777
|
|
|
8070
8778
|
// src/cli/commands/setup.ts
|
|
8071
|
-
import { copyFileSync as copyFileSync2, existsSync as
|
|
8072
|
-
import { basename as
|
|
8779
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync17, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8 } from "fs";
|
|
8780
|
+
import { basename as basename4, dirname as dirname6, join as join16 } from "path";
|
|
8073
8781
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
8074
8782
|
import { Command as Command14 } from "commander";
|
|
8075
8783
|
var packageRoot = [
|
|
8076
8784
|
fileURLToPath2(new URL("../..", import.meta.url)),
|
|
8077
8785
|
fileURLToPath2(new URL("../../..", import.meta.url))
|
|
8078
|
-
].find((candidate) =>
|
|
8786
|
+
].find((candidate) => existsSync17(join16(candidate, "package.json"))) ?? fileURLToPath2(new URL("../..", import.meta.url));
|
|
8079
8787
|
var SKILL_PAIRS = [
|
|
8080
8788
|
{
|
|
8081
|
-
from:
|
|
8082
|
-
to:
|
|
8789
|
+
from: join16(packageRoot, ".claude", "skills", "zam", "SKILL.md"),
|
|
8790
|
+
to: join16(".claude", "skills", "zam", "SKILL.md")
|
|
8083
8791
|
},
|
|
8084
8792
|
{
|
|
8085
|
-
from:
|
|
8086
|
-
to:
|
|
8793
|
+
from: join16(packageRoot, ".agent", "skills", "zam", "SKILL.md"),
|
|
8794
|
+
to: join16(".agent", "skills", "zam", "SKILL.md")
|
|
8087
8795
|
},
|
|
8088
8796
|
{
|
|
8089
|
-
from:
|
|
8090
|
-
to:
|
|
8797
|
+
from: join16(packageRoot, ".agents", "skills", "zam", "SKILL.md"),
|
|
8798
|
+
to: join16(".agents", "skills", "zam", "SKILL.md")
|
|
8091
8799
|
}
|
|
8092
8800
|
];
|
|
8093
8801
|
function copySkills(force, cwd = process.cwd()) {
|
|
8094
8802
|
let anyAction = false;
|
|
8095
8803
|
for (const { from, to } of SKILL_PAIRS) {
|
|
8096
|
-
const dest =
|
|
8097
|
-
if (!
|
|
8804
|
+
const dest = join16(cwd, to);
|
|
8805
|
+
if (!existsSync17(from)) {
|
|
8098
8806
|
console.warn(` warn source not found, skipping: ${from}`);
|
|
8099
8807
|
continue;
|
|
8100
8808
|
}
|
|
8101
|
-
if (
|
|
8809
|
+
if (existsSync17(dest) && !force) {
|
|
8102
8810
|
console.log(` skip ${to} (already present \xE2\u20AC\u201D use --force to update)`);
|
|
8103
8811
|
continue;
|
|
8104
8812
|
}
|
|
8105
|
-
|
|
8813
|
+
mkdirSync9(dirname6(dest), { recursive: true });
|
|
8106
8814
|
copyFileSync2(from, dest);
|
|
8107
8815
|
console.log(` copy ${to}`);
|
|
8108
8816
|
anyAction = true;
|
|
@@ -8131,12 +8839,12 @@ async function initDatabase(skipInit) {
|
|
|
8131
8839
|
}
|
|
8132
8840
|
function writeClaudeMd(skipClaudeMd, cwd = process.cwd()) {
|
|
8133
8841
|
if (skipClaudeMd) return;
|
|
8134
|
-
const dest =
|
|
8135
|
-
if (
|
|
8842
|
+
const dest = join16(cwd, "CLAUDE.md");
|
|
8843
|
+
if (existsSync17(dest)) {
|
|
8136
8844
|
console.log(` skip CLAUDE.md (already present)`);
|
|
8137
8845
|
return;
|
|
8138
8846
|
}
|
|
8139
|
-
const name =
|
|
8847
|
+
const name = basename4(cwd);
|
|
8140
8848
|
writeFileSync8(
|
|
8141
8849
|
dest,
|
|
8142
8850
|
`# ZAM Personal Kernel \xE2\u20AC\u201D ${name}
|
|
@@ -8165,12 +8873,12 @@ Use \`zam connector setup turso\` to store cloud credentials in
|
|
|
8165
8873
|
}
|
|
8166
8874
|
function writeAgentsMd(skipAgentsMd, cwd = process.cwd()) {
|
|
8167
8875
|
if (skipAgentsMd) return;
|
|
8168
|
-
const dest =
|
|
8169
|
-
if (
|
|
8876
|
+
const dest = join16(cwd, "AGENTS.md");
|
|
8877
|
+
if (existsSync17(dest)) {
|
|
8170
8878
|
console.log(` skip AGENTS.md (already present)`);
|
|
8171
8879
|
return;
|
|
8172
8880
|
}
|
|
8173
|
-
const name =
|
|
8881
|
+
const name = basename4(cwd);
|
|
8174
8882
|
writeFileSync8(
|
|
8175
8883
|
dest,
|
|
8176
8884
|
`# ZAM Personal Kernel - ${name}
|
|
@@ -8311,9 +9019,9 @@ skillCommand.command("add").description("Register a new agent skill").requiredOp
|
|
|
8311
9019
|
});
|
|
8312
9020
|
|
|
8313
9021
|
// src/cli/commands/snapshot.ts
|
|
8314
|
-
import { existsSync as
|
|
8315
|
-
import { homedir as
|
|
8316
|
-
import { dirname as dirname7, join as
|
|
9022
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync10, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
|
|
9023
|
+
import { homedir as homedir11 } from "os";
|
|
9024
|
+
import { dirname as dirname7, join as join17 } from "path";
|
|
8317
9025
|
import { Command as Command16 } from "commander";
|
|
8318
9026
|
function defaultOutName() {
|
|
8319
9027
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+Z$/, "").replace(/:/g, "-");
|
|
@@ -8333,7 +9041,7 @@ var exportCmd = new Command16("export").description("Write a portable SQL-text s
|
|
|
8333
9041
|
try {
|
|
8334
9042
|
db = await openDatabaseWithSync({ initialize: true });
|
|
8335
9043
|
const snapshot = await exportSnapshot(db);
|
|
8336
|
-
const personalDir = await getSetting(db, "personal.workspace_dir") ||
|
|
9044
|
+
const personalDir = await getSetting(db, "personal.workspace_dir") || join17(homedir11(), "Documents", "zam");
|
|
8337
9045
|
await db.close();
|
|
8338
9046
|
db = void 0;
|
|
8339
9047
|
const manifest = verifySnapshot(snapshot);
|
|
@@ -8342,10 +9050,10 @@ var exportCmd = new Command16("export").description("Write a portable SQL-text s
|
|
|
8342
9050
|
process.stdout.write(snapshot);
|
|
8343
9051
|
return;
|
|
8344
9052
|
}
|
|
8345
|
-
const out = opts.out ??
|
|
9053
|
+
const out = opts.out ?? join17(personalDir, "snapshots", defaultOutName());
|
|
8346
9054
|
const dir = dirname7(out);
|
|
8347
|
-
if (dir && dir !== "." && !
|
|
8348
|
-
|
|
9055
|
+
if (dir && dir !== "." && !existsSync18(dir)) {
|
|
9056
|
+
mkdirSync10(dir, { recursive: true });
|
|
8349
9057
|
}
|
|
8350
9058
|
writeFileSync9(out, snapshot, "utf-8");
|
|
8351
9059
|
console.log(`Snapshot written: ${out}`);
|
|
@@ -8361,11 +9069,11 @@ var exportCmd = new Command16("export").description("Write a portable SQL-text s
|
|
|
8361
9069
|
var importCmd = new Command16("import").description("Restore a snapshot into the database").argument("<file>", "Snapshot file to restore").option("--force", "Overwrite a non-empty database", false).action(async (file, opts) => {
|
|
8362
9070
|
let db;
|
|
8363
9071
|
try {
|
|
8364
|
-
if (!
|
|
9072
|
+
if (!existsSync18(file)) {
|
|
8365
9073
|
console.error(`Error: Snapshot file not found: ${file}`);
|
|
8366
9074
|
process.exit(1);
|
|
8367
9075
|
}
|
|
8368
|
-
const snapshot =
|
|
9076
|
+
const snapshot = readFileSync11(file, "utf-8");
|
|
8369
9077
|
db = await openDatabaseWithSync({ initialize: true });
|
|
8370
9078
|
const result = await importSnapshot(db, snapshot, { force: opts.force });
|
|
8371
9079
|
await db.close();
|
|
@@ -8383,11 +9091,11 @@ var importCmd = new Command16("import").description("Restore a snapshot into the
|
|
|
8383
9091
|
});
|
|
8384
9092
|
var verifyCmd = new Command16("verify").description("Check a snapshot's manifest and checksum without importing").argument("<file>", "Snapshot file to verify").action((file) => {
|
|
8385
9093
|
try {
|
|
8386
|
-
if (!
|
|
9094
|
+
if (!existsSync18(file)) {
|
|
8387
9095
|
console.error(`Error: Snapshot file not found: ${file}`);
|
|
8388
9096
|
process.exit(1);
|
|
8389
9097
|
}
|
|
8390
|
-
const manifest = verifySnapshot(
|
|
9098
|
+
const manifest = verifySnapshot(readFileSync11(file, "utf-8"));
|
|
8391
9099
|
const { total, nonEmpty } = summarize(manifest.tables);
|
|
8392
9100
|
console.log(`Valid snapshot (format v${manifest.version})`);
|
|
8393
9101
|
console.log(` created: ${manifest.createdAt}`);
|
|
@@ -8443,7 +9151,7 @@ import { Command as Command18 } from "commander";
|
|
|
8443
9151
|
var tokenCommand = new Command18("token").description(
|
|
8444
9152
|
"Manage knowledge tokens"
|
|
8445
9153
|
);
|
|
8446
|
-
tokenCommand.command("register").description("Register a new knowledge token").requiredOption("--slug <slug>", "Unique token slug").requiredOption("--concept <concept>", "Concept description").option("--domain <domain>", "Knowledge domain", "").option("--bloom <level>", "Bloom taxonomy level (1-5)", "1").option("--source-link <link>", "Source file path or reference URL", "").option("--question <question>", "Specific question prompt for recall", "").option("--json", "Output as JSON").action(async (opts) => {
|
|
9154
|
+
tokenCommand.command("register").description("Register a new knowledge token").requiredOption("--slug <slug>", "Unique token slug").requiredOption("--concept <concept>", "Concept description").option("--domain <domain>", "Knowledge domain", "").option("--bloom <level>", "Bloom taxonomy level (1-5)", "1").option("--source-link <link>", "Source file path or reference URL", "").option("--question <question>", "Specific question prompt for recall", "").option("--json", "Output as JSON").option("--quiet", "Suppress output (exit code only)").action(async (opts) => {
|
|
8447
9155
|
await withDb(async (db) => {
|
|
8448
9156
|
let question = opts.question || null;
|
|
8449
9157
|
if (!question) {
|
|
@@ -8461,6 +9169,7 @@ tokenCommand.command("register").description("Register a new knowledge token").r
|
|
|
8461
9169
|
source_link: opts.sourceLink || null,
|
|
8462
9170
|
question
|
|
8463
9171
|
});
|
|
9172
|
+
if (opts.quiet) return;
|
|
8464
9173
|
if (opts.json) {
|
|
8465
9174
|
console.log(JSON.stringify(token, null, 2));
|
|
8466
9175
|
} else {
|
|
@@ -8475,9 +9184,10 @@ tokenCommand.command("register").description("Register a new knowledge token").r
|
|
|
8475
9184
|
}
|
|
8476
9185
|
});
|
|
8477
9186
|
});
|
|
8478
|
-
tokenCommand.command("find").description("Fuzzy search for tokens").requiredOption("--query <query>", "Search query").option("--json", "Output as JSON").action(async (opts) => {
|
|
9187
|
+
tokenCommand.command("find").description("Fuzzy search for tokens").requiredOption("--query <query>", "Search query").option("--json", "Output as JSON").option("--quiet", "Suppress output (exit code only)").action(async (opts) => {
|
|
8479
9188
|
await withDb(async (db) => {
|
|
8480
9189
|
const results = await findTokens(db, opts.query);
|
|
9190
|
+
if (opts.quiet) return;
|
|
8481
9191
|
if (opts.json) {
|
|
8482
9192
|
console.log(JSON.stringify(results, null, 2));
|
|
8483
9193
|
return;
|
|
@@ -8499,12 +9209,13 @@ tokenCommand.command("find").description("Fuzzy search for tokens").requiredOpti
|
|
|
8499
9209
|
}
|
|
8500
9210
|
});
|
|
8501
9211
|
});
|
|
8502
|
-
tokenCommand.command("list").description("List all tokens").option("--domain <domain>", "Filter by domain").option("--json", "Output as JSON").action(async (opts) => {
|
|
9212
|
+
tokenCommand.command("list").description("List all tokens").option("--domain <domain>", "Filter by domain").option("--json", "Output as JSON").option("--quiet", "Suppress output (exit code only)").action(async (opts) => {
|
|
8503
9213
|
await withDb(async (db) => {
|
|
8504
9214
|
const tokens = await listTokens(
|
|
8505
9215
|
db,
|
|
8506
9216
|
opts.domain ? { domain: opts.domain } : void 0
|
|
8507
9217
|
);
|
|
9218
|
+
if (opts.quiet) return;
|
|
8508
9219
|
if (opts.json) {
|
|
8509
9220
|
console.log(JSON.stringify(tokens, null, 2));
|
|
8510
9221
|
return;
|
|
@@ -8532,7 +9243,7 @@ tokenCommand.command("edit").description("Edit a token's mutable fields").requir
|
|
|
8532
9243
|
).option(
|
|
8533
9244
|
"--source-link <link>",
|
|
8534
9245
|
"Updated source file path or reference URL (blank allowed)"
|
|
8535
|
-
).option("--question <question>", "Updated question text (blank allowed)").option("--json", "Output as JSON").action(async (opts) => {
|
|
9246
|
+
).option("--question <question>", "Updated question text (blank allowed)").option("--json", "Output as JSON").option("--quiet", "Suppress output (exit code only)").action(async (opts) => {
|
|
8536
9247
|
await withDb(async (db) => {
|
|
8537
9248
|
const updates = {};
|
|
8538
9249
|
if (opts.concept !== void 0) updates.concept = opts.concept;
|
|
@@ -8555,6 +9266,7 @@ tokenCommand.command("edit").description("Edit a token's mutable fields").requir
|
|
|
8555
9266
|
updates.symbiosis_mode = opts.mode === "none" ? null : opts.mode;
|
|
8556
9267
|
}
|
|
8557
9268
|
const token = await updateToken(db, opts.slug, updates);
|
|
9269
|
+
if (opts.quiet) return;
|
|
8558
9270
|
if (opts.json) {
|
|
8559
9271
|
jsonOut(token);
|
|
8560
9272
|
return;
|
|
@@ -8569,7 +9281,7 @@ tokenCommand.command("edit").description("Edit a token's mutable fields").requir
|
|
|
8569
9281
|
console.log(` Source: ${token.source_link || "(none)"}`);
|
|
8570
9282
|
});
|
|
8571
9283
|
});
|
|
8572
|
-
tokenCommand.command("prereq").description("Add a prerequisite edge between tokens").requiredOption("--token <slug>", "Token that requires a prerequisite").requiredOption("--requires <slug>", "Required prerequisite token").option("--json", "Output as JSON").action(async (opts) => {
|
|
9284
|
+
tokenCommand.command("prereq").description("Add a prerequisite edge between tokens").requiredOption("--token <slug>", "Token that requires a prerequisite").requiredOption("--requires <slug>", "Required prerequisite token").option("--json", "Output as JSON").option("--quiet", "Suppress output (exit code only)").action(async (opts) => {
|
|
8573
9285
|
await withDb(async (db) => {
|
|
8574
9286
|
const token = await getTokenBySlug(db, opts.token);
|
|
8575
9287
|
if (!token) {
|
|
@@ -8582,6 +9294,7 @@ tokenCommand.command("prereq").description("Add a prerequisite edge between toke
|
|
|
8582
9294
|
process.exit(1);
|
|
8583
9295
|
}
|
|
8584
9296
|
await addPrerequisite(db, token.id, requires.id);
|
|
9297
|
+
if (opts.quiet) return;
|
|
8585
9298
|
if (opts.json) {
|
|
8586
9299
|
console.log(
|
|
8587
9300
|
JSON.stringify(
|
|
@@ -8599,9 +9312,10 @@ tokenCommand.command("prereq").description("Add a prerequisite edge between toke
|
|
|
8599
9312
|
});
|
|
8600
9313
|
tokenCommand.command("deprecate").description(
|
|
8601
9314
|
"Mark a token as deprecated (excluded from reviews, not deleted)"
|
|
8602
|
-
).requiredOption("--slug <slug>", "Token slug to deprecate").option("--json", "Output as JSON").action(async (opts) => {
|
|
9315
|
+
).requiredOption("--slug <slug>", "Token slug to deprecate").option("--json", "Output as JSON").option("--quiet", "Suppress output (exit code only)").action(async (opts) => {
|
|
8603
9316
|
await withDb(async (db) => {
|
|
8604
9317
|
const token = await deprecateToken(db, opts.slug);
|
|
9318
|
+
if (opts.quiet) return;
|
|
8605
9319
|
if (opts.json) {
|
|
8606
9320
|
console.log(JSON.stringify(token, null, 2));
|
|
8607
9321
|
} else {
|
|
@@ -8611,7 +9325,7 @@ tokenCommand.command("deprecate").description(
|
|
|
8611
9325
|
}
|
|
8612
9326
|
});
|
|
8613
9327
|
});
|
|
8614
|
-
tokenCommand.command("delete").description("Hard-delete a token and its dependent learning data").requiredOption("--slug <slug>", "Token slug to delete").option("--force", "Actually delete the token").option("--json", "Output as JSON").action(async (opts) => {
|
|
9328
|
+
tokenCommand.command("delete").description("Hard-delete a token and its dependent learning data").requiredOption("--slug <slug>", "Token slug to delete").option("--force", "Actually delete the token").option("--json", "Output as JSON").option("--quiet", "Suppress output (exit code only)").action(async (opts) => {
|
|
8615
9329
|
await withDb(async (db) => {
|
|
8616
9330
|
const impact = await getTokenDeleteImpact(db, opts.slug);
|
|
8617
9331
|
if (!opts.force) {
|
|
@@ -8621,6 +9335,7 @@ tokenCommand.command("delete").description("Hard-delete a token and its dependen
|
|
|
8621
9335
|
requiresForce: true,
|
|
8622
9336
|
impact
|
|
8623
9337
|
};
|
|
9338
|
+
if (opts.quiet) return;
|
|
8624
9339
|
if (opts.json) {
|
|
8625
9340
|
jsonOut(preview);
|
|
8626
9341
|
return;
|
|
@@ -8641,6 +9356,7 @@ tokenCommand.command("delete").description("Hard-delete a token and its dependen
|
|
|
8641
9356
|
return;
|
|
8642
9357
|
}
|
|
8643
9358
|
const result = await deleteToken(db, opts.slug);
|
|
9359
|
+
if (opts.quiet) return;
|
|
8644
9360
|
if (opts.json) {
|
|
8645
9361
|
jsonOut({
|
|
8646
9362
|
slug: result.token.slug,
|
|
@@ -8656,7 +9372,7 @@ tokenCommand.command("delete").description("Hard-delete a token and its dependen
|
|
|
8656
9372
|
console.log(` Agent skills updated: ${result.impact.agent_skills}`);
|
|
8657
9373
|
});
|
|
8658
9374
|
});
|
|
8659
|
-
tokenCommand.command("status").description("Show full status of a token for a user").requiredOption("--token <slug>", "Token slug").option("--user <id>", "User ID (default: whoami)").option("--json", "Output as JSON").action(async (opts) => {
|
|
9375
|
+
tokenCommand.command("status").description("Show full status of a token for a user").requiredOption("--token <slug>", "Token slug").option("--user <id>", "User ID (default: whoami)").option("--json", "Output as JSON").option("--quiet", "Suppress output (exit code only)").action(async (opts) => {
|
|
8660
9376
|
await withDb(async (db) => {
|
|
8661
9377
|
const userId = await resolveUser(opts, db);
|
|
8662
9378
|
const token = await getTokenBySlug(db, opts.token);
|
|
@@ -8673,6 +9389,7 @@ tokenCommand.command("status").description("Show full status of a token for a us
|
|
|
8673
9389
|
prerequisites: prereqs,
|
|
8674
9390
|
dependents
|
|
8675
9391
|
};
|
|
9392
|
+
if (opts.quiet) return;
|
|
8676
9393
|
if (opts.json) {
|
|
8677
9394
|
console.log(JSON.stringify(status, null, 2));
|
|
8678
9395
|
return;
|
|
@@ -8718,9 +9435,9 @@ tokenCommand.command("status").description("Show full status of a token for a us
|
|
|
8718
9435
|
|
|
8719
9436
|
// src/cli/commands/ui.ts
|
|
8720
9437
|
import { spawn as spawn2, spawnSync } from "child_process";
|
|
8721
|
-
import { existsSync as
|
|
8722
|
-
import { homedir as
|
|
8723
|
-
import { dirname as dirname8, join as
|
|
9438
|
+
import { existsSync as existsSync19 } from "fs";
|
|
9439
|
+
import { homedir as homedir12 } from "os";
|
|
9440
|
+
import { dirname as dirname8, join as join18 } from "path";
|
|
8724
9441
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
8725
9442
|
import { Command as Command19 } from "commander";
|
|
8726
9443
|
var C3 = {
|
|
@@ -8736,8 +9453,8 @@ function findDesktopDir() {
|
|
|
8736
9453
|
for (const start of starts) {
|
|
8737
9454
|
let dir = start;
|
|
8738
9455
|
for (let i = 0; i < 10; i++) {
|
|
8739
|
-
if (
|
|
8740
|
-
return
|
|
9456
|
+
if (existsSync19(join18(dir, "desktop", "src-tauri", "tauri.conf.json"))) {
|
|
9457
|
+
return join18(dir, "desktop");
|
|
8741
9458
|
}
|
|
8742
9459
|
const parent = dirname8(dir);
|
|
8743
9460
|
if (parent === dir) break;
|
|
@@ -8747,30 +9464,30 @@ function findDesktopDir() {
|
|
|
8747
9464
|
return null;
|
|
8748
9465
|
}
|
|
8749
9466
|
function findBuiltApp(desktopDir) {
|
|
8750
|
-
const releaseDir =
|
|
9467
|
+
const releaseDir = join18(desktopDir, "src-tauri", "target", "release");
|
|
8751
9468
|
if (process.platform === "win32") {
|
|
8752
9469
|
for (const name of ["ZAM.exe", "zam.exe", "zam-desktop.exe"]) {
|
|
8753
|
-
const p =
|
|
8754
|
-
if (
|
|
9470
|
+
const p = join18(releaseDir, name);
|
|
9471
|
+
if (existsSync19(p)) return p;
|
|
8755
9472
|
}
|
|
8756
9473
|
} else if (process.platform === "darwin") {
|
|
8757
|
-
const app =
|
|
8758
|
-
if (
|
|
9474
|
+
const app = join18(releaseDir, "bundle", "macos", "ZAM.app");
|
|
9475
|
+
if (existsSync19(app)) return app;
|
|
8759
9476
|
} else {
|
|
8760
9477
|
for (const name of ["zam", "ZAM", "zam-desktop"]) {
|
|
8761
|
-
const p =
|
|
8762
|
-
if (
|
|
9478
|
+
const p = join18(releaseDir, name);
|
|
9479
|
+
if (existsSync19(p)) return p;
|
|
8763
9480
|
}
|
|
8764
9481
|
}
|
|
8765
9482
|
return null;
|
|
8766
9483
|
}
|
|
8767
9484
|
function findInstalledApp() {
|
|
8768
9485
|
const candidates = process.platform === "win32" ? [
|
|
8769
|
-
process.env.LOCALAPPDATA &&
|
|
8770
|
-
process.env.ProgramFiles &&
|
|
8771
|
-
process.env["ProgramFiles(x86)"] &&
|
|
8772
|
-
] : process.platform === "darwin" ? ["/Applications/ZAM.app",
|
|
8773
|
-
return candidates.find((candidate) => candidate &&
|
|
9486
|
+
process.env.LOCALAPPDATA && join18(process.env.LOCALAPPDATA, "Programs", "ZAM", "ZAM.exe"),
|
|
9487
|
+
process.env.ProgramFiles && join18(process.env.ProgramFiles, "ZAM", "ZAM.exe"),
|
|
9488
|
+
process.env["ProgramFiles(x86)"] && join18(process.env["ProgramFiles(x86)"], "ZAM", "ZAM.exe")
|
|
9489
|
+
] : process.platform === "darwin" ? ["/Applications/ZAM.app", join18(homedir12(), "Applications", "ZAM.app")] : ["/opt/ZAM/zam", "/usr/bin/zam-desktop"];
|
|
9490
|
+
return candidates.find((candidate) => candidate && existsSync19(candidate)) || null;
|
|
8774
9491
|
}
|
|
8775
9492
|
function runNpm(args, opts) {
|
|
8776
9493
|
const res = spawnSync("npm", args, {
|
|
@@ -8781,7 +9498,7 @@ function runNpm(args, opts) {
|
|
|
8781
9498
|
return res.status ?? 1;
|
|
8782
9499
|
}
|
|
8783
9500
|
function ensureDesktopDeps(desktopDir) {
|
|
8784
|
-
if (
|
|
9501
|
+
if (existsSync19(join18(desktopDir, "node_modules"))) return true;
|
|
8785
9502
|
console.log(
|
|
8786
9503
|
`${C3.cyan}Installing desktop dependencies (one-time)...${C3.reset}`
|
|
8787
9504
|
);
|
|
@@ -8807,13 +9524,13 @@ function requireRust() {
|
|
|
8807
9524
|
function hasMsvcBuildTools() {
|
|
8808
9525
|
if (process.platform !== "win32") return true;
|
|
8809
9526
|
const pf86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
|
8810
|
-
const vswhere =
|
|
9527
|
+
const vswhere = join18(
|
|
8811
9528
|
pf86,
|
|
8812
9529
|
"Microsoft Visual Studio",
|
|
8813
9530
|
"Installer",
|
|
8814
9531
|
"vswhere.exe"
|
|
8815
9532
|
);
|
|
8816
|
-
if (!
|
|
9533
|
+
if (!existsSync19(vswhere)) return false;
|
|
8817
9534
|
const res = spawnSync(
|
|
8818
9535
|
vswhere,
|
|
8819
9536
|
[
|
|
@@ -8845,7 +9562,7 @@ function requireMsvcOnWindows() {
|
|
|
8845
9562
|
return false;
|
|
8846
9563
|
}
|
|
8847
9564
|
function warnIfCliMissing(repoRoot) {
|
|
8848
|
-
if (!
|
|
9565
|
+
if (!existsSync19(join18(repoRoot, "dist", "cli", "index.js"))) {
|
|
8849
9566
|
console.warn(
|
|
8850
9567
|
`${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}`
|
|
8851
9568
|
);
|
|
@@ -8908,7 +9625,7 @@ var uiCommand = new Command19("ui").description("Launch the ZAM Desktop GUI (Act
|
|
|
8908
9625
|
).option("--shortcut", "Create Desktop + Start-menu shortcuts to the GUI").action((opts) => {
|
|
8909
9626
|
const installedApp = findInstalledApp();
|
|
8910
9627
|
if (!opts.dev && !opts.build && !opts.shortcut && installedApp) {
|
|
8911
|
-
launchApp(installedApp,
|
|
9628
|
+
launchApp(installedApp, homedir12());
|
|
8912
9629
|
return;
|
|
8913
9630
|
}
|
|
8914
9631
|
const desktopDir = findDesktopDir();
|
|
@@ -8929,7 +9646,7 @@ var uiCommand = new Command19("ui").description("Launch the ZAM Desktop GUI (Act
|
|
|
8929
9646
|
);
|
|
8930
9647
|
const code = runNpm(["run", "tauri", "build"], { cwd: desktopDir });
|
|
8931
9648
|
if (code === 0) {
|
|
8932
|
-
const bundle =
|
|
9649
|
+
const bundle = join18(
|
|
8933
9650
|
desktopDir,
|
|
8934
9651
|
"src-tauri",
|
|
8935
9652
|
"target",
|
|
@@ -9005,8 +9722,8 @@ ${C3.dim}After that, 'zam ui' launches it directly.${C3.reset}`
|
|
|
9005
9722
|
});
|
|
9006
9723
|
|
|
9007
9724
|
// src/cli/commands/update.ts
|
|
9008
|
-
import { readFileSync as
|
|
9009
|
-
import { dirname as dirname9, join as
|
|
9725
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
9726
|
+
import { dirname as dirname9, join as join19 } from "path";
|
|
9010
9727
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
9011
9728
|
import { Command as Command20 } from "commander";
|
|
9012
9729
|
var GITHUB_REPO = "zam-os/zam";
|
|
@@ -9029,7 +9746,7 @@ function currentVersion() {
|
|
|
9029
9746
|
for (const up of ["..", "../..", "../../.."]) {
|
|
9030
9747
|
try {
|
|
9031
9748
|
const pkg2 = JSON.parse(
|
|
9032
|
-
|
|
9749
|
+
readFileSync12(join19(here, up, "package.json"), "utf-8")
|
|
9033
9750
|
);
|
|
9034
9751
|
if (pkg2.version) return pkg2.version;
|
|
9035
9752
|
} catch {
|
|
@@ -9147,8 +9864,8 @@ var whoamiCommand = new Command21("whoami").description("Show or set the default
|
|
|
9147
9864
|
|
|
9148
9865
|
// src/cli/commands/workspace.ts
|
|
9149
9866
|
import { execSync as execSync6 } from "child_process";
|
|
9150
|
-
import { existsSync as
|
|
9151
|
-
import { join as
|
|
9867
|
+
import { existsSync as existsSync20, writeFileSync as writeFileSync10 } from "fs";
|
|
9868
|
+
import { join as join20 } from "path";
|
|
9152
9869
|
import { confirm as confirm3, input as input7 } from "@inquirer/prompts";
|
|
9153
9870
|
import { Command as Command22 } from "commander";
|
|
9154
9871
|
function runGit(cwd, args) {
|
|
@@ -9181,7 +9898,7 @@ workspaceCommand.command("publish").description("Publish your local workspace sa
|
|
|
9181
9898
|
);
|
|
9182
9899
|
process.exit(1);
|
|
9183
9900
|
}
|
|
9184
|
-
if (!
|
|
9901
|
+
if (!existsSync20(workspaceDir)) {
|
|
9185
9902
|
console.error(
|
|
9186
9903
|
`\x1B[31m\u2717 Workspace directory does not exist: ${workspaceDir}\x1B[0m`
|
|
9187
9904
|
);
|
|
@@ -9195,15 +9912,15 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
|
|
|
9195
9912
|
);
|
|
9196
9913
|
process.exit(1);
|
|
9197
9914
|
}
|
|
9198
|
-
const gitignorePath =
|
|
9199
|
-
if (!
|
|
9915
|
+
const gitignorePath = join20(workspaceDir, ".gitignore");
|
|
9916
|
+
if (!existsSync20(gitignorePath)) {
|
|
9200
9917
|
writeFileSync10(
|
|
9201
9918
|
gitignorePath,
|
|
9202
9919
|
"node_modules/\n.agent/\n.agents/\n.claude/\n.gemini/\n.goose/\n*.log\n",
|
|
9203
9920
|
"utf8"
|
|
9204
9921
|
);
|
|
9205
9922
|
}
|
|
9206
|
-
const hasGitRepo =
|
|
9923
|
+
const hasGitRepo = existsSync20(join20(workspaceDir, ".git"));
|
|
9207
9924
|
if (!hasGitRepo) {
|
|
9208
9925
|
console.log("Initializing local Git repository...");
|
|
9209
9926
|
runGit(workspaceDir, "init -b main");
|
|
@@ -9299,7 +10016,7 @@ Active workspace: \x1B[36m${workspaceDir}\x1B[0m`);
|
|
|
9299
10016
|
// src/cli/index.ts
|
|
9300
10017
|
var __dirname = dirname10(fileURLToPath5(import.meta.url));
|
|
9301
10018
|
var pkg = JSON.parse(
|
|
9302
|
-
|
|
10019
|
+
readFileSync13(join21(__dirname, "..", "..", "package.json"), "utf-8")
|
|
9303
10020
|
);
|
|
9304
10021
|
var program = new Command23();
|
|
9305
10022
|
program.name("zam").description(
|