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,47 +0,0 @@
1
- import { z } from "zod";
2
- import { ok } from "../helpers.js";
3
-
4
- export const name = "clear_context";
5
-
6
- export const description =
7
- "Reset active in-memory session context without deleting vault entries. Call this when switching projects or topics mid-session. With `scope`, all subsequent get_context calls should filter to that tag/project. Vault data is never modified.";
8
-
9
- export const inputSchema = {
10
- scope: z
11
- .string()
12
- .optional()
13
- .describe(
14
- "Optional tag or project name to focus on going forward. When provided, treat subsequent get_context calls as if filtered to this tag.",
15
- ),
16
- };
17
-
18
- /**
19
- * @param {object} args
20
- * @param {import('../types.js').BaseCtx & Partial<import('../types.js').HostedCtxExtensions>} _ctx
21
- */
22
- export function handler({ scope } = {}) {
23
- const lines = [
24
- "## Context Reset",
25
- "",
26
- "Active session context has been cleared. All previous context from this session should be disregarded.",
27
- "",
28
- "Vault entries are unchanged — no data was deleted.",
29
- ];
30
-
31
- if (scope?.trim()) {
32
- const trimmed = scope.trim();
33
- lines.push(
34
- "",
35
- `### Active Scope: \`${trimmed}\``,
36
- "",
37
- `Going forward, treat \`get_context\` calls as scoped to the tag or project **"${trimmed}"** unless the user explicitly requests a different scope or passes their own tag filters.`,
38
- );
39
- } else {
40
- lines.push(
41
- "",
42
- "No scope set. Use `get_context` normally — all vault entries are accessible.",
43
- );
44
- }
45
-
46
- return ok(lines.join("\n"));
47
- }
@@ -1,54 +0,0 @@
1
- import { z } from "zod";
2
- import { unlinkSync } from "node:fs";
3
- import { ok, err } from "../helpers.js";
4
-
5
- export const name = "delete_context";
6
-
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.";
9
-
10
- export const inputSchema = {
11
- id: z.string().describe("The entry ULID to delete"),
12
- };
13
-
14
- /**
15
- * @param {object} args
16
- * @param {import('../types.js').BaseCtx & Partial<import('../types.js').HostedCtxExtensions>} ctx
17
- * @param {import('../types.js').ToolShared} shared
18
- */
19
- export async function handler({ id }, ctx, { ensureIndexed }) {
20
- if (!id?.trim())
21
- return err("Required: id (non-empty string)", "INVALID_INPUT");
22
- await ensureIndexed();
23
-
24
- const entry = ctx.stmts.getEntryById.get(id);
25
- if (!entry) return err(`Entry not found: ${id}`, "NOT_FOUND");
26
-
27
- try {
28
- // Delete DB record first — if this fails, the file stays and no orphan is created
29
- const rowidResult = ctx.stmts.getRowid.get(id);
30
- if (rowidResult?.rowid) {
31
- try {
32
- ctx.deleteVec(Number(rowidResult.rowid));
33
- } catch {}
34
- }
35
- ctx.stmts.deleteEntry.run(id);
36
-
37
- // Delete file from disk after successful DB delete
38
- let fileWarning = null;
39
- if (entry.file_path) {
40
- try {
41
- unlinkSync(entry.file_path);
42
- } catch (e) {
43
- if (e.code !== "ENOENT") {
44
- fileWarning = `file could not be removed from disk (${e.code}): ${entry.file_path}`;
45
- }
46
- }
47
- }
48
-
49
- const msg = `Deleted ${entry.kind}: ${entry.title || "(untitled)"} [${id}]`;
50
- return ok(fileWarning ? `${msg}\nWarning: ${fileWarning}` : msg);
51
- } catch (e) {
52
- return err(e.message, "DELETE_FAILED");
53
- }
54
- }
@@ -1,272 +0,0 @@
1
- import { z } from "zod";
2
- import { readFileSync, existsSync } from "node:fs";
3
- import { execSync } from "node:child_process";
4
- import { join, basename } from "node:path";
5
- import { captureAndIndex } from "@context-vault/core/capture";
6
- import { ok, err, ensureVaultExists } from "../helpers.js";
7
-
8
- export const name = "ingest_project";
9
-
10
- export const description =
11
- "Scan a local project directory and register it as a project entity in the vault. Extracts metadata from package.json, git history, and README. Also creates a bucket entity for project-scoped tagging.";
12
-
13
- export const inputSchema = {
14
- path: z.string().describe("Absolute path to the project directory to ingest"),
15
- tags: z
16
- .array(z.string())
17
- .optional()
18
- .describe("Additional tags to apply (bucket tags are auto-generated)"),
19
- pillar: z
20
- .string()
21
- .optional()
22
- .describe("Parent pillar/domain name — creates a bucket:pillar tag"),
23
- };
24
-
25
- function safeRead(filePath) {
26
- try {
27
- return readFileSync(filePath, "utf-8");
28
- } catch {
29
- return null;
30
- }
31
- }
32
-
33
- function safeExec(cmd, cwd) {
34
- try {
35
- return execSync(cmd, {
36
- cwd,
37
- encoding: "utf-8",
38
- stdio: ["pipe", "pipe", "pipe"],
39
- }).trim();
40
- } catch {
41
- return null;
42
- }
43
- }
44
-
45
- function detectTechStack(projectPath, pkgJson) {
46
- const stack = [];
47
-
48
- if (
49
- existsSync(join(projectPath, "pyproject.toml")) ||
50
- existsSync(join(projectPath, "setup.py"))
51
- ) {
52
- stack.push("python");
53
- }
54
- if (existsSync(join(projectPath, "Cargo.toml"))) {
55
- stack.push("rust");
56
- }
57
- if (existsSync(join(projectPath, "go.mod"))) {
58
- stack.push("go");
59
- }
60
- if (pkgJson) {
61
- stack.push("javascript");
62
- const allDeps = {
63
- ...(pkgJson.dependencies || {}),
64
- ...(pkgJson.devDependencies || {}),
65
- };
66
- if (allDeps.typescript || existsSync(join(projectPath, "tsconfig.json"))) {
67
- stack.push("typescript");
68
- }
69
- if (allDeps.react || allDeps["react-dom"]) stack.push("react");
70
- if (allDeps.next || allDeps["next"]) stack.push("nextjs");
71
- if (allDeps.vue) stack.push("vue");
72
- if (allDeps.svelte) stack.push("svelte");
73
- if (allDeps.express) stack.push("express");
74
- if (allDeps.fastify) stack.push("fastify");
75
- if (allDeps.hono) stack.push("hono");
76
- if (allDeps.vite) stack.push("vite");
77
- if (allDeps.electron) stack.push("electron");
78
- if (allDeps.tauri || allDeps["@tauri-apps/api"]) stack.push("tauri");
79
- }
80
-
81
- return [...new Set(stack)];
82
- }
83
-
84
- function extractReadmeDescription(projectPath) {
85
- const raw =
86
- safeRead(join(projectPath, "README.md")) ||
87
- safeRead(join(projectPath, "readme.md"));
88
- if (!raw) return null;
89
- for (const line of raw.split("\n")) {
90
- const trimmed = line.trim();
91
- if (!trimmed || trimmed.startsWith("#")) continue;
92
- return trimmed.slice(0, 200);
93
- }
94
- return null;
95
- }
96
-
97
- function buildProjectBody({
98
- projectName,
99
- description,
100
- techStack,
101
- repoUrl,
102
- lastCommit,
103
- projectPath,
104
- hasClaudeMd,
105
- }) {
106
- const lines = [];
107
- lines.push(`## ${projectName}`);
108
- if (description) lines.push("", description);
109
- lines.push("", "### Metadata");
110
- lines.push(`- **Path**: \`${projectPath}\``);
111
- if (repoUrl) lines.push(`- **Repo**: ${repoUrl}`);
112
- if (techStack.length) lines.push(`- **Stack**: ${techStack.join(", ")}`);
113
- if (lastCommit) lines.push(`- **Last commit**: ${lastCommit}`);
114
- lines.push(`- **CLAUDE.md**: ${hasClaudeMd ? "yes" : "no"}`);
115
- return lines.join("\n");
116
- }
117
-
118
- /**
119
- * @param {object} args
120
- * @param {import('../types.js').BaseCtx & Partial<import('../types.js').HostedCtxExtensions>} ctx
121
- * @param {import('../types.js').ToolShared} shared
122
- */
123
- export async function handler(
124
- { path: projectPath, tags, pillar },
125
- ctx,
126
- { ensureIndexed },
127
- ) {
128
- const { config } = ctx;
129
-
130
- const vaultErr = ensureVaultExists(config);
131
- if (vaultErr) return vaultErr;
132
-
133
- if (!projectPath?.trim()) {
134
- return err(
135
- "Required: path (absolute path to project directory)",
136
- "INVALID_INPUT",
137
- );
138
- }
139
- if (!existsSync(projectPath)) {
140
- return err(`Directory not found: ${projectPath}`, "INVALID_INPUT");
141
- }
142
-
143
- await ensureIndexed();
144
-
145
- // Read package.json if present
146
- let pkgJson = null;
147
- const pkgPath = join(projectPath, "package.json");
148
- if (existsSync(pkgPath)) {
149
- try {
150
- pkgJson = JSON.parse(readFileSync(pkgPath, "utf-8"));
151
- } catch {
152
- pkgJson = null;
153
- }
154
- }
155
-
156
- // Derive project name
157
- let projectName = basename(projectPath);
158
- if (pkgJson?.name) {
159
- projectName = pkgJson.name.replace(/^@[^/]+\//, "");
160
- }
161
-
162
- // Slug-safe identity_key
163
- const identityKey = projectName
164
- .toLowerCase()
165
- .replace(/[^a-z0-9-]/g, "-")
166
- .replace(/-+/g, "-")
167
- .replace(/^-|-$/g, "");
168
-
169
- // Description: package.json > README
170
- const description =
171
- pkgJson?.description || extractReadmeDescription(projectPath) || null;
172
-
173
- // Tech stack detection
174
- const techStack = detectTechStack(projectPath, pkgJson);
175
-
176
- // Git metadata
177
- const isGitRepo = existsSync(join(projectPath, ".git"));
178
- const repoUrl = isGitRepo
179
- ? safeExec("git remote get-url origin", projectPath)
180
- : null;
181
- const lastCommit = isGitRepo
182
- ? safeExec("git log -1 --format=%ci", projectPath)
183
- : null;
184
-
185
- // CLAUDE.md presence
186
- const hasClaudeMd = existsSync(join(projectPath, "CLAUDE.md"));
187
-
188
- // Build tags
189
- const bucketTag = `bucket:${identityKey}`;
190
- const autoTags = [bucketTag];
191
- if (pillar) autoTags.push(`bucket:${pillar}`);
192
- const allTags = [...new Set([...autoTags, ...(tags || [])])];
193
-
194
- // Build body
195
- const body = buildProjectBody({
196
- projectName,
197
- description,
198
- techStack,
199
- repoUrl,
200
- lastCommit,
201
- projectPath,
202
- hasClaudeMd,
203
- });
204
-
205
- // Build meta
206
- const meta = {
207
- path: projectPath,
208
- ...(repoUrl ? { repo_url: repoUrl } : {}),
209
- ...(techStack.length ? { tech_stack: techStack } : {}),
210
- has_claude_md: hasClaudeMd,
211
- };
212
-
213
- // Save project entity
214
- const projectEntry = await captureAndIndex(ctx, {
215
- kind: "project",
216
- title: projectName,
217
- body,
218
- tags: allTags,
219
- identity_key: identityKey,
220
- meta,
221
- });
222
-
223
- // Save bucket entity if it doesn't already exist
224
- const bucketUserClause = "";
225
- const bucketParams = false ? [bucketTag] : [bucketTag];
226
- const bucketExists = ctx.db
227
- .prepare(
228
- `SELECT 1 FROM vault WHERE kind = 'bucket' AND identity_key = ? ${bucketUserClause} LIMIT 1`,
229
- )
230
- .get(...bucketParams);
231
-
232
- let bucketEntry = null;
233
- if (!bucketExists) {
234
- bucketEntry = await captureAndIndex(ctx, {
235
- kind: "bucket",
236
- title: projectName,
237
- body: `Bucket for project: ${projectName}`,
238
- tags: allTags,
239
- identity_key: bucketTag,
240
- meta: { project_path: projectPath },
241
- });
242
- }
243
-
244
- const relPath = projectEntry.filePath
245
- ? projectEntry.filePath.replace(config.vaultDir + "/", "")
246
- : projectEntry.filePath;
247
-
248
- const parts = [
249
- `✓ Ingested project → ${relPath}`,
250
- ` id: ${projectEntry.id}`,
251
- ` title: ${projectEntry.title}`,
252
- ` tags: ${allTags.join(", ")}`,
253
- ...(techStack.length ? [` stack: ${techStack.join(", ")}`] : []),
254
- ...(repoUrl ? [` repo: ${repoUrl}`] : []),
255
- ];
256
-
257
- if (bucketEntry) {
258
- const bucketRelPath = bucketEntry.filePath
259
- ? bucketEntry.filePath.replace(config.vaultDir + "/", "")
260
- : bucketEntry.filePath;
261
- parts.push(``, `✓ Created bucket → ${bucketRelPath}`);
262
- parts.push(` id: ${bucketEntry.id}`);
263
- } else {
264
- parts.push(``, ` (bucket '${bucketTag}' already exists — skipped)`);
265
- }
266
-
267
- parts.push(
268
- "",
269
- "_Use get_context with bucket tag to retrieve project-scoped entries._",
270
- );
271
- return ok(parts.join("\n"));
272
- }
@@ -1,87 +0,0 @@
1
- import { z } from "zod";
2
- import { captureAndIndex } from "@context-vault/core/capture";
3
- import { ok, err, ensureVaultExists } from "../helpers.js";
4
- import {
5
- MAX_KIND_LENGTH,
6
- MAX_TAG_LENGTH,
7
- MAX_TAGS_COUNT,
8
- } from "@context-vault/core/constants";
9
-
10
- const MAX_URL_LENGTH = 2048;
11
-
12
- export const name = "ingest_url";
13
-
14
- export const description =
15
- "Fetch a URL, extract its readable content, and save it as a vault entry. Useful for saving articles, documentation, or web pages to your knowledge vault.";
16
-
17
- export const inputSchema = {
18
- url: z.string().describe("The URL to fetch and save"),
19
- kind: z.string().optional().describe("Entry kind (default: reference)"),
20
- tags: z.array(z.string()).optional().describe("Tags for the entry"),
21
- };
22
-
23
- /**
24
- * @param {object} args
25
- * @param {import('../types.js').BaseCtx & Partial<import('../types.js').HostedCtxExtensions>} ctx
26
- * @param {import('../types.js').ToolShared} shared
27
- */
28
- export async function handler(
29
- { url: targetUrl, kind, tags },
30
- ctx,
31
- { ensureIndexed },
32
- ) {
33
- const { config } = ctx;
34
-
35
- const vaultErr = ensureVaultExists(config);
36
- if (vaultErr) return vaultErr;
37
-
38
- if (!targetUrl?.trim())
39
- return err("Required: url (non-empty string)", "INVALID_INPUT");
40
- if (targetUrl.length > MAX_URL_LENGTH)
41
- return err(`url must be under ${MAX_URL_LENGTH} chars`, "INVALID_INPUT");
42
- if (kind !== undefined && kind !== null) {
43
- if (typeof kind !== "string" || kind.length > MAX_KIND_LENGTH) {
44
- return err(
45
- `kind must be a string, max ${MAX_KIND_LENGTH} chars`,
46
- "INVALID_INPUT",
47
- );
48
- }
49
- }
50
- if (tags !== undefined && tags !== null) {
51
- if (!Array.isArray(tags))
52
- return err("tags must be an array of strings", "INVALID_INPUT");
53
- if (tags.length > MAX_TAGS_COUNT)
54
- return err(`tags: max ${MAX_TAGS_COUNT} tags allowed`, "INVALID_INPUT");
55
- for (const tag of tags) {
56
- if (typeof tag !== "string" || tag.length > MAX_TAG_LENGTH) {
57
- return err(
58
- `each tag must be a string, max ${MAX_TAG_LENGTH} chars`,
59
- "INVALID_INPUT",
60
- );
61
- }
62
- }
63
- }
64
-
65
- await ensureIndexed();
66
-
67
- try {
68
- const { ingestUrl } = await import("../../capture/ingest-url.js");
69
- const entryData = await ingestUrl(targetUrl, { kind, tags });
70
- const entry = await captureAndIndex(ctx, { ...entryData });
71
- const relPath = entry.filePath
72
- ? entry.filePath.replace(config.vaultDir + "/", "")
73
- : entry.filePath;
74
- const parts = [
75
- `✓ Ingested URL → ${relPath}`,
76
- ` id: ${entry.id}`,
77
- ` title: ${entry.title || "(untitled)"}`,
78
- ` source: ${entry.source || targetUrl}`,
79
- ];
80
- if (entry.tags?.length) parts.push(` tags: ${entry.tags.join(", ")}`);
81
- parts.push(` body: ${entry.body?.length || 0} chars`);
82
- parts.push("", "_Use this id to update or delete later._");
83
- return ok(parts.join("\n"));
84
- } catch (e) {
85
- return err(`Failed to ingest URL: ${e.message}`, "INGEST_FAILED");
86
- }
87
- }