@tekmemo/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3914 @@
1
+ const require_chunk = require('./chunk-C0xms8kb.cjs');
2
+ let _tekmemo_cloud_client = require("@tekmemo/cloud-client");
3
+ let node_fs_promises = require("node:fs/promises");
4
+ node_fs_promises = require_chunk.__toESM(node_fs_promises, 1);
5
+ let node_path = require("node:path");
6
+ node_path = require_chunk.__toESM(node_path, 1);
7
+ let tekmemo = require("tekmemo");
8
+ let node_crypto = require("node:crypto");
9
+ let node_module = require("node:module");
10
+ let commander = require("commander");
11
+ let _tekmemo_agentfs = require("@tekmemo/agentfs");
12
+ let _tekmemo_fs = require("@tekmemo/fs");
13
+ let zod = require("zod");
14
+ let node_readline_promises = require("node:readline/promises");
15
+ node_readline_promises = require_chunk.__toESM(node_readline_promises, 1);
16
+
17
+ //#region src/errors/cli-errors.ts
18
+ var CliError = class extends Error {
19
+ code;
20
+ exitCode;
21
+ cause;
22
+ constructor(code, message, options) {
23
+ super(message);
24
+ this.name = this.constructor.name;
25
+ this.code = code;
26
+ this.exitCode = options?.exitCode ?? 1;
27
+ this.cause = options?.cause;
28
+ }
29
+ };
30
+ var CliUsageError = class extends CliError {
31
+ constructor(message, options) {
32
+ super("CLI_USAGE_ERROR", message, {
33
+ exitCode: 1,
34
+ cause: options?.cause
35
+ });
36
+ }
37
+ };
38
+ var CliValidationError = class extends CliError {
39
+ constructor(message, options) {
40
+ super("CLI_VALIDATION_ERROR", message, {
41
+ exitCode: 1,
42
+ cause: options?.cause
43
+ });
44
+ }
45
+ };
46
+ var CliFsError = class extends CliError {
47
+ constructor(message, options) {
48
+ super("CLI_FS_ERROR", message, {
49
+ exitCode: 1,
50
+ cause: options?.cause
51
+ });
52
+ }
53
+ };
54
+ var CliProtocolError = class extends CliError {
55
+ constructor(message, options) {
56
+ super("CLI_PROTOCOL_ERROR", message, {
57
+ exitCode: 1,
58
+ cause: options?.cause
59
+ });
60
+ }
61
+ };
62
+ var CliJsonlError = class extends CliError {
63
+ constructor(message, options) {
64
+ super("CLI_JSONL_ERROR", message, {
65
+ exitCode: 1,
66
+ cause: options?.cause
67
+ });
68
+ }
69
+ };
70
+
71
+ //#endregion
72
+ //#region src/cloud/client.ts
73
+ function createCliCloudClient(options = {}) {
74
+ return (0, _tekmemo_cloud_client.createTekMemoCloudClient)(toCloudClientOptions(options));
75
+ }
76
+ function toCloudClientOptions(options = {}) {
77
+ const normalized = normalizeCloudConnectionOptions(options);
78
+ return {
79
+ baseUrl: normalized.baseUrl,
80
+ ...normalized.apiKey !== void 0 ? { apiKey: normalized.apiKey } : {},
81
+ ...normalized.projectId !== void 0 ? { defaultProjectId: normalized.projectId } : {},
82
+ ...normalized.workspaceId !== void 0 ? { defaultWorkspaceId: normalized.workspaceId } : {},
83
+ ...normalized.timeoutMs !== void 0 ? { timeoutMs: normalized.timeoutMs } : {},
84
+ userAgent: "@tekmemo/cli",
85
+ requireApiKey: !options.allowMissingApiKey,
86
+ acceptLegacyEnvelope: false
87
+ };
88
+ }
89
+ function normalizeCloudConnectionOptions(options = {}) {
90
+ const baseUrl = firstNonEmpty$1(options.cloudUrl, process.env.TEKMEMO_CLOUD_URL, process.env.TEKMEMO_API_URL);
91
+ if (!baseUrl) throw new CliUsageError("TekMemo Cloud URL is required. Pass --cloud-url or set TEKMEMO_CLOUD_URL.");
92
+ const apiKey = firstNonEmpty$1(options.apiKey, process.env.TEKMEMO_API_KEY);
93
+ if (!apiKey && !options.allowMissingApiKey) throw new CliUsageError("TekMemo Cloud API key is required. Pass --api-key or set TEKMEMO_API_KEY.");
94
+ const workspaceId = firstNonEmpty$1(options.workspaceId, process.env.TEKMEMO_WORKSPACE_ID);
95
+ const projectId = firstNonEmpty$1(options.projectId, process.env.TEKMEMO_PROJECT_ID);
96
+ if (!projectId && !options.allowMissingProjectId) throw new CliUsageError("TekMemo Cloud project ID is required. Pass --project-id or set TEKMEMO_PROJECT_ID.");
97
+ const timeoutMs = normalizeTimeoutMs$1(options.timeoutMs);
98
+ return {
99
+ baseUrl,
100
+ ...apiKey !== void 0 ? { apiKey } : {},
101
+ ...workspaceId !== void 0 ? { workspaceId } : {},
102
+ ...projectId !== void 0 ? { projectId } : {},
103
+ ...timeoutMs !== void 0 ? { timeoutMs } : {}
104
+ };
105
+ }
106
+ function formatCloudError(error) {
107
+ if (error instanceof _tekmemo_cloud_client.TekMemoCloudError) {
108
+ const parts = [error.message];
109
+ if (error.status !== void 0) parts.push(`status=${error.status}`);
110
+ if (error.code) parts.push(`code=${error.code}`);
111
+ if (error.requestId) parts.push(`requestId=${error.requestId}`);
112
+ if (error.retryAfterMs !== void 0) parts.push(`retryAfterMs=${error.retryAfterMs}`);
113
+ return parts.join(" ");
114
+ }
115
+ return error instanceof Error ? error.message : String(error);
116
+ }
117
+ function cloudConnectionSummary(options) {
118
+ return {
119
+ baseUrl: options.baseUrl,
120
+ apiKey: options.apiKey ? (0, _tekmemo_cloud_client.redactSecrets)(options.apiKey, [options.apiKey]) : null,
121
+ workspaceId: options.workspaceId ?? null,
122
+ projectId: options.projectId ?? null,
123
+ timeoutMs: options.timeoutMs ?? null
124
+ };
125
+ }
126
+ function firstNonEmpty$1(...values) {
127
+ for (const value of values) {
128
+ const trimmed = value?.trim();
129
+ if (trimmed) return trimmed;
130
+ }
131
+ }
132
+ function normalizeTimeoutMs$1(value) {
133
+ if (value === void 0) return void 0;
134
+ if (typeof value === "number") {
135
+ if (!Number.isInteger(value) || value < 1) throw new CliUsageError("timeout must be a positive integer in milliseconds.");
136
+ return value;
137
+ }
138
+ const parsed = Number(value);
139
+ if (!Number.isInteger(parsed) || parsed < 1) throw new CliUsageError("timeout must be a positive integer in milliseconds.");
140
+ return parsed;
141
+ }
142
+
143
+ //#endregion
144
+ //#region src/config/runtime.ts
145
+ async function resolveCliRuntimeConfig(input) {
146
+ const flags = input.flags ?? {};
147
+ const env = input.env ?? process.env;
148
+ const initialRoot = firstNonEmpty(flags.root, env.TEKMEMO_ROOT, input.cwd) ?? input.cwd;
149
+ const configPath = node_path.default.join(node_path.default.resolve(input.cwd, initialRoot), ".tekmemo", "config.json");
150
+ const config = await readOptionalConfig(configPath);
151
+ const configRoot = config.value?.root;
152
+ const root = node_path.default.resolve(input.cwd, firstNonEmpty(flags.root, env.TEKMEMO_ROOT, configRoot, initialRoot) ?? initialRoot);
153
+ const runtime = normalizeRuntime(firstNonEmpty(flags.runtime, env.TEKMEMO_RUNTIME, config.value?.runtime, "local"));
154
+ const readPolicy = normalizeReadPolicy(firstNonEmpty(flags.readPolicy, env.TEKMEMO_READ_POLICY, config.value?.hybrid?.readPolicy, "local-first"));
155
+ const writePolicy = normalizeWritePolicy(firstNonEmpty(flags.writePolicy, env.TEKMEMO_WRITE_POLICY, config.value?.hybrid?.writePolicy, "local-first"));
156
+ const timeoutMs = normalizeTimeoutMs(firstDefined(flags.timeoutMs, env.TEKMEMO_CLOUD_TIMEOUT_MS, config.value?.cloud?.timeoutMs));
157
+ return {
158
+ runtime,
159
+ root,
160
+ configPath,
161
+ configLoaded: config.loaded,
162
+ cloud: compactCloud({
163
+ cloudUrl: firstNonEmpty(flags.cloudUrl, env.TEKMEMO_CLOUD_URL, env.TEKMEMO_API_URL, config.value?.cloud?.baseUrl),
164
+ apiKey: firstNonEmpty(flags.apiKey, env.TEKMEMO_API_KEY),
165
+ workspaceId: firstNonEmpty(flags.workspaceId, env.TEKMEMO_WORKSPACE_ID, config.value?.cloud?.workspaceId),
166
+ projectId: firstNonEmpty(flags.projectId, env.TEKMEMO_PROJECT_ID, config.value?.cloud?.projectId),
167
+ ...timeoutMs !== void 0 ? { timeoutMs } : {}
168
+ }),
169
+ hybrid: {
170
+ readPolicy,
171
+ writePolicy
172
+ }
173
+ };
174
+ }
175
+ async function writeDefaultCliConfig(input) {
176
+ const root = node_path.default.resolve(input.cwd, input.root ?? ".");
177
+ const configPath = node_path.default.join(root, ".tekmemo", "config.json");
178
+ await node_fs_promises.default.mkdir(node_path.default.dirname(configPath), { recursive: true });
179
+ const exists = await fileExists(configPath);
180
+ if (exists && !input.force) return {
181
+ path: configPath,
182
+ created: false,
183
+ overwritten: false
184
+ };
185
+ const config = input.config ?? {
186
+ version: 1,
187
+ runtime: "local",
188
+ root: "."
189
+ };
190
+ await node_fs_promises.default.writeFile(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
191
+ return {
192
+ path: configPath,
193
+ created: !exists,
194
+ overwritten: exists
195
+ };
196
+ }
197
+ async function readOptionalConfig(configPath) {
198
+ try {
199
+ const raw = await node_fs_promises.default.readFile(configPath, "utf8");
200
+ return {
201
+ loaded: true,
202
+ value: validateConfig(JSON.parse(raw), configPath)
203
+ };
204
+ } catch (error) {
205
+ if (isNodeError$1(error) && error.code === "ENOENT") return { loaded: false };
206
+ if (error instanceof SyntaxError) throw new CliUsageError(`Invalid TekMemo config JSON at ${configPath}: ${error.message}`);
207
+ throw error;
208
+ }
209
+ }
210
+ function validateConfig(value, configPath) {
211
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new CliUsageError(`TekMemo config must be an object: ${configPath}`);
212
+ const record = value;
213
+ if (record.runtime !== void 0) normalizeRuntime(record.runtime);
214
+ if (record.hybrid?.readPolicy !== void 0) normalizeReadPolicy(record.hybrid.readPolicy);
215
+ if (record.hybrid?.writePolicy !== void 0) normalizeWritePolicy(record.hybrid.writePolicy);
216
+ if (record.cloud?.timeoutMs !== void 0) normalizeTimeoutMs(record.cloud.timeoutMs);
217
+ return record;
218
+ }
219
+ function normalizeRuntime(value) {
220
+ if (value === "local" || value === "cloud" || value === "hybrid") return value;
221
+ throw new CliUsageError("runtime must be local, cloud, or hybrid.");
222
+ }
223
+ function normalizeReadPolicy(value) {
224
+ if (value === "local-first" || value === "cloud-first" || value === "local-only" || value === "cloud-only") return value;
225
+ throw new CliUsageError("read policy must be local-first, cloud-first, local-only, or cloud-only.");
226
+ }
227
+ function normalizeWritePolicy(value) {
228
+ if (value === "local-first" || value === "cloud-first" || value === "local-only" || value === "cloud-only") return value;
229
+ throw new CliUsageError("write policy must be local-first, cloud-first, local-only, or cloud-only.");
230
+ }
231
+ function normalizeTimeoutMs(value) {
232
+ if (value === void 0 || value === null || value === "") return void 0;
233
+ const parsed = typeof value === "number" ? value : Number(value);
234
+ if (!Number.isInteger(parsed) || parsed < 1) throw new CliUsageError("cloud timeout must be a positive integer in milliseconds.");
235
+ return parsed;
236
+ }
237
+ function firstNonEmpty(...values) {
238
+ for (const value of values) {
239
+ if (typeof value !== "string") continue;
240
+ const trimmed = value.trim();
241
+ if (trimmed) return trimmed;
242
+ }
243
+ }
244
+ function firstDefined(...values) {
245
+ for (const value of values) if (value !== void 0) return value;
246
+ }
247
+ function compactCloud(input) {
248
+ return {
249
+ ...input.cloudUrl !== void 0 ? { cloudUrl: input.cloudUrl } : {},
250
+ ...input.apiKey !== void 0 ? { apiKey: input.apiKey } : {},
251
+ ...input.workspaceId !== void 0 ? { workspaceId: input.workspaceId } : {},
252
+ ...input.projectId !== void 0 ? { projectId: input.projectId } : {},
253
+ ...input.timeoutMs !== void 0 ? { timeoutMs: input.timeoutMs } : {}
254
+ };
255
+ }
256
+ async function fileExists(filePath) {
257
+ try {
258
+ await node_fs_promises.default.stat(filePath);
259
+ return true;
260
+ } catch (error) {
261
+ if (isNodeError$1(error) && error.code === "ENOENT") return false;
262
+ throw error;
263
+ }
264
+ }
265
+ function isNodeError$1(error) {
266
+ return error instanceof Error && "code" in error;
267
+ }
268
+
269
+ //#endregion
270
+ //#region src/fs/paths.ts
271
+ function normalizeRootDir(rootDir) {
272
+ if (typeof rootDir !== "string" || rootDir.trim().length === 0) throw new CliFsError("rootDir must be a non-empty string.");
273
+ if (rootDir.includes("\0")) throw new CliFsError("rootDir must not contain null bytes.");
274
+ return node_path.default.resolve(rootDir);
275
+ }
276
+ function resolveInsideRoot(rootDir, relativePath) {
277
+ if (typeof relativePath !== "string" || relativePath.trim().length === 0) throw new CliFsError("relativePath must be a non-empty string.");
278
+ if (relativePath.includes("\0")) throw new CliFsError("relativePath must not contain null bytes.");
279
+ if (node_path.default.isAbsolute(relativePath)) throw new CliFsError("relativePath must not be absolute.");
280
+ const normalized = relativePath.replaceAll("\\", "/");
281
+ if (normalized.split("/").includes("..")) throw new CliFsError("relativePath must not contain path traversal.");
282
+ const resolved = node_path.default.resolve(rootDir, normalized);
283
+ const relative = node_path.default.relative(rootDir, resolved);
284
+ if (relative.startsWith("..") || node_path.default.isAbsolute(relative)) throw new CliFsError("Resolved path escaped rootDir.");
285
+ return resolved;
286
+ }
287
+
288
+ //#endregion
289
+ //#region src/fs/tekmemo-fs.ts
290
+ var TekMemoFileSystem = class {
291
+ rootDir;
292
+ rejectSymlinkedTekMemoDir;
293
+ constructor(options) {
294
+ this.rootDir = normalizeRootDir(options.rootDir);
295
+ this.rejectSymlinkedTekMemoDir = options.rejectSymlinkedTekMemoDir ?? true;
296
+ }
297
+ resolve(relativePath) {
298
+ return resolveInsideRoot(this.rootDir, relativePath);
299
+ }
300
+ async ensureSafeRoot() {
301
+ await node_fs_promises.default.mkdir(this.rootDir, { recursive: true });
302
+ await this.rejectUnsafeTekMemoSymlinkIfNeeded();
303
+ }
304
+ async exists(relativePath) {
305
+ try {
306
+ await node_fs_promises.default.lstat(this.resolve(relativePath));
307
+ return true;
308
+ } catch (error) {
309
+ if (isNodeError(error) && error.code === "ENOENT") return false;
310
+ throw new CliFsError(`Failed checking path "${relativePath}".`, { cause: error });
311
+ }
312
+ }
313
+ async readText(relativePath) {
314
+ try {
315
+ return await node_fs_promises.default.readFile(this.resolve(relativePath), "utf8");
316
+ } catch (error) {
317
+ throw new CliFsError(`Failed reading "${relativePath}".`, { cause: error });
318
+ }
319
+ }
320
+ async readTextIfExists(relativePath) {
321
+ try {
322
+ return await node_fs_promises.default.readFile(this.resolve(relativePath), "utf8");
323
+ } catch (error) {
324
+ if (isNodeError(error) && error.code === "ENOENT") return void 0;
325
+ throw new CliFsError(`Failed reading "${relativePath}".`, { cause: error });
326
+ }
327
+ }
328
+ async writeText(relativePath, content) {
329
+ if (typeof content !== "string") throw new CliFsError("content must be a string.");
330
+ const target = this.resolve(relativePath);
331
+ const parent = node_path.default.dirname(target);
332
+ await node_fs_promises.default.mkdir(parent, { recursive: true });
333
+ const tmp = node_path.default.join(parent, `.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`);
334
+ try {
335
+ await node_fs_promises.default.writeFile(tmp, content, "utf8");
336
+ await node_fs_promises.default.rename(tmp, target);
337
+ } catch (error) {
338
+ await node_fs_promises.default.rm(tmp, { force: true }).catch(() => void 0);
339
+ throw new CliFsError(`Failed writing "${relativePath}".`, { cause: error });
340
+ }
341
+ }
342
+ async appendText(relativePath, content) {
343
+ if (typeof content !== "string") throw new CliFsError("content must be a string.");
344
+ const target = this.resolve(relativePath);
345
+ await node_fs_promises.default.mkdir(node_path.default.dirname(target), { recursive: true });
346
+ try {
347
+ await node_fs_promises.default.appendFile(target, content, "utf8");
348
+ } catch (error) {
349
+ throw new CliFsError(`Failed appending "${relativePath}".`, { cause: error });
350
+ }
351
+ }
352
+ async mkdir(relativePath) {
353
+ try {
354
+ await node_fs_promises.default.mkdir(this.resolve(relativePath), { recursive: true });
355
+ } catch (error) {
356
+ throw new CliFsError(`Failed creating directory "${relativePath}".`, { cause: error });
357
+ }
358
+ }
359
+ async stat(relativePath) {
360
+ try {
361
+ const stats = await node_fs_promises.default.lstat(this.resolve(relativePath));
362
+ return {
363
+ isFile: stats.isFile(),
364
+ isDirectory: stats.isDirectory(),
365
+ isSymbolicLink: stats.isSymbolicLink(),
366
+ size: stats.size
367
+ };
368
+ } catch (error) {
369
+ throw new CliFsError(`Failed stat for "${relativePath}".`, { cause: error });
370
+ }
371
+ }
372
+ async rejectUnsafeTekMemoSymlinkIfNeeded() {
373
+ if (!this.rejectSymlinkedTekMemoDir) return;
374
+ try {
375
+ if ((await node_fs_promises.default.lstat(this.resolve(".tekmemo"))).isSymbolicLink()) throw new CliFsError("Refusing to use symlinked .tekmemo directory.");
376
+ } catch (error) {
377
+ if (error instanceof CliFsError) throw error;
378
+ if (isNodeError(error) && error.code === "ENOENT") return;
379
+ throw new CliFsError("Failed checking .tekmemo directory safety.", { cause: error });
380
+ }
381
+ }
382
+ };
383
+ function isNodeError(error) {
384
+ return error instanceof Error && "code" in error;
385
+ }
386
+
387
+ //#endregion
388
+ //#region src/output/output.ts
389
+ const colors = {
390
+ reset: "\x1B[0m",
391
+ red: "\x1B[31m",
392
+ green: "\x1B[32m",
393
+ yellow: "\x1B[33m",
394
+ dim: "\x1B[2m"
395
+ };
396
+ function shouldDisableColor(noColor) {
397
+ if (noColor) return true;
398
+ if ("NO_COLOR" in process.env) return true;
399
+ if (process.env.TERM === "dumb") return true;
400
+ return false;
401
+ }
402
+ function createBufferedOutput(options) {
403
+ const stdout = [];
404
+ const stderr = [];
405
+ const c = shouldDisableColor(options?.noColor) ? {
406
+ red: "",
407
+ green: "",
408
+ yellow: "",
409
+ reset: "",
410
+ dim: ""
411
+ } : colors;
412
+ return {
413
+ stdout,
414
+ stderr,
415
+ write(message) {
416
+ stdout.push(message);
417
+ },
418
+ error(message) {
419
+ stderr.push(`${c.red}${message}${c.reset}`);
420
+ },
421
+ success(message) {
422
+ stdout.push(`${c.green}${message}${c.reset}`);
423
+ },
424
+ warn(message) {
425
+ stdout.push(`${c.yellow}${message}${c.reset}`);
426
+ }
427
+ };
428
+ }
429
+ function printHumanOrJson(output, value, human, json = false) {
430
+ if (json) {
431
+ output.write(JSON.stringify(value, null, 2));
432
+ return;
433
+ }
434
+ output.write(human);
435
+ }
436
+ function printJsonEnvelope(output, command, data) {
437
+ const envelope = {
438
+ ok: true,
439
+ command,
440
+ data
441
+ };
442
+ output.write(JSON.stringify(envelope, null, 2));
443
+ }
444
+ function printJsonError(output, command, code, message, details) {
445
+ const error = {
446
+ ok: false,
447
+ command,
448
+ error: {
449
+ code,
450
+ message,
451
+ ...details !== void 0 ? { details } : {}
452
+ }
453
+ };
454
+ output.write(JSON.stringify(error, null, 2));
455
+ }
456
+
457
+ //#endregion
458
+ //#region src/protocol/constants.ts
459
+ /**
460
+ * Flat CLI path map kept for command ergonomics.
461
+ * The values intentionally come from `tekmemo`, so the CLI cannot drift from
462
+ * the canonical protocol owned by the core package.
463
+ */
464
+ const TEKMEMO_PATHS = {
465
+ manifest: tekmemo.MANIFEST_PATH,
466
+ coreMemory: tekmemo.CORE_MEMORY_PATH,
467
+ notesMemory: tekmemo.NOTES_MEMORY_PATH,
468
+ memoryEvents: tekmemo.MEMORY_EVENTS_PATH,
469
+ conversations: tekmemo.CONVERSATIONS_MEMORY_PATH,
470
+ chunks: tekmemo.CHUNKS_INDEX_PATH,
471
+ graphNodes: tekmemo.GRAPH_NODES_PATH,
472
+ graphEdges: tekmemo.GRAPH_EDGES_PATH,
473
+ snapshots: tekmemo.SNAPSHOTS_INDEX_PATH,
474
+ snapshotsDir: `${tekmemo.TEKMEMO_DIR}/snapshots`,
475
+ tmpDir: `${tekmemo.TEKMEMO_DIR}/tmp`
476
+ };
477
+ const REQUIRED_FILES = tekmemo.CANONICAL_TEKMEMO_FILES;
478
+ const REQUIRED_DIRS = [
479
+ tekmemo.TEKMEMO_DIR,
480
+ `${tekmemo.TEKMEMO_DIR}/memory`,
481
+ `${tekmemo.TEKMEMO_DIR}/events`,
482
+ `${tekmemo.TEKMEMO_DIR}/indexes`,
483
+ `${tekmemo.TEKMEMO_DIR}/graph`,
484
+ `${tekmemo.TEKMEMO_DIR}/snapshots`,
485
+ `${tekmemo.TEKMEMO_DIR}/tmp`
486
+ ];
487
+
488
+ //#endregion
489
+ //#region src/protocol/jsonl.ts
490
+ function parseJsonl(content, options) {
491
+ const strict = options?.strict ?? false;
492
+ const records = [];
493
+ content.split(/\r?\n/).forEach((line, index) => {
494
+ const lineNumber = index + 1;
495
+ const trimmed = line.trim();
496
+ if (trimmed.length === 0) return;
497
+ try {
498
+ const parsed = JSON.parse(trimmed);
499
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new CliJsonlError(`Line ${lineNumber} is not a JSON object.`);
500
+ records.push({
501
+ line: lineNumber,
502
+ value: parsed
503
+ });
504
+ } catch (error) {
505
+ if (strict) {
506
+ if (error instanceof CliJsonlError) throw error;
507
+ throw new CliJsonlError(`Line ${lineNumber} is invalid JSON.`, { cause: error });
508
+ }
509
+ }
510
+ });
511
+ return records;
512
+ }
513
+ function stringifyJsonl(records) {
514
+ return records.map((record) => JSON.stringify(record)).join("\n") + (records.length > 0 ? "\n" : "");
515
+ }
516
+
517
+ //#endregion
518
+ //#region src/protocol/manifest.ts
519
+ function createDefaultManifest(input) {
520
+ return (0, tekmemo.createDefaultTekMemoManifest)({
521
+ projectId: input?.projectId ?? `proj_${(0, node_crypto.randomUUID)()}`,
522
+ ...input?.now !== void 0 ? { now: () => input.now } : {}
523
+ });
524
+ }
525
+ function parseManifest(content) {
526
+ try {
527
+ return (0, tekmemo.parseManifest)(content);
528
+ } catch (error) {
529
+ throw new CliProtocolError(`manifest.json is invalid: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
530
+ }
531
+ }
532
+ function validateManifest(value) {
533
+ try {
534
+ return (0, tekmemo.validateTekMemoManifest)(value);
535
+ } catch (error) {
536
+ throw new CliProtocolError(`manifest.json is invalid: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
537
+ }
538
+ }
539
+
540
+ //#endregion
541
+ //#region src/protocol/summary.ts
542
+ async function inspectTekMemo(fs) {
543
+ const exists = await fs.exists(".tekmemo");
544
+ const manifestContent = await fs.readTextIfExists(TEKMEMO_PATHS.manifest);
545
+ const manifest = manifestContent === void 0 ? void 0 : parseManifest(manifestContent);
546
+ const tracked = [
547
+ TEKMEMO_PATHS.manifest,
548
+ TEKMEMO_PATHS.coreMemory,
549
+ TEKMEMO_PATHS.notesMemory,
550
+ TEKMEMO_PATHS.memoryEvents,
551
+ TEKMEMO_PATHS.conversations,
552
+ TEKMEMO_PATHS.chunks,
553
+ TEKMEMO_PATHS.graphNodes,
554
+ TEKMEMO_PATHS.graphEdges,
555
+ TEKMEMO_PATHS.snapshots
556
+ ];
557
+ const files = [];
558
+ const recordCounts = {};
559
+ for (const filePath of tracked) {
560
+ const content = await fs.readTextIfExists(filePath);
561
+ const isJsonl = filePath.endsWith(".jsonl");
562
+ let records = 0;
563
+ if (content !== void 0 && isJsonl) {
564
+ records = parseJsonl(content, { strict: false }).length;
565
+ recordCounts[filePath] = records;
566
+ }
567
+ files.push({
568
+ path: filePath,
569
+ exists: content !== void 0,
570
+ bytes: content ? Buffer.byteLength(content) : 0,
571
+ ...content !== void 0 ? { lines: content.split(/\r?\n/).filter(Boolean).length } : {},
572
+ ...content !== void 0 && isJsonl ? { records } : {}
573
+ });
574
+ }
575
+ return {
576
+ rootDir: fs.rootDir,
577
+ exists,
578
+ ...manifest ? { manifest } : {},
579
+ files,
580
+ summary: {
581
+ eventCount: recordCounts[TEKMEMO_PATHS.memoryEvents] ?? 0,
582
+ conversationCount: recordCounts[TEKMEMO_PATHS.conversations] ?? 0,
583
+ chunkCount: recordCounts[TEKMEMO_PATHS.chunks] ?? 0,
584
+ graphNodeCount: recordCounts[TEKMEMO_PATHS.graphNodes] ?? 0,
585
+ graphEdgeCount: recordCounts[TEKMEMO_PATHS.graphEdges] ?? 0,
586
+ snapshotCount: recordCounts[TEKMEMO_PATHS.snapshots] ?? 0
587
+ }
588
+ };
589
+ }
590
+
591
+ //#endregion
592
+ //#region src/commands/agent.ts
593
+ const LATEST_AGENT_SESSION_PATH = `${TEKMEMO_PATHS.tmpDir}/agent-sessions/latest.json`;
594
+ /**
595
+ * Starts a local AgentFS-style session workspace.
596
+ *
597
+ * @param options - Command options.
598
+ * @returns CLI exit code.
599
+ */
600
+ async function runAgentStartCommand(options) {
601
+ const session = (0, _tekmemo_agentfs.createTekMemoAgentSession)({
602
+ client: createLocalAgentfsClient(options.fs),
603
+ memory: (0, _tekmemo_fs.createNodeFsMemoryStore)({
604
+ rootDir: options.fs.rootDir,
605
+ missingFileBehavior: "empty",
606
+ createRoot: true
607
+ }),
608
+ task: options.task,
609
+ projectId: options.projectId,
610
+ actorId: options.actorId,
611
+ sessionId: options.sessionId
612
+ });
613
+ await session.prepare();
614
+ const pointer = {
615
+ sessionId: session.sessionId,
616
+ projectId: options.projectId ?? null,
617
+ root: session.paths.root,
618
+ task: options.task,
619
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
620
+ paths: session.paths
621
+ };
622
+ await options.fs.writeText(LATEST_AGENT_SESSION_PATH, `${JSON.stringify(pointer, null, 2)}\n`);
623
+ if (options.json) {
624
+ printJsonEnvelope(options.output, "agent.start", pointer);
625
+ return 0;
626
+ }
627
+ options.output.success(`Started TekMemo agent session ${session.sessionId}`);
628
+ options.output.write(formatAgentInstructions(pointer));
629
+ return 0;
630
+ }
631
+ /**
632
+ * Prints paths for a known agent session.
633
+ *
634
+ * @param options - Command options.
635
+ * @returns CLI exit code.
636
+ */
637
+ async function runAgentPathsCommand(options) {
638
+ const pointer = await readSessionPointer(options.fs, options.session);
639
+ if (options.json) {
640
+ printJsonEnvelope(options.output, "agent.paths", pointer);
641
+ return 0;
642
+ }
643
+ options.output.write(formatAgentInstructions(pointer));
644
+ return 0;
645
+ }
646
+ /**
647
+ * Extracts output files from an agent session.
648
+ *
649
+ * @param options - Command options.
650
+ * @returns CLI exit code.
651
+ */
652
+ async function runAgentExtractCommand(options) {
653
+ const pointer = await readSessionPointer(options.fs, options.session);
654
+ const extracted = await (0, _tekmemo_agentfs.extractSessionMemory)(createLocalAgentfsClient(options.fs), pointer.paths);
655
+ if (options.json) {
656
+ printJsonEnvelope(options.output, "agent.extract", {
657
+ sessionId: pointer.sessionId,
658
+ extracted
659
+ });
660
+ return 0;
661
+ }
662
+ options.output.write([
663
+ `# TekMemo Agent Session ${pointer.sessionId}`,
664
+ "",
665
+ "## Summary",
666
+ extracted.summary || "No summary written.",
667
+ "",
668
+ "## Durable Memory",
669
+ extracted.durableMemory || "No durable memory written.",
670
+ "",
671
+ "## Follow-ups",
672
+ extracted.followUps || "No follow-ups written."
673
+ ].join("\n"));
674
+ return 0;
675
+ }
676
+ /**
677
+ * Completes an agent session and optionally persists durable memory locally.
678
+ *
679
+ * @param options - Command options.
680
+ * @returns CLI exit code.
681
+ */
682
+ async function runAgentCompleteCommand(options) {
683
+ const pointer = await readSessionPointer(options.fs, options.session);
684
+ const result = await (0, _tekmemo_agentfs.createTekMemoAgentSession)({
685
+ client: createLocalAgentfsClient(options.fs),
686
+ memory: (0, _tekmemo_fs.createNodeFsMemoryStore)({
687
+ rootDir: options.fs.rootDir,
688
+ missingFileBehavior: "empty",
689
+ createRoot: true
690
+ }),
691
+ task: pointer.task,
692
+ projectId: pointer.projectId ?? void 0,
693
+ sessionId: pointer.sessionId
694
+ }).complete({
695
+ extractDurableMemory: options.extract ?? false,
696
+ checkpointLabel: options.checkpointLabel
697
+ });
698
+ if (options.json) {
699
+ printJsonEnvelope(options.output, "agent.complete", {
700
+ sessionId: pointer.sessionId,
701
+ ...result
702
+ });
703
+ return 0;
704
+ }
705
+ options.output.success(`Completed TekMemo agent session ${pointer.sessionId}`);
706
+ if (result.durableMemoryWritten) options.output.success("Persisted extracted durable memory to notes.");
707
+ options.output.write(`Summary: ${result.extracted.summary || "No summary written."}`);
708
+ return 0;
709
+ }
710
+ /**
711
+ * Adapts the CLI filesystem to the AgentFS-like client contract.
712
+ *
713
+ * @param fs - CLI filesystem.
714
+ * @returns AgentFS-like client.
715
+ */
716
+ function createLocalAgentfsClient(fs) {
717
+ return {
718
+ readText(path) {
719
+ return fs.readText(toRelativeAgentPath(path));
720
+ },
721
+ writeText(path, content) {
722
+ return fs.writeText(toRelativeAgentPath(path), content);
723
+ },
724
+ appendText(path, content) {
725
+ return fs.appendText(toRelativeAgentPath(path), content);
726
+ },
727
+ exists(path) {
728
+ return fs.exists(toRelativeAgentPath(path));
729
+ },
730
+ sync: {
731
+ pull: async () => {},
732
+ push: async () => {},
733
+ checkpoint: async () => {}
734
+ }
735
+ };
736
+ }
737
+ /**
738
+ * Converts AgentFS absolute-ish paths into project-relative CLI paths.
739
+ *
740
+ * @param path - AgentFS path.
741
+ * @returns Project-relative path.
742
+ */
743
+ function toRelativeAgentPath(path) {
744
+ return path.replace(/^\/+/, "");
745
+ }
746
+ /**
747
+ * Reads a session pointer from `.tekmemo/tmp`.
748
+ *
749
+ * @param fs - CLI filesystem.
750
+ * @param session - Session ID or latest.
751
+ * @returns Session pointer.
752
+ */
753
+ async function readSessionPointer(fs, session) {
754
+ const latest = await fs.readText(LATEST_AGENT_SESSION_PATH);
755
+ const pointer = JSON.parse(latest);
756
+ if (!session || session === "latest" || session === pointer.sessionId) return pointer;
757
+ throw new Error(`Unknown session "${session}". Only latest session ${pointer.sessionId} is tracked locally.`);
758
+ }
759
+ /**
760
+ * Formats agent-facing instructions for Codex, Claude Code, or any file-native agent.
761
+ *
762
+ * @param pointer - Session pointer.
763
+ * @returns Markdown instructions.
764
+ */
765
+ function formatAgentInstructions(pointer) {
766
+ return [
767
+ "",
768
+ "## Agent Instructions",
769
+ `Session: ${pointer.sessionId}`,
770
+ `Task: ${pointer.task}`,
771
+ "",
772
+ "Read before editing:",
773
+ `- ${pointer.paths.context.core}`,
774
+ `- ${pointer.paths.context.notes}`,
775
+ "",
776
+ "Update during work:",
777
+ `- ${pointer.paths.working.plan}`,
778
+ `- ${pointer.paths.working.commands}`,
779
+ `- ${pointer.paths.working.errors}`,
780
+ `- ${pointer.paths.working.changes}`,
781
+ "",
782
+ "Write before finishing:",
783
+ `- ${pointer.paths.output.summary}`,
784
+ `- ${pointer.paths.output.durableMemory}`,
785
+ `- ${pointer.paths.output.followUps}`,
786
+ ""
787
+ ].join("\n");
788
+ }
789
+
790
+ //#endregion
791
+ //#region src/commands/chunks.ts
792
+ async function runChunksCommand(options) {
793
+ const content = await options.fs.readTextIfExists(TEKMEMO_PATHS.chunks);
794
+ const records = content ? parseJsonl(content, { strict: options.strict ?? false }) : [];
795
+ const selected = options.limit && options.limit > 0 ? records.slice(-options.limit) : records;
796
+ if (options.json) {
797
+ options.output.write(JSON.stringify(selected, null, 2));
798
+ return 0;
799
+ }
800
+ if (selected.length === 0) {
801
+ options.output.write("No chunk records found.");
802
+ return 0;
803
+ }
804
+ options.output.write(selected.map((record) => {
805
+ const id = typeof record.value.id === "string" ? record.value.id : typeof record.value.chunkId === "string" ? record.value.chunkId : `line ${record.line}`;
806
+ const source = typeof record.value.sourcePath === "string" ? record.value.sourcePath : "unknown source";
807
+ return `${id} ${typeof record.value.status === "string" ? record.value.status : typeof record.value.indexStatus === "string" ? record.value.indexStatus : "unknown"} ${source}`;
808
+ }).join("\n"));
809
+ return 0;
810
+ }
811
+
812
+ //#endregion
813
+ //#region src/utils/content.ts
814
+ async function resolveCommandContent(input) {
815
+ const sources = [
816
+ input.inline !== void 0,
817
+ input.stdin === true,
818
+ input.file !== void 0
819
+ ].filter(Boolean).length;
820
+ if (sources === 0) throw new CliUsageError("Provide content as an argument, --stdin, or --file <path>.");
821
+ if (sources > 1) throw new CliUsageError("Use only one content source: argument, --stdin, or --file.");
822
+ let content;
823
+ if (input.inline !== void 0) content = input.inline;
824
+ else if (input.file !== void 0) {
825
+ const path = resolveInsideRoot(input.rootDir, input.file);
826
+ content = await node_fs_promises.default.readFile(path, "utf8");
827
+ } else content = input.stdinContent ?? await readStdin();
828
+ if (content.includes("\0")) throw new CliUsageError("Content must not contain null bytes.");
829
+ if (content.trim().length === 0) throw new CliUsageError("Content must not be empty.");
830
+ return content.trimEnd();
831
+ }
832
+ async function readStdin() {
833
+ const chunks = [];
834
+ for await (const chunk of process.stdin) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
835
+ return Buffer.concat(chunks).toString("utf8");
836
+ }
837
+
838
+ //#endregion
839
+ //#region src/utils/metadata.ts
840
+ function parseMetadataJson(value) {
841
+ if (value === void 0 || value.trim().length === 0) return void 0;
842
+ let parsed;
843
+ try {
844
+ parsed = JSON.parse(value);
845
+ } catch (error) {
846
+ throw new CliUsageError(`metadata must be valid JSON: ${error instanceof Error ? error.message : String(error)}`);
847
+ }
848
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new CliUsageError("metadata must be a JSON object.");
849
+ assertJsonSerializable(parsed, "metadata");
850
+ return parsed;
851
+ }
852
+ function assertJsonSerializable(value, name = "value") {
853
+ try {
854
+ JSON.stringify(value);
855
+ } catch (error) {
856
+ throw new CliUsageError(`${name} must be JSON serializable.`, { cause: error });
857
+ }
858
+ }
859
+
860
+ //#endregion
861
+ //#region src/utils/numbers.ts
862
+ function parseNonNegativeInteger(value, name = "value") {
863
+ const parsed = Number.parseInt(value, 10);
864
+ if (!Number.isFinite(parsed) || parsed < 0 || String(parsed) !== String(value).trim()) throw new CliUsageError(`${name} must be a non-negative integer.`);
865
+ return parsed;
866
+ }
867
+ function parsePositiveInteger(value, name = "value") {
868
+ const parsed = parseNonNegativeInteger(value, name);
869
+ if (parsed === 0) throw new CliUsageError(`${name} must be greater than 0.`);
870
+ return parsed;
871
+ }
872
+ function parseConfidence(value) {
873
+ const parsed = Number.parseFloat(value);
874
+ if (!Number.isFinite(parsed) || parsed < 0 || parsed > 1) throw new CliUsageError("confidence must be a number between 0 and 1.");
875
+ return parsed;
876
+ }
877
+
878
+ //#endregion
879
+ //#region src/utils/secrets.ts
880
+ const SECRET_PATTERNS = [
881
+ {
882
+ kind: "env_assignment_secret",
883
+ pattern: /\b[A-Z0-9_]*(?:API[_-]?KEY|SECRET|TOKEN|PASSWORD|PRIVATE[_-]?KEY)[A-Z0-9_]*\s*=\s*[^\s]+/gi
884
+ },
885
+ {
886
+ kind: "openai_key",
887
+ pattern: /\bsk-[A-Za-z0-9_-]{20,}\b/g
888
+ },
889
+ {
890
+ kind: "github_token",
891
+ pattern: /\bgh[pousr]_[A-Za-z0-9_]{20,}\b/g
892
+ },
893
+ {
894
+ kind: "jwt",
895
+ pattern: /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g
896
+ },
897
+ {
898
+ kind: "pem_private_key",
899
+ pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----/g
900
+ }
901
+ ];
902
+ function scanForSecrets(content) {
903
+ const findings = [];
904
+ for (const { kind, pattern } of SECRET_PATTERNS) {
905
+ pattern.lastIndex = 0;
906
+ let match = pattern.exec(content);
907
+ while (match !== null) {
908
+ findings.push({
909
+ kind,
910
+ index: match.index,
911
+ preview: redactSecretPreview(match[0])
912
+ });
913
+ match = pattern.exec(content);
914
+ }
915
+ }
916
+ return findings;
917
+ }
918
+ function redactSecretPreview(value) {
919
+ const trimmed = value.trim();
920
+ if (trimmed.length <= 8) return "[redacted]";
921
+ return `${trimmed.slice(0, 4)}…${trimmed.slice(-4)}`;
922
+ }
923
+
924
+ //#endregion
925
+ //#region src/commands/cloud.ts
926
+ const MEMORY_KINDS = new Set([
927
+ "decision",
928
+ "constraint",
929
+ "goal",
930
+ "preference",
931
+ "reference",
932
+ "summary",
933
+ "note"
934
+ ]);
935
+ const RECALL_STRATEGIES = new Set([
936
+ "local",
937
+ "vector",
938
+ "hybrid"
939
+ ]);
940
+ const RECALL_FALLBACKS = new Set(["none", "local"]);
941
+ const INDEX_MODES = new Set([
942
+ "all",
943
+ "changed",
944
+ "core",
945
+ "notes"
946
+ ]);
947
+ const CONFLICT_RESOLUTIONS = new Set([
948
+ "keep_cloud",
949
+ "use_client",
950
+ "ignore"
951
+ ]);
952
+ async function runCloudHealthCommand(options) {
953
+ const result = await createCloudClient(options, true, true).health();
954
+ if (options.json) {
955
+ printJsonEnvelope(options.output, "cloud.health", result);
956
+ return 0;
957
+ }
958
+ options.output.write([
959
+ "TekMemo Cloud",
960
+ `ok: ${result.ok}`,
961
+ `name: ${result.name ?? "unknown"}`,
962
+ `version: ${result.version ?? "unknown"}`,
963
+ `capabilities: ${(result.capabilities ?? []).join(", ") || "none"}`,
964
+ ...result.warnings?.length ? result.warnings.map((warning) => `warning: ${warning}`) : []
965
+ ].join("\n"));
966
+ return result.ok ? 0 : 1;
967
+ }
968
+ async function runCloudContextCommand(options) {
969
+ const client = createCloudClient(options);
970
+ const topK = normalizeOptionalPositiveInteger(options.limit, "limit");
971
+ const maxBytes = normalizeOptionalPositiveInteger(options.maxBytes, "max bytes");
972
+ const includeCore = options.includeCore !== false;
973
+ const includeNotes = options.includeNotes !== false;
974
+ const includeRecent = options.includeRecent !== false;
975
+ const sections = [];
976
+ const data = {
977
+ query: options.query,
978
+ sections: []
979
+ };
980
+ if (includeCore) {
981
+ const core = await client.memory.readCore();
982
+ sections.push(`# Core Memory\n\n${core.content.trim()}`);
983
+ data.sections.push({
984
+ type: "core",
985
+ content: core.content
986
+ });
987
+ }
988
+ if (includeNotes || includeRecent) {
989
+ const notes = await client.memory.listNotes({ limit: topK ?? 10 });
990
+ const renderedNotes = notes.items.map(renderNote).join("\n\n");
991
+ sections.push(`# Notes\n\n${renderedNotes || "No cloud notes found."}`);
992
+ data.sections.push({
993
+ type: "notes",
994
+ items: notes.items,
995
+ nextCursor: notes.nextCursor
996
+ });
997
+ }
998
+ const recall = await client.recall.query({
999
+ query: options.query,
1000
+ ...topK !== void 0 ? { topK } : {},
1001
+ strategy: "hybrid",
1002
+ fallback: "local",
1003
+ rerank: true
1004
+ });
1005
+ sections.push(`# Recall\n\n${renderRecallHits(recall.items)}`);
1006
+ data.sections.push({
1007
+ type: "recall",
1008
+ result: recall
1009
+ });
1010
+ const text = truncateText(sections.filter(Boolean).join("\n\n---\n\n"), maxBytes);
1011
+ if (options.json) {
1012
+ printJsonEnvelope(options.output, "cloud.context", {
1013
+ ...data,
1014
+ text,
1015
+ truncated: maxBytes !== void 0 && text.length >= maxBytes
1016
+ });
1017
+ return 0;
1018
+ }
1019
+ options.output.write(text.trimEnd());
1020
+ return 0;
1021
+ }
1022
+ async function runCloudRecallCommand(options) {
1023
+ const client = createCloudClient(options);
1024
+ const topK = normalizeOptionalPositiveInteger(options.limit, "limit");
1025
+ const strategy = normalizeRecallStrategy(options.strategy);
1026
+ const fallback = normalizeRecallFallback(options.fallback);
1027
+ const result = await client.recall.query({
1028
+ query: options.query,
1029
+ ...topK !== void 0 ? { topK } : {},
1030
+ ...strategy !== void 0 ? { strategy } : {},
1031
+ ...fallback !== void 0 ? { fallback } : {},
1032
+ ...options.rerank !== void 0 ? { rerank: options.rerank } : {}
1033
+ });
1034
+ if (options.json) {
1035
+ printJsonEnvelope(options.output, "cloud.recall", result);
1036
+ return 0;
1037
+ }
1038
+ options.output.write(renderRecallHits(result.items));
1039
+ return 0;
1040
+ }
1041
+ async function runCloudRecallIndexCommand(options) {
1042
+ const client = createCloudClient(options);
1043
+ const mode = normalizeIndexMode(options.mode);
1044
+ const result = await client.recall.index({
1045
+ ...mode !== void 0 ? { mode } : {},
1046
+ ...options.force !== void 0 ? { force: options.force } : {}
1047
+ });
1048
+ if (options.json) {
1049
+ printJsonEnvelope(options.output, "cloud.recall.index", result);
1050
+ return 0;
1051
+ }
1052
+ options.output.success(`Recall indexing ${result.status}${result.jobId ? ` job=${result.jobId}` : ""}`);
1053
+ if (result.indexed !== void 0) options.output.write(`indexed: ${result.indexed}`);
1054
+ for (const warning of result.warnings ?? []) options.output.warn(`warning: ${warning}`);
1055
+ return 0;
1056
+ }
1057
+ async function runCloudRememberCommand(options) {
1058
+ const client = createCloudClient(options);
1059
+ const content = await resolveCommandContent({
1060
+ rootDir: options.rootDir ?? process.cwd(),
1061
+ inline: options.content,
1062
+ stdin: options.stdin,
1063
+ file: options.file,
1064
+ stdinContent: options.stdinContent
1065
+ });
1066
+ const findings = scanForSecrets(content);
1067
+ if (findings.length > 0 && !options.allowSecrets) {
1068
+ const data = {
1069
+ stored: false,
1070
+ secretFindings: findings
1071
+ };
1072
+ if (options.json) printJsonEnvelope(options.output, "cloud.remember", data);
1073
+ else options.output.error(`Refusing to store possible secret (${findings[0]?.kind}). Use --allow-secrets only after review.`);
1074
+ return 1;
1075
+ }
1076
+ const metadata = parseMetadataJson(options.metadata);
1077
+ const note = {
1078
+ content,
1079
+ kind: normalizeMemoryKind(options.kind),
1080
+ ...options.title ? { title: options.title } : {},
1081
+ ...options.tags?.length ? { tags: options.tags.map((tag) => tag.trim()).filter(Boolean) } : {},
1082
+ ...options.source ? { source: options.source } : {},
1083
+ ...options.confidence !== void 0 ? { confidence: normalizeConfidence(options.confidence) } : {},
1084
+ ...metadata ? { metadata } : {}
1085
+ };
1086
+ const result = await client.memory.createNote(note);
1087
+ if (options.json) {
1088
+ printJsonEnvelope(options.output, "cloud.remember", {
1089
+ ...result,
1090
+ secretFindings: findings
1091
+ });
1092
+ return 0;
1093
+ }
1094
+ options.output.success(`Stored cloud memory ${result.id}`);
1095
+ return 0;
1096
+ }
1097
+ async function runCloudReadCommand(options) {
1098
+ const client = createCloudClient(options);
1099
+ if (options.target === "core") {
1100
+ const result = await client.memory.readCore();
1101
+ if (options.json) printJsonEnvelope(options.output, "cloud.read", {
1102
+ target: "core",
1103
+ ...result
1104
+ });
1105
+ else options.output.write(result.content.trimEnd());
1106
+ return 0;
1107
+ }
1108
+ const limit = normalizeOptionalPositiveInteger(options.limit, "limit");
1109
+ const result = await client.memory.listNotes({ ...limit !== void 0 ? { limit } : {} });
1110
+ if (options.json) {
1111
+ printJsonEnvelope(options.output, "cloud.read", {
1112
+ target: "notes",
1113
+ ...result
1114
+ });
1115
+ return 0;
1116
+ }
1117
+ options.output.write(result.items.map(renderNote).join("\n\n") || "No cloud notes found.");
1118
+ return 0;
1119
+ }
1120
+ async function runCloudUpdateCoreCommand(options) {
1121
+ const client = createCloudClient(options);
1122
+ const content = await resolveCommandContent({
1123
+ rootDir: options.rootDir ?? process.cwd(),
1124
+ inline: options.content,
1125
+ stdin: options.stdin,
1126
+ file: options.file,
1127
+ stdinContent: options.stdinContent
1128
+ });
1129
+ const findings = scanForSecrets(content);
1130
+ if (findings.length > 0 && !options.allowSecrets) {
1131
+ const data = {
1132
+ updated: false,
1133
+ secretFindings: findings
1134
+ };
1135
+ if (options.json) printJsonEnvelope(options.output, "cloud.update-core", data);
1136
+ else options.output.error(`Refusing to store possible secret (${findings[0]?.kind}). Use --allow-secrets only after review.`);
1137
+ return 1;
1138
+ }
1139
+ const result = await client.memory.updateCore({ content });
1140
+ if (options.json) {
1141
+ printJsonEnvelope(options.output, "cloud.update-core", {
1142
+ ...result,
1143
+ secretFindings: findings
1144
+ });
1145
+ return 0;
1146
+ }
1147
+ options.output.success("Updated cloud core memory.");
1148
+ return 0;
1149
+ }
1150
+ async function runCloudRecentCommand(options) {
1151
+ return runCloudReadCommand({
1152
+ ...options,
1153
+ target: "notes"
1154
+ });
1155
+ }
1156
+ async function runCloudValidateCommand(options) {
1157
+ const client = createCloudClient(options);
1158
+ const errors = [];
1159
+ const warnings = [];
1160
+ let healthOk = false;
1161
+ try {
1162
+ const health = await client.health();
1163
+ healthOk = health.ok;
1164
+ for (const warning of health.warnings ?? []) warnings.push(warning);
1165
+ if (!health.ok) errors.push("Cloud health check returned ok=false.");
1166
+ } catch (error) {
1167
+ errors.push(`health: ${error instanceof Error ? error.message : String(error)}`);
1168
+ }
1169
+ try {
1170
+ await client.memory.readCore();
1171
+ } catch (error) {
1172
+ errors.push(`memory/core: ${error instanceof Error ? error.message : String(error)}`);
1173
+ }
1174
+ if (options.strict) try {
1175
+ await client.memory.listNotes({ limit: 1 });
1176
+ } catch (error) {
1177
+ errors.push(`memory/notes: ${error instanceof Error ? error.message : String(error)}`);
1178
+ }
1179
+ const result = {
1180
+ ok: healthOk && errors.length === 0,
1181
+ warnings,
1182
+ errors
1183
+ };
1184
+ if (options.json) {
1185
+ printJsonEnvelope(options.output, "cloud.validate", result);
1186
+ return result.ok ? 0 : 1;
1187
+ }
1188
+ if (result.ok) options.output.success("Cloud memory is valid.");
1189
+ else options.output.error("Cloud memory validation failed.");
1190
+ for (const warning of warnings) options.output.warn(`warning: ${warning}`);
1191
+ for (const error of errors) options.output.error(`error: ${error}`);
1192
+ return result.ok ? 0 : 1;
1193
+ }
1194
+ async function runCloudSnapshotCommand(options) {
1195
+ const data = {
1196
+ created: false,
1197
+ reason: "cloud_snapshots_not_available",
1198
+ message: "Cloud snapshots/exports are planned for the R2 milestone and are not exposed by the current project-scoped Cloud API.",
1199
+ label: options.label ?? "manual",
1200
+ type: options.type ?? "manual"
1201
+ };
1202
+ if (options.json) printJsonEnvelope(options.output, "cloud.snapshot", data);
1203
+ else options.output.error(data.message);
1204
+ return 2;
1205
+ }
1206
+ async function runCloudSyncStatusCommand(options) {
1207
+ const result = await createCloudClient(options).sync.status({ ...options.clientId ? { clientId: options.clientId } : {} });
1208
+ if (options.json) {
1209
+ printJsonEnvelope(options.output, "cloud.sync.status", result);
1210
+ return 0;
1211
+ }
1212
+ options.output.write([
1213
+ `serverVersion: ${result.serverVersion}`,
1214
+ `openConflicts: ${result.openConflicts}`,
1215
+ `clients: ${result.clients.length}`,
1216
+ ...result.recentEvents !== void 0 ? [`recentEvents: ${result.recentEvents}`] : []
1217
+ ].join("\n"));
1218
+ return 0;
1219
+ }
1220
+ async function runCloudSyncPullCommand(options) {
1221
+ const client = createCloudClient(options);
1222
+ const sinceServerVersion = normalizeOptionalNonNegativeInteger(options.sinceServerVersion, "since server version");
1223
+ const limit = normalizeOptionalPositiveInteger(options.limit, "limit");
1224
+ const result = await client.sync.pull({
1225
+ clientId: options.clientId,
1226
+ ...sinceServerVersion !== void 0 ? { sinceServerVersion } : {},
1227
+ ...limit !== void 0 ? { limit } : {}
1228
+ });
1229
+ if (options.json) {
1230
+ printJsonEnvelope(options.output, "cloud.sync.pull", result);
1231
+ return 0;
1232
+ }
1233
+ options.output.write(`Pulled ${result.events.length} event(s). serverVersion=${result.serverVersion}`);
1234
+ return 0;
1235
+ }
1236
+ async function runCloudSyncPushCommand(options) {
1237
+ const client = createCloudClient(options);
1238
+ const payload = await resolveJsonPayload({
1239
+ rootDir: options.rootDir ?? process.cwd(),
1240
+ inline: options.eventsJson,
1241
+ stdin: options.stdin,
1242
+ file: options.file,
1243
+ stdinContent: options.stdinContent,
1244
+ fieldName: "events JSON"
1245
+ });
1246
+ const events = extractSyncEvents(payload);
1247
+ const checkpoint = options.checkpointJson ? parseJsonObject(options.checkpointJson, "checkpoint JSON") : extractCheckpoint(payload);
1248
+ const result = await client.sync.push({
1249
+ clientId: options.clientId,
1250
+ events,
1251
+ ...checkpoint !== void 0 ? { checkpoint } : {}
1252
+ });
1253
+ if (options.json) {
1254
+ printJsonEnvelope(options.output, "cloud.sync.push", result);
1255
+ return 0;
1256
+ }
1257
+ options.output.write([
1258
+ `accepted: ${result.accepted.length}`,
1259
+ `duplicates: ${result.duplicates.length}`,
1260
+ `rejected: ${result.rejected.length}`,
1261
+ `conflicts: ${result.conflicts.length}`,
1262
+ `serverVersion: ${result.serverVersion}`
1263
+ ].join("\n"));
1264
+ return result.rejected.length === 0 && result.conflicts.length === 0 ? 0 : 1;
1265
+ }
1266
+ async function runCloudSyncResolveCommand(options) {
1267
+ const client = createCloudClient(options);
1268
+ const resolution = normalizeConflictResolution(options.resolution);
1269
+ const content = options.contentJson ? parseJsonObject(options.contentJson, "content JSON") : void 0;
1270
+ const result = await client.sync.resolveConflict({
1271
+ conflictId: options.conflictId,
1272
+ resolution,
1273
+ ...content !== void 0 ? { content } : {}
1274
+ });
1275
+ if (options.json) {
1276
+ printJsonEnvelope(options.output, "cloud.sync.resolve", result);
1277
+ return 0;
1278
+ }
1279
+ options.output.success(`Resolved conflict ${result.conflictId}`);
1280
+ if (result.serverVersion !== void 0) options.output.write(`serverVersion: ${result.serverVersion}`);
1281
+ return result.resolved ? 0 : 1;
1282
+ }
1283
+ async function runCloudReadinessCommand(options) {
1284
+ const result = await createCloudClient(options, true, true).readiness();
1285
+ if (options.json) {
1286
+ printJsonEnvelope(options.output, "cloud.readiness", result);
1287
+ return 0;
1288
+ }
1289
+ options.output.write([
1290
+ `ok: ${result.ok}`,
1291
+ `name: ${result.name ?? "unknown"}`,
1292
+ `version: ${result.version ?? "unknown"}`,
1293
+ `capabilities: ${(result.capabilities ?? []).join(", ") || "none"}`,
1294
+ ...result.warnings?.length ? result.warnings.map((warning) => `warning: ${warning}`) : []
1295
+ ].join("\n"));
1296
+ return result.ok ? 0 : 1;
1297
+ }
1298
+ async function runCloudContextComposeCommand(options) {
1299
+ const client = createCloudClient(options);
1300
+ const topK = normalizeOptionalPositiveInteger(options.topK, "topK");
1301
+ const result = await client.context.compose({
1302
+ query: options.query,
1303
+ ...topK !== void 0 ? { topK } : {},
1304
+ ...options.strategy !== void 0 ? { strategy: options.strategy } : {},
1305
+ ...options.rerank !== void 0 ? { rerank: options.rerank } : {},
1306
+ ...options.includeCoreMemory !== void 0 ? { includeCoreMemory: options.includeCoreMemory } : {},
1307
+ ...options.includeRecallResults !== void 0 ? { includeRecallResults: options.includeRecallResults } : {},
1308
+ ...options.includeGraphContext !== void 0 ? { includeGraphContext: options.includeGraphContext } : {}
1309
+ });
1310
+ if (options.json) {
1311
+ printJsonEnvelope(options.output, "cloud.context.compose", result);
1312
+ return 0;
1313
+ }
1314
+ options.output.write(result.context);
1315
+ return 0;
1316
+ }
1317
+ async function runCloudGraphListNodesCommand(options) {
1318
+ const client = createCloudClient(options);
1319
+ const limit = normalizeOptionalPositiveInteger(options.limit, "limit");
1320
+ const result = await client.graph.listNodes({
1321
+ ...limit !== void 0 ? { limit } : {},
1322
+ ...options.cursor ? { cursor: options.cursor } : {},
1323
+ ...options.status ? { status: options.status } : {}
1324
+ });
1325
+ if (options.json) {
1326
+ printJsonEnvelope(options.output, "cloud.graph.list-nodes", result);
1327
+ return 0;
1328
+ }
1329
+ options.output.write(result.items.map((node) => `Node: ${node.nodeId} - ${node.label}`).join("\n") || "No nodes found.");
1330
+ return 0;
1331
+ }
1332
+ async function runCloudGraphCreateNodeCommand(options) {
1333
+ const client = createCloudClient(options);
1334
+ const metadata = options.metadataJson ? parseJsonObject(options.metadataJson, "metadata JSON") : void 0;
1335
+ const result = await client.graph.createNode({
1336
+ nodeId: options.nodeId,
1337
+ type: options.type,
1338
+ label: options.label,
1339
+ ...options.summary ? { summary: options.summary } : {},
1340
+ ...options.aliases ? { aliases: options.aliases } : {},
1341
+ ...metadata ? { metadata } : {}
1342
+ });
1343
+ if (options.json) {
1344
+ printJsonEnvelope(options.output, "cloud.graph.create-node", result);
1345
+ return 0;
1346
+ }
1347
+ options.output.success(`Created node ${result.nodeId}`);
1348
+ return 0;
1349
+ }
1350
+ async function runCloudGraphListEdgesCommand(options) {
1351
+ const client = createCloudClient(options);
1352
+ const limit = normalizeOptionalPositiveInteger(options.limit, "limit");
1353
+ const result = await client.graph.listEdges({
1354
+ ...limit !== void 0 ? { limit } : {},
1355
+ ...options.cursor ? { cursor: options.cursor } : {},
1356
+ ...options.status ? { status: options.status } : {}
1357
+ });
1358
+ if (options.json) {
1359
+ printJsonEnvelope(options.output, "cloud.graph.list-edges", result);
1360
+ return 0;
1361
+ }
1362
+ options.output.write(result.items.map((edge) => `Edge: ${edge.edgeId ?? "(new)"} - ${edge.fromNodeId} -> ${edge.toNodeId}`).join("\n") || "No edges found.");
1363
+ return 0;
1364
+ }
1365
+ async function runCloudGraphCreateEdgeCommand(options) {
1366
+ const client = createCloudClient(options);
1367
+ const metadata = options.metadataJson ? parseJsonObject(options.metadataJson, "metadata JSON") : void 0;
1368
+ const weight = options.weight !== void 0 ? parseFloat(String(options.weight)) : void 0;
1369
+ const result = await client.graph.createEdge({
1370
+ ...options.edgeId ? { edgeId: options.edgeId } : {},
1371
+ fromNodeId: options.fromNodeId,
1372
+ toNodeId: options.toNodeId,
1373
+ type: options.type,
1374
+ ...options.directed !== void 0 ? { directed: options.directed } : {},
1375
+ ...weight !== void 0 ? { weight } : {},
1376
+ ...metadata ? { metadata } : {}
1377
+ });
1378
+ if (options.json) {
1379
+ printJsonEnvelope(options.output, "cloud.graph.create-edge", result);
1380
+ return 0;
1381
+ }
1382
+ options.output.success(`Created edge ${result.edgeId ?? "(new)"}`);
1383
+ return 0;
1384
+ }
1385
+ async function runCloudGraphNeighborsCommand(options) {
1386
+ const client = createCloudClient(options);
1387
+ const depth = options.depth !== void 0 ? parseInt(String(options.depth), 10) : void 0;
1388
+ const limit = normalizeOptionalPositiveInteger(options.limit, "limit");
1389
+ const result = await client.graph.neighbors({
1390
+ nodeId: options.nodeId,
1391
+ ...options.direction ? { direction: options.direction } : {},
1392
+ ...depth !== void 0 ? { depth } : {},
1393
+ ...limit !== void 0 ? { limit } : {}
1394
+ });
1395
+ if (options.json) {
1396
+ printJsonEnvelope(options.output, "cloud.graph.neighbors", result);
1397
+ return 0;
1398
+ }
1399
+ options.output.write(`Nodes: ${result.nodes.length}, Edges: ${result.edges.length}`);
1400
+ return 0;
1401
+ }
1402
+ async function runCloudGraphPathCommand(options) {
1403
+ const client = createCloudClient(options);
1404
+ const maxDepth = options.maxDepth !== void 0 ? parseInt(String(options.maxDepth), 10) : void 0;
1405
+ const result = await client.graph.path({
1406
+ fromNodeId: options.fromNodeId,
1407
+ toNodeId: options.toNodeId,
1408
+ ...maxDepth !== void 0 ? { maxDepth } : {}
1409
+ });
1410
+ if (options.json) {
1411
+ printJsonEnvelope(options.output, "cloud.graph.path", result);
1412
+ return 0;
1413
+ }
1414
+ options.output.write(`Nodes: ${result.nodes.length}, Edges: ${result.edges.length}`);
1415
+ return 0;
1416
+ }
1417
+ async function runCloudExtractionRunCommand(options) {
1418
+ const result = await createCloudClient(options).extraction.run({
1419
+ ...options.mode ? { mode: options.mode } : {},
1420
+ ...options.force !== void 0 ? { force: options.force } : {}
1421
+ });
1422
+ if (options.json) {
1423
+ printJsonEnvelope(options.output, "cloud.extraction.run", result);
1424
+ return 0;
1425
+ }
1426
+ options.output.success(`Extraction ${result.status}${result.jobId ? ` job=${result.jobId}` : ""}`);
1427
+ return 0;
1428
+ }
1429
+ async function runCloudExtractionJobsCommand(options) {
1430
+ const client = createCloudClient(options);
1431
+ const limit = normalizeOptionalPositiveInteger(options.limit, "limit");
1432
+ const result = await client.extraction.jobs({ ...limit !== void 0 ? { limit } : {} });
1433
+ if (options.json) {
1434
+ printJsonEnvelope(options.output, "cloud.extraction.jobs", result);
1435
+ return 0;
1436
+ }
1437
+ options.output.write(result.items.map((job) => `Job: ${job.jobId} - ${job.status}`).join("\n") || "No jobs found.");
1438
+ return 0;
1439
+ }
1440
+ async function runCloudEvalsRunCommand(options) {
1441
+ const result = await createCloudClient(options).evals.run({
1442
+ ...options.fixtureIds ? { fixtureIds: options.fixtureIds.split(",").map((s) => s.trim()) } : {},
1443
+ ...options.iterations !== void 0 ? { iterations: parseInt(String(options.iterations), 10) } : {}
1444
+ });
1445
+ if (options.json) {
1446
+ printJsonEnvelope(options.output, "cloud.evals.run", result);
1447
+ return 0;
1448
+ }
1449
+ options.output.success(`Eval pass rate: ${result.passRate}`);
1450
+ return 0;
1451
+ }
1452
+ async function runCloudBenchmarksRunCommand(options) {
1453
+ const result = await createCloudClient(options).benchmarks.run({
1454
+ ...options.fixtureIds ? { fixtureIds: options.fixtureIds.split(",").map((s) => s.trim()) } : {},
1455
+ ...options.iterations !== void 0 ? { iterations: parseInt(String(options.iterations), 10) } : {}
1456
+ });
1457
+ if (options.json) {
1458
+ printJsonEnvelope(options.output, "cloud.benchmarks.run", result);
1459
+ return 0;
1460
+ }
1461
+ options.output.success(`Benchmark pass rate: ${result.passRate}, avg latency: ${result.avgLatencyMs ?? "N/A"}ms`);
1462
+ return 0;
1463
+ }
1464
+ async function runCloudExportsCreateCommand(options) {
1465
+ const result = await createCloudClient(options).exports.create({ ...options.label ? { label: options.label } : {} });
1466
+ if (options.json) {
1467
+ printJsonEnvelope(options.output, "cloud.exports.create", result);
1468
+ return 0;
1469
+ }
1470
+ options.output.success(`Created export ${result.exportId}`);
1471
+ return 0;
1472
+ }
1473
+ async function runCloudExportsDownloadCommand(options) {
1474
+ const result = await createCloudClient(options).exports.downloadUrl({ exportId: options.exportId });
1475
+ if (options.json) {
1476
+ printJsonEnvelope(options.output, "cloud.exports.download", result);
1477
+ return 0;
1478
+ }
1479
+ options.output.write(`Download URL: ${result.downloadUrl}`);
1480
+ return 0;
1481
+ }
1482
+ async function runCloudSnapshotsCreateCommand(options) {
1483
+ const result = await createCloudClient(options).snapshots.create({
1484
+ ...options.label ? { label: options.label } : {},
1485
+ ...options.trigger ? { trigger: options.trigger } : {}
1486
+ });
1487
+ if (options.json) {
1488
+ printJsonEnvelope(options.output, "cloud.snapshots.create", result);
1489
+ return 0;
1490
+ }
1491
+ options.output.success(`Created snapshot ${result.snapshotId}`);
1492
+ return 0;
1493
+ }
1494
+ async function runCloudSnapshotsDownloadCommand(options) {
1495
+ const result = await createCloudClient(options).snapshots.downloadUrl({ snapshotId: options.snapshotId });
1496
+ if (options.json) {
1497
+ printJsonEnvelope(options.output, "cloud.snapshots.download", result);
1498
+ return 0;
1499
+ }
1500
+ options.output.write(`Download URL: ${result.downloadUrl}`);
1501
+ return 0;
1502
+ }
1503
+ async function runCloudProvidersListCommand(options) {
1504
+ const result = await createCloudClient(options).providers.list();
1505
+ if (options.json) {
1506
+ printJsonEnvelope(options.output, "cloud.providers.list", result);
1507
+ return 0;
1508
+ }
1509
+ options.output.write(result.map((cred) => `Provider: ${cred.provider} - ${cred.keyName}`).join("\n") || "No providers found.");
1510
+ return 0;
1511
+ }
1512
+ async function runCloudProvidersCreateCommand(options) {
1513
+ const result = await createCloudClient(options).providers.create({
1514
+ provider: options.provider,
1515
+ keyName: options.keyName,
1516
+ secret: options.secret,
1517
+ ...options.restUrl ? { restUrl: options.restUrl } : {},
1518
+ ...options.embeddingModel ? { embeddingModel: options.embeddingModel } : {},
1519
+ ...options.rerankModel ? { rerankModel: options.rerankModel } : {}
1520
+ });
1521
+ if (options.json) {
1522
+ printJsonEnvelope(options.output, "cloud.providers.create", result);
1523
+ return 0;
1524
+ }
1525
+ options.output.success(`Created provider credential ${result.credentialId}`);
1526
+ return 0;
1527
+ }
1528
+ async function runCloudProvidersTestCommand(options) {
1529
+ const result = await createCloudClient(options).providers.test({ credentialId: options.credentialId });
1530
+ if (options.json) {
1531
+ printJsonEnvelope(options.output, "cloud.providers.test", result);
1532
+ return 0;
1533
+ }
1534
+ options.output.write(`Test result: ${result.ok ? "OK" : "Failed"}${result.message ? ` - ${result.message}` : ""}`);
1535
+ return result.ok ? 0 : 1;
1536
+ }
1537
+ function createCloudClient(options, allowMissingApiKey = false, allowMissingProjectId = false) {
1538
+ return createCliCloudClient({
1539
+ cloudUrl: options.cloudUrl,
1540
+ apiKey: options.apiKey,
1541
+ workspaceId: options.workspaceId,
1542
+ projectId: options.projectId,
1543
+ timeoutMs: options.timeoutMs,
1544
+ allowMissingApiKey,
1545
+ allowMissingProjectId
1546
+ });
1547
+ }
1548
+ function normalizeMemoryKind(value) {
1549
+ const candidate = value ?? "note";
1550
+ if (!MEMORY_KINDS.has(candidate)) throw new CliUsageError(`kind must be one of: ${[...MEMORY_KINDS].join(", ")}.`);
1551
+ return candidate;
1552
+ }
1553
+ function normalizeRecallStrategy(value) {
1554
+ if (value === void 0) return void 0;
1555
+ if (RECALL_STRATEGIES.has(value)) return value;
1556
+ throw new CliUsageError("recall strategy must be local, vector, or hybrid.");
1557
+ }
1558
+ function normalizeRecallFallback(value) {
1559
+ if (value === void 0) return void 0;
1560
+ if (RECALL_FALLBACKS.has(value)) return value;
1561
+ throw new CliUsageError("recall fallback must be none or local.");
1562
+ }
1563
+ function normalizeIndexMode(value) {
1564
+ if (value === void 0) return void 0;
1565
+ if (INDEX_MODES.has(value)) return value;
1566
+ throw new CliUsageError("index mode must be all, changed, core, or notes.");
1567
+ }
1568
+ function normalizeConflictResolution(value) {
1569
+ if (CONFLICT_RESOLUTIONS.has(value)) return value;
1570
+ throw new CliUsageError("conflict resolution must be keep_cloud, use_client, or ignore.");
1571
+ }
1572
+ function normalizeConfidence(value) {
1573
+ return parseConfidence(String(value));
1574
+ }
1575
+ function normalizeOptionalPositiveInteger(value, name) {
1576
+ if (value === void 0) return void 0;
1577
+ const parsed = typeof value === "number" ? value : Number(value);
1578
+ if (!Number.isInteger(parsed) || parsed < 1) throw new CliUsageError(`${name} must be a positive integer.`);
1579
+ return parsed;
1580
+ }
1581
+ function normalizeOptionalNonNegativeInteger(value, name) {
1582
+ if (value === void 0) return void 0;
1583
+ const parsed = typeof value === "number" ? value : Number(value);
1584
+ if (!Number.isInteger(parsed) || parsed < 0) throw new CliUsageError(`${name} must be a non-negative integer.`);
1585
+ return parsed;
1586
+ }
1587
+ function renderNote(note) {
1588
+ const heading = note.title ? `${note.title} (${note.id})` : note.id;
1589
+ const tags = note.tags?.length ? `\n- tags: ${note.tags.join(", ")}` : "";
1590
+ const created = note.createdAt ? `\n- createdAt: ${note.createdAt}` : "";
1591
+ return `## ${heading}\n- kind: ${note.kind}${created}${tags}\n\n${note.content.trim()}`;
1592
+ }
1593
+ function renderRecallHits(items) {
1594
+ if (items.length === 0) return "No matching cloud memories found.";
1595
+ return items.map((item, index) => {
1596
+ const score = item.score === void 0 ? "" : ` score=${item.score}`;
1597
+ return `${index + 1}. ${item.text}${score}`;
1598
+ }).join("\n\n");
1599
+ }
1600
+ function truncateText(text, maxBytes) {
1601
+ if (maxBytes === void 0 || text.length <= maxBytes) return text;
1602
+ return `${text.slice(0, Math.max(0, maxBytes - 40)).trimEnd()}\n\n[truncated by --max-bytes]`;
1603
+ }
1604
+ async function resolveJsonPayload(input) {
1605
+ const raw = await resolveCommandContent({
1606
+ rootDir: input.rootDir,
1607
+ inline: input.inline,
1608
+ stdin: input.stdin,
1609
+ file: input.file,
1610
+ stdinContent: input.stdinContent
1611
+ });
1612
+ try {
1613
+ return JSON.parse(raw);
1614
+ } catch (error) {
1615
+ throw new CliUsageError(`${input.fieldName} must be valid JSON: ${error instanceof Error ? error.message : String(error)}`);
1616
+ }
1617
+ }
1618
+ function parseJsonObject(raw, fieldName) {
1619
+ try {
1620
+ const parsed = JSON.parse(raw);
1621
+ if (!isJsonObject(parsed)) throw new Error("value must be an object");
1622
+ return parsed;
1623
+ } catch (error) {
1624
+ throw new CliUsageError(`${fieldName} must be a JSON object: ${error instanceof Error ? error.message : String(error)}`);
1625
+ }
1626
+ }
1627
+ function extractSyncEvents(payload) {
1628
+ const events = Array.isArray(payload) ? payload : isJsonObject(payload) ? payload.events : void 0;
1629
+ if (!Array.isArray(events)) throw new CliUsageError("sync push payload must be an event array or an object with an events array.");
1630
+ return events.map((event, index) => {
1631
+ if (!isJsonObject(event)) throw new CliUsageError(`sync event at index ${index} must be an object.`);
1632
+ return event;
1633
+ });
1634
+ }
1635
+ function extractCheckpoint(payload) {
1636
+ if (!isJsonObject(payload) || payload.checkpoint === void 0) return void 0;
1637
+ if (!isJsonObject(payload.checkpoint)) throw new CliUsageError("sync checkpoint must be an object.");
1638
+ return payload.checkpoint;
1639
+ }
1640
+ function isJsonObject(value) {
1641
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1642
+ }
1643
+
1644
+ //#endregion
1645
+ //#region src/commands/context.ts
1646
+ function truncate(value, maxChars) {
1647
+ if (value.length <= maxChars) return value;
1648
+ return `${value.slice(0, Math.max(0, maxChars - 20)).trimEnd()}\n\n[truncated]`;
1649
+ }
1650
+ function searchText(file, content, query) {
1651
+ const lower = query.toLowerCase();
1652
+ return content.split(/\r?\n/).map((line, index) => ({
1653
+ file,
1654
+ line: index + 1,
1655
+ content: line.trim()
1656
+ })).filter((entry) => entry.content.toLowerCase().includes(lower));
1657
+ }
1658
+ async function runContextCommand(options) {
1659
+ const maxChars = typeof options.maxChars === "number" ? options.maxChars : options.maxChars ? parsePositiveInteger(options.maxChars, "max chars") : 12e3;
1660
+ const core = await options.fs.readTextIfExists(TEKMEMO_PATHS.coreMemory) ?? "";
1661
+ const notes = await options.fs.readTextIfExists(TEKMEMO_PATHS.notesMemory) ?? "";
1662
+ const eventContent = await options.fs.readTextIfExists(TEKMEMO_PATHS.memoryEvents) ?? "";
1663
+ const chunkContent = await options.fs.readTextIfExists(TEKMEMO_PATHS.chunks) ?? "";
1664
+ const events = eventContent ? parseJsonl(eventContent).slice(-10).map((record) => record.value) : [];
1665
+ const chunks = chunkContent ? parseJsonl(chunkContent).slice(-10).map((record) => record.value) : [];
1666
+ const matches = options.query ? [...searchText(TEKMEMO_PATHS.coreMemory, core, options.query), ...searchText(TEKMEMO_PATHS.notesMemory, notes, options.query)] : [];
1667
+ const data = {
1668
+ rootDir: options.fs.rootDir,
1669
+ query: options.query ?? null,
1670
+ core: truncate(core.trim(), Math.floor(maxChars * .45)),
1671
+ notes: truncate(notes.trim(), Math.floor(maxChars * .35)),
1672
+ matches,
1673
+ ...options.includeEvents ? { recentEvents: events } : {},
1674
+ ...options.includeChunks ? { recentChunks: chunks } : {}
1675
+ };
1676
+ if (options.json) {
1677
+ printJsonEnvelope(options.output, "context", data);
1678
+ return 0;
1679
+ }
1680
+ const sections = [
1681
+ "# TekMemo Context",
1682
+ `Root: ${options.fs.rootDir}`,
1683
+ options.query ? `Query: ${options.query}` : void 0,
1684
+ "",
1685
+ "## Core Memory",
1686
+ data.core || "No core memory found.",
1687
+ "",
1688
+ "## Notes Memory",
1689
+ data.notes || "No notes memory found."
1690
+ ];
1691
+ if (matches.length > 0) sections.push("", "## Text Matches", ...matches.slice(0, 20).map((m) => `- ${m.file}:${m.line} — ${m.content}`));
1692
+ if (options.includeEvents && events.length > 0) sections.push("", "## Recent Memory Events", ...events.map((event) => `- ${String(event.timestamp ?? "unknown")} ${String(event.type ?? "unknown")} ${String(event.summary ?? "")}`.trim()));
1693
+ options.output.write(truncate(sections.filter((section) => section !== void 0).join("\n"), maxChars));
1694
+ return 0;
1695
+ }
1696
+
1697
+ //#endregion
1698
+ //#region src/commands/diff.ts
1699
+ function lineCount(content) {
1700
+ return content.split(/\r?\n/).filter(Boolean).length;
1701
+ }
1702
+ function contentHash(content) {
1703
+ return (0, node_crypto.createHash)("sha256").update(content).digest("hex");
1704
+ }
1705
+ async function loadBundle(fs, path) {
1706
+ const raw = await fs.readText(path);
1707
+ const parsed = JSON.parse(raw);
1708
+ if (typeof parsed !== "object" || parsed === null || typeof parsed.files !== "object") throw new Error("Snapshot bundle is malformed.");
1709
+ return parsed;
1710
+ }
1711
+ function snapshotMatches(record, key) {
1712
+ if (record.id === key) return true;
1713
+ if (record.label === key) return true;
1714
+ const metadata = record.metadata;
1715
+ if (typeof metadata === "object" && metadata !== null && !Array.isArray(metadata)) return metadata.label === key;
1716
+ return false;
1717
+ }
1718
+ async function runDiffCommand(options) {
1719
+ const snapshotContent = await options.fs.readTextIfExists(TEKMEMO_PATHS.snapshots);
1720
+ if (!snapshotContent) {
1721
+ options.output.error("No snapshots found.");
1722
+ return 1;
1723
+ }
1724
+ const snapshots = parseJsonl(snapshotContent);
1725
+ const snapA = snapshots.find((s) => snapshotMatches(s.value, options.labelA));
1726
+ const snapB = snapshots.find((s) => snapshotMatches(s.value, options.labelB));
1727
+ if (!snapA) {
1728
+ options.output.error(`Snapshot "${options.labelA}" not found.`);
1729
+ return 1;
1730
+ }
1731
+ if (!snapB) {
1732
+ options.output.error(`Snapshot "${options.labelB}" not found.`);
1733
+ return 1;
1734
+ }
1735
+ const pathA = snapA.value.path;
1736
+ const pathB = snapB.value.path;
1737
+ if (typeof pathA !== "string" || typeof pathB !== "string") {
1738
+ options.output.error("Snapshot index contains a malformed path.");
1739
+ return 1;
1740
+ }
1741
+ let bundleA;
1742
+ let bundleB;
1743
+ try {
1744
+ bundleA = await loadBundle(options.fs, pathA);
1745
+ } catch {
1746
+ options.output.error(`Failed to load snapshot bundle: ${pathA}`);
1747
+ return 1;
1748
+ }
1749
+ try {
1750
+ bundleB = await loadBundle(options.fs, pathB);
1751
+ } catch {
1752
+ options.output.error(`Failed to load snapshot bundle: ${pathB}`);
1753
+ return 1;
1754
+ }
1755
+ const allPaths = new Set([...Object.keys(bundleA.files), ...Object.keys(bundleB.files)]);
1756
+ const diffs = [];
1757
+ for (const filePath of allPaths) {
1758
+ const contentA = bundleA.files[filePath];
1759
+ const contentB = bundleB.files[filePath];
1760
+ if (contentA === void 0 && contentB !== void 0) diffs.push({
1761
+ path: filePath,
1762
+ status: "added",
1763
+ bytesB: Buffer.byteLength(contentB)
1764
+ });
1765
+ else if (contentA !== void 0 && contentB === void 0) diffs.push({
1766
+ path: filePath,
1767
+ status: "removed",
1768
+ bytesA: Buffer.byteLength(contentA)
1769
+ });
1770
+ else if (contentA !== void 0 && contentB !== void 0) if (contentHash(contentA) === contentHash(contentB)) diffs.push({
1771
+ path: filePath,
1772
+ status: "unchanged",
1773
+ bytesA: Buffer.byteLength(contentA),
1774
+ bytesB: Buffer.byteLength(contentB)
1775
+ });
1776
+ else {
1777
+ const isJsonl = filePath.endsWith(".jsonl");
1778
+ diffs.push({
1779
+ path: filePath,
1780
+ status: "changed",
1781
+ bytesA: Buffer.byteLength(contentA),
1782
+ bytesB: Buffer.byteLength(contentB),
1783
+ ...isJsonl ? {
1784
+ recordsA: lineCount(contentA),
1785
+ recordsB: lineCount(contentB)
1786
+ } : {}
1787
+ });
1788
+ }
1789
+ }
1790
+ const changed = diffs.filter((d) => d.status !== "unchanged");
1791
+ const data = {
1792
+ labelA: options.labelA,
1793
+ labelB: options.labelB,
1794
+ snapshotA: {
1795
+ id: bundleA.id,
1796
+ createdAt: bundleA.createdAt ?? bundleA.timestamp,
1797
+ path: pathA
1798
+ },
1799
+ snapshotB: {
1800
+ id: bundleB.id,
1801
+ createdAt: bundleB.createdAt ?? bundleB.timestamp,
1802
+ path: pathB
1803
+ },
1804
+ totalFiles: allPaths.size,
1805
+ changedFiles: changed.length,
1806
+ diffs: changed
1807
+ };
1808
+ if (options.json) {
1809
+ printJsonEnvelope(options.output, "diff", data);
1810
+ return 0;
1811
+ }
1812
+ options.output.write(`Comparing "${options.labelA}" vs "${options.labelB}"`);
1813
+ options.output.write(` [A] ${data.snapshotA.createdAt ?? "unknown"} [B] ${data.snapshotB.createdAt ?? "unknown"}`);
1814
+ options.output.write("");
1815
+ if (changed.length === 0) {
1816
+ options.output.success("No differences found. Snapshots are identical.");
1817
+ return 0;
1818
+ }
1819
+ const statusLabel = {
1820
+ added: "+",
1821
+ removed: "-",
1822
+ changed: "~"
1823
+ };
1824
+ for (const diff of changed) {
1825
+ let line = ` ${statusLabel[diff.status] ?? "?"} ${diff.path}`;
1826
+ if (diff.status === "changed") {
1827
+ const sizeInfo = `${diff.bytesA}B → ${diff.bytesB}B`;
1828
+ const recordInfo = diff.recordsA !== void 0 && diff.recordsB !== void 0 ? ` (${diff.recordsA} → ${diff.recordsB} records)` : "";
1829
+ line += ` ${sizeInfo}${recordInfo}`;
1830
+ } else if (diff.status === "added" && diff.bytesB !== void 0) line += ` (${diff.bytesB}B)`;
1831
+ else if (diff.status === "removed" && diff.bytesA !== void 0) line += ` (${diff.bytesA}B)`;
1832
+ options.output.write(line);
1833
+ }
1834
+ options.output.write(`\n${changed.length} file(s) changed out of ${allPaths.size} total.`);
1835
+ return 0;
1836
+ }
1837
+
1838
+ //#endregion
1839
+ //#region src/protocol/schemas.ts
1840
+ const JsonRecordSchema = zod.z.record(zod.z.string(), zod.z.unknown());
1841
+ const IsoDateSchema = zod.z.string().datetime();
1842
+ const NonEmptyStringSchema = zod.z.string().min(1);
1843
+ const ManifestSchema = zod.z.object({
1844
+ version: NonEmptyStringSchema,
1845
+ projectId: NonEmptyStringSchema.optional(),
1846
+ createdAt: IsoDateSchema,
1847
+ updatedAt: IsoDateSchema,
1848
+ memory: zod.z.object({
1849
+ core: NonEmptyStringSchema,
1850
+ notes: NonEmptyStringSchema
1851
+ }),
1852
+ events: zod.z.object({
1853
+ memoryEvents: NonEmptyStringSchema,
1854
+ conversations: NonEmptyStringSchema
1855
+ }),
1856
+ indexes: zod.z.object({ chunks: NonEmptyStringSchema }),
1857
+ graph: zod.z.object({
1858
+ nodes: NonEmptyStringSchema,
1859
+ edges: NonEmptyStringSchema
1860
+ }),
1861
+ snapshots: zod.z.object({ index: NonEmptyStringSchema })
1862
+ });
1863
+ const ConversationEntrySchema = zod.z.object({
1864
+ timestamp: IsoDateSchema,
1865
+ role: zod.z.enum([
1866
+ "user",
1867
+ "assistant",
1868
+ "system",
1869
+ "tool"
1870
+ ]),
1871
+ content: zod.z.string(),
1872
+ summary: zod.z.string().optional(),
1873
+ metadata: JsonRecordSchema.optional()
1874
+ });
1875
+ const MemoryEventSchema = zod.z.object({
1876
+ id: NonEmptyStringSchema,
1877
+ type: zod.z.enum([
1878
+ "memory.created",
1879
+ "memory.updated",
1880
+ "memory.merged",
1881
+ "memory.conflicted",
1882
+ "memory.decayed",
1883
+ "memory.forgotten",
1884
+ "memory.restored",
1885
+ "memory.indexed",
1886
+ "memory.reindexed",
1887
+ "snapshot.created",
1888
+ "sync.started",
1889
+ "sync.completed",
1890
+ "sync.failed"
1891
+ ]),
1892
+ timestamp: IsoDateSchema,
1893
+ projectId: NonEmptyStringSchema.optional(),
1894
+ sourcePath: NonEmptyStringSchema.optional(),
1895
+ actor: zod.z.object({
1896
+ type: zod.z.enum([
1897
+ "user",
1898
+ "agent",
1899
+ "system",
1900
+ "api"
1901
+ ]),
1902
+ id: NonEmptyStringSchema.optional()
1903
+ }).optional(),
1904
+ summary: NonEmptyStringSchema.optional(),
1905
+ metadata: JsonRecordSchema.optional()
1906
+ });
1907
+ const ChunkRecordSchema = zod.z.object({
1908
+ chunkId: NonEmptyStringSchema,
1909
+ sourcePath: NonEmptyStringSchema,
1910
+ sourceType: zod.z.enum([
1911
+ "document",
1912
+ "note",
1913
+ "conversation",
1914
+ "event",
1915
+ "import",
1916
+ "graph"
1917
+ ]),
1918
+ sourceId: NonEmptyStringSchema,
1919
+ sourceHash: NonEmptyStringSchema,
1920
+ textHash: NonEmptyStringSchema,
1921
+ memoryType: zod.z.enum([
1922
+ "core",
1923
+ "notes",
1924
+ "conversation",
1925
+ "event",
1926
+ "chunk",
1927
+ "graph"
1928
+ ]),
1929
+ index: zod.z.number().int().nonnegative(),
1930
+ startOffset: zod.z.number().int().nonnegative(),
1931
+ endOffset: zod.z.number().int().nonnegative(),
1932
+ status: zod.z.enum([
1933
+ "active",
1934
+ "stale",
1935
+ "deleted"
1936
+ ]),
1937
+ createdAt: IsoDateSchema,
1938
+ updatedAt: IsoDateSchema.optional(),
1939
+ sectionName: zod.z.string().optional(),
1940
+ metadata: JsonRecordSchema.optional()
1941
+ });
1942
+ const SnapshotEntrySchema = zod.z.object({
1943
+ id: NonEmptyStringSchema,
1944
+ path: NonEmptyStringSchema,
1945
+ type: zod.z.enum([
1946
+ "manual",
1947
+ "automatic",
1948
+ "pre-sync",
1949
+ "pre-restore"
1950
+ ]),
1951
+ status: zod.z.enum([
1952
+ "available",
1953
+ "expired",
1954
+ "deleted"
1955
+ ]),
1956
+ createdAt: IsoDateSchema,
1957
+ expiresAt: IsoDateSchema.optional(),
1958
+ checksum: NonEmptyStringSchema.optional(),
1959
+ metadata: JsonRecordSchema.optional()
1960
+ });
1961
+ const LegacySnapshotEntrySchema = zod.z.object({
1962
+ id: NonEmptyStringSchema,
1963
+ timestamp: IsoDateSchema,
1964
+ label: NonEmptyStringSchema,
1965
+ path: NonEmptyStringSchema,
1966
+ metadata: JsonRecordSchema.optional()
1967
+ });
1968
+ const MemoryChunkSchema = ChunkRecordSchema;
1969
+
1970
+ //#endregion
1971
+ //#region src/commands/doctor.ts
1972
+ async function runDoctorCommand(options) {
1973
+ const issues = [];
1974
+ for (const dir of REQUIRED_DIRS) if (!await options.fs.exists(dir)) issues.push({
1975
+ level: "error",
1976
+ code: "missing_dir",
1977
+ message: `Missing directory: ${dir}`
1978
+ });
1979
+ for (const file of REQUIRED_FILES) if (!await options.fs.exists(file)) issues.push({
1980
+ level: "error",
1981
+ code: "missing_file",
1982
+ message: `Missing file: ${file}`
1983
+ });
1984
+ const manifestContent = await options.fs.readTextIfExists(TEKMEMO_PATHS.manifest);
1985
+ if (manifestContent) try {
1986
+ const parsed = JSON.parse(manifestContent);
1987
+ ManifestSchema.parse(parsed);
1988
+ } catch (error) {
1989
+ issues.push({
1990
+ level: "error",
1991
+ code: "invalid_manifest",
1992
+ message: `manifest.json: ${error instanceof Error ? error.message : String(error)}`
1993
+ });
1994
+ }
1995
+ const validationMap = {
1996
+ [TEKMEMO_PATHS.memoryEvents]: MemoryEventSchema,
1997
+ [TEKMEMO_PATHS.conversations]: ConversationEntrySchema,
1998
+ [TEKMEMO_PATHS.chunks]: MemoryChunkSchema,
1999
+ [TEKMEMO_PATHS.snapshots]: SnapshotEntrySchema
2000
+ };
2001
+ const conversationIds = /* @__PURE__ */ new Set();
2002
+ for (const [file, schema] of Object.entries(validationMap)) {
2003
+ const content = await options.fs.readTextIfExists(file);
2004
+ if (content === void 0) continue;
2005
+ const records = parseJsonl(content, { strict: options.strict ?? false });
2006
+ for (const record of records) try {
2007
+ const validated = schema.parse(record.value);
2008
+ if (file === TEKMEMO_PATHS.conversations && typeof validated.id === "string") conversationIds.add(validated.id);
2009
+ } catch (error) {
2010
+ issues.push({
2011
+ level: "error",
2012
+ code: "invalid_line",
2013
+ message: `${file}:${record.line}: ${error instanceof Error ? error.message : String(error)}`
2014
+ });
2015
+ }
2016
+ }
2017
+ const eventContent = await options.fs.readTextIfExists(TEKMEMO_PATHS.memoryEvents);
2018
+ if (eventContent) {
2019
+ const events = parseJsonl(eventContent);
2020
+ for (const event of events) {
2021
+ const docId = event.value.documentId;
2022
+ if (typeof docId !== "string") continue;
2023
+ if (docId === "core" || docId === "notes") continue;
2024
+ if (conversationIds.size > 0 && !conversationIds.has(docId)) issues.push({
2025
+ level: "warning",
2026
+ code: "orphaned_event",
2027
+ message: `${TEKMEMO_PATHS.memoryEvents}:${event.line}: Event references unknown document/conversation "${docId}"`
2028
+ });
2029
+ }
2030
+ }
2031
+ const result = {
2032
+ ok: issues.filter((issue) => issue.level === "error").length === 0,
2033
+ issues
2034
+ };
2035
+ if (options.json) options.output.write(JSON.stringify(result, null, 2));
2036
+ else if (result.ok) if (issues.length > 0) options.output.warn(["TekMemo doctor passed with warnings:", ...issues.map((issue) => `- [${issue.level}] ${issue.message}`)].join("\n"));
2037
+ else options.output.success("TekMemo doctor passed.");
2038
+ else options.output.error(["TekMemo doctor found errors:", ...issues.map((issue) => `- [${issue.level}] ${issue.message}`)].join("\n"));
2039
+ return result.ok ? 0 : 1;
2040
+ }
2041
+
2042
+ //#endregion
2043
+ //#region src/commands/edit.ts
2044
+ async function runEditCommand(options) {
2045
+ const findings = scanForSecrets(options.message);
2046
+ if (findings.length > 0 && !options.allowSecrets) {
2047
+ if (options.json) printJsonEnvelope(options.output, "edit", {
2048
+ updated: false,
2049
+ secretFindings: findings
2050
+ });
2051
+ else options.output.error(`Refusing to store possible secret (${findings[0]?.kind}). Use --allow-secrets only after review.`);
2052
+ return 1;
2053
+ }
2054
+ const file = options.type === "core" ? TEKMEMO_PATHS.coreMemory : TEKMEMO_PATHS.notesMemory;
2055
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
2056
+ const entry = options.type === "note" ? `\n## ${timestamp}\n- kind: note\n- tags: none\n- confidence: 1\n\n${options.message.trim()}\n` : `\n${options.message.trim()}\n`;
2057
+ await options.fs.appendText(file, entry);
2058
+ const event = {
2059
+ id: `evt_${Date.now()}_${Math.random().toString(16).slice(2)}`,
2060
+ type: "memory.updated",
2061
+ timestamp,
2062
+ sourcePath: file,
2063
+ actor: { type: "user" },
2064
+ summary: `${options.type} memory updated by CLI`,
2065
+ metadata: {
2066
+ document: options.type,
2067
+ command: "edit",
2068
+ createdBy: "@tekmemo/cli"
2069
+ }
2070
+ };
2071
+ await options.fs.appendText(TEKMEMO_PATHS.memoryEvents, stringifyJsonl([event]));
2072
+ const data = {
2073
+ updated: true,
2074
+ path: file,
2075
+ eventId: event.id,
2076
+ secretFindings: findings
2077
+ };
2078
+ if (options.json) printJsonEnvelope(options.output, "edit", data);
2079
+ else options.output.success(`Updated ${file}`);
2080
+ return 0;
2081
+ }
2082
+
2083
+ //#endregion
2084
+ //#region src/commands/events.ts
2085
+ async function runEventsCommand(options) {
2086
+ const content = await options.fs.readTextIfExists(TEKMEMO_PATHS.memoryEvents);
2087
+ const records = content ? parseJsonl(content, { strict: options.strict ?? false }) : [];
2088
+ const selected = options.limit && options.limit > 0 ? records.slice(-options.limit) : records;
2089
+ if (options.json) {
2090
+ options.output.write(JSON.stringify(selected, null, 2));
2091
+ return 0;
2092
+ }
2093
+ if (selected.length === 0) {
2094
+ options.output.write("No memory events found.");
2095
+ return 0;
2096
+ }
2097
+ options.output.write(selected.map((record) => {
2098
+ const type = typeof record.value.type === "string" ? record.value.type : "unknown";
2099
+ return `${typeof record.value.timestamp === "string" ? record.value.timestamp : `line ${record.line}`} ${type}${typeof record.value.summary === "string" ? ` — ${record.value.summary}` : ""}`;
2100
+ }).join("\n"));
2101
+ return 0;
2102
+ }
2103
+
2104
+ //#endregion
2105
+ //#region src/commands/init.ts
2106
+ async function runInitCommand(options) {
2107
+ await options.fs.ensureSafeRoot();
2108
+ let projectId = options.projectId?.trim();
2109
+ if (projectId !== void 0 && projectId.length === 0) projectId = void 0;
2110
+ if (!projectId && !options.json && !options.noInput && process.stdout.isTTY) {
2111
+ options.output.write("Initializing TekMemo...");
2112
+ const rl = node_readline_promises.default.createInterface({
2113
+ input: process.stdin,
2114
+ output: process.stdout
2115
+ });
2116
+ try {
2117
+ const answer = await rl.question("Enter project ID (leave empty for random): ");
2118
+ if (answer.trim()) projectId = answer.trim();
2119
+ } finally {
2120
+ rl.close();
2121
+ }
2122
+ }
2123
+ for (const dir of REQUIRED_DIRS) await options.fs.mkdir(dir);
2124
+ if (await options.fs.readTextIfExists(TEKMEMO_PATHS.manifest) && !options.force) {
2125
+ const data = {
2126
+ created: false,
2127
+ rootDir: options.fs.rootDir,
2128
+ message: ".tekmemo already exists. Use --force to overwrite seed files."
2129
+ };
2130
+ if (options.json) printJsonEnvelope(options.output, "init", data);
2131
+ else options.output.write(data.message);
2132
+ return 0;
2133
+ }
2134
+ const manifest = createDefaultManifest(projectId ? { projectId } : void 0);
2135
+ const seedFiles = {
2136
+ [TEKMEMO_PATHS.manifest]: `${JSON.stringify(manifest, null, 2)}\n`,
2137
+ [TEKMEMO_PATHS.coreMemory]: "# Core Memory\n\n",
2138
+ [TEKMEMO_PATHS.notesMemory]: "# Notes\n\n",
2139
+ [TEKMEMO_PATHS.memoryEvents]: "",
2140
+ [TEKMEMO_PATHS.conversations]: "",
2141
+ [TEKMEMO_PATHS.chunks]: "",
2142
+ [TEKMEMO_PATHS.graphNodes]: "",
2143
+ [TEKMEMO_PATHS.graphEdges]: "",
2144
+ [TEKMEMO_PATHS.snapshots]: ""
2145
+ };
2146
+ const created = [];
2147
+ const overwritten = [];
2148
+ const skipped = [];
2149
+ for (const file of REQUIRED_FILES) {
2150
+ const exists = await options.fs.exists(file);
2151
+ if (!exists || options.force) {
2152
+ await options.fs.writeText(file, seedFiles[file] ?? "");
2153
+ if (exists) overwritten.push(file);
2154
+ else created.push(file);
2155
+ } else skipped.push(file);
2156
+ }
2157
+ const data = {
2158
+ created: true,
2159
+ rootDir: options.fs.rootDir,
2160
+ manifest,
2161
+ files: {
2162
+ created,
2163
+ overwritten,
2164
+ skipped
2165
+ }
2166
+ };
2167
+ if (options.json) printJsonEnvelope(options.output, "init", data);
2168
+ else options.output.success(`Initialized .tekmemo at ${options.fs.rootDir} (Project ID: ${manifest.projectId ?? "none"})`);
2169
+ return 0;
2170
+ }
2171
+
2172
+ //#endregion
2173
+ //#region src/commands/inspect.ts
2174
+ async function runInspectCommand(options) {
2175
+ const inspection = await inspectTekMemo(options.fs);
2176
+ if (options.json) {
2177
+ options.output.write(JSON.stringify(inspection, null, 2));
2178
+ return 0;
2179
+ }
2180
+ const lines = [
2181
+ `TekMemo root: ${inspection.rootDir}`,
2182
+ `.tekmemo exists: ${inspection.exists ? "yes" : "no"}`,
2183
+ inspection.manifest ? `Project: ${inspection.manifest.projectId}` : "Project: missing manifest",
2184
+ "",
2185
+ "Files:"
2186
+ ];
2187
+ for (const file of inspection.files) lines.push(`- ${file.path}: ${file.exists ? `${file.bytes} bytes` : "missing"}${file.records !== void 0 ? `, ${file.records} records` : ""}`);
2188
+ lines.push("");
2189
+ lines.push("Summary:");
2190
+ lines.push(`- events: ${inspection.summary.eventCount}`);
2191
+ lines.push(`- conversations: ${inspection.summary.conversationCount}`);
2192
+ lines.push(`- chunks: ${inspection.summary.chunkCount}`);
2193
+ lines.push(`- graph nodes: ${inspection.summary.graphNodeCount}`);
2194
+ lines.push(`- graph edges: ${inspection.summary.graphEdgeCount}`);
2195
+ lines.push(`- snapshots: ${inspection.summary.snapshotCount}`);
2196
+ options.output.write(lines.join("\n"));
2197
+ return 0;
2198
+ }
2199
+
2200
+ //#endregion
2201
+ //#region src/commands/read.ts
2202
+ const TARGET_PATHS = {
2203
+ core: TEKMEMO_PATHS.coreMemory,
2204
+ notes: TEKMEMO_PATHS.notesMemory,
2205
+ manifest: TEKMEMO_PATHS.manifest
2206
+ };
2207
+ async function runReadCommand(options) {
2208
+ const path = TARGET_PATHS[options.target];
2209
+ const content = await options.fs.readTextIfExists(path);
2210
+ if (content === void 0) {
2211
+ options.output.error(`${path} does not exist. Run tekmemo init first.`);
2212
+ return 1;
2213
+ }
2214
+ if (options.json) printJsonEnvelope(options.output, "read", {
2215
+ target: options.target,
2216
+ path,
2217
+ content
2218
+ });
2219
+ else options.output.write(content.trimEnd());
2220
+ return 0;
2221
+ }
2222
+
2223
+ //#endregion
2224
+ //#region src/commands/remember.ts
2225
+ const NOTE_KINDS = new Set([
2226
+ "decision",
2227
+ "constraint",
2228
+ "goal",
2229
+ "preference",
2230
+ "reference",
2231
+ "summary",
2232
+ "note"
2233
+ ]);
2234
+ function normalizeKind(kind) {
2235
+ const normalized = (kind ?? "note").trim();
2236
+ if (!NOTE_KINDS.has(normalized)) throw new CliUsageError(`Invalid memory kind "${normalized}". Allowed: ${[...NOTE_KINDS].join(", ")}`);
2237
+ return normalized;
2238
+ }
2239
+ function parseActor(actor) {
2240
+ if (!actor) return { type: "user" };
2241
+ const [type, id] = actor.split(":", 2);
2242
+ if (type !== "user" && type !== "agent" && type !== "system" && type !== "api") throw new CliUsageError("actor must be one of user, agent, system, or api, optionally followed by :id.");
2243
+ return {
2244
+ type,
2245
+ ...id ? { id } : {}
2246
+ };
2247
+ }
2248
+ function formatTimestampedNote(input) {
2249
+ const heading = input.title ? `${input.timestamp} — ${input.title.replace(/\s+/g, " ").trim()}` : input.timestamp;
2250
+ const tags = input.tags?.length ? input.tags.join(", ") : "none";
2251
+ return [
2252
+ `## ${heading}`,
2253
+ `- kind: ${input.kind}`,
2254
+ `- tags: ${tags}`,
2255
+ `- confidence: ${input.confidence}`,
2256
+ input.source ? `- source: ${input.source}` : void 0,
2257
+ input.metadata ? `- metadata: ${JSON.stringify(input.metadata)}` : void 0,
2258
+ "",
2259
+ input.content.trim(),
2260
+ ""
2261
+ ].filter((line) => line !== void 0).join("\n");
2262
+ }
2263
+ async function runRememberCommand(options) {
2264
+ const content = await resolveCommandContent({
2265
+ rootDir: options.fs.rootDir,
2266
+ inline: options.content,
2267
+ stdin: options.stdin,
2268
+ file: options.file,
2269
+ stdinContent: options.stdinContent
2270
+ });
2271
+ const findings = scanForSecrets(content);
2272
+ if (findings.length > 0 && !options.allowSecrets) {
2273
+ const data = { secretFindings: findings };
2274
+ if (options.json) printJsonEnvelope(options.output, "remember", {
2275
+ stored: false,
2276
+ ...data
2277
+ });
2278
+ else options.output.error(`Refusing to store possible secret (${findings[0]?.kind}). Use --allow-secrets only after review.`);
2279
+ return 1;
2280
+ }
2281
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
2282
+ const kind = normalizeKind(options.kind);
2283
+ const tags = (options.tags ?? []).map((tag) => tag.trim()).filter(Boolean);
2284
+ const confidence = typeof options.confidence === "number" ? options.confidence : options.confidence ? parseConfidence(options.confidence) : 1;
2285
+ const metadata = parseMetadataJson(options.metadata);
2286
+ const actor = parseActor(options.actor);
2287
+ const note = formatTimestampedNote({
2288
+ timestamp,
2289
+ kind,
2290
+ content,
2291
+ ...options.title ? { title: options.title } : {},
2292
+ ...tags.length ? { tags } : {},
2293
+ confidence,
2294
+ ...options.source ? { source: options.source } : {},
2295
+ ...metadata ? { metadata } : {}
2296
+ });
2297
+ const nextNotes = `${(await options.fs.readTextIfExists(TEKMEMO_PATHS.notesMemory) ?? "# Notes\n").trimEnd()}\n\n${note}`.trimStart();
2298
+ await options.fs.writeText(TEKMEMO_PATHS.notesMemory, `${nextNotes.trimEnd()}\n`);
2299
+ const eventId = `evt_${Date.now()}_${Math.random().toString(16).slice(2)}`;
2300
+ const event = {
2301
+ id: eventId,
2302
+ type: "memory.created",
2303
+ timestamp,
2304
+ sourcePath: TEKMEMO_PATHS.notesMemory,
2305
+ actor,
2306
+ summary: options.title ?? content.split(/\r?\n/)[0]?.slice(0, 140) ?? "Stored memory note",
2307
+ metadata: {
2308
+ kind,
2309
+ tags,
2310
+ confidence,
2311
+ ...options.source ? { source: options.source } : {},
2312
+ ...metadata ? { userMetadata: metadata } : {},
2313
+ createdBy: "@tekmemo/cli"
2314
+ }
2315
+ };
2316
+ await options.fs.appendText(TEKMEMO_PATHS.memoryEvents, stringifyJsonl([event]));
2317
+ const data = {
2318
+ stored: true,
2319
+ eventId,
2320
+ path: TEKMEMO_PATHS.notesMemory,
2321
+ kind,
2322
+ tags,
2323
+ confidence,
2324
+ secretFindings: findings
2325
+ };
2326
+ if (options.json) printJsonEnvelope(options.output, "remember", data);
2327
+ else options.output.success(`Stored ${kind} memory in ${TEKMEMO_PATHS.notesMemory}`);
2328
+ return 0;
2329
+ }
2330
+
2331
+ //#endregion
2332
+ //#region src/utils/labels.ts
2333
+ function validateSnapshotLabel(label) {
2334
+ if (typeof label !== "string" || label.trim().length === 0) throw new CliUsageError("Snapshot label must be a non-empty string.");
2335
+ const normalized = label.trim();
2336
+ if (normalized.length > 80) throw new CliUsageError("Snapshot label must be 80 characters or fewer.");
2337
+ if (normalized.includes("\0")) throw new CliUsageError("Snapshot label must not contain null bytes.");
2338
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(normalized)) throw new CliUsageError("Snapshot label may only contain letters, numbers, dots, underscores, and hyphens, and must start with a letter or number.");
2339
+ return normalized;
2340
+ }
2341
+ function createSafeIdFromLabel(label, timestamp = (/* @__PURE__ */ new Date()).toISOString()) {
2342
+ return `${validateSnapshotLabel(label)}-${timestamp.replace(/[:.]/g, "-")}`.slice(0, 120);
2343
+ }
2344
+
2345
+ //#endregion
2346
+ //#region src/commands/snapshot.ts
2347
+ function checksum(value) {
2348
+ return (0, node_crypto.createHash)("sha256").update(JSON.stringify(value)).digest("hex");
2349
+ }
2350
+ async function runSnapshotCommand(options) {
2351
+ const label = validateSnapshotLabel(options.label);
2352
+ const createdAt = (/* @__PURE__ */ new Date()).toISOString();
2353
+ const id = createSafeIdFromLabel(label, createdAt);
2354
+ const path = `${TEKMEMO_PATHS.snapshotsDir}/${id}.json`;
2355
+ const files = {};
2356
+ for (const filePath of REQUIRED_FILES) {
2357
+ const content = await options.fs.readTextIfExists(filePath);
2358
+ if (content !== void 0) files[filePath] = content;
2359
+ }
2360
+ const bundleWithoutChecksum = {
2361
+ id,
2362
+ label,
2363
+ createdAt,
2364
+ protocolVersion: "1",
2365
+ files
2366
+ };
2367
+ const bundle = {
2368
+ ...bundleWithoutChecksum,
2369
+ checksum: checksum(bundleWithoutChecksum)
2370
+ };
2371
+ await options.fs.writeText(path, `${JSON.stringify(bundle, null, 2)}\n`);
2372
+ const record = {
2373
+ id,
2374
+ path,
2375
+ type: "manual",
2376
+ status: "available",
2377
+ createdAt,
2378
+ checksum: bundle.checksum,
2379
+ metadata: {
2380
+ label,
2381
+ fileCount: Object.keys(files).length,
2382
+ createdBy: "@tekmemo/cli"
2383
+ }
2384
+ };
2385
+ await options.fs.appendText(TEKMEMO_PATHS.snapshots, stringifyJsonl([record]));
2386
+ await options.fs.appendText(TEKMEMO_PATHS.memoryEvents, stringifyJsonl([{
2387
+ id: `evt_${id}`,
2388
+ type: "snapshot.created",
2389
+ timestamp: createdAt,
2390
+ sourcePath: path,
2391
+ actor: {
2392
+ type: "system",
2393
+ id: "@tekmemo/cli"
2394
+ },
2395
+ summary: `Created snapshot ${label}`,
2396
+ metadata: {
2397
+ snapshotId: id,
2398
+ label,
2399
+ checksum: bundle.checksum
2400
+ }
2401
+ }]));
2402
+ const data = {
2403
+ id,
2404
+ label,
2405
+ path,
2406
+ createdAt,
2407
+ checksum: bundle.checksum,
2408
+ fileCount: Object.keys(files).length
2409
+ };
2410
+ if (options.json) printJsonEnvelope(options.output, "snapshot", data);
2411
+ else options.output.success(`Created snapshot "${label}" at ${path}`);
2412
+ return 0;
2413
+ }
2414
+
2415
+ //#endregion
2416
+ //#region src/commands/validate.ts
2417
+ async function runValidateCommand(options) {
2418
+ const issues = [];
2419
+ for (const dir of REQUIRED_DIRS) if (!await options.fs.exists(dir)) issues.push({
2420
+ code: "missing_dir",
2421
+ message: `Missing required directory: ${dir}`
2422
+ });
2423
+ for (const file of REQUIRED_FILES) if (!await options.fs.exists(file)) issues.push({
2424
+ code: "missing_file",
2425
+ message: `Missing required file: ${file}`
2426
+ });
2427
+ const manifestContent = await options.fs.readTextIfExists(TEKMEMO_PATHS.manifest);
2428
+ if (manifestContent === void 0) issues.push({
2429
+ code: "missing_manifest",
2430
+ message: "manifest.json is missing"
2431
+ });
2432
+ else try {
2433
+ const manifest = parseManifest(manifestContent);
2434
+ for (const key of ["core", "notes"]) {
2435
+ const filePath = manifest.memory[key];
2436
+ if (!await options.fs.exists(filePath)) issues.push({
2437
+ code: "manifest_ref_missing",
2438
+ message: `Manifest references ${filePath} but file is missing`
2439
+ });
2440
+ }
2441
+ } catch (error) {
2442
+ issues.push({
2443
+ code: "invalid_manifest",
2444
+ message: `manifest.json: ${error instanceof Error ? error.message : String(error)}`
2445
+ });
2446
+ }
2447
+ const strictSchemas = {
2448
+ [TEKMEMO_PATHS.memoryEvents]: MemoryEventSchema,
2449
+ [TEKMEMO_PATHS.conversations]: ConversationEntrySchema,
2450
+ [TEKMEMO_PATHS.chunks]: MemoryChunkSchema,
2451
+ [TEKMEMO_PATHS.snapshots]: SnapshotEntrySchema
2452
+ };
2453
+ for (const [file, schema] of Object.entries(strictSchemas)) {
2454
+ const content = await options.fs.readTextIfExists(file);
2455
+ if (content === void 0) continue;
2456
+ const lines = content.split(/\r?\n/);
2457
+ for (let i = 0; i < lines.length; i++) {
2458
+ const line = lines[i]?.trim();
2459
+ if (!line) continue;
2460
+ const lineNumber = i + 1;
2461
+ let parsed;
2462
+ try {
2463
+ parsed = JSON.parse(line);
2464
+ } catch {
2465
+ issues.push({
2466
+ code: "invalid_json",
2467
+ message: `${file}:${lineNumber}: Invalid JSON`
2468
+ });
2469
+ continue;
2470
+ }
2471
+ const result = schema.safeParse(parsed);
2472
+ if (!result.success) {
2473
+ const firstIssue = result.error.issues[0];
2474
+ const path = firstIssue?.path.join(".") ?? "unknown";
2475
+ const msg = firstIssue?.message ?? "validation failed";
2476
+ issues.push({
2477
+ code: "schema_violation",
2478
+ message: `${file}:${lineNumber}: ${path} — ${msg}`
2479
+ });
2480
+ }
2481
+ }
2482
+ }
2483
+ const ok = issues.length === 0;
2484
+ if (options.json) options.output.write(JSON.stringify({
2485
+ ok,
2486
+ issues
2487
+ }, null, 2));
2488
+ else if (ok) options.output.success("Validation passed. All protocol files are valid.");
2489
+ else options.output.error(["Validation failed:", ...issues.map((issue) => `- [${issue.code}] ${issue.message}`)].join("\n"));
2490
+ return ok ? 0 : 1;
2491
+ }
2492
+
2493
+ //#endregion
2494
+ //#region src/commands/runtime.ts
2495
+ async function runRuntimeContextCommand(options) {
2496
+ if (options.config.runtime === "cloud") return runCloudContextCommand({
2497
+ output: options.output,
2498
+ json: options.json,
2499
+ ...options.config.cloud,
2500
+ query: options.query ?? "project context",
2501
+ maxBytes: options.maxChars
2502
+ });
2503
+ if (options.config.runtime === "local") return runContextCommand({
2504
+ fs: options.fs,
2505
+ output: options.output,
2506
+ json: options.json,
2507
+ query: options.query,
2508
+ maxChars: options.maxChars,
2509
+ includeEvents: options.includeEvents,
2510
+ includeChunks: options.includeChunks
2511
+ });
2512
+ return runHybridContextCommand(options);
2513
+ }
2514
+ async function runRuntimeRememberCommand(options) {
2515
+ if (options.config.runtime === "cloud") return runCloudRememberFromRuntime(options);
2516
+ if (options.config.runtime === "local") return runLocalRememberFromRuntime(options);
2517
+ return runWritePolicy(options.config.hybrid.writePolicy, () => runLocalRememberFromRuntime(options), () => runCloudRememberFromRuntime(options));
2518
+ }
2519
+ async function runRuntimeReadCommand(options) {
2520
+ if (options.target === "manifest") return runLocalReadFromRuntime(options);
2521
+ if (options.config.runtime === "cloud") return runCloudReadFromRuntime(options);
2522
+ if (options.config.runtime === "local") return runLocalReadFromRuntime(options);
2523
+ return runReadPolicy(options.config.hybrid.readPolicy, () => runLocalReadFromRuntime(options), () => runCloudReadFromRuntime(options));
2524
+ }
2525
+ async function runRuntimeValidateCommand(options) {
2526
+ if (options.config.runtime === "cloud") return runCloudValidateCommand({
2527
+ output: options.output,
2528
+ json: options.json,
2529
+ ...options.config.cloud
2530
+ });
2531
+ if (options.config.runtime === "local") return runValidateCommand({
2532
+ fs: options.fs,
2533
+ output: options.output,
2534
+ json: options.json
2535
+ });
2536
+ const local = createBufferedOutput({ noColor: true });
2537
+ const cloud = createBufferedOutput({ noColor: true });
2538
+ const localExit = await runValidateCommand({
2539
+ fs: options.fs,
2540
+ output: local,
2541
+ json: options.json
2542
+ });
2543
+ const cloudExit = await runCloudValidateCommand({
2544
+ output: cloud,
2545
+ json: options.json,
2546
+ ...options.config.cloud
2547
+ }).catch((error) => {
2548
+ cloud.error(error instanceof Error ? error.message : String(error));
2549
+ return 1;
2550
+ });
2551
+ if (options.json) {
2552
+ printJsonEnvelope(options.output, "validate", {
2553
+ runtime: "hybrid",
2554
+ local: {
2555
+ exitCode: localExit,
2556
+ stdout: local.stdout,
2557
+ stderr: local.stderr
2558
+ },
2559
+ cloud: {
2560
+ exitCode: cloudExit,
2561
+ stdout: cloud.stdout,
2562
+ stderr: cloud.stderr
2563
+ }
2564
+ });
2565
+ return localExit === 0 && cloudExit === 0 ? 0 : 1;
2566
+ }
2567
+ options.output.write([
2568
+ "# Local validation",
2569
+ ...local.stdout,
2570
+ "",
2571
+ "# Cloud validation",
2572
+ ...cloud.stdout
2573
+ ].join("\n"));
2574
+ for (const line of [...local.stderr, ...cloud.stderr]) options.output.error(line);
2575
+ return localExit === 0 && cloudExit === 0 ? 0 : 1;
2576
+ }
2577
+ async function runRuntimeSnapshotCommand(options) {
2578
+ if (options.config.runtime === "cloud") return runCloudSnapshotCommand({
2579
+ output: options.output,
2580
+ json: options.json,
2581
+ ...options.config.cloud,
2582
+ label: options.label
2583
+ });
2584
+ if (options.config.runtime === "local") return runSnapshotCommand({
2585
+ fs: options.fs,
2586
+ output: options.output,
2587
+ json: options.json,
2588
+ label: options.label ?? "manual"
2589
+ });
2590
+ return runWritePolicy(options.config.hybrid.writePolicy, () => runSnapshotCommand({
2591
+ fs: options.fs,
2592
+ output: options.output,
2593
+ json: options.json,
2594
+ label: options.label ?? "manual"
2595
+ }), () => runCloudSnapshotCommand({
2596
+ output: options.output,
2597
+ json: options.json,
2598
+ ...options.config.cloud,
2599
+ label: options.label
2600
+ }));
2601
+ }
2602
+ async function runHybridContextCommand(options) {
2603
+ const readPolicy = options.config.hybrid.readPolicy;
2604
+ if (readPolicy === "local-only") return runContextCommand({
2605
+ fs: options.fs,
2606
+ output: options.output,
2607
+ json: options.json,
2608
+ query: options.query,
2609
+ maxChars: options.maxChars,
2610
+ includeEvents: options.includeEvents,
2611
+ includeChunks: options.includeChunks
2612
+ });
2613
+ if (readPolicy === "cloud-only") return runCloudContextCommand({
2614
+ output: options.output,
2615
+ json: options.json,
2616
+ ...options.config.cloud,
2617
+ query: options.query ?? "project context",
2618
+ maxBytes: options.maxChars
2619
+ });
2620
+ const local = createBufferedOutput({ noColor: true });
2621
+ const cloud = createBufferedOutput({ noColor: true });
2622
+ const localExit = await runContextCommand({
2623
+ fs: options.fs,
2624
+ output: local,
2625
+ json: false,
2626
+ query: options.query,
2627
+ maxChars: options.maxChars,
2628
+ includeEvents: options.includeEvents,
2629
+ includeChunks: options.includeChunks
2630
+ });
2631
+ const cloudExit = await runCloudContextCommand({
2632
+ output: cloud,
2633
+ json: false,
2634
+ ...options.config.cloud,
2635
+ query: options.query ?? "project context",
2636
+ maxBytes: options.maxChars
2637
+ }).catch((error) => {
2638
+ cloud.error(error instanceof Error ? error.message : String(error));
2639
+ return 1;
2640
+ });
2641
+ const first = readPolicy === "cloud-first" ? {
2642
+ label: "Cloud",
2643
+ out: cloud,
2644
+ code: cloudExit
2645
+ } : {
2646
+ label: "Local",
2647
+ out: local,
2648
+ code: localExit
2649
+ };
2650
+ const second = readPolicy === "cloud-first" ? {
2651
+ label: "Local",
2652
+ out: local,
2653
+ code: localExit
2654
+ } : {
2655
+ label: "Cloud",
2656
+ out: cloud,
2657
+ code: cloudExit
2658
+ };
2659
+ if (options.json) {
2660
+ printJsonEnvelope(options.output, "context", {
2661
+ runtime: "hybrid",
2662
+ readPolicy,
2663
+ [first.label.toLowerCase()]: {
2664
+ exitCode: first.code,
2665
+ stdout: first.out.stdout,
2666
+ stderr: first.out.stderr
2667
+ },
2668
+ [second.label.toLowerCase()]: {
2669
+ exitCode: second.code,
2670
+ stdout: second.out.stdout,
2671
+ stderr: second.out.stderr
2672
+ }
2673
+ });
2674
+ return first.code === 0 || second.code === 0 ? 0 : 1;
2675
+ }
2676
+ options.output.write([
2677
+ `# TekMemo Hybrid Context`,
2678
+ `Read policy: ${readPolicy}`,
2679
+ "",
2680
+ `## ${first.label} Context`,
2681
+ ...first.out.stdout,
2682
+ "",
2683
+ `## ${second.label} Context`,
2684
+ ...second.out.stdout
2685
+ ].join("\n"));
2686
+ for (const line of [...first.out.stderr, ...second.out.stderr]) options.output.error(line);
2687
+ return first.code === 0 || second.code === 0 ? 0 : 1;
2688
+ }
2689
+ async function runLocalRememberFromRuntime(options) {
2690
+ return runRememberCommand({
2691
+ fs: options.fs,
2692
+ output: options.output,
2693
+ json: options.json,
2694
+ content: options.content,
2695
+ stdin: options.stdin,
2696
+ file: options.file,
2697
+ stdinContent: options.stdinContent,
2698
+ kind: options.kind,
2699
+ title: options.title,
2700
+ tags: options.tags,
2701
+ confidence: options.confidence,
2702
+ source: options.source,
2703
+ actor: options.actor,
2704
+ metadata: options.metadata,
2705
+ allowSecrets: options.allowSecrets
2706
+ });
2707
+ }
2708
+ async function runCloudRememberFromRuntime(options) {
2709
+ return runCloudRememberCommand({
2710
+ output: options.output,
2711
+ json: options.json,
2712
+ rootDir: options.config.root,
2713
+ stdinContent: options.stdinContent,
2714
+ ...options.config.cloud,
2715
+ content: options.content,
2716
+ stdin: options.stdin,
2717
+ file: options.file,
2718
+ kind: options.kind,
2719
+ title: options.title,
2720
+ tags: options.tags,
2721
+ confidence: options.confidence,
2722
+ source: options.source,
2723
+ metadata: options.metadata,
2724
+ allowSecrets: options.allowSecrets
2725
+ });
2726
+ }
2727
+ async function runLocalReadFromRuntime(options) {
2728
+ return runReadCommand({
2729
+ fs: options.fs,
2730
+ output: options.output,
2731
+ json: options.json,
2732
+ target: options.target
2733
+ });
2734
+ }
2735
+ async function runCloudReadFromRuntime(options) {
2736
+ if (options.target === "manifest") throw new Error("Cloud runtime cannot read local manifest. Use --runtime local or read core/notes.");
2737
+ return runCloudReadCommand({
2738
+ output: options.output,
2739
+ json: options.json,
2740
+ ...options.config.cloud,
2741
+ target: options.target
2742
+ });
2743
+ }
2744
+ async function runReadPolicy(policy, local, cloud) {
2745
+ if (policy === "local-only") return local();
2746
+ if (policy === "cloud-only") return cloud();
2747
+ if (policy === "cloud-first") {
2748
+ const cloudCode = await cloud();
2749
+ return cloudCode === 0 ? cloudCode : local();
2750
+ }
2751
+ const localCode = await local();
2752
+ return localCode === 0 ? localCode : cloud();
2753
+ }
2754
+ async function runWritePolicy(policy, local, cloud) {
2755
+ if (policy === "local-only") return local();
2756
+ if (policy === "cloud-only") return cloud();
2757
+ if (policy === "cloud-first") {
2758
+ const cloudCode = await cloud();
2759
+ const localCode = await local();
2760
+ return cloudCode === 0 && localCode === 0 ? 0 : 1;
2761
+ }
2762
+ const localCode = await local();
2763
+ const cloudCode = await cloud();
2764
+ return localCode === 0 && cloudCode === 0 ? 0 : 1;
2765
+ }
2766
+
2767
+ //#endregion
2768
+ //#region src/commands/search.ts
2769
+ async function runSearchCommand(options) {
2770
+ const matches = [];
2771
+ const filesToSearch = [
2772
+ TEKMEMO_PATHS.coreMemory,
2773
+ TEKMEMO_PATHS.notesMemory,
2774
+ TEKMEMO_PATHS.conversations
2775
+ ];
2776
+ let matcher;
2777
+ if (options.regex) {
2778
+ let pattern;
2779
+ try {
2780
+ pattern = new RegExp(options.query, "i");
2781
+ } catch {
2782
+ options.output.error(`Invalid regular expression: ${options.query}`);
2783
+ return 1;
2784
+ }
2785
+ matcher = (line) => pattern.test(line);
2786
+ } else {
2787
+ const query = options.query.toLowerCase();
2788
+ matcher = (line) => line.toLowerCase().includes(query);
2789
+ }
2790
+ for (const file of filesToSearch) {
2791
+ const content = await options.fs.readTextIfExists(file);
2792
+ if (!content) continue;
2793
+ const lines = content.split(/\r?\n/);
2794
+ for (let i = 0; i < lines.length; i++) {
2795
+ const line = lines[i];
2796
+ if (line !== void 0 && matcher(line)) matches.push({
2797
+ file,
2798
+ line: i + 1,
2799
+ content: line.trim()
2800
+ });
2801
+ }
2802
+ }
2803
+ if (options.json) {
2804
+ options.output.write(JSON.stringify({
2805
+ query: options.query,
2806
+ matches
2807
+ }, null, 2));
2808
+ return 0;
2809
+ }
2810
+ if (matches.length === 0) {
2811
+ options.output.write(`No matches found for "${options.query}".`);
2812
+ return 0;
2813
+ }
2814
+ for (const match of matches) options.output.write(`${match.file}:${match.line}: ${match.content}`);
2815
+ options.output.success(`Found ${matches.length} match(es).`);
2816
+ return 0;
2817
+ }
2818
+
2819
+ //#endregion
2820
+ //#region src/runner.ts
2821
+ const pkg = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href)("../package.json");
2822
+ const parsePositiveOption = parsePositiveInteger;
2823
+ const parseNonNegativeOption = parseNonNegativeInteger;
2824
+ function createFs(root) {
2825
+ return new TekMemoFileSystem({ rootDir: root });
2826
+ }
2827
+ async function runTekMemoCli(input) {
2828
+ const output = input.output ?? createBufferedOutput(input.noColor === void 0 ? void 0 : { noColor: input.noColor });
2829
+ let exitCode = 0;
2830
+ let currentCommand = "tekmemo";
2831
+ let wantsJson = input.argv.includes("--json") || input.argv.includes("-j");
2832
+ const program = new commander.Command();
2833
+ program.name("tekmemo").description("Production-grade CLI for TekMemo .tekmemo/ memory, context, validation, snapshots, and agent tools.").version(pkg.version).option("-r, --root <path>", "project root containing .tekmemo/", input.cwd ?? process.cwd()).option("--runtime <mode>", "runtime mode: local, cloud, or hybrid").option("--cloud-url <url>", "TekMemo Cloud API URL; defaults to config or TEKMEMO_CLOUD_URL").option("--api-key <key>", "TekMemo Cloud API key; defaults to TEKMEMO_API_KEY").option("--workspace-id <id>", "default cloud workspace ID").option("--project-id <id>", "default cloud project ID").option("--timeout-ms <n>", "cloud request timeout in milliseconds", parsePositiveOption).option("--read-policy <policy>", "hybrid read policy: local-first, cloud-first, local-only, cloud-only").option("--write-policy <policy>", "hybrid write policy: local-first, cloud-first, local-only, cloud-only").option("-j, --json", "output machine-readable JSON", false).option("-v, --verbose", "show detailed output", input.verbose ?? false).option("-q, --quiet", "suppress all output except errors", input.quiet ?? false).option("--no-color", "disable colored output", input.noColor ?? false).exitOverride().showHelpAfterError().configureOutput({
2834
+ writeOut: (str) => {
2835
+ if (!program.opts().quiet) output.write(str.trim());
2836
+ },
2837
+ writeErr: (str) => output.error(str.trim()),
2838
+ getOutHelpWidth: () => 100,
2839
+ getErrHelpWidth: () => 100
2840
+ });
2841
+ async function globals() {
2842
+ const opts = program.opts();
2843
+ wantsJson = Boolean(opts.json);
2844
+ const config = await resolveCliRuntimeConfig({
2845
+ cwd: input.cwd ?? process.cwd(),
2846
+ flags: {
2847
+ root: opts.root,
2848
+ runtime: opts.runtime,
2849
+ cloudUrl: opts.cloudUrl,
2850
+ apiKey: opts.apiKey,
2851
+ workspaceId: opts.workspaceId,
2852
+ projectId: opts.projectId,
2853
+ timeoutMs: opts.timeoutMs,
2854
+ readPolicy: opts.readPolicy,
2855
+ writePolicy: opts.writePolicy
2856
+ }
2857
+ });
2858
+ return {
2859
+ root: config.root,
2860
+ json: Boolean(opts.json),
2861
+ verbose: Boolean(opts.verbose),
2862
+ quiet: Boolean(opts.quiet),
2863
+ config
2864
+ };
2865
+ }
2866
+ program.command("init").description("initialize canonical .tekmemo/ files").option("-f, --force", "overwrite existing seed files", false).option("-p, --project-id <id>", "explicit project ID").option("--no-input", "skip interactive prompts", false).action(async (options) => {
2867
+ currentCommand = "init";
2868
+ const g = await globals();
2869
+ exitCode = await runInitCommand({
2870
+ fs: createFs(g.root),
2871
+ output,
2872
+ json: g.json,
2873
+ force: options.force,
2874
+ projectId: options.projectId,
2875
+ noInput: options.noInput ?? !process.stdout.isTTY
2876
+ });
2877
+ });
2878
+ program.command("inspect").description("summarize local TekMemo memory state").action(async () => {
2879
+ currentCommand = "inspect";
2880
+ const g = await globals();
2881
+ exitCode = await runInspectCommand({
2882
+ fs: createFs(g.root),
2883
+ output,
2884
+ json: g.json
2885
+ });
2886
+ });
2887
+ program.command("context").description("pack project memory into an agent-friendly context block").option("-q, --query <query>", "prioritize lines matching a task/query").option("--max-chars <n>", "maximum output characters", parsePositiveOption, 12e3).option("--include-events", "include recent memory events", false).option("--include-chunks", "include recent chunk records", false).action(async (options) => {
2888
+ currentCommand = "context";
2889
+ const g = await globals();
2890
+ exitCode = await runRuntimeContextCommand({
2891
+ fs: createFs(g.root),
2892
+ output,
2893
+ json: g.json,
2894
+ config: g.config,
2895
+ query: options.query,
2896
+ maxChars: options.maxChars,
2897
+ includeEvents: options.includeEvents,
2898
+ includeChunks: options.includeChunks
2899
+ });
2900
+ });
2901
+ program.command("remember").description("store a durable note for humans or coding agents").argument("[content]", "memory content").option("--stdin", "read memory content from stdin", false).option("--file <path>", "read memory content from a file inside the selected root").option("-k, --kind <kind>", "decision | constraint | goal | preference | reference | summary | note", "note").option("--title <title>", "optional note title").option("-t, --tag <tag>", "tag to attach; repeatable", collect, []).option("--confidence <n>", "confidence from 0 to 1").option("--source <source>", "source identifier, file, URL, or agent name").option("--actor <actor>", "actor type or type:id, e.g. agent:claude-code", "user").option("--metadata-json <json>", "metadata JSON object").option("--allow-secrets", "allow content that looks like a secret after manual review", false).action(async (content, options) => {
2902
+ currentCommand = "remember";
2903
+ const g = await globals();
2904
+ exitCode = await runRuntimeRememberCommand({
2905
+ fs: createFs(g.root),
2906
+ output,
2907
+ json: g.json,
2908
+ config: g.config,
2909
+ content,
2910
+ stdin: options.stdin,
2911
+ file: options.file,
2912
+ stdinContent: input.stdinContent,
2913
+ kind: options.kind,
2914
+ title: options.title,
2915
+ tags: options.tag,
2916
+ confidence: options.confidence,
2917
+ source: options.source,
2918
+ actor: options.actor,
2919
+ metadata: options.metadataJson,
2920
+ allowSecrets: options.allowSecrets
2921
+ });
2922
+ });
2923
+ program.command("read").description("read a canonical memory document").argument("<target>", "core | notes | manifest").action(async (target) => {
2924
+ currentCommand = "read";
2925
+ const g = await globals();
2926
+ if (target !== "core" && target !== "notes" && target !== "manifest") {
2927
+ output.error("read target must be core, notes, or manifest");
2928
+ exitCode = 1;
2929
+ return;
2930
+ }
2931
+ exitCode = await runRuntimeReadCommand({
2932
+ fs: createFs(g.root),
2933
+ output,
2934
+ json: g.json,
2935
+ config: g.config,
2936
+ target
2937
+ });
2938
+ });
2939
+ program.command("events").description("read memory event log").option("-l, --limit <n>", "limit number of events", parseNonNegativeOption, 0).option("-s, --strict", "strict protocol validation", false).action(async (options) => {
2940
+ currentCommand = "events";
2941
+ const g = await globals();
2942
+ exitCode = await runEventsCommand({
2943
+ fs: createFs(g.root),
2944
+ output,
2945
+ json: g.json,
2946
+ limit: options.limit,
2947
+ strict: options.strict
2948
+ });
2949
+ });
2950
+ program.command("chunks").description("read local chunk index").option("-l, --limit <n>", "limit number of chunks", parseNonNegativeOption, 0).option("-s, --strict", "strict protocol validation", false).action(async (options) => {
2951
+ currentCommand = "chunks";
2952
+ const g = await globals();
2953
+ exitCode = await runChunksCommand({
2954
+ fs: createFs(g.root),
2955
+ output,
2956
+ json: g.json,
2957
+ limit: options.limit,
2958
+ strict: options.strict
2959
+ });
2960
+ });
2961
+ program.command("snapshot").description("create local memory snapshot bundle").option("-l, --label <name>", "snapshot label", "manual").action(async (options) => {
2962
+ currentCommand = "snapshot";
2963
+ const g = await globals();
2964
+ exitCode = await runRuntimeSnapshotCommand({
2965
+ fs: createFs(g.root),
2966
+ output,
2967
+ json: g.json,
2968
+ config: g.config,
2969
+ label: options.label
2970
+ });
2971
+ });
2972
+ program.command("doctor").description("find missing or corrupt memory files").option("-s, --strict", "strict protocol validation", false).action(async (options) => {
2973
+ currentCommand = "doctor";
2974
+ const g = await globals();
2975
+ exitCode = await runDoctorCommand({
2976
+ fs: createFs(g.root),
2977
+ output,
2978
+ json: g.json,
2979
+ strict: options.strict
2980
+ });
2981
+ });
2982
+ program.command("validate").description("strict protocol validation for CI").action(async () => {
2983
+ currentCommand = "validate";
2984
+ const g = await globals();
2985
+ exitCode = await runRuntimeValidateCommand({
2986
+ fs: createFs(g.root),
2987
+ output,
2988
+ json: g.json,
2989
+ config: g.config
2990
+ });
2991
+ });
2992
+ program.command("search").description("search memory files for a query").argument("<query>", "text to search for").option("-e, --regex", "treat query as a regular expression", false).action(async (query, options) => {
2993
+ currentCommand = "search";
2994
+ const g = await globals();
2995
+ exitCode = await runSearchCommand({
2996
+ fs: createFs(g.root),
2997
+ output,
2998
+ json: g.json,
2999
+ query,
3000
+ regex: options.regex
3001
+ });
3002
+ });
3003
+ program.command("edit").description("legacy alias: append a note or core memory text").argument("<type>", "note or core").argument("<message>", "content to append").option("--allow-secrets", "allow content that looks like a secret after manual review", false).action(async (type, message, options) => {
3004
+ currentCommand = "edit";
3005
+ const g = await globals();
3006
+ if (type !== "note" && type !== "core") {
3007
+ output.error("Edit type must be 'note' or 'core'");
3008
+ exitCode = 1;
3009
+ return;
3010
+ }
3011
+ exitCode = await runEditCommand({
3012
+ fs: createFs(g.root),
3013
+ output,
3014
+ json: g.json,
3015
+ type,
3016
+ message,
3017
+ allowSecrets: options.allowSecrets
3018
+ });
3019
+ });
3020
+ program.command("diff").description("compare two memory snapshots by ID or label").argument("<labelA>", "first snapshot ID or label").argument("<labelB>", "second snapshot ID or label").action(async (labelA, labelB) => {
3021
+ currentCommand = "diff";
3022
+ const g = await globals();
3023
+ exitCode = await runDiffCommand({
3024
+ fs: createFs(g.root),
3025
+ output,
3026
+ json: g.json,
3027
+ labelA,
3028
+ labelB
3029
+ });
3030
+ });
3031
+ const agent = program.command("agent").description("manage AgentFS-backed TekMemo coding sessions");
3032
+ agent.command("start").description("start an AgentFS-style workspace for Codex, Claude Code, or another coding agent").requiredOption("--task <task>", "agent task or brief").option("--project <id>", "project ID").option("--actor <id>", "actor ID, e.g. assistant:codex").option("--session <id>", "explicit safe session ID").action(async (options) => {
3033
+ currentCommand = "agent.start";
3034
+ const g = await globals();
3035
+ exitCode = await runAgentStartCommand({
3036
+ fs: createFs(g.root),
3037
+ output,
3038
+ json: g.json,
3039
+ task: options.task,
3040
+ projectId: options.project ?? g.config.cloud.projectId,
3041
+ actorId: options.actor,
3042
+ sessionId: options.session
3043
+ });
3044
+ });
3045
+ agent.command("paths").description("print paths for the latest or selected agent session").option("--session <id>", "session ID or latest", "latest").action(async (options) => {
3046
+ currentCommand = "agent.paths";
3047
+ const g = await globals();
3048
+ exitCode = await runAgentPathsCommand({
3049
+ fs: createFs(g.root),
3050
+ output,
3051
+ json: g.json,
3052
+ session: options.session
3053
+ });
3054
+ });
3055
+ agent.command("extract").description("extract summary, durable memory, and follow-ups from an agent session").option("--session <id>", "session ID or latest", "latest").action(async (options) => {
3056
+ currentCommand = "agent.extract";
3057
+ const g = await globals();
3058
+ exitCode = await runAgentExtractCommand({
3059
+ fs: createFs(g.root),
3060
+ output,
3061
+ json: g.json,
3062
+ session: options.session
3063
+ });
3064
+ });
3065
+ agent.command("complete").description("complete an agent session and optionally persist durable memory").option("--session <id>", "session ID or latest", "latest").option("--extract", "append output/durable-memory.md to TekMemo notes", false).option("--checkpoint-label <label>", "checkpoint label").action(async (options) => {
3066
+ currentCommand = "agent.complete";
3067
+ const g = await globals();
3068
+ exitCode = await runAgentCompleteCommand({
3069
+ fs: createFs(g.root),
3070
+ output,
3071
+ json: g.json,
3072
+ session: options.session,
3073
+ extract: options.extract,
3074
+ checkpointLabel: options.checkpointLabel
3075
+ });
3076
+ });
3077
+ const cloud = program.command("cloud").description("use TekMemo Cloud through @tekmemo/cloud-client").option("--cloud-url <url>", "TekMemo Cloud API URL; defaults to TEKMEMO_CLOUD_URL or TEKMEMO_API_URL").option("--api-key <key>", "TekMemo Cloud API key; defaults to TEKMEMO_API_KEY").option("--workspace-id <id>", "default cloud workspace ID; defaults to TEKMEMO_WORKSPACE_ID").option("--project-id <id>", "default cloud project ID; defaults to TEKMEMO_PROJECT_ID").option("--timeout-ms <n>", "cloud request timeout in milliseconds", parsePositiveOption);
3078
+ async function cloudGlobals() {
3079
+ const g = await globals();
3080
+ const opts = cloud.opts();
3081
+ return {
3082
+ ...g,
3083
+ cloudUrl: opts.cloudUrl ?? g.config.cloud.cloudUrl,
3084
+ apiKey: opts.apiKey ?? g.config.cloud.apiKey,
3085
+ workspaceId: opts.workspaceId ?? g.config.cloud.workspaceId,
3086
+ projectId: opts.projectId ?? g.config.cloud.projectId,
3087
+ timeoutMs: opts.timeoutMs ?? g.config.cloud.timeoutMs
3088
+ };
3089
+ }
3090
+ cloud.command("health").description("check TekMemo Cloud health").action(async () => {
3091
+ currentCommand = "cloud.health";
3092
+ const g = await cloudGlobals();
3093
+ exitCode = await runCloudHealthCommand({
3094
+ output,
3095
+ json: g.json,
3096
+ cloudUrl: g.cloudUrl,
3097
+ apiKey: g.apiKey,
3098
+ workspaceId: g.workspaceId,
3099
+ projectId: g.projectId,
3100
+ timeoutMs: g.timeoutMs
3101
+ });
3102
+ });
3103
+ cloud.command("context").description("pack cloud memory into an agent-friendly context block").requiredOption("-q, --query <query>", "task/query used to build context").option("-l, --limit <n>", "maximum recall items", parsePositiveOption).option("--max-bytes <n>", "maximum response bytes", parsePositiveOption).option("--include-core", "include core memory", true).option("--include-notes", "include notes memory", true).option("--include-recent", "include recent memory", true).action(async (options) => {
3104
+ currentCommand = "cloud.context";
3105
+ const g = await cloudGlobals();
3106
+ exitCode = await runCloudContextCommand({
3107
+ output,
3108
+ json: g.json,
3109
+ cloudUrl: g.cloudUrl,
3110
+ apiKey: g.apiKey,
3111
+ workspaceId: g.workspaceId,
3112
+ projectId: g.projectId,
3113
+ timeoutMs: g.timeoutMs,
3114
+ query: options.query,
3115
+ limit: options.limit,
3116
+ maxBytes: options.maxBytes,
3117
+ includeCore: options.includeCore,
3118
+ includeNotes: options.includeNotes,
3119
+ includeRecent: options.includeRecent
3120
+ });
3121
+ });
3122
+ cloud.command("recall").description("search TekMemo Cloud memory").argument("<query>", "text to search for").option("-l, --limit <n>", "maximum recall items", parsePositiveOption).option("--strategy <strategy>", "local | vector | hybrid").option("--fallback <mode>", "none | local").option("--rerank", "request reranking", false).action(async (query, options) => {
3123
+ currentCommand = "cloud.recall";
3124
+ const g = await cloudGlobals();
3125
+ exitCode = await runCloudRecallCommand({
3126
+ output,
3127
+ json: g.json,
3128
+ cloudUrl: g.cloudUrl,
3129
+ apiKey: g.apiKey,
3130
+ workspaceId: g.workspaceId,
3131
+ projectId: g.projectId,
3132
+ timeoutMs: g.timeoutMs,
3133
+ query,
3134
+ limit: options.limit,
3135
+ strategy: options.strategy,
3136
+ fallback: options.fallback,
3137
+ rerank: options.rerank
3138
+ });
3139
+ });
3140
+ cloud.command("index").description("request TekMemo Cloud recall indexing for the project").option("--mode <mode>", "all | changed | core | notes", "changed").option("--force", "force re-indexing", false).action(async (options) => {
3141
+ currentCommand = "cloud.index";
3142
+ const g = await cloudGlobals();
3143
+ exitCode = await runCloudRecallIndexCommand({
3144
+ output,
3145
+ json: g.json,
3146
+ cloudUrl: g.cloudUrl,
3147
+ apiKey: g.apiKey,
3148
+ workspaceId: g.workspaceId,
3149
+ projectId: g.projectId,
3150
+ timeoutMs: g.timeoutMs,
3151
+ mode: options.mode,
3152
+ force: options.force
3153
+ });
3154
+ });
3155
+ cloud.command("remember").description("store durable memory in TekMemo Cloud").argument("[content]", "memory content").option("--stdin", "read memory content from stdin", false).option("--file <path>", "read memory content from a file inside the selected root").option("-k, --kind <kind>", "decision | constraint | goal | preference | reference | summary | note", "note").option("--title <title>", "optional note title").option("-t, --tag <tag>", "tag to attach; repeatable", collect, []).option("--confidence <n>", "confidence from 0 to 1").option("--source <source>", "source identifier, file, URL, or agent name").option("--metadata-json <json>", "metadata JSON object").option("--allow-secrets", "allow content that looks like a secret after manual review", false).action(async (content, options) => {
3156
+ currentCommand = "cloud.remember";
3157
+ const g = await cloudGlobals();
3158
+ exitCode = await runCloudRememberCommand({
3159
+ output,
3160
+ json: g.json,
3161
+ rootDir: g.root,
3162
+ stdinContent: input.stdinContent,
3163
+ cloudUrl: g.cloudUrl,
3164
+ apiKey: g.apiKey,
3165
+ workspaceId: g.workspaceId,
3166
+ projectId: g.projectId,
3167
+ timeoutMs: g.timeoutMs,
3168
+ content,
3169
+ stdin: options.stdin,
3170
+ file: options.file,
3171
+ kind: options.kind,
3172
+ title: options.title,
3173
+ tags: options.tag,
3174
+ confidence: options.confidence,
3175
+ source: options.source,
3176
+ metadata: options.metadataJson,
3177
+ allowSecrets: options.allowSecrets
3178
+ });
3179
+ });
3180
+ cloud.command("read").description("read a TekMemo Cloud memory document").argument("<target>", "core | notes").option("-l, --limit <n>", "maximum notes when target is notes", parsePositiveOption).action(async (target, options) => {
3181
+ currentCommand = "cloud.read";
3182
+ const g = await cloudGlobals();
3183
+ if (target !== "core" && target !== "notes") {
3184
+ output.error("cloud read target must be core or notes");
3185
+ exitCode = 1;
3186
+ return;
3187
+ }
3188
+ exitCode = await runCloudReadCommand({
3189
+ output,
3190
+ json: g.json,
3191
+ cloudUrl: g.cloudUrl,
3192
+ apiKey: g.apiKey,
3193
+ workspaceId: g.workspaceId,
3194
+ projectId: g.projectId,
3195
+ timeoutMs: g.timeoutMs,
3196
+ target,
3197
+ limit: options.limit
3198
+ });
3199
+ });
3200
+ cloud.command("update-core").description("replace TekMemo Cloud core memory").argument("[content]", "new core memory content").option("--stdin", "read core memory from stdin", false).option("--file <path>", "read core memory from a file inside the selected root").option("--allow-secrets", "allow content that looks like a secret after manual review", false).action(async (content, options) => {
3201
+ currentCommand = "cloud.update-core";
3202
+ const g = await cloudGlobals();
3203
+ exitCode = await runCloudUpdateCoreCommand({
3204
+ output,
3205
+ json: g.json,
3206
+ rootDir: g.root,
3207
+ stdinContent: input.stdinContent,
3208
+ cloudUrl: g.cloudUrl,
3209
+ apiKey: g.apiKey,
3210
+ workspaceId: g.workspaceId,
3211
+ projectId: g.projectId,
3212
+ timeoutMs: g.timeoutMs,
3213
+ content,
3214
+ stdin: options.stdin,
3215
+ file: options.file,
3216
+ allowSecrets: options.allowSecrets
3217
+ });
3218
+ });
3219
+ cloud.command("recent").description("list recent TekMemo Cloud memory events").option("-l, --limit <n>", "maximum recent items", parsePositiveOption).action(async (options) => {
3220
+ currentCommand = "cloud.recent";
3221
+ const g = await cloudGlobals();
3222
+ exitCode = await runCloudRecentCommand({
3223
+ output,
3224
+ json: g.json,
3225
+ cloudUrl: g.cloudUrl,
3226
+ apiKey: g.apiKey,
3227
+ workspaceId: g.workspaceId,
3228
+ projectId: g.projectId,
3229
+ timeoutMs: g.timeoutMs,
3230
+ limit: options.limit
3231
+ });
3232
+ });
3233
+ cloud.command("validate").description("validate TekMemo Cloud memory").option("-s, --strict", "strict protocol validation", false).action(async (options) => {
3234
+ currentCommand = "cloud.validate";
3235
+ const g = await cloudGlobals();
3236
+ exitCode = await runCloudValidateCommand({
3237
+ output,
3238
+ json: g.json,
3239
+ cloudUrl: g.cloudUrl,
3240
+ apiKey: g.apiKey,
3241
+ workspaceId: g.workspaceId,
3242
+ projectId: g.projectId,
3243
+ timeoutMs: g.timeoutMs,
3244
+ strict: options.strict
3245
+ });
3246
+ });
3247
+ cloud.command("snapshot").description("explain TekMemo Cloud snapshot availability").option("-l, --label <name>", "snapshot label", "manual").option("--type <type>", "manual | automatic | pre-sync | pre-restore", "manual").action(async (options) => {
3248
+ currentCommand = "cloud.snapshot";
3249
+ const g = await cloudGlobals();
3250
+ exitCode = await runCloudSnapshotCommand({
3251
+ output,
3252
+ json: g.json,
3253
+ cloudUrl: g.cloudUrl,
3254
+ apiKey: g.apiKey,
3255
+ workspaceId: g.workspaceId,
3256
+ projectId: g.projectId,
3257
+ timeoutMs: g.timeoutMs,
3258
+ label: options.label,
3259
+ type: options.type
3260
+ });
3261
+ });
3262
+ const sync = cloud.command("sync").description("use TekMemo Cloud memory sync APIs");
3263
+ sync.command("status").description("read cloud sync status").option("--client-id <id>", "optional sync client ID").action(async (options) => {
3264
+ currentCommand = "cloud.sync.status";
3265
+ const g = await cloudGlobals();
3266
+ exitCode = await runCloudSyncStatusCommand({
3267
+ output,
3268
+ json: g.json,
3269
+ cloudUrl: g.cloudUrl,
3270
+ apiKey: g.apiKey,
3271
+ workspaceId: g.workspaceId,
3272
+ projectId: g.projectId,
3273
+ timeoutMs: g.timeoutMs,
3274
+ clientId: options.clientId
3275
+ });
3276
+ });
3277
+ sync.command("pull").description("pull cloud sync events").requiredOption("--client-id <id>", "sync client ID").option("--since-server-version <n>", "pull events after this server version", parseNonNegativeOption).option("-l, --limit <n>", "maximum events to return", parsePositiveOption).action(async (options) => {
3278
+ currentCommand = "cloud.sync.pull";
3279
+ const g = await cloudGlobals();
3280
+ exitCode = await runCloudSyncPullCommand({
3281
+ output,
3282
+ json: g.json,
3283
+ cloudUrl: g.cloudUrl,
3284
+ apiKey: g.apiKey,
3285
+ workspaceId: g.workspaceId,
3286
+ projectId: g.projectId,
3287
+ timeoutMs: g.timeoutMs,
3288
+ clientId: options.clientId,
3289
+ sinceServerVersion: options.sinceServerVersion,
3290
+ limit: options.limit
3291
+ });
3292
+ });
3293
+ sync.command("push").description("push local sync events to TekMemo Cloud").requiredOption("--client-id <id>", "sync client ID").option("--events-json <json>", "event array or object with events array").option("--checkpoint-json <json>", "optional checkpoint JSON object").option("--stdin", "read events JSON from stdin", false).option("--file <path>", "read events JSON from a file inside the selected root").action(async (options) => {
3294
+ currentCommand = "cloud.sync.push";
3295
+ const g = await cloudGlobals();
3296
+ exitCode = await runCloudSyncPushCommand({
3297
+ output,
3298
+ json: g.json,
3299
+ rootDir: g.root,
3300
+ stdinContent: input.stdinContent,
3301
+ cloudUrl: g.cloudUrl,
3302
+ apiKey: g.apiKey,
3303
+ workspaceId: g.workspaceId,
3304
+ projectId: g.projectId,
3305
+ timeoutMs: g.timeoutMs,
3306
+ clientId: options.clientId,
3307
+ eventsJson: options.eventsJson,
3308
+ checkpointJson: options.checkpointJson,
3309
+ stdin: options.stdin,
3310
+ file: options.file
3311
+ });
3312
+ });
3313
+ sync.command("resolve").description("resolve a cloud sync conflict").argument("<conflictId>", "conflict ID").requiredOption("--resolution <resolution>", "keep_cloud | use_client | ignore").option("--content-json <json>", "optional resolution content JSON object").action(async (conflictId, options) => {
3314
+ currentCommand = "cloud.sync.resolve";
3315
+ const g = await cloudGlobals();
3316
+ exitCode = await runCloudSyncResolveCommand({
3317
+ output,
3318
+ json: g.json,
3319
+ cloudUrl: g.cloudUrl,
3320
+ apiKey: g.apiKey,
3321
+ workspaceId: g.workspaceId,
3322
+ projectId: g.projectId,
3323
+ timeoutMs: g.timeoutMs,
3324
+ conflictId,
3325
+ resolution: options.resolution,
3326
+ contentJson: options.contentJson
3327
+ });
3328
+ });
3329
+ cloud.command("readiness").description("check TekMemo Cloud readiness").action(async () => {
3330
+ currentCommand = "cloud.readiness";
3331
+ const g = await cloudGlobals();
3332
+ exitCode = await runCloudReadinessCommand({
3333
+ output,
3334
+ json: g.json,
3335
+ cloudUrl: g.cloudUrl,
3336
+ apiKey: g.apiKey,
3337
+ workspaceId: g.workspaceId,
3338
+ projectId: g.projectId,
3339
+ timeoutMs: g.timeoutMs
3340
+ });
3341
+ });
3342
+ cloud.command("context-compose").description("compose full context package from cloud").requiredOption("-q, --query <query>", "task/query used to build context").option("-l, --limit <n>", "maximum recall items", parsePositiveOption).option("--strategy <strategy>", "auto | vector | local").option("--rerank", "request reranking", false).option("--include-core-memory", "include core memory", true).option("--include-recall-results", "include recall results", true).option("--include-graph-context", "include graph context", true).action(async (options) => {
3343
+ currentCommand = "cloud.context-compose";
3344
+ const g = await cloudGlobals();
3345
+ exitCode = await runCloudContextComposeCommand({
3346
+ output,
3347
+ json: g.json,
3348
+ cloudUrl: g.cloudUrl,
3349
+ apiKey: g.apiKey,
3350
+ workspaceId: g.workspaceId,
3351
+ projectId: g.projectId,
3352
+ timeoutMs: g.timeoutMs,
3353
+ query: options.query,
3354
+ topK: options.limit,
3355
+ strategy: options.strategy,
3356
+ rerank: options.rerank,
3357
+ includeCoreMemory: options.includeCoreMemory,
3358
+ includeRecallResults: options.includeRecallResults,
3359
+ includeGraphContext: options.includeGraphContext
3360
+ });
3361
+ });
3362
+ const graph = cloud.command("graph").description("graph memory operations");
3363
+ graph.command("list-nodes").description("list graph nodes").option("-l, --limit <n>", "maximum nodes to return", parsePositiveOption).option("--cursor <string>", "pagination cursor").option("--status <status>", "active | deprecated | conflicted | deleted").action(async (options) => {
3364
+ currentCommand = "cloud.graph.list-nodes";
3365
+ const g = await cloudGlobals();
3366
+ exitCode = await runCloudGraphListNodesCommand({
3367
+ output,
3368
+ json: g.json,
3369
+ cloudUrl: g.cloudUrl,
3370
+ apiKey: g.apiKey,
3371
+ workspaceId: g.workspaceId,
3372
+ projectId: g.projectId,
3373
+ timeoutMs: g.timeoutMs,
3374
+ limit: options.limit,
3375
+ cursor: options.cursor,
3376
+ status: options.status
3377
+ });
3378
+ });
3379
+ graph.command("create-node").description("create a graph node").requiredOption("--node-id <id>", "node ID").requiredOption("--type <type>", "node type").requiredOption("--label <label>", "node label").option("--summary <summary>", "node summary").option("--aliases <aliases>", "comma-separated aliases").option("--metadata-json <json>", "metadata JSON object").action(async (options) => {
3380
+ currentCommand = "cloud.graph.create-node";
3381
+ const g = await cloudGlobals();
3382
+ const aliases = options.aliases ? options.aliases.split(",").map((s) => s.trim()) : void 0;
3383
+ exitCode = await runCloudGraphCreateNodeCommand({
3384
+ output,
3385
+ json: g.json,
3386
+ cloudUrl: g.cloudUrl,
3387
+ apiKey: g.apiKey,
3388
+ workspaceId: g.workspaceId,
3389
+ projectId: g.projectId,
3390
+ timeoutMs: g.timeoutMs,
3391
+ nodeId: options.nodeId,
3392
+ type: options.type,
3393
+ label: options.label,
3394
+ summary: options.summary,
3395
+ aliases,
3396
+ metadataJson: options.metadataJson
3397
+ });
3398
+ });
3399
+ graph.command("list-edges").description("list graph edges").option("-l, --limit <n>", "maximum edges to return", parsePositiveOption).option("--cursor <string>", "pagination cursor").option("--status <status>", "active | deprecated | conflicted | deleted").action(async (options) => {
3400
+ currentCommand = "cloud.graph.list-edges";
3401
+ const g = await cloudGlobals();
3402
+ exitCode = await runCloudGraphListEdgesCommand({
3403
+ output,
3404
+ json: g.json,
3405
+ cloudUrl: g.cloudUrl,
3406
+ apiKey: g.apiKey,
3407
+ workspaceId: g.workspaceId,
3408
+ projectId: g.projectId,
3409
+ timeoutMs: g.timeoutMs,
3410
+ limit: options.limit,
3411
+ cursor: options.cursor,
3412
+ status: options.status
3413
+ });
3414
+ });
3415
+ graph.command("create-edge").description("create a graph edge").option("--edge-id <id>", "edge ID").requiredOption("--from <id>", "from node ID").requiredOption("--to <id>", "to node ID").requiredOption("--type <type>", "edge type").option("--directed", "directed edge", true).option("--weight <n>", "edge weight (0-1)", parsePositiveOption).option("--metadata-json <json>", "metadata JSON object").action(async (options) => {
3416
+ currentCommand = "cloud.graph.create-edge";
3417
+ const g = await cloudGlobals();
3418
+ exitCode = await runCloudGraphCreateEdgeCommand({
3419
+ output,
3420
+ json: g.json,
3421
+ cloudUrl: g.cloudUrl,
3422
+ apiKey: g.apiKey,
3423
+ workspaceId: g.workspaceId,
3424
+ projectId: g.projectId,
3425
+ timeoutMs: g.timeoutMs,
3426
+ edgeId: options.edgeId,
3427
+ fromNodeId: options.from,
3428
+ toNodeId: options.to,
3429
+ type: options.type,
3430
+ directed: options.directed,
3431
+ weight: options.weight,
3432
+ metadataJson: options.metadataJson
3433
+ });
3434
+ });
3435
+ graph.command("neighbors").description("find graph neighbors").requiredOption("--node-id <id>", "seed node ID").option("--direction <dir>", "in | out | both").option("--depth <n>", "search depth", parsePositiveOption).option("-l, --limit <n>", "maximum results", parsePositiveOption).action(async (options) => {
3436
+ currentCommand = "cloud.graph.neighbors";
3437
+ const g = await cloudGlobals();
3438
+ exitCode = await runCloudGraphNeighborsCommand({
3439
+ output,
3440
+ json: g.json,
3441
+ cloudUrl: g.cloudUrl,
3442
+ apiKey: g.apiKey,
3443
+ workspaceId: g.workspaceId,
3444
+ projectId: g.projectId,
3445
+ timeoutMs: g.timeoutMs,
3446
+ nodeId: options.nodeId,
3447
+ direction: options.direction,
3448
+ depth: options.depth,
3449
+ limit: options.limit
3450
+ });
3451
+ });
3452
+ graph.command("path").description("find graph path between nodes").requiredOption("--from <id>", "start node ID").requiredOption("--to <id>", "target node ID").option("--max-depth <n>", "maximum search depth", parsePositiveOption).action(async (options) => {
3453
+ currentCommand = "cloud.graph.path";
3454
+ const g = await cloudGlobals();
3455
+ exitCode = await runCloudGraphPathCommand({
3456
+ output,
3457
+ json: g.json,
3458
+ cloudUrl: g.cloudUrl,
3459
+ apiKey: g.apiKey,
3460
+ workspaceId: g.workspaceId,
3461
+ projectId: g.projectId,
3462
+ timeoutMs: g.timeoutMs,
3463
+ fromNodeId: options.from,
3464
+ toNodeId: options.to,
3465
+ maxDepth: options.maxDepth
3466
+ });
3467
+ });
3468
+ const extraction = cloud.command("extraction").description("extraction operations");
3469
+ extraction.command("run").description("run graph extraction").option("--mode <mode>", "full | core | notes | sync | connectors").option("--force", "force re-extraction", false).action(async (options) => {
3470
+ currentCommand = "cloud.extraction.run";
3471
+ const g = await cloudGlobals();
3472
+ exitCode = await runCloudExtractionRunCommand({
3473
+ output,
3474
+ json: g.json,
3475
+ cloudUrl: g.cloudUrl,
3476
+ apiKey: g.apiKey,
3477
+ workspaceId: g.workspaceId,
3478
+ projectId: g.projectId,
3479
+ timeoutMs: g.timeoutMs,
3480
+ mode: options.mode,
3481
+ force: options.force
3482
+ });
3483
+ });
3484
+ extraction.command("jobs").description("list extraction jobs").option("-l, --limit <n>", "maximum jobs to return", parsePositiveOption).action(async (options) => {
3485
+ currentCommand = "cloud.extraction.jobs";
3486
+ const g = await cloudGlobals();
3487
+ exitCode = await runCloudExtractionJobsCommand({
3488
+ output,
3489
+ json: g.json,
3490
+ cloudUrl: g.cloudUrl,
3491
+ apiKey: g.apiKey,
3492
+ workspaceId: g.workspaceId,
3493
+ projectId: g.projectId,
3494
+ timeoutMs: g.timeoutMs,
3495
+ limit: options.limit
3496
+ });
3497
+ });
3498
+ cloud.command("evals").description("run context quality evals").option("--fixture-ids <ids>", "comma-separated fixture IDs").option("--iterations <n>", "number of iterations", parsePositiveOption).option("--thresholds-json <json>", "thresholds JSON object").action(async (options) => {
3499
+ currentCommand = "cloud.evals.run";
3500
+ const g = await cloudGlobals();
3501
+ exitCode = await runCloudEvalsRunCommand({
3502
+ output,
3503
+ json: g.json,
3504
+ cloudUrl: g.cloudUrl,
3505
+ apiKey: g.apiKey,
3506
+ workspaceId: g.workspaceId,
3507
+ projectId: g.projectId,
3508
+ timeoutMs: g.timeoutMs,
3509
+ fixtureIds: options.fixtureIds,
3510
+ iterations: options.iterations,
3511
+ thresholdsJson: options.thresholdsJson
3512
+ });
3513
+ });
3514
+ cloud.command("benchmarks").description("run context benchmarks").option("--fixture-ids <ids>", "comma-separated fixture IDs").option("--iterations <n>", "number of iterations", parsePositiveOption).option("--thresholds-json <json>", "thresholds JSON object").action(async (options) => {
3515
+ currentCommand = "cloud.benchmarks.run";
3516
+ const g = await cloudGlobals();
3517
+ exitCode = await runCloudBenchmarksRunCommand({
3518
+ output,
3519
+ json: g.json,
3520
+ cloudUrl: g.cloudUrl,
3521
+ apiKey: g.apiKey,
3522
+ workspaceId: g.workspaceId,
3523
+ projectId: g.projectId,
3524
+ timeoutMs: g.timeoutMs,
3525
+ fixtureIds: options.fixtureIds,
3526
+ iterations: options.iterations,
3527
+ thresholdsJson: options.thresholdsJson
3528
+ });
3529
+ });
3530
+ const exports = cloud.command("exports").description("export operations");
3531
+ exports.command("create").description("create memory export").option("--label <name>", "export label").action(async (options) => {
3532
+ currentCommand = "cloud.exports.create";
3533
+ const g = await cloudGlobals();
3534
+ exitCode = await runCloudExportsCreateCommand({
3535
+ output,
3536
+ json: g.json,
3537
+ cloudUrl: g.cloudUrl,
3538
+ apiKey: g.apiKey,
3539
+ workspaceId: g.workspaceId,
3540
+ projectId: g.projectId,
3541
+ timeoutMs: g.timeoutMs,
3542
+ label: options.label
3543
+ });
3544
+ });
3545
+ exports.command("download").description("download export archive").requiredOption("--export-id <id>", "export ID").action(async (options) => {
3546
+ currentCommand = "cloud.exports.download";
3547
+ const g = await cloudGlobals();
3548
+ exitCode = await runCloudExportsDownloadCommand({
3549
+ output,
3550
+ json: g.json,
3551
+ cloudUrl: g.cloudUrl,
3552
+ apiKey: g.apiKey,
3553
+ workspaceId: g.workspaceId,
3554
+ projectId: g.projectId,
3555
+ timeoutMs: g.timeoutMs,
3556
+ exportId: options.exportId
3557
+ });
3558
+ });
3559
+ const snapshots = cloud.command("snapshots").description("snapshot operations");
3560
+ snapshots.command("create").description("create memory snapshot").option("--label <name>", "snapshot label").option("--trigger <trigger>", "manual | sync | system").action(async (options) => {
3561
+ currentCommand = "cloud.snapshots.create";
3562
+ const g = await cloudGlobals();
3563
+ exitCode = await runCloudSnapshotsCreateCommand({
3564
+ output,
3565
+ json: g.json,
3566
+ cloudUrl: g.cloudUrl,
3567
+ apiKey: g.apiKey,
3568
+ workspaceId: g.workspaceId,
3569
+ projectId: g.projectId,
3570
+ timeoutMs: g.timeoutMs,
3571
+ label: options.label,
3572
+ trigger: options.trigger
3573
+ });
3574
+ });
3575
+ snapshots.command("download").description("download snapshot archive").requiredOption("--snapshot-id <id>", "snapshot ID").action(async (options) => {
3576
+ currentCommand = "cloud.snapshots.download";
3577
+ const g = await cloudGlobals();
3578
+ exitCode = await runCloudSnapshotsDownloadCommand({
3579
+ output,
3580
+ json: g.json,
3581
+ cloudUrl: g.cloudUrl,
3582
+ apiKey: g.apiKey,
3583
+ workspaceId: g.workspaceId,
3584
+ projectId: g.projectId,
3585
+ timeoutMs: g.timeoutMs,
3586
+ snapshotId: options.snapshotId
3587
+ });
3588
+ });
3589
+ const providers = cloud.command("providers").description("provider operations");
3590
+ providers.command("list").description("list provider credentials").action(async () => {
3591
+ currentCommand = "cloud.providers.list";
3592
+ const g = await cloudGlobals();
3593
+ exitCode = await runCloudProvidersListCommand({
3594
+ output,
3595
+ json: g.json,
3596
+ cloudUrl: g.cloudUrl,
3597
+ apiKey: g.apiKey,
3598
+ workspaceId: g.workspaceId,
3599
+ projectId: g.projectId,
3600
+ timeoutMs: g.timeoutMs
3601
+ });
3602
+ });
3603
+ providers.command("create").description("create provider credential").requiredOption("--provider <provider>", "voyageai | openai | upstash-vector").requiredOption("--key-name <name>", "key name").requiredOption("--secret <secret>", "provider secret").option("--rest-url <url>", "REST URL (required for upstash-vector)").option("--embedding-model <model>", "embedding model").option("--rerank-model <model>", "rerank model").action(async (options) => {
3604
+ currentCommand = "cloud.providers.create";
3605
+ const g = await cloudGlobals();
3606
+ exitCode = await runCloudProvidersCreateCommand({
3607
+ output,
3608
+ json: g.json,
3609
+ cloudUrl: g.cloudUrl,
3610
+ apiKey: g.apiKey,
3611
+ workspaceId: g.workspaceId,
3612
+ projectId: g.projectId,
3613
+ timeoutMs: g.timeoutMs,
3614
+ provider: options.provider,
3615
+ keyName: options.keyName,
3616
+ secret: options.secret,
3617
+ restUrl: options.restUrl,
3618
+ embeddingModel: options.embeddingModel,
3619
+ rerankModel: options.rerankModel
3620
+ });
3621
+ });
3622
+ providers.command("test").description("test provider credential").requiredOption("--credential-id <id>", "credential ID").action(async (options) => {
3623
+ currentCommand = "cloud.providers.test";
3624
+ const g = await cloudGlobals();
3625
+ exitCode = await runCloudProvidersTestCommand({
3626
+ output,
3627
+ json: g.json,
3628
+ cloudUrl: g.cloudUrl,
3629
+ apiKey: g.apiKey,
3630
+ workspaceId: g.workspaceId,
3631
+ projectId: g.projectId,
3632
+ timeoutMs: g.timeoutMs,
3633
+ credentialId: options.credentialId
3634
+ });
3635
+ });
3636
+ const config = program.command("config").description("inspect or create .tekmemo/config.json");
3637
+ config.command("get").description("print resolved CLI configuration").action(async () => {
3638
+ currentCommand = "config.get";
3639
+ const g = await globals();
3640
+ const safeConfig = {
3641
+ ...g.config,
3642
+ cloud: {
3643
+ ...g.config.cloud,
3644
+ apiKey: g.config.cloud.apiKey ? "<redacted>" : void 0
3645
+ }
3646
+ };
3647
+ if (g.json) printJsonEnvelope(output, "config.get", safeConfig);
3648
+ else output.write(JSON.stringify(safeConfig, null, 2));
3649
+ });
3650
+ config.command("init").description("create .tekmemo/config.json without storing secrets").option("-f, --force", "overwrite existing config", false).option("--runtime <mode>", "runtime mode: local, cloud, or hybrid", "local").option("--cloud-url <url>", "TekMemo Cloud API URL").option("--workspace-id <id>", "cloud workspace ID").option("--project-id <id>", "cloud project ID").option("--read-policy <policy>", "hybrid read policy", "local-first").option("--write-policy <policy>", "hybrid write policy", "local-first").action(async (options) => {
3651
+ currentCommand = "config.init";
3652
+ const g = await globals();
3653
+ const result = await writeDefaultCliConfig({
3654
+ cwd: input.cwd ?? process.cwd(),
3655
+ root: g.root,
3656
+ force: options.force,
3657
+ config: {
3658
+ version: 1,
3659
+ runtime: options.runtime,
3660
+ root: ".",
3661
+ cloud: {
3662
+ ...options.cloudUrl ? { baseUrl: options.cloudUrl } : {},
3663
+ ...options.workspaceId ? { workspaceId: options.workspaceId } : {},
3664
+ ...options.projectId ? { projectId: options.projectId } : {}
3665
+ },
3666
+ hybrid: {
3667
+ readPolicy: options.readPolicy,
3668
+ writePolicy: options.writePolicy
3669
+ }
3670
+ }
3671
+ });
3672
+ if (g.json) printJsonEnvelope(output, "config.init", result);
3673
+ else if (result.created) output.success(`Created ${result.path}`);
3674
+ else if (result.overwritten) output.success(`Overwrote ${result.path}`);
3675
+ else output.warn(`${result.path} already exists. Use --force to overwrite.`);
3676
+ });
3677
+ try {
3678
+ const args = normalizeArgv(input.argv);
3679
+ await program.parseAsync(args);
3680
+ return {
3681
+ exitCode,
3682
+ stdout: output.stdout,
3683
+ stderr: output.stderr
3684
+ };
3685
+ } catch (error) {
3686
+ if (error instanceof CliError) {
3687
+ exitCode = error.exitCode;
3688
+ if (wantsJson) printJsonError(output, currentCommand, error.code, error.message);
3689
+ else output.error(error.message);
3690
+ } else if (isCommanderError(error)) exitCode = typeof error.exitCode === "number" ? error.exitCode : 1;
3691
+ else {
3692
+ exitCode = 1;
3693
+ const message = error instanceof Error ? error.message : String(error);
3694
+ if (wantsJson) printJsonError(output, currentCommand, "CLI_UNEXPECTED_ERROR", message);
3695
+ else output.error(message);
3696
+ }
3697
+ return {
3698
+ exitCode,
3699
+ stdout: output.stdout,
3700
+ stderr: output.stderr
3701
+ };
3702
+ }
3703
+ }
3704
+ function collect(value, previous) {
3705
+ previous.push(value);
3706
+ return previous;
3707
+ }
3708
+ function normalizeArgv(argv) {
3709
+ if (argv.length > 0 && !argv[0]?.endsWith("node") && !argv[0]?.includes("/") && argv[0] !== "tekmemo") return [
3710
+ "node",
3711
+ "tekmemo",
3712
+ ...argv
3713
+ ];
3714
+ if (argv[0] === "tekmemo") return ["node", ...argv];
3715
+ return [...argv];
3716
+ }
3717
+ function isCommanderError(error) {
3718
+ return error instanceof commander.CommanderError;
3719
+ }
3720
+
3721
+ //#endregion
3722
+ Object.defineProperty(exports, 'CliError', {
3723
+ enumerable: true,
3724
+ get: function () {
3725
+ return CliError;
3726
+ }
3727
+ });
3728
+ Object.defineProperty(exports, 'CliFsError', {
3729
+ enumerable: true,
3730
+ get: function () {
3731
+ return CliFsError;
3732
+ }
3733
+ });
3734
+ Object.defineProperty(exports, 'CliJsonlError', {
3735
+ enumerable: true,
3736
+ get: function () {
3737
+ return CliJsonlError;
3738
+ }
3739
+ });
3740
+ Object.defineProperty(exports, 'CliProtocolError', {
3741
+ enumerable: true,
3742
+ get: function () {
3743
+ return CliProtocolError;
3744
+ }
3745
+ });
3746
+ Object.defineProperty(exports, 'CliUsageError', {
3747
+ enumerable: true,
3748
+ get: function () {
3749
+ return CliUsageError;
3750
+ }
3751
+ });
3752
+ Object.defineProperty(exports, 'CliValidationError', {
3753
+ enumerable: true,
3754
+ get: function () {
3755
+ return CliValidationError;
3756
+ }
3757
+ });
3758
+ Object.defineProperty(exports, 'REQUIRED_DIRS', {
3759
+ enumerable: true,
3760
+ get: function () {
3761
+ return REQUIRED_DIRS;
3762
+ }
3763
+ });
3764
+ Object.defineProperty(exports, 'REQUIRED_FILES', {
3765
+ enumerable: true,
3766
+ get: function () {
3767
+ return REQUIRED_FILES;
3768
+ }
3769
+ });
3770
+ Object.defineProperty(exports, 'TEKMEMO_PATHS', {
3771
+ enumerable: true,
3772
+ get: function () {
3773
+ return TEKMEMO_PATHS;
3774
+ }
3775
+ });
3776
+ Object.defineProperty(exports, 'TekMemoFileSystem', {
3777
+ enumerable: true,
3778
+ get: function () {
3779
+ return TekMemoFileSystem;
3780
+ }
3781
+ });
3782
+ Object.defineProperty(exports, 'cloudConnectionSummary', {
3783
+ enumerable: true,
3784
+ get: function () {
3785
+ return cloudConnectionSummary;
3786
+ }
3787
+ });
3788
+ Object.defineProperty(exports, 'createBufferedOutput', {
3789
+ enumerable: true,
3790
+ get: function () {
3791
+ return createBufferedOutput;
3792
+ }
3793
+ });
3794
+ Object.defineProperty(exports, 'createCliCloudClient', {
3795
+ enumerable: true,
3796
+ get: function () {
3797
+ return createCliCloudClient;
3798
+ }
3799
+ });
3800
+ Object.defineProperty(exports, 'createDefaultManifest', {
3801
+ enumerable: true,
3802
+ get: function () {
3803
+ return createDefaultManifest;
3804
+ }
3805
+ });
3806
+ Object.defineProperty(exports, 'createSafeIdFromLabel', {
3807
+ enumerable: true,
3808
+ get: function () {
3809
+ return createSafeIdFromLabel;
3810
+ }
3811
+ });
3812
+ Object.defineProperty(exports, 'formatCloudError', {
3813
+ enumerable: true,
3814
+ get: function () {
3815
+ return formatCloudError;
3816
+ }
3817
+ });
3818
+ Object.defineProperty(exports, 'inspectTekMemo', {
3819
+ enumerable: true,
3820
+ get: function () {
3821
+ return inspectTekMemo;
3822
+ }
3823
+ });
3824
+ Object.defineProperty(exports, 'normalizeCloudConnectionOptions', {
3825
+ enumerable: true,
3826
+ get: function () {
3827
+ return normalizeCloudConnectionOptions;
3828
+ }
3829
+ });
3830
+ Object.defineProperty(exports, 'parseJsonl', {
3831
+ enumerable: true,
3832
+ get: function () {
3833
+ return parseJsonl;
3834
+ }
3835
+ });
3836
+ Object.defineProperty(exports, 'parseManifest', {
3837
+ enumerable: true,
3838
+ get: function () {
3839
+ return parseManifest;
3840
+ }
3841
+ });
3842
+ Object.defineProperty(exports, 'printHumanOrJson', {
3843
+ enumerable: true,
3844
+ get: function () {
3845
+ return printHumanOrJson;
3846
+ }
3847
+ });
3848
+ Object.defineProperty(exports, 'printJsonEnvelope', {
3849
+ enumerable: true,
3850
+ get: function () {
3851
+ return printJsonEnvelope;
3852
+ }
3853
+ });
3854
+ Object.defineProperty(exports, 'printJsonError', {
3855
+ enumerable: true,
3856
+ get: function () {
3857
+ return printJsonError;
3858
+ }
3859
+ });
3860
+ Object.defineProperty(exports, 'redactSecretPreview', {
3861
+ enumerable: true,
3862
+ get: function () {
3863
+ return redactSecretPreview;
3864
+ }
3865
+ });
3866
+ Object.defineProperty(exports, 'resolveCliRuntimeConfig', {
3867
+ enumerable: true,
3868
+ get: function () {
3869
+ return resolveCliRuntimeConfig;
3870
+ }
3871
+ });
3872
+ Object.defineProperty(exports, 'runTekMemoCli', {
3873
+ enumerable: true,
3874
+ get: function () {
3875
+ return runTekMemoCli;
3876
+ }
3877
+ });
3878
+ Object.defineProperty(exports, 'scanForSecrets', {
3879
+ enumerable: true,
3880
+ get: function () {
3881
+ return scanForSecrets;
3882
+ }
3883
+ });
3884
+ Object.defineProperty(exports, 'stringifyJsonl', {
3885
+ enumerable: true,
3886
+ get: function () {
3887
+ return stringifyJsonl;
3888
+ }
3889
+ });
3890
+ Object.defineProperty(exports, 'toCloudClientOptions', {
3891
+ enumerable: true,
3892
+ get: function () {
3893
+ return toCloudClientOptions;
3894
+ }
3895
+ });
3896
+ Object.defineProperty(exports, 'validateManifest', {
3897
+ enumerable: true,
3898
+ get: function () {
3899
+ return validateManifest;
3900
+ }
3901
+ });
3902
+ Object.defineProperty(exports, 'validateSnapshotLabel', {
3903
+ enumerable: true,
3904
+ get: function () {
3905
+ return validateSnapshotLabel;
3906
+ }
3907
+ });
3908
+ Object.defineProperty(exports, 'writeDefaultCliConfig', {
3909
+ enumerable: true,
3910
+ get: function () {
3911
+ return writeDefaultCliConfig;
3912
+ }
3913
+ });
3914
+ //# sourceMappingURL=runner-CiA5dFku.cjs.map