context-vault 3.1.6 → 3.1.8

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 (184) hide show
  1. package/bin/cli.js +1369 -1774
  2. package/dist/archive.d.ts +23 -0
  3. package/dist/archive.d.ts.map +1 -0
  4. package/dist/archive.js +197 -0
  5. package/dist/archive.js.map +1 -0
  6. package/dist/consolidation.d.ts +14 -0
  7. package/dist/consolidation.d.ts.map +1 -0
  8. package/dist/consolidation.js +59 -0
  9. package/dist/consolidation.js.map +1 -0
  10. package/dist/error-log.d.ts +4 -0
  11. package/dist/error-log.d.ts.map +1 -0
  12. package/dist/error-log.js +33 -0
  13. package/dist/error-log.js.map +1 -0
  14. package/dist/helpers.d.ts +10 -0
  15. package/dist/helpers.d.ts.map +1 -0
  16. package/dist/helpers.js +42 -0
  17. package/dist/helpers.js.map +1 -0
  18. package/dist/linking.d.ts +13 -0
  19. package/dist/linking.d.ts.map +1 -0
  20. package/dist/linking.js +86 -0
  21. package/dist/linking.js.map +1 -0
  22. package/dist/migrate-dirs.d.ts +16 -0
  23. package/dist/migrate-dirs.d.ts.map +1 -0
  24. package/dist/migrate-dirs.js +127 -0
  25. package/dist/migrate-dirs.js.map +1 -0
  26. package/dist/register-tools.d.ts +3 -0
  27. package/dist/register-tools.d.ts.map +1 -0
  28. package/dist/register-tools.js +161 -0
  29. package/dist/register-tools.js.map +1 -0
  30. package/dist/server.d.ts +3 -0
  31. package/dist/server.d.ts.map +1 -0
  32. package/dist/server.js +241 -0
  33. package/dist/server.js.map +1 -0
  34. package/dist/status.d.ts +18 -0
  35. package/dist/status.d.ts.map +1 -0
  36. package/dist/status.js +265 -0
  37. package/dist/status.js.map +1 -0
  38. package/dist/telemetry.d.ts +6 -0
  39. package/dist/telemetry.d.ts.map +1 -0
  40. package/dist/telemetry.js +74 -0
  41. package/dist/telemetry.js.map +1 -0
  42. package/dist/temporal.d.ts +9 -0
  43. package/dist/temporal.d.ts.map +1 -0
  44. package/dist/temporal.js +76 -0
  45. package/dist/temporal.js.map +1 -0
  46. package/dist/tools/clear-context.d.ts +11 -0
  47. package/dist/tools/clear-context.d.ts.map +1 -0
  48. package/dist/tools/clear-context.js +28 -0
  49. package/dist/tools/clear-context.js.map +1 -0
  50. package/dist/tools/context-status.d.ts +6 -0
  51. package/dist/tools/context-status.d.ts.map +1 -0
  52. package/dist/tools/context-status.js +160 -0
  53. package/dist/tools/context-status.js.map +1 -0
  54. package/dist/tools/create-snapshot.d.ts +13 -0
  55. package/dist/tools/create-snapshot.d.ts.map +1 -0
  56. package/dist/tools/create-snapshot.js +161 -0
  57. package/dist/tools/create-snapshot.js.map +1 -0
  58. package/dist/tools/delete-context.d.ts +9 -0
  59. package/dist/tools/delete-context.d.ts.map +1 -0
  60. package/dist/tools/delete-context.js +45 -0
  61. package/dist/tools/delete-context.js.map +1 -0
  62. package/dist/tools/get-context.d.ts +85 -0
  63. package/dist/tools/get-context.d.ts.map +1 -0
  64. package/dist/tools/get-context.js +576 -0
  65. package/dist/tools/get-context.js.map +1 -0
  66. package/dist/tools/ingest-project.d.ts +11 -0
  67. package/dist/tools/ingest-project.d.ts.map +1 -0
  68. package/dist/tools/ingest-project.js +226 -0
  69. package/dist/tools/ingest-project.js.map +1 -0
  70. package/dist/tools/ingest-url.d.ts +11 -0
  71. package/dist/tools/ingest-url.d.ts.map +1 -0
  72. package/dist/tools/ingest-url.js +62 -0
  73. package/dist/tools/ingest-url.js.map +1 -0
  74. package/dist/tools/list-buckets.d.ts +9 -0
  75. package/dist/tools/list-buckets.d.ts.map +1 -0
  76. package/dist/tools/list-buckets.js +76 -0
  77. package/dist/tools/list-buckets.js.map +1 -0
  78. package/dist/tools/list-context.d.ts +19 -0
  79. package/dist/tools/list-context.d.ts.map +1 -0
  80. package/dist/tools/list-context.js +110 -0
  81. package/dist/tools/list-context.js.map +1 -0
  82. package/dist/tools/save-context.d.ts +36 -0
  83. package/dist/tools/save-context.d.ts.map +1 -0
  84. package/dist/tools/save-context.js +458 -0
  85. package/dist/tools/save-context.js.map +1 -0
  86. package/dist/tools/session-start.d.ts +11 -0
  87. package/dist/tools/session-start.d.ts.map +1 -0
  88. package/dist/tools/session-start.js +224 -0
  89. package/dist/tools/session-start.js.map +1 -0
  90. package/dist/types.d.ts +37 -0
  91. package/dist/types.d.ts.map +1 -0
  92. package/dist/types.js +2 -0
  93. package/dist/types.js.map +1 -0
  94. package/node_modules/@context-vault/core/dist/capture.d.ts +1 -1
  95. package/node_modules/@context-vault/core/dist/capture.d.ts.map +1 -1
  96. package/node_modules/@context-vault/core/dist/capture.js +34 -47
  97. package/node_modules/@context-vault/core/dist/capture.js.map +1 -1
  98. package/node_modules/@context-vault/core/dist/categories.js +30 -30
  99. package/node_modules/@context-vault/core/dist/config.d.ts +1 -1
  100. package/node_modules/@context-vault/core/dist/config.d.ts.map +1 -1
  101. package/node_modules/@context-vault/core/dist/config.js +37 -43
  102. package/node_modules/@context-vault/core/dist/config.js.map +1 -1
  103. package/node_modules/@context-vault/core/dist/constants.d.ts +1 -1
  104. package/node_modules/@context-vault/core/dist/constants.d.ts.map +1 -1
  105. package/node_modules/@context-vault/core/dist/constants.js +4 -4
  106. package/node_modules/@context-vault/core/dist/constants.js.map +1 -1
  107. package/node_modules/@context-vault/core/dist/db.d.ts +2 -2
  108. package/node_modules/@context-vault/core/dist/db.d.ts.map +1 -1
  109. package/node_modules/@context-vault/core/dist/db.js +21 -20
  110. package/node_modules/@context-vault/core/dist/db.js.map +1 -1
  111. package/node_modules/@context-vault/core/dist/embed.d.ts.map +1 -1
  112. package/node_modules/@context-vault/core/dist/embed.js +11 -11
  113. package/node_modules/@context-vault/core/dist/embed.js.map +1 -1
  114. package/node_modules/@context-vault/core/dist/files.d.ts.map +1 -1
  115. package/node_modules/@context-vault/core/dist/files.js +12 -13
  116. package/node_modules/@context-vault/core/dist/files.js.map +1 -1
  117. package/node_modules/@context-vault/core/dist/formatters.js +5 -5
  118. package/node_modules/@context-vault/core/dist/frontmatter.d.ts.map +1 -1
  119. package/node_modules/@context-vault/core/dist/frontmatter.js +23 -23
  120. package/node_modules/@context-vault/core/dist/frontmatter.js.map +1 -1
  121. package/node_modules/@context-vault/core/dist/index.d.ts +1 -1
  122. package/node_modules/@context-vault/core/dist/index.d.ts.map +1 -1
  123. package/node_modules/@context-vault/core/dist/index.js +58 -46
  124. package/node_modules/@context-vault/core/dist/index.js.map +1 -1
  125. package/node_modules/@context-vault/core/dist/ingest-url.d.ts.map +1 -1
  126. package/node_modules/@context-vault/core/dist/ingest-url.js +30 -33
  127. package/node_modules/@context-vault/core/dist/ingest-url.js.map +1 -1
  128. package/node_modules/@context-vault/core/dist/main.d.ts +13 -13
  129. package/node_modules/@context-vault/core/dist/main.d.ts.map +1 -1
  130. package/node_modules/@context-vault/core/dist/main.js +12 -12
  131. package/node_modules/@context-vault/core/dist/main.js.map +1 -1
  132. package/node_modules/@context-vault/core/dist/search.d.ts +1 -1
  133. package/node_modules/@context-vault/core/dist/search.d.ts.map +1 -1
  134. package/node_modules/@context-vault/core/dist/search.js +20 -22
  135. package/node_modules/@context-vault/core/dist/search.js.map +1 -1
  136. package/node_modules/@context-vault/core/dist/types.d.ts +1 -1
  137. package/node_modules/@context-vault/core/package.json +1 -1
  138. package/node_modules/@context-vault/core/src/capture.ts +44 -81
  139. package/node_modules/@context-vault/core/src/categories.ts +30 -30
  140. package/node_modules/@context-vault/core/src/config.ts +45 -60
  141. package/node_modules/@context-vault/core/src/constants.ts +8 -10
  142. package/node_modules/@context-vault/core/src/db.ts +37 -56
  143. package/node_modules/@context-vault/core/src/embed.ts +15 -26
  144. package/node_modules/@context-vault/core/src/files.ts +13 -16
  145. package/node_modules/@context-vault/core/src/formatters.ts +5 -5
  146. package/node_modules/@context-vault/core/src/frontmatter.ts +26 -30
  147. package/node_modules/@context-vault/core/src/index.ts +94 -100
  148. package/node_modules/@context-vault/core/src/ingest-url.ts +56 -93
  149. package/node_modules/@context-vault/core/src/main.ts +13 -18
  150. package/node_modules/@context-vault/core/src/search.ts +34 -56
  151. package/node_modules/@context-vault/core/src/types.ts +1 -1
  152. package/package.json +10 -4
  153. package/scripts/postinstall.js +18 -25
  154. package/scripts/prepack.js +13 -19
  155. package/src/archive.ts +244 -0
  156. package/src/consolidation.ts +78 -0
  157. package/src/{error-log.js → error-log.ts} +10 -10
  158. package/src/helpers.ts +61 -0
  159. package/src/{linking.js → linking.ts} +22 -20
  160. package/src/migrate-dirs.ts +152 -0
  161. package/src/register-tools.ts +183 -0
  162. package/src/{server.js → server.ts} +89 -109
  163. package/src/{status.js → status.ts} +94 -108
  164. package/src/telemetry.ts +80 -0
  165. package/src/{temporal.js → temporal.ts} +29 -33
  166. package/src/tools/clear-context.ts +41 -0
  167. package/src/tools/{context-status.js → context-status.ts} +43 -66
  168. package/src/tools/{create-snapshot.js → create-snapshot.ts} +54 -65
  169. package/src/tools/delete-context.ts +53 -0
  170. package/src/tools/{get-context.js → get-context.ts} +142 -205
  171. package/src/tools/ingest-project.ts +260 -0
  172. package/src/tools/ingest-url.ts +74 -0
  173. package/src/tools/{list-buckets.js → list-buckets.ts} +27 -37
  174. package/src/tools/{list-context.js → list-context.ts} +46 -71
  175. package/src/tools/{save-context.js → save-context.ts} +148 -204
  176. package/src/tools/{session-start.js → session-start.ts} +72 -79
  177. package/src/types.ts +29 -0
  178. package/src/helpers.js +0 -57
  179. package/src/register-tools.js +0 -175
  180. package/src/telemetry.js +0 -80
  181. package/src/tools/clear-context.js +0 -47
  182. package/src/tools/delete-context.js +0 -54
  183. package/src/tools/ingest-project.js +0 -272
  184. package/src/tools/ingest-url.js +0 -87
