context-vault 3.4.3 → 3.4.5

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 (67) hide show
  1. package/assets/agent-rules.md +50 -0
  2. package/assets/setup-prompt.md +58 -0
  3. package/assets/skills/vault-setup/skill.md +81 -0
  4. package/bin/cli.js +533 -11
  5. package/dist/helpers.d.ts +2 -0
  6. package/dist/helpers.d.ts.map +1 -1
  7. package/dist/helpers.js +23 -0
  8. package/dist/helpers.js.map +1 -1
  9. package/dist/server.js +52 -12
  10. package/dist/server.js.map +1 -1
  11. package/dist/tools/context-status.js +29 -28
  12. package/dist/tools/context-status.js.map +1 -1
  13. package/dist/tools/get-context.d.ts +2 -1
  14. package/dist/tools/get-context.d.ts.map +1 -1
  15. package/dist/tools/get-context.js +44 -20
  16. package/dist/tools/get-context.js.map +1 -1
  17. package/dist/tools/list-context.d.ts.map +1 -1
  18. package/dist/tools/list-context.js +8 -8
  19. package/dist/tools/list-context.js.map +1 -1
  20. package/dist/tools/save-context.d.ts +2 -1
  21. package/dist/tools/save-context.d.ts.map +1 -1
  22. package/dist/tools/save-context.js +100 -24
  23. package/dist/tools/save-context.js.map +1 -1
  24. package/dist/tools/session-start.d.ts.map +1 -1
  25. package/dist/tools/session-start.js +39 -5
  26. package/dist/tools/session-start.js.map +1 -1
  27. package/node_modules/@context-vault/core/dist/capture.d.ts.map +1 -1
  28. package/node_modules/@context-vault/core/dist/capture.js +11 -0
  29. package/node_modules/@context-vault/core/dist/capture.js.map +1 -1
  30. package/node_modules/@context-vault/core/dist/config.d.ts +8 -0
  31. package/node_modules/@context-vault/core/dist/config.d.ts.map +1 -1
  32. package/node_modules/@context-vault/core/dist/config.js +20 -1
  33. package/node_modules/@context-vault/core/dist/config.js.map +1 -1
  34. package/node_modules/@context-vault/core/dist/context.d.ts +34 -0
  35. package/node_modules/@context-vault/core/dist/context.d.ts.map +1 -0
  36. package/node_modules/@context-vault/core/dist/context.js +55 -0
  37. package/node_modules/@context-vault/core/dist/context.js.map +1 -0
  38. package/node_modules/@context-vault/core/dist/db.d.ts +3 -1
  39. package/node_modules/@context-vault/core/dist/db.d.ts.map +1 -1
  40. package/node_modules/@context-vault/core/dist/db.js +29 -2
  41. package/node_modules/@context-vault/core/dist/db.js.map +1 -1
  42. package/node_modules/@context-vault/core/dist/frontmatter.d.ts.map +1 -1
  43. package/node_modules/@context-vault/core/dist/frontmatter.js +2 -0
  44. package/node_modules/@context-vault/core/dist/frontmatter.js.map +1 -1
  45. package/node_modules/@context-vault/core/dist/search.d.ts +1 -0
  46. package/node_modules/@context-vault/core/dist/search.d.ts.map +1 -1
  47. package/node_modules/@context-vault/core/dist/search.js +57 -3
  48. package/node_modules/@context-vault/core/dist/search.js.map +1 -1
  49. package/node_modules/@context-vault/core/dist/types.d.ts +6 -0
  50. package/node_modules/@context-vault/core/dist/types.d.ts.map +1 -1
  51. package/node_modules/@context-vault/core/package.json +5 -1
  52. package/node_modules/@context-vault/core/src/capture.ts +9 -0
  53. package/node_modules/@context-vault/core/src/config.ts +22 -1
  54. package/node_modules/@context-vault/core/src/context.ts +65 -0
  55. package/node_modules/@context-vault/core/src/db.ts +29 -2
  56. package/node_modules/@context-vault/core/src/frontmatter.ts +2 -0
  57. package/node_modules/@context-vault/core/src/search.ts +54 -2
  58. package/node_modules/@context-vault/core/src/types.ts +6 -0
  59. package/package.json +2 -2
  60. package/scripts/prepack.js +17 -0
  61. package/src/helpers.ts +25 -0
  62. package/src/server.ts +57 -11
  63. package/src/tools/context-status.ts +30 -30
  64. package/src/tools/get-context.ts +48 -25
  65. package/src/tools/list-context.ts +8 -11
  66. package/src/tools/save-context.ts +101 -26
  67. package/src/tools/session-start.ts +36 -5
