context-vault 3.1.6 → 3.1.7

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.
Files changed (82) hide show
  1. package/bin/cli.js +1369 -1774
  2. package/node_modules/@context-vault/core/dist/capture.d.ts +1 -1
  3. package/node_modules/@context-vault/core/dist/capture.d.ts.map +1 -1
  4. package/node_modules/@context-vault/core/dist/capture.js +34 -47
  5. package/node_modules/@context-vault/core/dist/capture.js.map +1 -1
  6. package/node_modules/@context-vault/core/dist/categories.js +30 -30
  7. package/node_modules/@context-vault/core/dist/config.d.ts +1 -1
  8. package/node_modules/@context-vault/core/dist/config.d.ts.map +1 -1
  9. package/node_modules/@context-vault/core/dist/config.js +37 -43
  10. package/node_modules/@context-vault/core/dist/config.js.map +1 -1
  11. package/node_modules/@context-vault/core/dist/constants.d.ts.map +1 -1
  12. package/node_modules/@context-vault/core/dist/constants.js +4 -4
  13. package/node_modules/@context-vault/core/dist/constants.js.map +1 -1
  14. package/node_modules/@context-vault/core/dist/db.d.ts +2 -2
  15. package/node_modules/@context-vault/core/dist/db.d.ts.map +1 -1
  16. package/node_modules/@context-vault/core/dist/db.js +21 -20
  17. package/node_modules/@context-vault/core/dist/db.js.map +1 -1
  18. package/node_modules/@context-vault/core/dist/embed.d.ts.map +1 -1
  19. package/node_modules/@context-vault/core/dist/embed.js +11 -11
  20. package/node_modules/@context-vault/core/dist/embed.js.map +1 -1
  21. package/node_modules/@context-vault/core/dist/files.d.ts.map +1 -1
  22. package/node_modules/@context-vault/core/dist/files.js +12 -13
  23. package/node_modules/@context-vault/core/dist/files.js.map +1 -1
  24. package/node_modules/@context-vault/core/dist/formatters.js +5 -5
  25. package/node_modules/@context-vault/core/dist/frontmatter.d.ts.map +1 -1
  26. package/node_modules/@context-vault/core/dist/frontmatter.js +23 -23
  27. package/node_modules/@context-vault/core/dist/frontmatter.js.map +1 -1
  28. package/node_modules/@context-vault/core/dist/index.d.ts +1 -1
  29. package/node_modules/@context-vault/core/dist/index.d.ts.map +1 -1
  30. package/node_modules/@context-vault/core/dist/index.js +58 -46
  31. package/node_modules/@context-vault/core/dist/index.js.map +1 -1
  32. package/node_modules/@context-vault/core/dist/ingest-url.d.ts.map +1 -1
  33. package/node_modules/@context-vault/core/dist/ingest-url.js +30 -33
  34. package/node_modules/@context-vault/core/dist/ingest-url.js.map +1 -1
  35. package/node_modules/@context-vault/core/dist/main.d.ts +13 -13
  36. package/node_modules/@context-vault/core/dist/main.d.ts.map +1 -1
  37. package/node_modules/@context-vault/core/dist/main.js +12 -12
  38. package/node_modules/@context-vault/core/dist/main.js.map +1 -1
  39. package/node_modules/@context-vault/core/dist/search.d.ts +1 -1
  40. package/node_modules/@context-vault/core/dist/search.d.ts.map +1 -1
  41. package/node_modules/@context-vault/core/dist/search.js +20 -22
  42. package/node_modules/@context-vault/core/dist/search.js.map +1 -1
  43. package/node_modules/@context-vault/core/dist/types.d.ts +1 -1
  44. package/node_modules/@context-vault/core/package.json +1 -1
  45. package/node_modules/@context-vault/core/src/capture.ts +44 -81
  46. package/node_modules/@context-vault/core/src/categories.ts +30 -30
  47. package/node_modules/@context-vault/core/src/config.ts +45 -60
  48. package/node_modules/@context-vault/core/src/constants.ts +8 -10
  49. package/node_modules/@context-vault/core/src/db.ts +37 -56
  50. package/node_modules/@context-vault/core/src/embed.ts +15 -26
  51. package/node_modules/@context-vault/core/src/files.ts +13 -16
  52. package/node_modules/@context-vault/core/src/formatters.ts +5 -5
  53. package/node_modules/@context-vault/core/src/frontmatter.ts +26 -30
  54. package/node_modules/@context-vault/core/src/index.ts +94 -100
  55. package/node_modules/@context-vault/core/src/ingest-url.ts +56 -93
  56. package/node_modules/@context-vault/core/src/main.ts +13 -18
  57. package/node_modules/@context-vault/core/src/search.ts +34 -56
  58. package/node_modules/@context-vault/core/src/types.ts +1 -1
  59. package/package.json +2 -2
  60. package/scripts/postinstall.js +18 -25
  61. package/scripts/prepack.js +13 -19
  62. package/src/archive.js +211 -0
  63. package/src/error-log.js +7 -7
  64. package/src/helpers.js +11 -13
  65. package/src/linking.js +8 -11
  66. package/src/migrate-dirs.js +139 -0
  67. package/src/register-tools.js +46 -48
  68. package/src/server.js +73 -99
  69. package/src/status.js +35 -71
  70. package/src/telemetry.js +18 -22
  71. package/src/temporal.js +19 -30
  72. package/src/tools/clear-context.js +15 -18
  73. package/src/tools/context-status.js +37 -57
  74. package/src/tools/create-snapshot.js +45 -57
  75. package/src/tools/delete-context.js +11 -12
  76. package/src/tools/get-context.js +112 -160
  77. package/src/tools/ingest-project.js +66 -86
  78. package/src/tools/ingest-url.js +25 -41
  79. package/src/tools/list-buckets.js +19 -25
  80. package/src/tools/list-context.js +35 -58
  81. package/src/tools/save-context.js +126 -182
  82. package/src/tools/session-start.js +46 -62