@@ -1,45 +1,44 @@
1
- import { z } from "zod";
2
- import { execSync } from "node:child_process";
3
- import { ok, err, ensureVaultExists } from "../helpers.js";
1
+ import { z } from 'zod';
2
+ import { execSync } from 'node:child_process';
3
+ import { ok, err, ensureVaultExists } from '../helpers.js';
4
+ import type { LocalCtx, SharedCtx, ToolResult } from '../types.js';
4
5
 
5
6
  const DEFAULT_MAX_TOKENS = 4000;
6
7
  const RECENT_DAYS = 7;
7
8
  const MAX_BODY_PER_ENTRY = 400;
8
- const PRIORITY_KINDS = ["decision", "insight", "pattern"];
9
- const SESSION_SUMMARY_KIND = "session";
9
+ const PRIORITY_KINDS = ['decision', 'insight', 'pattern'];
10
+ const SESSION_SUMMARY_KIND = 'session';
10
11
 
11
- export const name = "session_start";
12
+ export const name = 'session_start';
12
13
 
13
14
  export const description =
14
- "Auto-assemble a context brief for the current project on session start. Pulls recent entries, last session summary, and active decisions/blockers into a token-budgeted capsule formatted for agent consumption.";
15
+ 'Auto-assemble a context brief for the current project on session start. Pulls recent entries, last session summary, and active decisions/blockers into a token-budgeted capsule formatted for agent consumption.';
15
16
 