@@ -61,6 +61,8 @@ export interface PreparedStatements {
61
61
  deleteVecStmt: StatementSync;
62
62
  updateSupersededBy: StatementSync;
63
63
  clearSupersededByRef: StatementSync;
64
+ insertCtxVecStmt: StatementSync;
65
+ deleteCtxVecStmt: StatementSync;
64
66
  }
65
67
  export interface VaultEntry {
66
68
  id: string;
@@ -161,6 +163,8 @@ export interface BaseCtx {
161
163
  embed: (text: string) => Promise<Float32Array | null>;
162
164
  insertVec: (rowid: number, embedding: Float32Array) => void;
163
165
  deleteVec: (rowid: number) => void;
166
+ insertCtxVec: (rowid: number, embedding: Float32Array) => void;
167
+ deleteCtxVec: (rowid: number) => void;
164
168
  }
165
169
  export interface SearchOptions {
166
170
  kindFilter?: string | null;
@@ -173,5 +177,7 @@ export interface SearchOptions {
173
177
  decayDays?: number;
174
178
  includeSuperseeded?: boolean;
175
179
  includeEphemeral?: boolean;
180
+ /** Pre-computed context embedding for contextual reinstatement boosting. */
181
+ contextEmbedding?: Float32Array | null;
176
182
  }
177
183
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE/D,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,MAAM,EAAE,YAAY,CAAC;IACrB,aAAa,EAAE,mBAAmB,CAAC;IACnC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC1D;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,YAAY,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,cAAc,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,gBAAgB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACpC;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,aAAa,CAAC;IAC3B,WAAW,EAAE,aAAa,CAAC;IAC3B,WAAW,EAAE,aAAa,CAAC;IAC3B,QAAQ,EAAE,aAAa,CAAC;IACxB,cAAc,EAAE,aAAa,CAAC;IAC9B,YAAY,EAAE,aAAa,CAAC;IAC5B,gBAAgB,EAAE,aAAa,CAAC;IAChC,mBAAmB,EAAE,aAAa,CAAC;IACnC,iBAAiB,EAAE,aAAa,CAAC;IACjC,eAAe,EAAE,aAAa,CAAC;IAC/B,aAAa,EAAE,aAAa,CAAC;IAC7B,aAAa,EAAE,aAAa,CAAC;IAC7B,kBAAkB,EAAE,aAAa,CAAC;IAClC,oBAAoB,EAAE,aAAa,CAAC;IACpC,gBAAgB,EAAE,aAAa,CAAC;IAChC,gBAAgB,EAAE,aAAa,CAAC;CACjC;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAa,SAAQ,UAAU;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACtC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC7B,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI,CAAC;IAC5D,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC1C,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI,CAAC;IAC3D,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC1C,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI,CAAC;IAC3D,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,YAAY,CAAC;IACjB,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,kBAAkB,CAAC;IAC1B,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;IACtD,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,KAAK,IAAI,CAAC;IAC5D,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,YAAY,KAAK,IAAI,CAAC;IAC/D,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;CACxC"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@context-vault/core",
3
- "version": "3.4.3",
3
+ "version": "3.4.5",
4
4
  "type": "module",
5
5
  "description": "Pure local engine: capture, index, search, and utilities for context-vault",
6
6
  "main": "dist/main.js",
@@ -61,6 +61,10 @@
61
61
  "./types": {
62
62
  "types": "./dist/types.d.ts",
63
63
  "import": "./dist/types.js"
64
+ },
65
+ "./context": {
66
+ "types": "./dist/context.d.ts",
67
+ "import": "./dist/context.js"
64
68
  }
65
69
  },
66
70
  "files": [
@@ -36,6 +36,7 @@ function writeEntryFile(
36
36
  expires_at?: string | null;
37
37
  supersedes?: string[] | null;
38
38
  related_to?: string[] | null;
39
+ tier?: string | null;
39
40
  }
40
41
  ): string {
41
42
  const resolvedFolder = params.folder || (params.meta?.folder as string) || '';
@@ -50,6 +51,10 @@ function writeEntryFile(
50
51
  const created = params.createdAt || new Date().toISOString();
51
52
  const fmFields: Record<string, unknown> = { id: params.id };
52
53
 
54
+ if (params.title) fmFields.title = params.title;
55
+ fmFields.kind = kind;
56
+ if (params.tier) fmFields.tier = params.tier;
57
+
53
58
  if (params.meta) {
54
59
  for (const [k, v] of Object.entries(params.meta)) {
55
60
  if (k === 'folder') continue;
@@ -150,6 +155,7 @@ export function writeEntry(ctx: BaseCtx, data: CaptureInput): CaptureResult {
150
155
  expires_at: data.expires_at,
151
156
  supersedes: data.supersedes,
152
157
  related_to: data.related_to,
158
+ tier: data.tier,
153
159
  });
154
160
 
155
161
  return {
@@ -225,6 +231,9 @@ export function updateEntryFile(
225
231
 
226
232
  const now = new Date().toISOString();
227
233
  const fmFields: Record<string, unknown> = { id: existing.id };
234
+ if (title) fmFields.title = title;
235
+ fmFields.kind = existing.kind;
236
+ if (existing.tier) fmFields.tier = existing.tier;
228
237
  for (const [k, v] of Object.entries(mergedMeta)) {
229
238
  if (k === 'folder') continue;
230
239
  if (v !== null && v !== undefined) fmFields[k] = v;
@@ -1,9 +1,30 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join, resolve } from 'node:path';
3
- import { homedir } from 'node:os';
3
+ import { homedir, tmpdir } from 'node:os';
4
4
  import { DEFAULT_GROWTH_THRESHOLDS, DEFAULT_LIFECYCLE } from './constants.js';
5
5
  import type { VaultConfig } from './types.js';
6
6
 
7
+ /**
8
+ * Guard against writes to the real config file during test runs.
9
+ * Set CONTEXT_VAULT_TEST=1 in test helpers to activate.
10
+ *
11
+ * Allows writes if the target path is under a temp directory (tests with
12
+ * HOME overridden to a temp dir). Blocks writes to non-temp paths.
13
+ */
14
+ export function assertNotTestMode(targetPath: string): void {
15
+ if (process.env.CONTEXT_VAULT_TEST !== '1') return;
16
+ const resolved = resolve(targetPath);
17
+ const tmp = tmpdir();
18
+ // Allow writes to temp directories (tests with HOME isolation)
19
+ if (resolved.startsWith(tmp) || resolved.startsWith('/tmp/') || resolved.startsWith('/var/folders/')) {
20
+ return;
21
+ }
22
+ throw new Error(
23
+ `[context-vault] Refusing to write to real config in test mode (${targetPath}). ` +
24
+ 'Set HOME or CONTEXT_VAULT_DATA_DIR to a temp directory.'
25
+ );
26
+ }
27
+
7
28
  export function parseArgs(argv: string[]): Record<string, string | number> {
8
29
  const args: Record<string, string | number> = {};
9
30
  for (let i = 2; i < argv.length; i++) {
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Contextual reinstatement: encoding context capture and serialization.
3
+ *
4
+ * Inspired by hippocampal contextual reinstatement in neuroscience:
5
+ * the brain stores the situation (place, task, goal) alongside each memory,
6
+ * and re-entering a similar situation boosts recall. This module provides
7
+ * the same mechanism for vault entries.
8
+ */
9
+
10
+ export interface EncodingContext {
11
+ project?: string;
12
+ arc?: string;
13
+ task?: string;
14
+ cwd?: string;
15
+ session_id?: string;
16
+ [key: string]: string | undefined;
17
+ }
18
+
19
+ /**
20
+ * Serialize an EncodingContext into a natural-language sentence suitable
21
+ * for embedding with MiniLM. The output is a simple key-value string
22
+ * that produces meaningful sentence embeddings for similarity matching.
23
+ *
24
+ * Example: "project: leadfront, arc: auth-rewrite, task: implementing JWT rotation"
25
+ */
26
+ export function serializeContext(ctx: EncodingContext): string {
27
+ const parts: string[] = [];
28
+ for (const [key, value] of Object.entries(ctx)) {
29
+ if (value != null && typeof value === 'string' && value.trim()) {
30
+ parts.push(`${key}: ${value.trim()}`);
31
+ }
32
+ }
33
+ return parts.join(', ');
34
+ }
35
+
36
+ /**
37
+ * Parse and validate a context parameter from tool input.
38
+ * Accepts either a string (used as-is for embedding) or a structured object.
39
+ * Returns null if the input is empty or invalid.
40
+ */
41
+ export function parseContextParam(input: unknown): { text: string; structured: EncodingContext | null } | null {
42
+ if (input == null) return null;
43
+
44
+ if (typeof input === 'string') {
45
+ const trimmed = input.trim();
46
+ if (!trimmed) return null;
47
+ return { text: trimmed, structured: null };
48
+ }
49
+
50
+ if (typeof input === 'object' && !Array.isArray(input)) {
51
+ const obj = input as Record<string, unknown>;
52
+ const ctx: EncodingContext = {};
53
+ let hasValue = false;
54
+ for (const [key, value] of Object.entries(obj)) {
55
+ if (typeof value === 'string' && value.trim()) {
56
+ ctx[key] = value.trim();
57
+ hasValue = true;
58
+ }
59
+ }
60
+ if (!hasValue) return null;
61
+ return { text: serializeContext(ctx), structured: ctx };
62
+ }
63
+
64
+ return null;
65
+ }
@@ -175,9 +175,11 @@ export const SCHEMA_DDL = `
175
175
  END;
176
176
 
177
177
  CREATE VIRTUAL TABLE IF NOT EXISTS vault_vec USING vec0(embedding float[384]);
178
+
179
+ CREATE VIRTUAL TABLE IF NOT EXISTS vault_ctx_vec USING vec0(embedding float[384]);
178
180
  `;
179
181
 
180
- const CURRENT_VERSION = 15;
182
+ const CURRENT_VERSION = 16;
181
183
 
182
184
  export async function initDatabase(dbPath: string): Promise<DatabaseSync> {
183
185
  const sqliteVec = await loadSqliteVec();
@@ -202,6 +204,17 @@ export async function initDatabase(dbPath: string): Promise<DatabaseSync> {
202
204
  const version = (db.prepare('PRAGMA user_version').get() as { user_version: number })
203
205
  .user_version;
204
206
 
207
+ // v15 -> v16: add vault_ctx_vec table for contextual reinstatement
208
+ if (version === 15) {
209
+ try {
210
+ db.exec('CREATE VIRTUAL TABLE IF NOT EXISTS vault_ctx_vec USING vec0(embedding float[384])');
211
+ db.exec(`PRAGMA user_version = ${CURRENT_VERSION}`);
212
+ } catch (e) {
213
+ console.error(`[context-vault] v15->v16 migration failed: ${(e as Error).message}`);
214
+ }
215
+ return db;
216
+ }
217
+
205
218
  if (version > 0 && version < 15) {
206
219
  console.error(`[context-vault] Schema v${version} is outdated. Rebuilding database...`);
207
220
 
@@ -243,7 +256,7 @@ export async function initDatabase(dbPath: string): Promise<DatabaseSync> {
243
256
  return freshDb;
244
257
  }
245
258
 
246
- if (version < 15) {
259
+ if (version < 16) {
247
260
  db.exec(SCHEMA_DDL);
248
261
  db.exec(`PRAGMA user_version = ${CURRENT_VERSION}`);
249
262
  }
@@ -279,6 +292,8 @@ export function prepareStatements(db: DatabaseSync): PreparedStatements {
279
292
  clearSupersededByRef: db.prepare(
280
293
  `UPDATE vault SET superseded_by = NULL WHERE superseded_by = ?`
281
294
  ),
295
+ insertCtxVecStmt: db.prepare(`INSERT INTO vault_ctx_vec (rowid, embedding) VALUES (?, ?)`),
296
+ deleteCtxVecStmt: db.prepare(`DELETE FROM vault_ctx_vec WHERE rowid = ?`),
282
297
  };
283
298
  } catch (e) {
284
299
  throw new Error(
@@ -301,6 +316,18 @@ export function deleteVec(stmts: PreparedStatements, rowid: number): void {
301
316
  stmts.deleteVecStmt.run(safeRowid);
302
317
  }
303
318
 
319
+ export function insertCtxVec(stmts: PreparedStatements, rowid: number, embedding: Float32Array): void {
320
+ const safeRowid = BigInt(rowid);
321
+ if (safeRowid < 1n) throw new Error(`Invalid rowid: ${rowid}`);
322
+ stmts.insertCtxVecStmt.run(safeRowid, embedding);
323
+ }
324
+
325
+ export function deleteCtxVec(stmts: PreparedStatements, rowid: number): void {
326
+ const safeRowid = BigInt(rowid);
327
+ if (safeRowid < 1n) throw new Error(`Invalid rowid: ${rowid}`);
328
+ stmts.deleteCtxVecStmt.run(safeRowid);
329
+ }
330
+
304
331
  export function testConnection(db: DatabaseSync): boolean {
305
332
  try {
306
333
  db.prepare('SELECT 1').get();
@@ -59,6 +59,8 @@ export function parseFrontmatter(text: string): {
59
59
  const RESERVED_FM_KEYS = new Set([
60
60
  'id',
61
61
  'title',
62
+ 'kind',
63
+ 'tier',
62
64
  'tags',
63
65
  'source',
64
66
  'created',
@@ -106,6 +106,7 @@ export async function hybridSearch(
106
106
  decayDays = 30,
107
107
  includeSuperseeded = false,
108
108
  includeEphemeral = false,
109
+ contextEmbedding = null,
109
110
  } = opts;
110
111
 
111
112
  const rowMap = new Map<string, VaultEntry>();
@@ -209,9 +210,60 @@ export async function hybridSearch(
209
210
  }
210
211
  }
211
212
 
213
+ // Context vector pass: KNN against vault_ctx_vec for contextual reinstatement
214
+ const ctxRankedIds: string[] = [];
215
+ if (contextEmbedding) {
216
+ try {
217
+ const ctxVecCount = (
218
+ ctx.db.prepare('SELECT COUNT(*) as c FROM vault_ctx_vec').get() as { c: number }
219
+ ).c;
220
+ if (ctxVecCount > 0) {
221
+ const ctxRows = ctx.db
222
+ .prepare(
223
+ `SELECT v.rowid, v.distance FROM vault_ctx_vec v WHERE embedding MATCH ? ORDER BY distance LIMIT 15`
224
+ )
225
+ .all(contextEmbedding, 15) as { rowid: number; distance: number }[];
226
+
227
+ if (ctxRows.length) {
228
+ const ctxRowids = ctxRows.map((cr) => cr.rowid);
229
+ const placeholders = ctxRowids.map(() => '?').join(',');
230
+ const ctxHydrated = ctx.db
231
+ .prepare(`SELECT rowid, * FROM vault WHERE rowid IN (${placeholders})`)
232
+ .all(...ctxRowids) as unknown as (VaultEntry & { rowid: number })[];
233
+
234
+ const ctxByRowid = new Map<number, VaultEntry & { rowid: number }>();
235
+ for (const row of ctxHydrated) ctxByRowid.set(row.rowid, row);
236
+
237
+ for (const cr of ctxRows) {
238
+ const row = ctxByRowid.get(cr.rowid);
239
+ if (!row) continue;
240
+ if (kindFilter && row.kind !== kindFilter) continue;
241
+ if (categoryFilter && row.category !== categoryFilter) continue;
242
+ if (excludeEvents && row.category === 'event') continue;
243
+ if (since && row.created_at < since) continue;
244
+ if (until && row.created_at > until) continue;
245
+ if (row.expires_at && new Date(row.expires_at) <= new Date()) continue;
246
+
247
+ const { rowid: _rowid, ...cleanRow } = row;
248
+ ctxRankedIds.push(cleanRow.id);
249
+ if (!rowMap.has(cleanRow.id)) rowMap.set(cleanRow.id, cleanRow);
250
+ if (!idToRowid.has(cleanRow.id)) idToRowid.set(cleanRow.id, Number(row.rowid));
251
+ }
252
+ }
253
+ }
254
+ } catch (err) {
255
+ if (!(err as Error).message?.includes('no such table')) {
256
+ console.error(`[retrieve] Context vector search error: ${(err as Error).message}`);
257
+ }
258
+ }
259
+ }
260
+
212
261
  if (rowMap.size === 0) return [];
213
262
 
214
- const rrfScores = reciprocalRankFusion([ftsRankedIds, vecRankedIds]);
263
+ // Build ranked lists for RRF: content FTS + content vec + optional context vec
264
+ const rankedLists = [ftsRankedIds, vecRankedIds];
265
+ if (ctxRankedIds.length > 0) rankedLists.push(ctxRankedIds);
266
+ const rrfScores = reciprocalRankFusion(rankedLists);
215
267
 
216
268
  for (const [id, entry] of rowMap) {
217
269
  const boost = recencyBoost(entry.created_at, entry.category, decayDays);
@@ -274,7 +326,7 @@ export async function hybridSearch(
274
326
  return finalPage;
275
327
  }
276
328
 
277
- function trackAccess(ctx: BaseCtx, entries: SearchResult[]): void {
329
+ export function trackAccess(ctx: BaseCtx, entries: SearchResult[]): void {
278
330
  if (!entries.length) return;
279
331
  try {
280
332
  const placeholders = entries.map(() => '?').join(',');
@@ -53,6 +53,8 @@ export interface PreparedStatements {
53
53
  deleteVecStmt: StatementSync;
54
54
  updateSupersededBy: StatementSync;
55
55
  clearSupersededByRef: StatementSync;
56
+ insertCtxVecStmt: StatementSync;
57
+ deleteCtxVecStmt: StatementSync;
56
58
  }
57
59
 
58
60
  export interface VaultEntry {
@@ -151,6 +153,8 @@ export interface BaseCtx {
151
153
  embed: (text: string) => Promise<Float32Array | null>;
152
154
  insertVec: (rowid: number, embedding: Float32Array) => void;
153
155
  deleteVec: (rowid: number) => void;
156
+ insertCtxVec: (rowid: number, embedding: Float32Array) => void;
157
+ deleteCtxVec: (rowid: number) => void;
154
158
  }
155
159
 
156
160
  export interface SearchOptions {
@@ -164,4 +168,6 @@ export interface SearchOptions {
164
168
  decayDays?: number;
165
169
  includeSuperseeded?: boolean;
166
170
  includeEphemeral?: boolean;
171
+ /** Pre-computed context embedding for contextual reinstatement boosting. */
172
+ contextEmbedding?: Float32Array | null;
167
173
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-vault",
3
- "version": "3.4.3",
3
+ "version": "3.4.5",
4
4
  "type": "module",
5
5
  "description": "Persistent memory for AI agents — saves and searches knowledge across sessions",
6
6
  "bin": {
@@ -63,7 +63,7 @@
63
63
  "@context-vault/core"
64
64
  ],
65
65
  "dependencies": {
66
- "@context-vault/core": "^3.4.3",
66
+ "@context-vault/core": "^3.4.5",
67
67
  "@modelcontextprotocol/sdk": "^1.26.0",
68
68
  "adm-zip": "^0.5.16",
69
69
  "sqlite-vec": "^0.1.0"
@@ -35,3 +35,20 @@ delete corePkg.dependencies;
35
35
  writeFileSync(corePkgPath, JSON.stringify(corePkg, null, 2) + '\n');
36
36
 
37
37
  console.log('[prepack] Bundled @context-vault/core into node_modules');
38
+
39
+ // Copy monorepo-level assets (agent-rules.md, setup-prompt.md) into local assets/
40
+ // so they ship with the npm package.
41
+ const MONOREPO_ASSETS = join(LOCAL_ROOT, '..', '..', 'assets');
42
+ const LOCAL_ASSETS = join(LOCAL_ROOT, 'assets');
43
+ const ASSET_FILES = ['agent-rules.md', 'setup-prompt.md'];
44
+
45
+ for (const file of ASSET_FILES) {
46
+ const src = join(MONOREPO_ASSETS, file);
47
+ const dest = join(LOCAL_ASSETS, file);
48
+ try {
49
+ cpSync(src, dest);
50
+ console.log(`[prepack] Copied ${file} to assets/`);
51
+ } catch (e) {
52
+ console.warn(`[prepack] Warning: could not copy ${file}: ${e.message}`);
53
+ }
54
+ }
package/src/helpers.ts CHANGED
@@ -58,4 +58,29 @@ export function ensureValidKind(kind: string): ToolResult | null {
58
58
  return null;
59
59
  }
60
60
 
61
+ const KIND_ICONS: Record<string, string> = {
62
+ insight: '◆',
63
+ decision: '◇',
64
+ pattern: '◈',
65
+ reference: '▸',
66
+ event: '○',
67
+ session: '◎',
68
+ brief: '▪',
69
+ bucket: '▫',
70
+ contact: '●',
71
+ project: '■',
72
+ tool: '▹',
73
+ source: '►',
74
+ feedback: '◉',
75
+ };
76
+
77
+ export function kindIcon(kind: string): string {
78
+ return KIND_ICONS[kind] || '·';
79
+ }
80
+
81
+ export function fmtDate(date: string | null | undefined): string {
82
+ if (!date) return '';
83
+ return date.split('T')[0];
84
+ }
85
+
61
86
  export { pkg };
package/src/server.ts CHANGED
@@ -7,9 +7,9 @@ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/
7
7
  import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
8
8
  import { createMcpExpressApp } from '@modelcontextprotocol/sdk/server/express.js';
9
9
  import type { IncomingMessage, ServerResponse } from 'node:http';
10
- import { existsSync, mkdirSync, writeFileSync, readFileSync, unlinkSync } from 'node:fs';
10
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, unlinkSync, readdirSync } from 'node:fs';
11
11
  import { join, dirname } from 'node:path';
12
- import { homedir } from 'node:os';
12
+ import { homedir, tmpdir } from 'node:os';
13
13
  import { fileURLToPath } from 'node:url';
14
14
 
15
15
  const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -26,6 +26,8 @@ import {
26
26
  prepareStatements,
27
27
  insertVec,
28
28
  deleteVec,
29
+ insertCtxVec,
30
+ deleteCtxVec,
29
31
  } from '@context-vault/core/db';
30
32
  import { registerTools } from './register-tools.js';
31
33
  import { pruneExpired } from '@context-vault/core/index';
@@ -310,6 +312,29 @@ async function main(): Promise<void> {
310
312
 
311
313
  config.vaultDirExists = existsSync(config.vaultDir);
312
314
 
315
+ // Validate vaultDir sanity
316
+ const osTmp = tmpdir();
317
+ const isTempPath = config.vaultDir.startsWith(osTmp) ||
318
+ config.vaultDir.startsWith('/tmp/') ||
319
+ config.vaultDir.startsWith('/var/folders/');
320
+ if (isTempPath) {
321
+ console.error(`[context-vault] WARNING: vaultDir points to a temp directory: ${config.vaultDir}`);
322
+ console.error(`[context-vault] This is likely from a test run that overwrote ~/.context-mcp/config.json`);
323
+ console.error(`[context-vault] Fix: run 'context-vault reconnect' or 'context-vault setup'`);
324
+ }
325
+
326
+ if (config.vaultDirExists) {
327
+ try {
328
+ const entries = readdirSync(config.vaultDir);
329
+ const hasMdFiles = entries.some(f => f.endsWith('.md'));
330
+ const hasMarker = entries.includes('.context-mcp');
331
+ if (!hasMdFiles && hasMarker) {
332
+ console.error(`[context-vault] WARNING: vaultDir has no markdown files but has a marker file`);
333
+ console.error(`[context-vault] The vault may be misconfigured. Run 'context-vault reconnect'`);
334
+ }
335
+ } catch {}
336
+ }
337
+
313
338
  console.error(`[context-vault] Vault: ${config.vaultDir}`);
314
339
  console.error(`[context-vault] Database: ${config.dbPath}`);
315
340
  console.error(`[context-vault] Dev dir: ${config.devDir}`);
@@ -328,6 +353,8 @@ async function main(): Promise<void> {
328
353
  embed,
329
354
  insertVec: (rowid: number, embedding: Float32Array) => insertVec(stmts, rowid, embedding),
330
355
  deleteVec: (rowid: number) => deleteVec(stmts, rowid),
356
+ insertCtxVec: (rowid: number, embedding: Float32Array) => insertCtxVec(stmts, rowid, embedding),
357
+ deleteCtxVec: (rowid: number) => deleteCtxVec(stmts, rowid),
331
358
  activeOps: { count: 0 },
332
359
  toolStats: { ok: 0, errors: 0, lastError: null },
333
360
  };
@@ -480,15 +507,34 @@ async function main(): Promise<void> {
480
507
  await transport.handleRequest(req, res, (req as any).body);
481
508
  return;
482
509
  } else if (sessionId) {
483
- // Stale session (e.g., daemon restarted). Per MCP spec, 404 tells
484
- // the client to re-initialize automatically.
485
- res.writeHead(404, { 'Content-Type': 'application/json' });
486
- res.end(JSON.stringify({
487
- jsonrpc: '2.0',
488
- error: { code: -32000, message: 'Session not found. Please reinitialize.' },
489
- id: null,
490
- }));
491
- return;
510
+ // Stale session (e.g., daemon restarted). Claude Code's MCP client
511
+ // does not auto-reinitialize on 404, so we recover transparently:
512
+ // create a new transport reusing the stale session ID, force it into
513
+ // initialized state, and handle the request as if nothing happened.
514
+ console.error(`[context-vault] Recovering stale session ${sessionId.slice(0, 8)}...`);
515
+
516
+ transport = new StreamableHTTPServerTransport({
517
+ sessionIdGenerator: () => sessionId,
518
+ onsessioninitialized: (sid: string) => {
519
+ transports[sid] = transport;
520
+ },
521
+ });
522
+ transport.onclose = () => {
523
+ if (transports[sessionId]) delete transports[sessionId];
524
+ };
525
+
526
+ const sessionServer = createServer();
527
+ await sessionServer.connect(transport);
528
+
529
+ // Force transport into initialized state, bypassing the initialize
530
+ // handshake. The inner WebStandardStreamableHTTPServerTransport holds
531
+ // the _initialized flag and sessionId.
532
+ const inner = (transport as any)._webStandardTransport;
533
+ inner._initialized = true;
534
+ inner.sessionId = sessionId;
535
+ transports[sessionId] = transport;
536
+
537
+ // Fall through to handleRequest below
492
538
  } else {
493
539
  res.writeHead(400, { 'Content-Type': 'application/json' });
494
540
  res.end(JSON.stringify({
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { gatherVaultStatus, computeGrowthWarnings } from '../status.js';
4
4
  import { errorLogPath, errorLogCount } from '../error-log.js';
5
- import { ok, err } from '../helpers.js';
5
+ import { ok, err, kindIcon } from '../helpers.js';
6
6
  import type { LocalCtx, ToolResult } from '../types.js';
7
7
 
8
8
  function relativeTime(ts: number): string {
@@ -30,53 +30,53 @@ export function handler(_args: Record<string, any>, ctx: LocalCtx): ToolResult {
30
30
  const hasIssues = status.stalePaths || (status.embeddingStatus?.missing ?? 0) > 0;
31
31
  const healthIcon = hasIssues ? '⚠' : '✓';
32
32
 
33
+ const schemaVersion = (ctx.db.prepare('PRAGMA user_version').get() as any)?.user_version ?? 'unknown';
34
+ const embedPct = status.embeddingStatus
35
+ ? (status.embeddingStatus.total > 0 ? Math.round((status.embeddingStatus.indexed / status.embeddingStatus.total) * 100) : 100)
36
+ : null;
37
+ const embedStr = status.embeddingStatus
38
+ ? `${status.embeddingStatus.indexed}/${status.embeddingStatus.total} (${embedPct}%)`
39
+ : 'n/a';
40
+ const modelStr = status.embedModelAvailable === false ? '⚠ unavailable' : status.embedModelAvailable === true ? '✓ loaded' : 'unknown';
41
+
33
42
  const lines = [
34
- `## ${healthIcon} Vault Status (connected)`,
43
+ `## ${healthIcon} Vault Dashboard`,
35
44
  ``,
36
- `Vault: ${config.vaultDir} (${config.vaultDirExists ? status.fileCount + ' files' : 'missing'})`,
37
- `Database: ${config.dbPath} (${status.dbSize})`,
38
- `Dev dir: ${config.devDir}`,
39
- `Data dir: ${config.dataDir}`,
40
- `Config: ${config.configPath}`,
41
- `Resolved via: ${status.resolvedFrom}`,
42
- `Schema: v9 (updated_at, superseded_by)`,
45
+ `| | |`,
46
+ `|---|---|`,
47
+ `| **Vault** | ${config.vaultDir} (${config.vaultDirExists ? status.fileCount + ' files' : '⚠ missing'}) |`,
48
+ `| **Database** | ${config.dbPath} (${status.dbSize}) |`,
49
+ `| **Schema** | v${schemaVersion} |`,
50
+ `| **Embeddings** | ${embedStr} |`,
51
+ `| **Model** | ${modelStr} |`,
52
+ `| **Event decay** | ${config.eventDecayDays} days |`,
43
53
  ];
44
-
45
- if (status.embeddingStatus) {
46
- const { indexed, total, missing } = status.embeddingStatus;
47
- const pct = total > 0 ? Math.round((indexed / total) * 100) : 100;
48
- lines.push(`Embeddings: ${indexed}/${total} (${pct}%)`);
49
- }
50
- if (status.embedModelAvailable === false) {
51
- lines.push(`Embed model: unavailable (semantic search disabled, FTS still works)`);
52
- } else if (status.embedModelAvailable === true) {
53
- lines.push(`Embed model: loaded`);
54
- }
55
- lines.push(`Decay: ${config.eventDecayDays} days (event recency window)`);
56
54
  if (status.expiredCount > 0) {
57
- lines.push(
58
- `Expired: ${status.expiredCount} entries pending prune (run \`context-vault prune\` to remove now)`
59
- );
55
+ lines.push(`| **Expired** | ${status.expiredCount} pending prune |`);
60
56
  }
61
57
 
58
+ // Indexed kinds as compact table
62
59
  lines.push(``, `### Indexed`);
63
-
64
60
  if (status.kindCounts.length) {
65
- for (const { kind, c } of status.kindCounts) lines.push(`- ${c} ${kind}s`);
61
+ lines.push('| Kind | Count |');
62
+ lines.push('|---|---|');
63
+ for (const { kind, c } of status.kindCounts) {
64
+ lines.push(`| ${kindIcon(kind)} \`${kind}\` | ${c} |`);
65
+ }
66
66
  } else {
67
- lines.push(`- (empty)`);
67
+ lines.push(`_(empty vault)_`);
68
68
  }
69
69
 
70
70
  if (status.categoryCounts.length) {
71
71
  lines.push(``);
72
72
  lines.push(`### Categories`);
73
- for (const { category, c } of status.categoryCounts) lines.push(`- ${category}: ${c}`);
73
+ for (const { category, c } of status.categoryCounts) lines.push(`- **${category}**: ${c}`);
74
74
  }
75
75
 
76
76
  if (status.subdirs.length) {
77
77
  lines.push(``);
78
- lines.push(`### Disk Directories`);
79
- for (const { name, count } of status.subdirs) lines.push(`- ${name}/: ${count} files`);
78
+ lines.push(`### Disk`);
79
+ for (const { name, count } of status.subdirs) lines.push(`- \`${name}/\` ${count} files`);
80
80
  }
81
81
 
82
82
  if (status.stalePaths) {