context-vault 2.17.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +783 -108
- package/node_modules/@context-vault/core/dist/capture.d.ts +21 -0
- package/node_modules/@context-vault/core/dist/capture.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/capture.js +269 -0
- package/node_modules/@context-vault/core/dist/capture.js.map +1 -0
- package/node_modules/@context-vault/core/dist/categories.d.ts +6 -0
- package/node_modules/@context-vault/core/dist/categories.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/categories.js +50 -0
- package/node_modules/@context-vault/core/dist/categories.js.map +1 -0
- package/node_modules/@context-vault/core/dist/config.d.ts +4 -0
- package/node_modules/@context-vault/core/dist/config.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/config.js +190 -0
- package/node_modules/@context-vault/core/dist/config.js.map +1 -0
- package/node_modules/@context-vault/core/dist/constants.d.ts +33 -0
- package/node_modules/@context-vault/core/dist/constants.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/constants.js +23 -0
- package/node_modules/@context-vault/core/dist/constants.js.map +1 -0
- package/node_modules/@context-vault/core/dist/db.d.ts +13 -0
- package/node_modules/@context-vault/core/dist/db.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/db.js +191 -0
- package/node_modules/@context-vault/core/dist/db.js.map +1 -0
- package/node_modules/@context-vault/core/dist/embed.d.ts +5 -0
- package/node_modules/@context-vault/core/dist/embed.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/embed.js +78 -0
- package/node_modules/@context-vault/core/dist/embed.js.map +1 -0
- package/node_modules/@context-vault/core/dist/files.d.ts +13 -0
- package/node_modules/@context-vault/core/dist/files.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/files.js +66 -0
- package/node_modules/@context-vault/core/dist/files.js.map +1 -0
- package/node_modules/@context-vault/core/dist/formatters.d.ts +8 -0
- package/node_modules/@context-vault/core/dist/formatters.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/formatters.js +18 -0
- package/node_modules/@context-vault/core/dist/formatters.js.map +1 -0
- package/node_modules/@context-vault/core/dist/frontmatter.d.ts +12 -0
- package/node_modules/@context-vault/core/dist/frontmatter.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/frontmatter.js +101 -0
- package/node_modules/@context-vault/core/dist/frontmatter.js.map +1 -0
- package/node_modules/@context-vault/core/dist/index.d.ts +10 -0
- package/node_modules/@context-vault/core/dist/index.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/index.js +297 -0
- package/node_modules/@context-vault/core/dist/index.js.map +1 -0
- package/node_modules/@context-vault/core/dist/ingest-url.d.ts +20 -0
- package/node_modules/@context-vault/core/dist/ingest-url.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/ingest-url.js +113 -0
- package/node_modules/@context-vault/core/dist/ingest-url.js.map +1 -0
- package/node_modules/@context-vault/core/dist/main.d.ts +14 -0
- package/node_modules/@context-vault/core/dist/main.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/main.js +25 -0
- package/node_modules/@context-vault/core/dist/main.js.map +1 -0
- package/node_modules/@context-vault/core/dist/search.d.ts +18 -0
- package/node_modules/@context-vault/core/dist/search.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/search.js +238 -0
- package/node_modules/@context-vault/core/dist/search.js.map +1 -0
- package/node_modules/@context-vault/core/dist/types.d.ts +176 -0
- package/node_modules/@context-vault/core/dist/types.d.ts.map +1 -0
- package/node_modules/@context-vault/core/dist/types.js +2 -0
- package/node_modules/@context-vault/core/dist/types.js.map +1 -0
- package/node_modules/@context-vault/core/package.json +66 -16
- package/node_modules/@context-vault/core/src/capture.ts +308 -0
- package/node_modules/@context-vault/core/src/categories.ts +54 -0
- package/node_modules/@context-vault/core/src/{core/config.js → config.ts} +34 -33
- package/node_modules/@context-vault/core/src/{constants.js → constants.ts} +6 -3
- package/node_modules/@context-vault/core/src/db.ts +229 -0
- package/node_modules/@context-vault/core/src/{index/embed.js → embed.ts} +10 -35
- package/node_modules/@context-vault/core/src/files.ts +80 -0
- package/node_modules/@context-vault/core/src/{capture/formatters.js → formatters.ts} +13 -11
- package/node_modules/@context-vault/core/src/{core/frontmatter.js → frontmatter.ts} +27 -33
- package/node_modules/@context-vault/core/src/index.ts +351 -0
- package/node_modules/@context-vault/core/src/ingest-url.ts +99 -0
- package/node_modules/@context-vault/core/src/main.ts +111 -0
- package/node_modules/@context-vault/core/src/search.ts +285 -0
- package/node_modules/@context-vault/core/src/types.ts +166 -0
- package/package.json +12 -7
- package/scripts/postinstall.js +1 -1
- package/{node_modules/@context-vault/core/src/core → src}/error-log.js +1 -15
- package/{node_modules/@context-vault/core/src/server → src}/helpers.js +9 -4
- package/src/linking.js +100 -0
- package/{node_modules/@context-vault/core/src/server/tools.js → src/register-tools.js} +14 -15
- package/src/{server/index.js → server.js} +8 -35
- package/src/status.js +235 -0
- package/{node_modules/@context-vault/core/src/core → src}/telemetry.js +9 -19
- package/src/temporal.js +97 -0
- package/{node_modules/@context-vault/core/src/server → src}/tools/context-status.js +3 -4
- package/{node_modules/@context-vault/core/src/server → src}/tools/create-snapshot.js +43 -75
- package/{node_modules/@context-vault/core/src/server → src}/tools/delete-context.js +0 -2
- package/{node_modules/@context-vault/core/src/server → src}/tools/get-context.js +118 -35
- package/{node_modules/@context-vault/core/src/server → src}/tools/ingest-project.js +5 -6
- package/{node_modules/@context-vault/core/src/server → src}/tools/ingest-url.js +3 -4
- package/{node_modules/@context-vault/core/src/server → src}/tools/list-buckets.js +4 -5
- package/{node_modules/@context-vault/core/src/server → src}/tools/list-context.js +3 -6
- package/{node_modules/@context-vault/core/src/server → src}/tools/save-context.js +41 -21
- package/{node_modules/@context-vault/core/src/server → src}/tools/session-start.js +9 -16
- package/node_modules/@context-vault/core/src/capture/file-ops.js +0 -97
- package/node_modules/@context-vault/core/src/capture/import-pipeline.js +0 -46
- package/node_modules/@context-vault/core/src/capture/importers.js +0 -387
- package/node_modules/@context-vault/core/src/capture/index.js +0 -236
- package/node_modules/@context-vault/core/src/capture/ingest-url.js +0 -252
- package/node_modules/@context-vault/core/src/consolidation/index.js +0 -112
- package/node_modules/@context-vault/core/src/core/categories.js +0 -72
- package/node_modules/@context-vault/core/src/core/files.js +0 -108
- package/node_modules/@context-vault/core/src/core/status.js +0 -350
- package/node_modules/@context-vault/core/src/index/db.js +0 -416
- package/node_modules/@context-vault/core/src/index/index.js +0 -522
- package/node_modules/@context-vault/core/src/index.js +0 -66
- package/node_modules/@context-vault/core/src/retrieve/index.js +0 -500
- package/node_modules/@context-vault/core/src/server/tools/submit-feedback.js +0 -55
- package/node_modules/@context-vault/core/src/sync/sync.js +0 -235
- package/src/hooks/post-tool-call.mjs +0 -62
- package/src/hooks/session-end.mjs +0 -492
- /package/{node_modules/@context-vault/core/src/server → src}/tools/clear-context.js +0 -0
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { captureAndIndex, updateEntryFile } from "
|
|
3
|
-
import { indexEntry } from "
|
|
4
|
-
import { categoryFor, defaultTierFor } from "
|
|
5
|
-
import { normalizeKind } from "
|
|
2
|
+
import { captureAndIndex, updateEntryFile } from "@context-vault/core/capture";
|
|
3
|
+
import { indexEntry } from "@context-vault/core/index";
|
|
4
|
+
import { categoryFor, defaultTierFor } from "@context-vault/core/categories";
|
|
5
|
+
import { normalizeKind } from "@context-vault/core/files";
|
|
6
6
|
import { ok, err, ensureVaultExists, ensureValidKind } from "../helpers.js";
|
|
7
|
-
import { maybeShowFeedbackPrompt } from "
|
|
7
|
+
import { maybeShowFeedbackPrompt } from "../telemetry.js";
|
|
8
|
+
import { validateRelatedTo } from "../linking.js";
|
|
8
9
|
import {
|
|
9
10
|
MAX_BODY_LENGTH,
|
|
10
11
|
MAX_TITLE_LENGTH,
|
|
@@ -14,7 +15,7 @@ import {
|
|
|
14
15
|
MAX_META_LENGTH,
|
|
15
16
|
MAX_SOURCE_LENGTH,
|
|
16
17
|
MAX_IDENTITY_KEY_LENGTH,
|
|
17
|
-
} from "
|
|
18
|
+
} from "@context-vault/core/constants";
|
|
18
19
|
|
|
19
20
|
const DEFAULT_SIMILARITY_THRESHOLD = 0.85;
|
|
20
21
|
const SKIP_THRESHOLD = 0.95;
|
|
@@ -24,7 +25,7 @@ async function findSimilar(
|
|
|
24
25
|
ctx,
|
|
25
26
|
embedding,
|
|
26
27
|
threshold,
|
|
27
|
-
|
|
28
|
+
|
|
28
29
|
{ hydrate = false } = {},
|
|
29
30
|
) {
|
|
30
31
|
try {
|
|
@@ -43,9 +44,15 @@ async function findSimilar(
|
|
|
43
44
|
|
|
44
45
|
const rowids = vecRows.map((vr) => vr.rowid);
|
|
45
46
|
const placeholders = rowids.map(() => "?").join(",");
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
// Local mode has no user_id column — omit it from the SELECT list.
|
|
48
|
+
const isLocal = ctx.stmts._mode === "local";
|
|
49
|
+
const columns = isLocal
|
|
50
|
+
? hydrate
|
|
51
|
+
? "rowid, id, title, body, kind, tags, category, updated_at"
|
|
52
|
+
: "rowid, id, title, category"
|
|
53
|
+
: hydrate
|
|
54
|
+
? "rowid, id, title, body, kind, tags, category, updated_at"
|
|
55
|
+
: "rowid, id, title, category";
|
|
49
56
|
const hydratedRows = ctx.db
|
|
50
57
|
.prepare(`SELECT ${columns} FROM vault WHERE rowid IN (${placeholders})`)
|
|
51
58
|
.all(...rowids);
|
|
@@ -59,7 +66,6 @@ async function findSimilar(
|
|
|
59
66
|
if (similarity < threshold) continue;
|
|
60
67
|
const row = byRowid.get(vr.rowid);
|
|
61
68
|
if (!row) continue;
|
|
62
|
-
if (userId !== undefined && row.user_id !== userId) continue;
|
|
63
69
|
if (row.category === "entity") continue;
|
|
64
70
|
const entry = { id: row.id, title: row.title, score: similarity };
|
|
65
71
|
if (hydrate) {
|
|
@@ -294,6 +300,12 @@ export const inputSchema = {
|
|
|
294
300
|
.describe(
|
|
295
301
|
"Array of entry IDs that this entry supersedes/replaces. Those entries will be marked with superseded_by pointing to this new entry and excluded from future search results by default.",
|
|
296
302
|
),
|
|
303
|
+
related_to: z
|
|
304
|
+
.array(z.string())
|
|
305
|
+
.optional()
|
|
306
|
+
.describe(
|
|
307
|
+
"Array of entry IDs this entry is related to. Enables bidirectional graph traversal — use get_context with follow_links:true to retrieve linked entries.",
|
|
308
|
+
),
|
|
297
309
|
source_files: z
|
|
298
310
|
.array(
|
|
299
311
|
z.object({
|
|
@@ -359,6 +371,7 @@ export async function handler(
|
|
|
359
371
|
identity_key,
|
|
360
372
|
expires_at,
|
|
361
373
|
supersedes,
|
|
374
|
+
related_to,
|
|
362
375
|
source_files,
|
|
363
376
|
dry_run,
|
|
364
377
|
similarity_threshold,
|
|
@@ -369,12 +382,14 @@ export async function handler(
|
|
|
369
382
|
{ ensureIndexed },
|
|
370
383
|
) {
|
|
371
384
|
const { config } = ctx;
|
|
372
|
-
const userId = ctx.userId !== undefined ? ctx.userId : undefined;
|
|
373
385
|
const suggestMode = conflict_resolution !== "off";
|
|
374
386
|
|
|
375
387
|
const vaultErr = ensureVaultExists(config);
|
|
376
388
|
if (vaultErr) return vaultErr;
|
|
377
389
|
|
|
390
|
+
const relatedToErr = validateRelatedTo(related_to);
|
|
391
|
+
if (relatedToErr) return err(relatedToErr, "INVALID_INPUT");
|
|
392
|
+
|
|
378
393
|
const inputErr = validateSaveInput({
|
|
379
394
|
kind,
|
|
380
395
|
title,
|
|
@@ -395,7 +410,6 @@ export async function handler(
|
|
|
395
410
|
if (!existing) return err(`Entry not found: ${id}`, "NOT_FOUND");
|
|
396
411
|
|
|
397
412
|
// Ownership check: don't leak existence across users
|
|
398
|
-
if (userId !== undefined && existing.user_id !== userId) {
|
|
399
413
|
return err(`Entry not found: ${id}`, "NOT_FOUND");
|
|
400
414
|
}
|
|
401
415
|
|
|
@@ -428,9 +442,15 @@ export async function handler(
|
|
|
428
442
|
source,
|
|
429
443
|
expires_at,
|
|
430
444
|
supersedes,
|
|
445
|
+
related_to,
|
|
431
446
|
source_files,
|
|
432
447
|
});
|
|
433
448
|
await indexEntry(ctx, entry);
|
|
449
|
+
if (entry.related_to?.length && ctx.stmts.updateRelatedTo) {
|
|
450
|
+
ctx.stmts.updateRelatedTo.run(JSON.stringify(entry.related_to), entry.id);
|
|
451
|
+
} else if (entry.related_to === null && ctx.stmts.updateRelatedTo) {
|
|
452
|
+
ctx.stmts.updateRelatedTo.run(null, entry.id);
|
|
453
|
+
}
|
|
434
454
|
const relPath = entry.filePath
|
|
435
455
|
? entry.filePath.replace(config.vaultDir + "/", "")
|
|
436
456
|
: entry.filePath;
|
|
@@ -474,7 +494,7 @@ export async function handler(
|
|
|
474
494
|
ctx,
|
|
475
495
|
queryEmbedding,
|
|
476
496
|
threshold,
|
|
477
|
-
|
|
497
|
+
|
|
478
498
|
{ hydrate: suggestMode },
|
|
479
499
|
);
|
|
480
500
|
}
|
|
@@ -531,8 +551,9 @@ export async function handler(
|
|
|
531
551
|
identity_key,
|
|
532
552
|
expires_at,
|
|
533
553
|
supersedes,
|
|
554
|
+
related_to,
|
|
534
555
|
source_files,
|
|
535
|
-
|
|
556
|
+
|
|
536
557
|
tier: effectiveTier,
|
|
537
558
|
});
|
|
538
559
|
|
|
@@ -561,9 +582,8 @@ export async function handler(
|
|
|
561
582
|
(t) => typeof t === "string" && t.startsWith("bucket:"),
|
|
562
583
|
);
|
|
563
584
|
for (const bt of bucketTags) {
|
|
564
|
-
const bucketUserClause =
|
|
565
|
-
const bucketParams =
|
|
566
|
-
userId !== undefined ? [bt, userId] : [bt];
|
|
585
|
+
const bucketUserClause = "";
|
|
586
|
+
const bucketParams = false ? [bt] : [bt];
|
|
567
587
|
const exists = ctx.db
|
|
568
588
|
.prepare(
|
|
569
589
|
`SELECT 1 FROM vault WHERE kind = 'bucket' AND identity_key = ? ${bucketUserClause} LIMIT 1`,
|
|
@@ -591,11 +611,11 @@ export async function handler(
|
|
|
591
611
|
try {
|
|
592
612
|
const countRow = ctx.db
|
|
593
613
|
.prepare(
|
|
594
|
-
|
|
595
|
-
|
|
614
|
+
false
|
|
615
|
+
|
|
596
616
|
: "SELECT COUNT(*) as c FROM vault",
|
|
597
617
|
)
|
|
598
|
-
.get(...(
|
|
618
|
+
.get(...([]));
|
|
599
619
|
if (countRow.c >= criticalLimit) {
|
|
600
620
|
parts.push(
|
|
601
621
|
``,
|
|
@@ -79,7 +79,6 @@ function formatEntry(entry) {
|
|
|
79
79
|
|
|
80
80
|
export async function handler({ project, max_tokens, buckets }, ctx, { ensureIndexed }) {
|
|
81
81
|
const { config } = ctx;
|
|
82
|
-
const userId = ctx.userId !== undefined ? ctx.userId : undefined;
|
|
83
82
|
|
|
84
83
|
const vaultErr = ensureVaultExists(config);
|
|
85
84
|
if (vaultErr) return vaultErr;
|
|
@@ -110,7 +109,7 @@ export async function handler({ project, max_tokens, buckets }, ctx, { ensureInd
|
|
|
110
109
|
);
|
|
111
110
|
tokensUsed += estimateTokens(sections.join("\n"));
|
|
112
111
|
|
|
113
|
-
const lastSession = queryLastSession(ctx,
|
|
112
|
+
const lastSession = queryLastSession(ctx, effectiveTags);
|
|
114
113
|
if (lastSession) {
|
|
115
114
|
const sessionBlock = [
|
|
116
115
|
"## Last Session Summary",
|
|
@@ -128,7 +127,7 @@ export async function handler({ project, max_tokens, buckets }, ctx, { ensureInd
|
|
|
128
127
|
ctx,
|
|
129
128
|
PRIORITY_KINDS,
|
|
130
129
|
sinceDate,
|
|
131
|
-
|
|
130
|
+
|
|
132
131
|
effectiveTags,
|
|
133
132
|
);
|
|
134
133
|
if (decisions.length > 0) {
|
|
@@ -150,7 +149,7 @@ export async function handler({ project, max_tokens, buckets }, ctx, { ensureInd
|
|
|
150
149
|
}
|
|
151
150
|
}
|
|
152
151
|
|
|
153
|
-
const recent = queryRecent(ctx, sinceDate,
|
|
152
|
+
const recent = queryRecent(ctx, sinceDate, effectiveTags);
|
|
154
153
|
const seenIds = new Set(decisions.map((d) => d.id));
|
|
155
154
|
if (lastSession) seenIds.add(lastSession.id);
|
|
156
155
|
const deduped = recent.filter((r) => !seenIds.has(r.id));
|
|
@@ -202,13 +201,11 @@ export async function handler({ project, max_tokens, buckets }, ctx, { ensureInd
|
|
|
202
201
|
return result;
|
|
203
202
|
}
|
|
204
203
|
|
|
205
|
-
function queryLastSession(ctx,
|
|
204
|
+
function queryLastSession(ctx, effectiveTags) {
|
|
206
205
|
const clauses = [`kind = '${SESSION_SUMMARY_KIND}'`];
|
|
207
206
|
const params = [];
|
|
208
207
|
|
|
209
|
-
if (
|
|
210
|
-
clauses.push("user_id = ?");
|
|
211
|
-
params.push(userId);
|
|
208
|
+
if (false) {
|
|
212
209
|
}
|
|
213
210
|
clauses.push("(expires_at IS NULL OR expires_at > datetime('now'))");
|
|
214
211
|
clauses.push("superseded_by IS NULL");
|
|
@@ -228,7 +225,7 @@ function queryLastSession(ctx, userId, effectiveTags) {
|
|
|
228
225
|
return rows[0] || null;
|
|
229
226
|
}
|
|
230
227
|
|
|
231
|
-
function queryByKinds(ctx, kinds, since,
|
|
228
|
+
function queryByKinds(ctx, kinds, since, effectiveTags) {
|
|
232
229
|
const kindPlaceholders = kinds.map(() => "?").join(",");
|
|
233
230
|
const clauses = [`kind IN (${kindPlaceholders})`];
|
|
234
231
|
const params = [...kinds];
|
|
@@ -236,9 +233,7 @@ function queryByKinds(ctx, kinds, since, userId, effectiveTags) {
|
|
|
236
233
|
clauses.push("created_at >= ?");
|
|
237
234
|
params.push(since);
|
|
238
235
|
|
|
239
|
-
if (
|
|
240
|
-
clauses.push("user_id = ?");
|
|
241
|
-
params.push(userId);
|
|
236
|
+
if (false) {
|
|
242
237
|
}
|
|
243
238
|
clauses.push("(expires_at IS NULL OR expires_at > datetime('now'))");
|
|
244
239
|
clauses.push("superseded_by IS NULL");
|
|
@@ -258,13 +253,11 @@ function queryByKinds(ctx, kinds, since, userId, effectiveTags) {
|
|
|
258
253
|
return rows;
|
|
259
254
|
}
|
|
260
255
|
|
|
261
|
-
function queryRecent(ctx, since,
|
|
256
|
+
function queryRecent(ctx, since, effectiveTags) {
|
|
262
257
|
const clauses = ["created_at >= ?"];
|
|
263
258
|
const params = [since];
|
|
264
259
|
|
|
265
|
-
if (
|
|
266
|
-
clauses.push("user_id = ?");
|
|
267
|
-
params.push(userId);
|
|
260
|
+
if (false) {
|
|
268
261
|
}
|
|
269
262
|
clauses.push("(expires_at IS NULL OR expires_at > datetime('now'))");
|
|
270
263
|
clauses.push("superseded_by IS NULL");
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* file-ops.js — Capture-specific file operations
|
|
3
|
-
*
|
|
4
|
-
* Writes markdown entry files with frontmatter to the vault directory.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { mkdirSync, writeFileSync } from "node:fs";
|
|
8
|
-
import { resolve, relative } from "node:path";
|
|
9
|
-
import { formatFrontmatter } from "../core/frontmatter.js";
|
|
10
|
-
import { slugify, kindToPath } from "../core/files.js";
|
|
11
|
-
import { formatBody } from "./formatters.js";
|
|
12
|
-
|
|
13
|
-
export function safeFolderPath(vaultDir, kind, folder) {
|
|
14
|
-
const base = resolve(vaultDir, kindToPath(kind));
|
|
15
|
-
if (!folder) return base;
|
|
16
|
-
const resolved = resolve(base, folder);
|
|
17
|
-
const rel = relative(base, resolved);
|
|
18
|
-
if (rel.startsWith("..") || resolve(base, rel) !== resolved) {
|
|
19
|
-
throw new Error(`Folder path escapes vault: "${folder}"`);
|
|
20
|
-
}
|
|
21
|
-
return resolved;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function writeEntryFile(
|
|
25
|
-
vaultDir,
|
|
26
|
-
kind,
|
|
27
|
-
{
|
|
28
|
-
id,
|
|
29
|
-
title,
|
|
30
|
-
body,
|
|
31
|
-
meta,
|
|
32
|
-
tags,
|
|
33
|
-
source,
|
|
34
|
-
createdAt,
|
|
35
|
-
updatedAt,
|
|
36
|
-
folder,
|
|
37
|
-
category,
|
|
38
|
-
identity_key,
|
|
39
|
-
expires_at,
|
|
40
|
-
supersedes,
|
|
41
|
-
},
|
|
42
|
-
) {
|
|
43
|
-
// P5: folder is now a top-level param; also accept from meta for backward compat
|
|
44
|
-
const resolvedFolder = folder || meta?.folder || "";
|
|
45
|
-
const dir = safeFolderPath(vaultDir, kind, resolvedFolder);
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
mkdirSync(dir, { recursive: true });
|
|
49
|
-
} catch (e) {
|
|
50
|
-
throw new Error(`Failed to create directory "${dir}": ${e.message}`);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const created = createdAt || new Date().toISOString();
|
|
54
|
-
const fmFields = { id };
|
|
55
|
-
|
|
56
|
-
// Add kind-specific meta fields to frontmatter (flattened, not nested)
|
|
57
|
-
if (meta) {
|
|
58
|
-
for (const [k, v] of Object.entries(meta)) {
|
|
59
|
-
if (k === "folder") continue;
|
|
60
|
-
if (v !== null && v !== undefined) fmFields[k] = v;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (identity_key) fmFields.identity_key = identity_key;
|
|
65
|
-
if (expires_at) fmFields.expires_at = expires_at;
|
|
66
|
-
if (supersedes?.length) fmFields.supersedes = supersedes;
|
|
67
|
-
fmFields.tags = tags || [];
|
|
68
|
-
fmFields.source = source || "claude-code";
|
|
69
|
-
fmFields.created = created;
|
|
70
|
-
if (updatedAt && updatedAt !== created) fmFields.updated = updatedAt;
|
|
71
|
-
|
|
72
|
-
const mdBody = formatBody(kind, { title, body, meta });
|
|
73
|
-
|
|
74
|
-
// Entity kinds: deterministic filename from identity_key (no ULID suffix)
|
|
75
|
-
let filename;
|
|
76
|
-
if (category === "entity" && identity_key) {
|
|
77
|
-
const identitySlug = slugify(identity_key);
|
|
78
|
-
filename = identitySlug
|
|
79
|
-
? `${identitySlug}.md`
|
|
80
|
-
: `${id.slice(-8).toLowerCase()}.md`;
|
|
81
|
-
} else {
|
|
82
|
-
const slug = slugify((title || body).slice(0, 40));
|
|
83
|
-
const shortId = id.slice(-8).toLowerCase();
|
|
84
|
-
filename = slug ? `${slug}-${shortId}.md` : `${shortId}.md`;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const filePath = resolve(dir, filename);
|
|
88
|
-
const md = formatFrontmatter(fmFields) + mdBody;
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
writeFileSync(filePath, md);
|
|
92
|
-
} catch (e) {
|
|
93
|
-
throw new Error(`Failed to write entry file "${filePath}": ${e.message}`);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return filePath;
|
|
97
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { captureAndIndex } from "./index.js";
|
|
2
|
-
|
|
3
|
-
export async function importEntries(ctx, entries, opts = {}) {
|
|
4
|
-
const { onProgress, source } = opts;
|
|
5
|
-
let imported = 0;
|
|
6
|
-
let failed = 0;
|
|
7
|
-
const errors = [];
|
|
8
|
-
|
|
9
|
-
for (let i = 0; i < entries.length; i++) {
|
|
10
|
-
const entry = entries[i];
|
|
11
|
-
|
|
12
|
-
if (onProgress) {
|
|
13
|
-
onProgress(i + 1, entries.length);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
try {
|
|
17
|
-
if (!entry.body?.trim()) {
|
|
18
|
-
failed++;
|
|
19
|
-
errors.push({ index: i, title: entry.title, error: "Empty body" });
|
|
20
|
-
continue;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
await captureAndIndex(ctx, {
|
|
24
|
-
kind: entry.kind || "insight",
|
|
25
|
-
title: entry.title || null,
|
|
26
|
-
body: entry.body,
|
|
27
|
-
meta: entry.meta,
|
|
28
|
-
tags: entry.tags,
|
|
29
|
-
source: entry.source || source || "import",
|
|
30
|
-
identity_key: entry.identity_key,
|
|
31
|
-
expires_at: entry.expires_at,
|
|
32
|
-
userId: ctx.userId || null,
|
|
33
|
-
});
|
|
34
|
-
imported++;
|
|
35
|
-
} catch (err) {
|
|
36
|
-
failed++;
|
|
37
|
-
errors.push({
|
|
38
|
-
index: i,
|
|
39
|
-
title: entry.title || null,
|
|
40
|
-
error: err.message,
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return { imported, failed, errors };
|
|
46
|
-
}
|