16
17
  export const inputSchema = {
17
18
  project: z
18
19
  .string()
19
20
  .optional()
20
21
  .describe(
21
- "Project name or tag to scope the brief. Auto-detected from cwd/git remote if not provided.",
22
+ 'Project name or tag to scope the brief. Auto-detected from cwd/git remote if not provided.'
22
23
  ),
23
24
  max_tokens: z
24
25
  .number()
25
26
  .optional()
26
- .describe(
27
- "Token budget for the capsule (rough estimate: 1 token ~ 4 chars). Default: 4000.",
28
- ),
27
+ .describe('Token budget for the capsule (rough estimate: 1 token ~ 4 chars). Default: 4000.'),
29
28
  buckets: z
30
29
  .array(z.string())
31
30
  .optional()
32
31
  .describe(
33
- "Bucket names to scope the session brief. Each name expands to a 'bucket:<name>' tag filter. When provided, the brief only includes entries from these buckets.",
32
+ "Bucket names to scope the session brief. Each name expands to a 'bucket:<name>' tag filter. When provided, the brief only includes entries from these buckets."
34
33
  ),
35
34
  };
36
35
 
37
36
  function detectProject() {
38
37
  try {
39
- const remote = execSync("git remote get-url origin 2>/dev/null", {
40
- encoding: "utf-8",
38
+ const remote = execSync('git remote get-url origin 2>/dev/null', {
39
+ encoding: 'utf-8',
41
40
  timeout: 3000,
42
- stdio: ["pipe", "pipe", "pipe"],
41
+ stdio: ['pipe', 'pipe', 'pipe'],
43
42
  }).trim();
44
43
  if (remote) {
45
44
  const match = remote.match(/\/([^/]+?)(?:\.git)?$/);
@@ -56,32 +55,32 @@ function detectProject() {
56
55
  return null;
57
56
  }
58
57
 
59
- function truncateBody(body, maxLen = MAX_BODY_PER_ENTRY) {
60
- if (!body) return "(no body)";
58
+ function truncateBody(body: string | null | undefined, maxLen = MAX_BODY_PER_ENTRY): string {
59
+ if (!body) return '(no body)';
61
60
  if (body.length <= maxLen) return body;
62
- return body.slice(0, maxLen) + "...";
61
+ return body.slice(0, maxLen) + '...';
63
62
  }
64
63
 
65
- function estimateTokens(text) {
66
- return Math.ceil((text || "").length / 4);
64
+ function estimateTokens(text: string | null | undefined): number {
65
+ return Math.ceil((text || '').length / 4);
67
66
  }
68
67
 
69
- function formatEntry(entry) {
68
+ function formatEntry(entry: any): string {
70
69
  const tags = entry.tags ? JSON.parse(entry.tags) : [];
71
- const tagStr = tags.length ? tags.join(", ") : "none";
72
- const date = entry.updated_at || entry.created_at || "unknown";
70
+ const tagStr = tags.length ? tags.join(', ') : 'none';
71
+ const date = entry.updated_at || entry.created_at || 'unknown';
73
72
  return [
74
- `- **${entry.title || "(untitled)"}** [${entry.kind}]`,
73
+ `- **${entry.title || '(untitled)'}** [${entry.kind}]`,
75
74
  ` tags: ${tagStr} | ${date} | id: \`${entry.id}\``,
76
- ` ${truncateBody(entry.body).replace(/\n+/g, " ").trim()}`,
77
- ].join("\n");
75
+ ` ${truncateBody(entry.body).replace(/\n+/g, ' ').trim()}`,
76
+ ].join('\n');
78
77
  }
79
78
 
80
79
  export async function handler(
81
- { project, max_tokens, buckets },
82
- ctx,
83
- { ensureIndexed },
84
- ) {
80
+ { project, max_tokens, buckets }: Record<string, any>,
81
+ ctx: LocalCtx,
82
+ { ensureIndexed }: SharedCtx
83
+ ): Promise<ToolResult> {
85
84
  const { config } = ctx;
86
85
 
87
86
  const vaultErr = ensureVaultExists(config);
@@ -92,36 +91,26 @@ export async function handler(
92
91
  const effectiveProject = project?.trim() || detectProject();
93
92
  const tokenBudget = max_tokens || DEFAULT_MAX_TOKENS;
94
93
 
95
- const bucketTags = buckets?.length ? buckets.map((b) => `bucket:${b}`) : [];
96
- const effectiveTags = bucketTags.length
97
- ? bucketTags
98
- : effectiveProject
99
- ? [effectiveProject]
100
- : [];
94
+ const bucketTags = buckets?.length ? buckets.map((b: string) => `bucket:${b}`) : [];
95
+ const effectiveTags = bucketTags.length ? bucketTags : effectiveProject ? [effectiveProject] : [];
101
96
 
102
97
  const sinceDate = new Date(Date.now() - RECENT_DAYS * 86400000).toISOString();
103
98
 
104
99
  const sections = [];
105
100
  let tokensUsed = 0;
106
101
 
102
+ sections.push(`# Session Brief${effectiveProject ? ` — ${effectiveProject}` : ''}`);
103
+ const bucketsLabel = buckets?.length ? ` | buckets: ${buckets.join(', ')}` : '';
107
104
  sections.push(
108
- `# Session Brief${effectiveProject ? ` ${effectiveProject}` : ""}`,
109
- );
110
- const bucketsLabel = buckets?.length
111
- ? ` | buckets: ${buckets.join(", ")}`
112
- : "";
113
- sections.push(
114
- `_Generated ${new Date().toISOString().slice(0, 10)} | budget: ${tokenBudget} tokens${bucketsLabel}_\n`,
105
+ `_Generated ${new Date().toISOString().slice(0, 10)} | budget: ${tokenBudget} tokens${bucketsLabel}_\n`
115
106
  );
116
- tokensUsed += estimateTokens(sections.join("\n"));
107
+ tokensUsed += estimateTokens(sections.join('\n'));
117
108
 
118
109
  const lastSession = queryLastSession(ctx, effectiveTags);
119
110
  if (lastSession) {
120
- const sessionBlock = [
121
- "## Last Session Summary",
122
- truncateBody(lastSession.body, 600),
123
- "",
124
- ].join("\n");
111
+ const sessionBlock = ['## Last Session Summary', truncateBody(lastSession.body, 600), ''].join(
112
+ '\n'
113
+ );
125
114
  const sessionTokens = estimateTokens(sessionBlock);
126
115
  if (tokensUsed + sessionTokens <= tokenBudget) {
127
116
  sections.push(sessionBlock);
@@ -134,10 +123,10 @@ export async function handler(
134
123
  PRIORITY_KINDS,
135
124
  sinceDate,
136
125
 
137
- effectiveTags,
126
+ effectiveTags
138
127
  );
139
128
  if (decisions.length > 0) {
140
- const header = "## Active Decisions, Insights & Patterns\n";
129
+ const header = '## Active Decisions, Insights & Patterns\n';
141
130
  const headerTokens = estimateTokens(header);
142
131
  if (tokensUsed + headerTokens <= tokenBudget) {
143
132
  const entryLines = [];
@@ -150,15 +139,15 @@ export async function handler(
150
139
  tokensUsed += lineTokens;
151
140
  }
152
141
  if (entryLines.length > 0) {
153
- sections.push(header + entryLines.join("\n") + "\n");
142
+ sections.push(header + entryLines.join('\n') + '\n');
154
143
  }
155
144
  }
156
145
  }
157
146
 
158
147
  const recent = queryRecent(ctx, sinceDate, effectiveTags);
159
- const seenIds = new Set(decisions.map((d) => d.id));
148
+ const seenIds = new Set(decisions.map((d: any) => d.id));
160
149
  if (lastSession) seenIds.add(lastSession.id);
161
- const deduped = recent.filter((r) => !seenIds.has(r.id));
150
+ const deduped = recent.filter((r: any) => !seenIds.has(r.id));
162
151
 
163
152
  if (deduped.length > 0) {
164
153
  const header = `## Recent Entries (last ${RECENT_DAYS} days)\n`;
@@ -174,7 +163,7 @@ export async function handler(
174
163
  tokensUsed += lineTokens;
175
164
  }
176
165
  if (entryLines.length > 0) {
177
- sections.push(header + entryLines.join("\n") + "\n");
166
+ sections.push(header + entryLines.join('\n') + '\n');
178
167
  }
179
168
  }
180
169
  }
@@ -182,17 +171,16 @@ export async function handler(
182
171
  const totalEntries =
183
172
  (lastSession ? 1 : 0) +
184
173
  decisions.length +
185
- deduped.filter((d) => {
186
- const line = formatEntry(d);
174
+ deduped.filter((_d: any) => {
187
175
  return true;
188
176
  }).length;
189
177
 
190
- sections.push("---");
178
+ sections.push('---');
191
179
  sections.push(
192
- `_${tokensUsed} / ${tokenBudget} tokens used | project: ${effectiveProject || "unscoped"}_`,
180
+ `_${tokensUsed} / ${tokenBudget} tokens used | project: ${effectiveProject || 'unscoped'}_`
193
181
  );
194
182
 
195
- const result = ok(sections.join("\n"));
183
+ const result: ToolResult = ok(sections.join('\n'));
196
184
  result._meta = {
197
185
  project: effectiveProject || null,
198
186
  buckets: buckets || null,
@@ -207,76 +195,81 @@ export async function handler(
207
195
  return result;
208
196
  }
209
197
 
210
- function queryLastSession(ctx, effectiveTags) {
198
+ function queryLastSession(ctx: LocalCtx, effectiveTags: string[]): any {
211
199
  const clauses = [`kind = '${SESSION_SUMMARY_KIND}'`];
212
- const params = [];
200
+ const params: any[] = [];
213
201
 
214
202
  if (false) {
215
203
  }
216
204
  clauses.push("(expires_at IS NULL OR expires_at > datetime('now'))");
217
- clauses.push("superseded_by IS NULL");
205
+ clauses.push('superseded_by IS NULL');
218
206
 
219
- const where = `WHERE ${clauses.join(" AND ")}`;
207
+ const where = `WHERE ${clauses.join(' AND ')}`;
220
208
  const rows = ctx.db
221
209
  .prepare(`SELECT * FROM vault ${where} ORDER BY created_at DESC LIMIT 5`)
222
210
  .all(...params);
223
211
 
224
212
  if (effectiveTags.length) {
225
- const match = rows.find((r) => {
213
+ const match = rows.find((r: any) => {
226
214
  const tags = r.tags ? JSON.parse(r.tags) : [];
227
- return effectiveTags.some((t) => tags.includes(t));
215
+ return effectiveTags.some((t: string) => tags.includes(t));
228
216
  });
229
217
  if (match) return match;
230
218
  }
231
219
  return rows[0] || null;
232
220
  }
233
221
 
234
- function queryByKinds(ctx, kinds, since, effectiveTags) {
235
- const kindPlaceholders = kinds.map(() => "?").join(",");
222
+ function queryByKinds(
223
+ ctx: LocalCtx,
224
+ kinds: string[],
225
+ since: string,
226
+ effectiveTags: string[]
227
+ ): any[] {
228
+ const kindPlaceholders = kinds.map(() => '?').join(',');
236
229
  const clauses = [`kind IN (${kindPlaceholders})`];
237
230
  const params = [...kinds];
238
231
 
239
- clauses.push("created_at >= ?");
232
+ clauses.push('created_at >= ?');
240
233
  params.push(since);
241
234
 
242
235
  if (false) {
243
236
  }
244
237
  clauses.push("(expires_at IS NULL OR expires_at > datetime('now'))");
245
- clauses.push("superseded_by IS NULL");
238
+ clauses.push('superseded_by IS NULL');
246
239
 
247
- const where = `WHERE ${clauses.join(" AND ")}`;
240
+ const where = `WHERE ${clauses.join(' AND ')}`;
248
241
  const rows = ctx.db
249
242
  .prepare(`SELECT * FROM vault ${where} ORDER BY created_at DESC LIMIT 50`)
250
243
  .all(...params);
251
244
 
252
245
  if (effectiveTags.length) {
253
- const tagged = rows.filter((r) => {
246
+ const tagged = rows.filter((r: any) => {
254
247
  const tags = r.tags ? JSON.parse(r.tags) : [];
255
- return effectiveTags.some((t) => tags.includes(t));
248
+ return effectiveTags.some((t: string) => tags.includes(t));
256
249
  });
257
250
  if (tagged.length > 0) return tagged;
258
251
  }
259
252
  return rows;
260
253
  }
261
254
 
262
- function queryRecent(ctx, since, effectiveTags) {
263
- const clauses = ["created_at >= ?"];
255
+ function queryRecent(ctx: LocalCtx, since: string, effectiveTags: string[]): any[] {
256
+ const clauses = ['created_at >= ?'];
264
257
  const params = [since];
265
258
 
266
259
  if (false) {
267
260
  }
268
261
  clauses.push("(expires_at IS NULL OR expires_at > datetime('now'))");
269
- clauses.push("superseded_by IS NULL");
262
+ clauses.push('superseded_by IS NULL');
270
263
 
271
- const where = `WHERE ${clauses.join(" AND ")}`;
264
+ const where = `WHERE ${clauses.join(' AND ')}`;
272
265
  const rows = ctx.db
273
266
  .prepare(`SELECT * FROM vault ${where} ORDER BY created_at DESC LIMIT 50`)
274
267
  .all(...params);
275
268
 
276
269
  if (effectiveTags.length) {
277
- const tagged = rows.filter((r) => {
270
+ const tagged = rows.filter((r: any) => {
278
271
  const tags = r.tags ? JSON.parse(r.tags) : [];
279
- return effectiveTags.some((t) => tags.includes(t));
272
+ return effectiveTags.some((t: string) => tags.includes(t));
280
273
  });
281
274
  if (tagged.length > 0) return tagged;
282
275
  }
package/src/types.ts ADDED
@@ -0,0 +1,29 @@
1
+ import type { BaseCtx, VaultConfig } from '@context-vault/core/types';
2
+
3
+ export interface LocalCtx extends BaseCtx {
4
+ activeOps: { count: number };
5
+ toolStats: {
6
+ ok: number;
7
+ errors: number;
8
+ lastError: { tool: string; code: string; timestamp: number } | null;
9
+ };
10
+ }
11
+
12
+ export interface SharedCtx {
13
+ ensureIndexed: (opts?: { blocking?: boolean }) => Promise<void>;
14
+ reindexFailed?: boolean;
15
+ }
16
+
17
+ export interface ToolResult {
18
+ content: Array<{ type: string; text: string }>;
19
+ isError?: boolean;
20
+ code?: string;
21
+ _meta?: Record<string, unknown>;
22
+ }
23
+
24
+ export interface ToolModule {
25
+ name: string;
26
+ description: string;
27
+ inputSchema: Record<string, unknown>;
28
+ handler: (args: Record<string, unknown>, ctx: LocalCtx, shared: SharedCtx) => Promise<ToolResult>;
29
+ }
package/src/helpers.js DELETED
@@ -1,57 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import { join, dirname } from "node:path";
3
- import { fileURLToPath } from "node:url";
4
-
5
- const __dirname = dirname(fileURLToPath(import.meta.url));
6
- const pkg = JSON.parse(
7
- readFileSync(join(__dirname, "..", "package.json"), "utf-8"),
8
- );
9
-
10
- export function ok(text) {
11
- return { content: [{ type: "text", text }] };
12
- }
13
-
14
- export function err(text, code = "UNKNOWN", meta = {}) {
15
- return {
16
- content: [{ type: "text", text }],
17
- isError: true,
18
- code,
19
- _meta: {
20
- cv_version: pkg.version,
21
- node_version: process.version,
22
- platform: process.platform,
23
- arch: process.arch,
24
- ...meta,
25
- },
26
- };
27
- }
28
-
29
- export function errWithHint(text, code, hint) {
30
- const prompt = hint
31
- ? `\n\n**Debug with AI:** Paste this into Claude Code or your AI assistant:\n> "${hint}"`
32
- : "";
33
- return err(text + prompt, code);
34
- }
35
-
36
- export function ensureVaultExists(config) {
37
- if (!config.vaultDirExists) {
38
- return errWithHint(
39
- `Vault directory not found: ${config.vaultDir}. Run context-status for diagnostics.`,
40
- "VAULT_NOT_FOUND",
41
- "My context-vault can't find the vault directory. Run `context-vault doctor` and help me fix it.",
42
- );
43
- }
44
- return null;
45
- }
46
-
47
- export function ensureValidKind(kind) {
48
- if (!/^[a-z][a-z0-9_-]*$/.test(kind)) {
49
- return err(
50
- "Required: kind (lowercase alphanumeric, e.g. 'insight', 'reference')",
51
- "INVALID_KIND",
52
- );
53
- }
54
- return null;
55
- }
56
-
57
- export { pkg };
@@ -1,175 +0,0 @@
1
- import { reindex } from "@context-vault/core/index";
2
- import { captureAndIndex } from "@context-vault/core/capture";
3
- import { err } from "./helpers.js";
4
- import { sendTelemetryEvent } from "./telemetry.js";
5
- import { readFileSync } from "node:fs";
6
- import { join, dirname } from "node:path";
7
- import { fileURLToPath } from "node:url";
8
-
9
- const __dirname = dirname(fileURLToPath(import.meta.url));
10
- const pkg = JSON.parse(
11
- readFileSync(join(__dirname, "..", "package.json"), "utf-8"),
12
- );
13
-
14
- import * as getContext from "./tools/get-context.js";
15
- import * as saveContext from "./tools/save-context.js";
16
- import * as listContext from "./tools/list-context.js";
17
- import * as deleteContext from "./tools/delete-context.js";
18
- import * as ingestUrl from "./tools/ingest-url.js";
19
- import * as contextStatus from "./tools/context-status.js";
20
- import * as clearContext from "./tools/clear-context.js";
21
- import * as createSnapshot from "./tools/create-snapshot.js";
22
- import * as sessionStart from "./tools/session-start.js";
23
- import * as listBuckets from "./tools/list-buckets.js";
24
- import * as ingestProject from "./tools/ingest-project.js";
25
-
26
- const toolModules = [
27
- getContext,
28
- saveContext,
29
- listContext,
30
- deleteContext,
31
- ingestUrl,
32
- ingestProject,
33
- contextStatus,
34
- clearContext,
35
- createSnapshot,
36
- sessionStart,
37
- listBuckets,
38
- ];
39
-
40
- const TOOL_TIMEOUT_MS = 60_000;
41
-
42
- export function registerTools(server, ctx) {
43
- function tracked(handler, toolName) {
44
- return async (...args) => {
45
- if (ctx.activeOps) ctx.activeOps.count++;
46
- let timer;
47
- let handlerPromise;
48
- try {
49
- handlerPromise = Promise.resolve(handler(...args));
50
- const result = await Promise.race([
51
- handlerPromise,
52
- new Promise((_, reject) => {
53
- timer = setTimeout(
54
- () => reject(new Error("TOOL_TIMEOUT")),
55
- TOOL_TIMEOUT_MS,
56
- );
57
- }),
58
- ]);
59
- if (ctx.toolStats) ctx.toolStats.ok++;
60
- return result;
61
- } catch (e) {
62
- if (e.message === "TOOL_TIMEOUT") {
63
- handlerPromise?.catch(() => {});
64
- if (ctx.toolStats) {
65
- ctx.toolStats.errors++;
66
- ctx.toolStats.lastError = {
67
- tool: toolName,
68
- code: "TIMEOUT",
69
- timestamp: Date.now(),
70
- };
71
- }
72
- sendTelemetryEvent(ctx.config, {
73
- event: "tool_error",
74
- code: "TIMEOUT",
75
- tool: toolName,
76
- cv_version: pkg.version,
77
- });
78
- return err(
79
- "Tool timed out after 60s. Try a simpler query or run `context-vault reindex` first.",
80
- "TIMEOUT",
81
- );
82
- }
83
- if (ctx.toolStats) {
84
- ctx.toolStats.errors++;
85
- ctx.toolStats.lastError = {
86
- tool: toolName,
87
- code: "UNKNOWN",
88
- timestamp: Date.now(),
89
- };
90
- }
91
- sendTelemetryEvent(ctx.config, {
92
- event: "tool_error",
93
- code: "UNKNOWN",
94
- tool: toolName,
95
- cv_version: pkg.version,
96
- });
97
- try {
98
- await captureAndIndex(ctx, {
99
- kind: "feedback",
100
- title: `Unhandled error in ${toolName ?? "tool"} call`,
101
- body: `${e.message}\n\n${e.stack ?? ""}`,
102
- tags: ["bug", "auto-captured"],
103
- source: "auto-capture",
104
- meta: {
105
- tool: toolName,
106
- error_type: e.constructor?.name,
107
- cv_version: pkg.version,
108
- auto: true,
109
- },
110
- });
111
- } catch {}
112
- return err(e.message, "INTERNAL_ERROR");
113
- } finally {
114
- clearTimeout(timer);
115
- if (ctx.activeOps) ctx.activeOps.count--;
116
- }
117
- };
118
- }
119
-
120
- let reindexDone = false;
121
- let reindexPromise = null;
122
- let reindexAttempts = 0;
123
- let reindexFailed = false;
124
- const MAX_REINDEX_ATTEMPTS = 2;
125
-
126
- async function ensureIndexed() {
127
- if (reindexDone) return;
128
- if (reindexPromise) return reindexPromise;
129
- const promise = reindex(ctx, { fullSync: true })
130
- .then((stats) => {
131
- reindexDone = true;
132
- const total = stats.added + stats.updated + stats.removed;
133
- if (total > 0) {
134
- console.error(
135
- `[context-vault] Auto-reindex: +${stats.added} ~${stats.updated} -${stats.removed} (${stats.unchanged} unchanged)`,
136
- );
137
- }
138
- })
139
- .catch((e) => {
140
- reindexAttempts++;
141
- console.error(
142
- `[context-vault] Auto-reindex failed (attempt ${reindexAttempts}/${MAX_REINDEX_ATTEMPTS}): ${e.message}`,
143
- );
144
- if (reindexAttempts >= MAX_REINDEX_ATTEMPTS) {
145
- console.error(
146
- `[context-vault] Giving up on auto-reindex. Run \`context-vault reindex\` manually to diagnose.`,
147
- );
148
- reindexDone = true;
149
- reindexFailed = true;
150
- } else {
151
- reindexPromise = null;
152
- }
153
- });
154
- reindexPromise = promise;
155
- return reindexPromise;
156
- }
157
-
158
- const shared = {
159
- ensureIndexed,
160
- get reindexFailed() {
161
- return reindexFailed;
162
- },
163
- };
164
-
165
- for (const mod of toolModules) {
166
- server.tool(
167
- mod.name,
168
- mod.description,
169
- mod.inputSchema,
170
- tracked((args) => mod.handler(args, ctx, shared), mod.name),
171
- );
172
- }
173
-
174
- ensureIndexed().catch(() => {});
175
- }
package/src/telemetry.js DELETED
@@ -1,80 +0,0 @@
1
- import { existsSync, writeFileSync } from "node:fs";
2
- import { join } from "node:path";
3
- import {
4
- API_URL,
5
- MARKETING_URL,
6
- GITHUB_ISSUES_URL,
7
- } from "@context-vault/core/constants";
8
-
9
- const TELEMETRY_ENDPOINT = `${API_URL}/telemetry`;
10
- const NOTICE_MARKER = ".telemetry-notice-shown";
11
- const FEEDBACK_PROMPT_MARKER = ".feedback-prompt-shown";
12
-
13
- export function isTelemetryEnabled(config) {
14
- const envVal = process.env.CONTEXT_VAULT_TELEMETRY;
15
- if (envVal !== undefined) return envVal === "1" || envVal === "true";
16
- return config?.telemetry === true;
17
- }
18
-
19
- export function sendTelemetryEvent(config, payload) {
20
- if (!isTelemetryEnabled(config)) return;
21
-
22
- const event = {
23
- event: payload.event,
24
- code: payload.code || null,
25
- tool: payload.tool || null,
26
- cv_version: payload.cv_version,
27
- node_version: process.version,
28
- platform: process.platform,
29
- arch: process.arch,
30
- ts: new Date().toISOString(),
31
- };
32
-
33
- fetch(TELEMETRY_ENDPOINT, {
34
- method: "POST",
35
- headers: { "Content-Type": "application/json" },
36
- body: JSON.stringify(event),
37
- signal: AbortSignal.timeout(5000),
38
- }).catch(() => {});
39
- }
40
-
41
- export function maybeShowTelemetryNotice(dataDir) {
42
- try {
43
- const markerPath = join(dataDir, NOTICE_MARKER);
44
- if (existsSync(markerPath)) return;
45
- writeFileSync(markerPath, new Date().toISOString() + "\n");
46
- } catch {
47
- return;
48
- }
49
-
50
- const lines = [
51
- "[context-vault] Telemetry: disabled by default.",
52
- "[context-vault] To help improve context-vault, you can opt in to anonymous error reporting.",
53
- "[context-vault] Reports contain only: event type, error code, tool name, version, node version, platform, arch, timestamp.",
54
- "[context-vault] No vault content, file paths, or personal data is ever sent.",
55
- '[context-vault] Opt in: set "telemetry": true in ~/.context-mcp/config.json or set CONTEXT_VAULT_TELEMETRY=1.',
56
- `[context-vault] Full payload schema: ${MARKETING_URL}/telemetry`,
57
- ];
58
- for (const line of lines) {
59
- process.stderr.write(line + "\n");
60
- }
61
- }
62
-
63
- export function maybeShowFeedbackPrompt(dataDir) {
64
- try {
65
- const markerPath = join(dataDir, FEEDBACK_PROMPT_MARKER);
66
- if (existsSync(markerPath)) return;
67
- writeFileSync(markerPath, new Date().toISOString() + "\n");
68
- } catch {
69
- return;
70
- }
71
-
72
- const lines = [
73
- "[context-vault] First entry saved — nice work!",
74
- "[context-vault] Got feedback, a bug, or a feature request?",
75
- `[context-vault] Open an issue: ${GITHUB_ISSUES_URL}`,
76
- ];
77
- for (const line of lines) {
78
- process.stderr.write(line + "\n");
79
- }
80
- }