@@ -1,22 +1,22 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import { join } from "node:path";
3
- import { gatherVaultStatus, computeGrowthWarnings } from "../status.js";
4
- import { errorLogPath, errorLogCount } from "../error-log.js";
5
- import { ok, err } from "../helpers.js";
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { gatherVaultStatus, computeGrowthWarnings } from '../status.js';
4
+ import { errorLogPath, errorLogCount } from '../error-log.js';
5
+ import { ok, err } from '../helpers.js';
6
6
 
7
7
  function relativeTime(ts) {
8
8
  const secs = Math.floor((Date.now() - ts) / 1000);
9
9
  if (secs < 60) return `${secs}s ago`;
10
10
  const mins = Math.floor(secs / 60);
11
- if (mins < 60) return `${mins} minute${mins === 1 ? "" : "s"} ago`;
11
+ if (mins < 60) return `${mins} minute${mins === 1 ? '' : 's'} ago`;
12
12
  const hrs = Math.floor(mins / 60);
13
- return `${hrs} hour${hrs === 1 ? "" : "s"} ago`;
13
+ return `${hrs} hour${hrs === 1 ? '' : 's'} ago`;
14
14
  }
15
15
 
16
- export const name = "context_status";
16
+ export const name = 'context_status';
17
17
 
18
18
  export const description =
19
- "Show vault health: resolved config, file counts per kind, database size, and any issues. Use to verify setup or troubleshoot. Call this when a user asks about their vault or to debug search issues.";
19
+ 'Show vault health: resolved config, file counts per kind, database size, and any issues. Use to verify setup or troubleshoot. Call this when a user asks about their vault or to debug search issues.';
20
20
 
21
21
  export const inputSchema = {};
22
22
 
@@ -31,12 +31,12 @@ export function handler(_args, ctx) {
31
31
  const status = gatherVaultStatus(ctx);
32
32
 
33
33
  const hasIssues = status.stalePaths || status.embeddingStatus?.missing > 0;
34
- const healthIcon = hasIssues ? "" : "";
34
+ const healthIcon = hasIssues ? '' : '';
35
35
 
36
36
  const lines = [
37
37
  `## ${healthIcon} Vault Status (connected)`,
38
38
  ``,
39
- `Vault: ${config.vaultDir} (${config.vaultDirExists ? status.fileCount + " files" : "missing"})`,
39
+ `Vault: ${config.vaultDir} (${config.vaultDirExists ? status.fileCount + ' files' : 'missing'})`,
40
40
  `Database: ${config.dbPath} (${status.dbSize})`,
41
41
  `Dev dir: ${config.devDir}`,
42
42
  `Data dir: ${config.dataDir}`,
@@ -51,26 +51,21 @@ export function handler(_args, ctx) {
51
51
  lines.push(`Embeddings: ${indexed}/${total} (${pct}%)`);
52
52
  }
53
53
  if (status.embedModelAvailable === false) {
54
- lines.push(
55
- `Embed model: unavailable (semantic search disabled, FTS still works)`,
56
- );
54
+ lines.push(`Embed model: unavailable (semantic search disabled, FTS still works)`);
57
55
  } else if (status.embedModelAvailable === true) {
58
56
  lines.push(`Embed model: loaded`);
59
57
  }
60
- lines.push(
61
- `Decay: ${config.eventDecayDays} days (event recency window)`,
62
- );
58
+ lines.push(`Decay: ${config.eventDecayDays} days (event recency window)`);
63
59
  if (status.expiredCount > 0) {
64
60
  lines.push(
65
- `Expired: ${status.expiredCount} entries pending prune (run \`context-vault prune\` to remove now)`,
61
+ `Expired: ${status.expiredCount} entries pending prune (run \`context-vault prune\` to remove now)`
66
62
  );
67
63
  }
68
64
 
69
65
  lines.push(``, `### Indexed`);
70
66
 
71
67
  if (status.kindCounts.length) {
72
- for (const { kind, c } of status.kindCounts)
73
- lines.push(`- ${c} ${kind}s`);
68
+ for (const { kind, c } of status.kindCounts) lines.push(`- ${c} ${kind}s`);
74
69
  } else {
75
70
  lines.push(`- (empty)`);
76
71
  }
@@ -78,23 +73,19 @@ export function handler(_args, ctx) {
78
73
  if (status.categoryCounts.length) {
79
74
  lines.push(``);
80
75
  lines.push(`### Categories`);
81
- for (const { category, c } of status.categoryCounts)
82
- lines.push(`- ${category}: ${c}`);
76
+ for (const { category, c } of status.categoryCounts) lines.push(`- ${category}: ${c}`);
83
77
  }
84
78
 
85
79
  if (status.subdirs.length) {
86
80
  lines.push(``);
87
81
  lines.push(`### Disk Directories`);
88
- for (const { name, count } of status.subdirs)
89
- lines.push(`- ${name}/: ${count} files`);
82
+ for (const { name, count } of status.subdirs) lines.push(`- ${name}/: ${count} files`);
90
83
  }
91
84
 
92
85
  if (status.stalePaths) {
93
86
  lines.push(``);
94
87
  lines.push(`### ⚠ Stale Paths`);
95
- lines.push(
96
- `DB contains ${status.staleCount} paths not matching current vault dir.`,
97
- );
88
+ lines.push(`DB contains ${status.staleCount} paths not matching current vault dir.`);
98
89
  lines.push(`Auto-reindex will fix this on next search or save.`);
99
90
  }
100
91
 
@@ -102,19 +93,13 @@ export function handler(_args, ctx) {
102
93
  lines.push(``);
103
94
  lines.push(`### ⚠ Potentially Stale Knowledge`);
104
95
  lines.push(
105
- `Not updated within kind staleness window (pattern: 180d, decision: 365d, reference: 90d):`,
96
+ `Not updated within kind staleness window (pattern: 180d, decision: 365d, reference: 90d):`
106
97
  );
107
98
  for (const entry of status.staleKnowledge) {
108
- const lastUpdated = entry.last_updated
109
- ? entry.last_updated.split("T")[0]
110
- : "unknown";
111
- lines.push(
112
- `- "${entry.title}" (${entry.kind}) — last updated ${lastUpdated}`,
113
- );
99
+ const lastUpdated = entry.last_updated ? entry.last_updated.split('T')[0] : 'unknown';
100
+ lines.push(`- "${entry.title}" (${entry.kind}) — last updated ${lastUpdated}`);
114
101
  }
115
- lines.push(
116
- `Use save_context to refresh or add expires_at to retire stale entries.`,
117
- );
102
+ lines.push(`Use save_context to refresh or add expires_at to retire stale entries.`);
118
103
  }
119
104
 
120
105
  // Error log
@@ -127,10 +112,10 @@ export function handler(_args, ctx) {
127
112
  }
128
113
 
129
114
  // Last startup error
130
- const lastErrorPath = join(config.dataDir, ".last-error");
115
+ const lastErrorPath = join(config.dataDir, '.last-error');
131
116
  if (existsSync(lastErrorPath)) {
132
117
  try {
133
- const lastError = readFileSync(lastErrorPath, "utf-8").trim();
118
+ const lastError = readFileSync(lastErrorPath, 'utf-8').trim();
134
119
  lines.push(``, `### Last Startup Error`);
135
120
  lines.push(`\`\`\``);
136
121
  lines.push(lastError);
@@ -145,13 +130,11 @@ export function handler(_args, ctx) {
145
130
  lines.push(`- Tool calls (session): ${ts.ok} ok, ${ts.errors} errors`);
146
131
  if (ts.lastError) {
147
132
  const { tool, code, timestamp } = ts.lastError;
148
- lines.push(
149
- `- Last error: ${tool ?? "unknown"} — ${code} (${relativeTime(timestamp)})`,
150
- );
133
+ lines.push(`- Last error: ${tool ?? 'unknown'} — ${code} (${relativeTime(timestamp)})`);
151
134
  }
152
135
  if (status.autoCapturedFeedbackCount > 0) {
153
136
  lines.push(
154
- `- Auto-captured feedback entries: ${status.autoCapturedFeedbackCount} (run get_context with kind:feedback tags:auto-captured)`,
137
+ `- Auto-captured feedback entries: ${status.autoCapturedFeedbackCount} (run get_context with kind:feedback tags:auto-captured)`
155
138
  );
156
139
  }
157
140
  }
@@ -159,19 +142,19 @@ export function handler(_args, ctx) {
159
142
  // Growth warnings
160
143
  const growth = computeGrowthWarnings(status, config.thresholds);
161
144
  if (growth.hasWarnings) {
162
- lines.push("", "### ⚠ Vault Growth Warning");
145
+ lines.push('', '### ⚠ Vault Growth Warning');
163
146
  for (const w of growth.warnings) {
164
147
  lines.push(` ${w.message}`);
165
148
  }
166
149
  if (growth.kindBreakdown.length) {
167
- lines.push("");
168
- lines.push(" Breakdown by kind:");
150
+ lines.push('');
151
+ lines.push(' Breakdown by kind:');
169
152
  for (const { kind, count, pct } of growth.kindBreakdown) {
170
153
  lines.push(` ${kind}: ${count.toLocaleString()} (${pct}%)`);
171
154
  }
172
155
  }
173
156
  if (growth.actions.length) {
174
- lines.push("", "Suggested growth actions:");
157
+ lines.push('', 'Suggested growth actions:');
175
158
  for (const a of growth.actions) {
176
159
  lines.push(` • ${a}`);
177
160
  }
@@ -180,23 +163,20 @@ export function handler(_args, ctx) {
180
163
 
181
164
  // Suggested actions
182
165
  const actions = [];
183
- if (status.stalePaths)
184
- actions.push("- Run `context-vault reindex` to fix stale paths");
166
+ if (status.stalePaths) actions.push('- Run `context-vault reindex` to fix stale paths');
185
167
  if (status.embeddingStatus?.missing > 0)
186
- actions.push(
187
- "- Run `context-vault reindex` to generate missing embeddings",
188
- );
168
+ actions.push('- Run `context-vault reindex` to generate missing embeddings');
189
169
  if (!config.vaultDirExists)
190
- actions.push("- Run `context-vault setup` to create the vault directory");
170
+ actions.push('- Run `context-vault setup` to create the vault directory');
191
171
  if (status.kindCounts.length === 0 && config.vaultDirExists)
192
- actions.push("- Use `save_context` to add your first entry");
172
+ actions.push('- Use `save_context` to add your first entry');
193
173
 
194
174
  if (actions.length) {
195
- lines.push("", "### Suggested Actions", ...actions);
175
+ lines.push('', '### Suggested Actions', ...actions);
196
176
  }
197
177
 
198
- return ok(lines.join("\n"));
178
+ return ok(lines.join('\n'));
199
179
  } catch (e) {
200
- return err(e.message, "STATUS_FAILED");
180
+ return err(e.message, 'STATUS_FAILED');
201
181
  }
202
182
  }
@@ -1,76 +1,75 @@
1
- import { z } from "zod";
2
- import { hybridSearch } from "@context-vault/core/search";
3
- import { captureAndIndex } from "@context-vault/core/capture";
4
- import { normalizeKind } from "@context-vault/core/files";
5
- import { ok, err, ensureVaultExists } from "../helpers.js";
1
+ import { z } from 'zod';
2
+ import { hybridSearch } from '@context-vault/core/search';
3
+ import { captureAndIndex } from '@context-vault/core/capture';
4
+ import { normalizeKind } from '@context-vault/core/files';
5
+ import { ok, err, ensureVaultExists } from '../helpers.js';
6
6
 
7
- const NOISE_KINDS = new Set(["prompt-history", "task-notification"]);
7
+ const NOISE_KINDS = new Set(['prompt-history', 'task-notification']);
8
8
  const MAX_ENTRIES_FOR_GATHER = 40;
9
9
  const MAX_BODY_PER_ENTRY = 600;
10
10
 
11
- export const name = "create_snapshot";
11
+ export const name = 'create_snapshot';
12
12
 
13
13
  export const description =
14
14
  "Pull all relevant vault entries matching a topic, deduplicate, and save them as a structured context brief (kind: 'brief'). Entries are formatted as markdown — no external API or LLM call required. The calling agent can synthesize the gathered content directly. Retrieve with: get_context(kind: 'brief', identity_key: '<key>').";
15
15
 
16
16
  export const inputSchema = {
17
- topic: z.string().describe("The topic or project name to snapshot"),
17
+ topic: z.string().describe('The topic or project name to snapshot'),
18
18
  tags: z
19
19
  .array(z.string())
20
20
  .optional()
21
- .describe("Optional tag filters — entries must match at least one"),
21
+ .describe('Optional tag filters — entries must match at least one'),
22
22
  buckets: z
23
23
  .array(z.string())
24
24
  .optional()
25
25
  .describe(
26
- "Filter by project-scoped buckets. Each name expands to a 'bucket:<name>' tag. Composes with 'tags' via OR (entries matching any tag or any bucket are included).",
26
+ "Filter by project-scoped buckets. Each name expands to a 'bucket:<name>' tag. Composes with 'tags' via OR (entries matching any tag or any bucket are included)."
27
27
  ),
28
28
  kinds: z
29
29
  .array(z.string())
30
30
  .optional()
31
- .describe("Optional kind filters to restrict which entry types are pulled"),
31
+ .describe('Optional kind filters to restrict which entry types are pulled'),
32
32
  identity_key: z
33
33
  .string()
34
34
  .optional()
35
35
  .describe(
36
- "Deterministic key for the saved brief (defaults to slugified topic). Use the same key to overwrite a previous snapshot.",
36
+ 'Deterministic key for the saved brief (defaults to slugified topic). Use the same key to overwrite a previous snapshot.'
37
37
  ),
38
38
  };
39
39
 
40
40
  function formatGatheredEntries(topic, entries) {
41
41
  const header = [
42
42
  `# ${topic} — Context Brief`,
43
- "",
44
- `*Gathered from ${entries.length} vault ${entries.length === 1 ? "entry" : "entries"}. Synthesize the content below to extract key decisions, patterns, and constraints.*`,
45
- "",
46
- "---",
47
- "",
48
- ].join("\n");
43
+ '',
44
+ `*Gathered from ${entries.length} vault ${entries.length === 1 ? 'entry' : 'entries'}. Synthesize the content below to extract key decisions, patterns, and constraints.*`,
45
+ '',
46
+ '---',
47
+ '',
48
+ ].join('\n');
49
49
 
50
50
  const body = entries
51
51
  .map((e, i) => {
52
52
  const tags = e.tags ? JSON.parse(e.tags) : [];
53
- const tagStr = tags.length ? tags.join(", ") : "none";
54
- const updated = e.updated_at || e.created_at || "unknown";
53
+ const tagStr = tags.length ? tags.join(', ') : 'none';
54
+ const updated = e.updated_at || e.created_at || 'unknown';
55
55
  const bodyText = e.body
56
- ? e.body.slice(0, MAX_BODY_PER_ENTRY) +
57
- (e.body.length > MAX_BODY_PER_ENTRY ? "…" : "")
58
- : "(no body)";
56
+ ? e.body.slice(0, MAX_BODY_PER_ENTRY) + (e.body.length > MAX_BODY_PER_ENTRY ? '…' : '')
57
+ : '(no body)';
59
58
  const title = e.title || `Entry ${i + 1}`;
60
59
  return [
61
60
  `## ${i + 1}. [${e.kind}] ${title}`,
62
- "",
61
+ '',
63
62
  `**Tags:** ${tagStr}`,
64
63
  `**Updated:** ${updated}`,
65
64
  `**ID:** \`${e.id}\``,
66
- "",
65
+ '',
67
66
  bodyText,
68
- "",
69
- "---",
70
- "",
71
- ].join("\n");
67
+ '',
68
+ '---',
69
+ '',
70
+ ].join('\n');
72
71
  })
73
- .join("");
72
+ .join('');
74
73
 
75
74
  return header + body;
76
75
  }
@@ -78,15 +77,15 @@ function formatGatheredEntries(topic, entries) {
78
77
  function slugifyTopic(topic) {
79
78
  return topic
80
79
  .toLowerCase()
81
- .replace(/[^a-z0-9]+/g, "-")
82
- .replace(/^-+|-+$/g, "")
80
+ .replace(/[^a-z0-9]+/g, '-')
81
+ .replace(/^-+|-+$/g, '')
83
82
  .slice(0, 120);
84
83
  }
85
84
 
86
85
  export async function handler(
87
86
  { topic, tags, buckets, kinds, identity_key },
88
87
  ctx,
89
- { ensureIndexed },
88
+ { ensureIndexed }
90
89
  ) {
91
90
  const { config } = ctx;
92
91
 
@@ -94,7 +93,7 @@ export async function handler(
94
93
  if (vaultErr) return vaultErr;
95
94
 
96
95
  if (!topic?.trim()) {
97
- return err("Required: topic (non-empty string)", "INVALID_INPUT");
96
+ return err('Required: topic (non-empty string)', 'INVALID_INPUT');
98
97
  }
99
98
 
100
99
  await ensureIndexed();
@@ -130,7 +129,7 @@ export async function handler(
130
129
  });
131
130
  }
132
131
  } catch (e) {
133
- return err(e.message, "SEARCH_FAILED");
132
+ return err(e.message, 'SEARCH_FAILED');
134
133
  }
135
134
 
136
135
  if (effectiveTags.length) {
@@ -140,40 +139,33 @@ export async function handler(
140
139
  });
141
140
  }
142
141
 
143
- const noiseIds = candidates
144
- .filter((r) => NOISE_KINDS.has(r.kind))
145
- .map((r) => r.id);
142
+ const noiseIds = candidates.filter((r) => NOISE_KINDS.has(r.kind)).map((r) => r.id);
146
143
 
147
144
  const gatherEntries = candidates.filter((r) => !NOISE_KINDS.has(r.kind));
148
145
 
149
146
  if (gatherEntries.length === 0) {
150
147
  return err(
151
148
  `No entries found for topic "${topic}". Try a broader topic or different tags.`,
152
- "NO_ENTRIES",
149
+ 'NO_ENTRIES'
153
150
  );
154
151
  }
155
152
 
156
153
  const briefBody = formatGatheredEntries(topic, gatherEntries);
157
154
 
158
- const effectiveIdentityKey =
159
- identity_key ?? `snapshot-${slugifyTopic(topic)}`;
155
+ const effectiveIdentityKey = identity_key ?? `snapshot-${slugifyTopic(topic)}`;
160
156
 
161
- const briefTags = [
162
- "snapshot",
163
- ...(tags ?? []),
164
- ...(normalizedKinds.length > 0 ? [] : []),
165
- ];
157
+ const briefTags = ['snapshot', ...(tags ?? []), ...(normalizedKinds.length > 0 ? [] : [])];
166
158
 
167
159
  const supersedes = noiseIds.length > 0 ? noiseIds : undefined;
168
160
 
169
161
  let entry;
170
162
  try {
171
163
  entry = await captureAndIndex(ctx, {
172
- kind: "brief",
164
+ kind: 'brief',
173
165
  title: `${topic} — Context Brief`,
174
166
  body: briefBody,
175
167
  tags: briefTags,
176
- source: "create_snapshot",
168
+ source: 'create_snapshot',
177
169
  identity_key: effectiveIdentityKey,
178
170
  supersedes,
179
171
 
@@ -185,7 +177,7 @@ export async function handler(
185
177
  },
186
178
  });
187
179
  } catch (e) {
188
- return err(e.message, "SAVE_FAILED");
180
+ return err(e.message, 'SAVE_FAILED');
189
181
  }
190
182
 
191
183
  const parts = [
@@ -193,16 +185,12 @@ export async function handler(
193
185
  ` title: ${entry.title}`,
194
186
  ` identity_key: ${effectiveIdentityKey}`,
195
187
  ` synthesized from: ${gatherEntries.length} entries`,
196
- noiseIds.length > 0
197
- ? ` noise superseded: ${noiseIds.length} entries`
198
- : null,
199
- "",
200
- "_Retrieve with: get_context(kind: 'brief', identity_key: '" +
201
- effectiveIdentityKey +
202
- "')_",
188
+ noiseIds.length > 0 ? ` noise superseded: ${noiseIds.length} entries` : null,
189
+ '',
190
+ "_Retrieve with: get_context(kind: 'brief', identity_key: '" + effectiveIdentityKey + "')_",
203
191
  ]
204
192
  .filter((l) => l !== null)
205
- .join("\n");
193
+ .join('\n');
206
194
 
207
195
  return ok(parts);
208
196
  }
@@ -1,14 +1,14 @@
1
- import { z } from "zod";
2
- import { unlinkSync } from "node:fs";
3
- import { ok, err } from "../helpers.js";
1
+ import { z } from 'zod';
2
+ import { unlinkSync } from 'node:fs';
3
+ import { ok, err } from '../helpers.js';
4
4
 
5
- export const name = "delete_context";
5
+ export const name = 'delete_context';
6
6
 
7
7
  export const description =
8
- "Delete an entry from your vault by its ULID id. Removes the file from disk and cleans up the search index.";
8
+ 'Delete an entry from your vault by its ULID id. Removes the file from disk and cleans up the search index.';
9
9
 
10
10
  export const inputSchema = {
11
- id: z.string().describe("The entry ULID to delete"),
11
+ id: z.string().describe('The entry ULID to delete'),
12
12
  };
13
13
 
14
14
  /**
@@ -17,12 +17,11 @@ export const inputSchema = {
17
17
  * @param {import('../types.js').ToolShared} shared
18
18
  */
19
19
  export async function handler({ id }, ctx, { ensureIndexed }) {
20
- if (!id?.trim())
21
- return err("Required: id (non-empty string)", "INVALID_INPUT");
20
+ if (!id?.trim()) return err('Required: id (non-empty string)', 'INVALID_INPUT');
22
21
  await ensureIndexed();
23
22
 
24
23
  const entry = ctx.stmts.getEntryById.get(id);
25
- if (!entry) return err(`Entry not found: ${id}`, "NOT_FOUND");
24
+ if (!entry) return err(`Entry not found: ${id}`, 'NOT_FOUND');
26
25
 
27
26
  try {
28
27
  // Delete DB record first — if this fails, the file stays and no orphan is created
@@ -40,15 +39,15 @@ export async function handler({ id }, ctx, { ensureIndexed }) {
40
39
  try {
41
40
  unlinkSync(entry.file_path);
42
41
  } catch (e) {
43
- if (e.code !== "ENOENT") {
42
+ if (e.code !== 'ENOENT') {
44
43
  fileWarning = `file could not be removed from disk (${e.code}): ${entry.file_path}`;
45
44
  }
46
45
  }
47
46
  }
48
47
 
49
- const msg = `Deleted ${entry.kind}: ${entry.title || "(untitled)"} [${id}]`;
48
+ const msg = `Deleted ${entry.kind}: ${entry.title || '(untitled)'} [${id}]`;
50
49
  return ok(fileWarning ? `${msg}\nWarning: ${fileWarning}` : msg);
51
50
  } catch (e) {
52
- return err(e.message, "DELETE_FAILED");
51
+ return err(e.message, 'DELETE_FAILED');
53
52
  }
54
53
  }