context-vault 3.1.6 → 3.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +1369 -1774
- package/node_modules/@context-vault/core/dist/capture.d.ts +1 -1
- package/node_modules/@context-vault/core/dist/capture.d.ts.map +1 -1
- package/node_modules/@context-vault/core/dist/capture.js +34 -47
- package/node_modules/@context-vault/core/dist/capture.js.map +1 -1
- package/node_modules/@context-vault/core/dist/categories.js +30 -30
- package/node_modules/@context-vault/core/dist/config.d.ts +1 -1
- package/node_modules/@context-vault/core/dist/config.d.ts.map +1 -1
- package/node_modules/@context-vault/core/dist/config.js +37 -43
- package/node_modules/@context-vault/core/dist/config.js.map +1 -1
- package/node_modules/@context-vault/core/dist/constants.d.ts.map +1 -1
- package/node_modules/@context-vault/core/dist/constants.js +4 -4
- package/node_modules/@context-vault/core/dist/constants.js.map +1 -1
- package/node_modules/@context-vault/core/dist/db.d.ts +2 -2
- package/node_modules/@context-vault/core/dist/db.d.ts.map +1 -1
- package/node_modules/@context-vault/core/dist/db.js +21 -20
- package/node_modules/@context-vault/core/dist/db.js.map +1 -1
- package/node_modules/@context-vault/core/dist/embed.d.ts.map +1 -1
- package/node_modules/@context-vault/core/dist/embed.js +11 -11
- package/node_modules/@context-vault/core/dist/embed.js.map +1 -1
- package/node_modules/@context-vault/core/dist/files.d.ts.map +1 -1
- package/node_modules/@context-vault/core/dist/files.js +12 -13
- package/node_modules/@context-vault/core/dist/files.js.map +1 -1
- package/node_modules/@context-vault/core/dist/formatters.js +5 -5
- package/node_modules/@context-vault/core/dist/frontmatter.d.ts.map +1 -1
- package/node_modules/@context-vault/core/dist/frontmatter.js +23 -23
- package/node_modules/@context-vault/core/dist/frontmatter.js.map +1 -1
- package/node_modules/@context-vault/core/dist/index.d.ts +1 -1
- package/node_modules/@context-vault/core/dist/index.d.ts.map +1 -1
- package/node_modules/@context-vault/core/dist/index.js +58 -46
- package/node_modules/@context-vault/core/dist/index.js.map +1 -1
- package/node_modules/@context-vault/core/dist/ingest-url.d.ts.map +1 -1
- package/node_modules/@context-vault/core/dist/ingest-url.js +30 -33
- package/node_modules/@context-vault/core/dist/ingest-url.js.map +1 -1
- package/node_modules/@context-vault/core/dist/main.d.ts +13 -13
- package/node_modules/@context-vault/core/dist/main.d.ts.map +1 -1
- package/node_modules/@context-vault/core/dist/main.js +12 -12
- package/node_modules/@context-vault/core/dist/main.js.map +1 -1
- package/node_modules/@context-vault/core/dist/search.d.ts +1 -1
- package/node_modules/@context-vault/core/dist/search.d.ts.map +1 -1
- package/node_modules/@context-vault/core/dist/search.js +20 -22
- package/node_modules/@context-vault/core/dist/search.js.map +1 -1
- package/node_modules/@context-vault/core/dist/types.d.ts +1 -1
- package/node_modules/@context-vault/core/package.json +1 -1
- package/node_modules/@context-vault/core/src/capture.ts +44 -81
- package/node_modules/@context-vault/core/src/categories.ts +30 -30
- package/node_modules/@context-vault/core/src/config.ts +45 -60
- package/node_modules/@context-vault/core/src/constants.ts +8 -10
- package/node_modules/@context-vault/core/src/db.ts +37 -56
- package/node_modules/@context-vault/core/src/embed.ts +15 -26
- package/node_modules/@context-vault/core/src/files.ts +13 -16
- package/node_modules/@context-vault/core/src/formatters.ts +5 -5
- package/node_modules/@context-vault/core/src/frontmatter.ts +26 -30
- package/node_modules/@context-vault/core/src/index.ts +94 -100
- package/node_modules/@context-vault/core/src/ingest-url.ts +56 -93
- package/node_modules/@context-vault/core/src/main.ts +13 -18
- package/node_modules/@context-vault/core/src/search.ts +34 -56
- package/node_modules/@context-vault/core/src/types.ts +1 -1
- package/package.json +2 -2
- package/scripts/postinstall.js +18 -25
- package/scripts/prepack.js +13 -19
- package/src/archive.js +211 -0
- package/src/error-log.js +7 -7
- package/src/helpers.js +11 -13
- package/src/linking.js +8 -11
- package/src/migrate-dirs.js +139 -0
- package/src/register-tools.js +46 -48
- package/src/server.js +73 -99
- package/src/status.js +35 -71
- package/src/telemetry.js +18 -22
- package/src/temporal.js +19 -30
- package/src/tools/clear-context.js +15 -18
- package/src/tools/context-status.js +37 -57
- package/src/tools/create-snapshot.js +45 -57
- package/src/tools/delete-context.js +11 -12
- package/src/tools/get-context.js +112 -160
- package/src/tools/ingest-project.js +66 -86
- package/src/tools/ingest-url.js +25 -41
- package/src/tools/list-buckets.js +19 -25
- package/src/tools/list-context.js +35 -58
- package/src/tools/save-context.js +126 -182
- package/src/tools/session-start.js +46 -62
|
@@ -1,30 +1,27 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import { readFileSync, existsSync } from
|
|
3
|
-
import { execSync } from
|
|
4
|
-
import { join, basename } from
|
|
5
|
-
import { captureAndIndex } from
|
|
6
|
-
import { ok, err, ensureVaultExists } from
|
|
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
7
|
|
|
8
|
-
export const name =
|
|
8
|
+
export const name = 'ingest_project';
|
|
9
9
|
|
|
10
10
|
export const description =
|
|
11
|
-
|
|
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
12
|
|
|
13
13
|
export const inputSchema = {
|
|
14
|
-
path: z.string().describe(
|
|
14
|
+
path: z.string().describe('Absolute path to the project directory to ingest'),
|
|
15
15
|
tags: z
|
|
16
16
|
.array(z.string())
|
|
17
17
|
.optional()
|
|
18
|
-
.describe(
|
|
19
|
-
pillar: z
|
|
20
|
-
.string()
|
|
21
|
-
.optional()
|
|
22
|
-
.describe("Parent pillar/domain name — creates a bucket:pillar tag"),
|
|
18
|
+
.describe('Additional tags to apply (bucket tags are auto-generated)'),
|
|
19
|
+
pillar: z.string().optional().describe('Parent pillar/domain name — creates a bucket:pillar tag'),
|
|
23
20
|
};
|
|
24
21
|
|
|
25
22
|
function safeRead(filePath) {
|
|
26
23
|
try {
|
|
27
|
-
return readFileSync(filePath,
|
|
24
|
+
return readFileSync(filePath, 'utf-8');
|
|
28
25
|
} catch {
|
|
29
26
|
return null;
|
|
30
27
|
}
|
|
@@ -34,8 +31,8 @@ function safeExec(cmd, cwd) {
|
|
|
34
31
|
try {
|
|
35
32
|
return execSync(cmd, {
|
|
36
33
|
cwd,
|
|
37
|
-
encoding:
|
|
38
|
-
stdio: [
|
|
34
|
+
encoding: 'utf-8',
|
|
35
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
39
36
|
}).trim();
|
|
40
37
|
} catch {
|
|
41
38
|
return null;
|
|
@@ -46,49 +43,47 @@ function detectTechStack(projectPath, pkgJson) {
|
|
|
46
43
|
const stack = [];
|
|
47
44
|
|
|
48
45
|
if (
|
|
49
|
-
existsSync(join(projectPath,
|
|
50
|
-
existsSync(join(projectPath,
|
|
46
|
+
existsSync(join(projectPath, 'pyproject.toml')) ||
|
|
47
|
+
existsSync(join(projectPath, 'setup.py'))
|
|
51
48
|
) {
|
|
52
|
-
stack.push(
|
|
49
|
+
stack.push('python');
|
|
53
50
|
}
|
|
54
|
-
if (existsSync(join(projectPath,
|
|
55
|
-
stack.push(
|
|
51
|
+
if (existsSync(join(projectPath, 'Cargo.toml'))) {
|
|
52
|
+
stack.push('rust');
|
|
56
53
|
}
|
|
57
|
-
if (existsSync(join(projectPath,
|
|
58
|
-
stack.push(
|
|
54
|
+
if (existsSync(join(projectPath, 'go.mod'))) {
|
|
55
|
+
stack.push('go');
|
|
59
56
|
}
|
|
60
57
|
if (pkgJson) {
|
|
61
|
-
stack.push(
|
|
58
|
+
stack.push('javascript');
|
|
62
59
|
const allDeps = {
|
|
63
60
|
...(pkgJson.dependencies || {}),
|
|
64
61
|
...(pkgJson.devDependencies || {}),
|
|
65
62
|
};
|
|
66
|
-
if (allDeps.typescript || existsSync(join(projectPath,
|
|
67
|
-
stack.push(
|
|
63
|
+
if (allDeps.typescript || existsSync(join(projectPath, 'tsconfig.json'))) {
|
|
64
|
+
stack.push('typescript');
|
|
68
65
|
}
|
|
69
|
-
if (allDeps.react || allDeps[
|
|
70
|
-
if (allDeps.next || allDeps[
|
|
71
|
-
if (allDeps.vue) stack.push(
|
|
72
|
-
if (allDeps.svelte) stack.push(
|
|
73
|
-
if (allDeps.express) stack.push(
|
|
74
|
-
if (allDeps.fastify) stack.push(
|
|
75
|
-
if (allDeps.hono) stack.push(
|
|
76
|
-
if (allDeps.vite) stack.push(
|
|
77
|
-
if (allDeps.electron) stack.push(
|
|
78
|
-
if (allDeps.tauri || allDeps[
|
|
66
|
+
if (allDeps.react || allDeps['react-dom']) stack.push('react');
|
|
67
|
+
if (allDeps.next || allDeps['next']) stack.push('nextjs');
|
|
68
|
+
if (allDeps.vue) stack.push('vue');
|
|
69
|
+
if (allDeps.svelte) stack.push('svelte');
|
|
70
|
+
if (allDeps.express) stack.push('express');
|
|
71
|
+
if (allDeps.fastify) stack.push('fastify');
|
|
72
|
+
if (allDeps.hono) stack.push('hono');
|
|
73
|
+
if (allDeps.vite) stack.push('vite');
|
|
74
|
+
if (allDeps.electron) stack.push('electron');
|
|
75
|
+
if (allDeps.tauri || allDeps['@tauri-apps/api']) stack.push('tauri');
|
|
79
76
|
}
|
|
80
77
|
|
|
81
78
|
return [...new Set(stack)];
|
|
82
79
|
}
|
|
83
80
|
|
|
84
81
|
function extractReadmeDescription(projectPath) {
|
|
85
|
-
const raw =
|
|
86
|
-
safeRead(join(projectPath, "README.md")) ||
|
|
87
|
-
safeRead(join(projectPath, "readme.md"));
|
|
82
|
+
const raw = safeRead(join(projectPath, 'README.md')) || safeRead(join(projectPath, 'readme.md'));
|
|
88
83
|
if (!raw) return null;
|
|
89
|
-
for (const line of raw.split(
|
|
84
|
+
for (const line of raw.split('\n')) {
|
|
90
85
|
const trimmed = line.trim();
|
|
91
|
-
if (!trimmed || trimmed.startsWith(
|
|
86
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
92
87
|
return trimmed.slice(0, 200);
|
|
93
88
|
}
|
|
94
89
|
return null;
|
|
@@ -105,14 +100,14 @@ function buildProjectBody({
|
|
|
105
100
|
}) {
|
|
106
101
|
const lines = [];
|
|
107
102
|
lines.push(`## ${projectName}`);
|
|
108
|
-
if (description) lines.push(
|
|
109
|
-
lines.push(
|
|
103
|
+
if (description) lines.push('', description);
|
|
104
|
+
lines.push('', '### Metadata');
|
|
110
105
|
lines.push(`- **Path**: \`${projectPath}\``);
|
|
111
106
|
if (repoUrl) lines.push(`- **Repo**: ${repoUrl}`);
|
|
112
|
-
if (techStack.length) lines.push(`- **Stack**: ${techStack.join(
|
|
107
|
+
if (techStack.length) lines.push(`- **Stack**: ${techStack.join(', ')}`);
|
|
113
108
|
if (lastCommit) lines.push(`- **Last commit**: ${lastCommit}`);
|
|
114
|
-
lines.push(`- **CLAUDE.md**: ${hasClaudeMd ?
|
|
115
|
-
return lines.join(
|
|
109
|
+
lines.push(`- **CLAUDE.md**: ${hasClaudeMd ? 'yes' : 'no'}`);
|
|
110
|
+
return lines.join('\n');
|
|
116
111
|
}
|
|
117
112
|
|
|
118
113
|
/**
|
|
@@ -120,34 +115,27 @@ function buildProjectBody({
|
|
|
120
115
|
* @param {import('../types.js').BaseCtx & Partial<import('../types.js').HostedCtxExtensions>} ctx
|
|
121
116
|
* @param {import('../types.js').ToolShared} shared
|
|
122
117
|
*/
|
|
123
|
-
export async function handler(
|
|
124
|
-
{ path: projectPath, tags, pillar },
|
|
125
|
-
ctx,
|
|
126
|
-
{ ensureIndexed },
|
|
127
|
-
) {
|
|
118
|
+
export async function handler({ path: projectPath, tags, pillar }, ctx, { ensureIndexed }) {
|
|
128
119
|
const { config } = ctx;
|
|
129
120
|
|
|
130
121
|
const vaultErr = ensureVaultExists(config);
|
|
131
122
|
if (vaultErr) return vaultErr;
|
|
132
123
|
|
|
133
124
|
if (!projectPath?.trim()) {
|
|
134
|
-
return err(
|
|
135
|
-
"Required: path (absolute path to project directory)",
|
|
136
|
-
"INVALID_INPUT",
|
|
137
|
-
);
|
|
125
|
+
return err('Required: path (absolute path to project directory)', 'INVALID_INPUT');
|
|
138
126
|
}
|
|
139
127
|
if (!existsSync(projectPath)) {
|
|
140
|
-
return err(`Directory not found: ${projectPath}`,
|
|
128
|
+
return err(`Directory not found: ${projectPath}`, 'INVALID_INPUT');
|
|
141
129
|
}
|
|
142
130
|
|
|
143
131
|
await ensureIndexed();
|
|
144
132
|
|
|
145
133
|
// Read package.json if present
|
|
146
134
|
let pkgJson = null;
|
|
147
|
-
const pkgPath = join(projectPath,
|
|
135
|
+
const pkgPath = join(projectPath, 'package.json');
|
|
148
136
|
if (existsSync(pkgPath)) {
|
|
149
137
|
try {
|
|
150
|
-
pkgJson = JSON.parse(readFileSync(pkgPath,
|
|
138
|
+
pkgJson = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
151
139
|
} catch {
|
|
152
140
|
pkgJson = null;
|
|
153
141
|
}
|
|
@@ -156,34 +144,29 @@ export async function handler(
|
|
|
156
144
|
// Derive project name
|
|
157
145
|
let projectName = basename(projectPath);
|
|
158
146
|
if (pkgJson?.name) {
|
|
159
|
-
projectName = pkgJson.name.replace(/^@[^/]+\//,
|
|
147
|
+
projectName = pkgJson.name.replace(/^@[^/]+\//, '');
|
|
160
148
|
}
|
|
161
149
|
|
|
162
150
|
// Slug-safe identity_key
|
|
163
151
|
const identityKey = projectName
|
|
164
152
|
.toLowerCase()
|
|
165
|
-
.replace(/[^a-z0-9-]/g,
|
|
166
|
-
.replace(/-+/g,
|
|
167
|
-
.replace(/^-|-$/g,
|
|
153
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
154
|
+
.replace(/-+/g, '-')
|
|
155
|
+
.replace(/^-|-$/g, '');
|
|
168
156
|
|
|
169
157
|
// Description: package.json > README
|
|
170
|
-
const description =
|
|
171
|
-
pkgJson?.description || extractReadmeDescription(projectPath) || null;
|
|
158
|
+
const description = pkgJson?.description || extractReadmeDescription(projectPath) || null;
|
|
172
159
|
|
|
173
160
|
// Tech stack detection
|
|
174
161
|
const techStack = detectTechStack(projectPath, pkgJson);
|
|
175
162
|
|
|
176
163
|
// Git metadata
|
|
177
|
-
const isGitRepo = existsSync(join(projectPath,
|
|
178
|
-
const repoUrl = isGitRepo
|
|
179
|
-
|
|
180
|
-
: null;
|
|
181
|
-
const lastCommit = isGitRepo
|
|
182
|
-
? safeExec("git log -1 --format=%ci", projectPath)
|
|
183
|
-
: null;
|
|
164
|
+
const isGitRepo = existsSync(join(projectPath, '.git'));
|
|
165
|
+
const repoUrl = isGitRepo ? safeExec('git remote get-url origin', projectPath) : null;
|
|
166
|
+
const lastCommit = isGitRepo ? safeExec('git log -1 --format=%ci', projectPath) : null;
|
|
184
167
|
|
|
185
168
|
// CLAUDE.md presence
|
|
186
|
-
const hasClaudeMd = existsSync(join(projectPath,
|
|
169
|
+
const hasClaudeMd = existsSync(join(projectPath, 'CLAUDE.md'));
|
|
187
170
|
|
|
188
171
|
// Build tags
|
|
189
172
|
const bucketTag = `bucket:${identityKey}`;
|
|
@@ -212,7 +195,7 @@ export async function handler(
|
|
|
212
195
|
|
|
213
196
|
// Save project entity
|
|
214
197
|
const projectEntry = await captureAndIndex(ctx, {
|
|
215
|
-
kind:
|
|
198
|
+
kind: 'project',
|
|
216
199
|
title: projectName,
|
|
217
200
|
body,
|
|
218
201
|
tags: allTags,
|
|
@@ -221,18 +204,18 @@ export async function handler(
|
|
|
221
204
|
});
|
|
222
205
|
|
|
223
206
|
// Save bucket entity if it doesn't already exist
|
|
224
|
-
const bucketUserClause =
|
|
207
|
+
const bucketUserClause = '';
|
|
225
208
|
const bucketParams = false ? [bucketTag] : [bucketTag];
|
|
226
209
|
const bucketExists = ctx.db
|
|
227
210
|
.prepare(
|
|
228
|
-
`SELECT 1 FROM vault WHERE kind = 'bucket' AND identity_key = ? ${bucketUserClause} LIMIT 1
|
|
211
|
+
`SELECT 1 FROM vault WHERE kind = 'bucket' AND identity_key = ? ${bucketUserClause} LIMIT 1`
|
|
229
212
|
)
|
|
230
213
|
.get(...bucketParams);
|
|
231
214
|
|
|
232
215
|
let bucketEntry = null;
|
|
233
216
|
if (!bucketExists) {
|
|
234
217
|
bucketEntry = await captureAndIndex(ctx, {
|
|
235
|
-
kind:
|
|
218
|
+
kind: 'bucket',
|
|
236
219
|
title: projectName,
|
|
237
220
|
body: `Bucket for project: ${projectName}`,
|
|
238
221
|
tags: allTags,
|
|
@@ -242,21 +225,21 @@ export async function handler(
|
|
|
242
225
|
}
|
|
243
226
|
|
|
244
227
|
const relPath = projectEntry.filePath
|
|
245
|
-
? projectEntry.filePath.replace(config.vaultDir +
|
|
228
|
+
? projectEntry.filePath.replace(config.vaultDir + '/', '')
|
|
246
229
|
: projectEntry.filePath;
|
|
247
230
|
|
|
248
231
|
const parts = [
|
|
249
232
|
`✓ Ingested project → ${relPath}`,
|
|
250
233
|
` id: ${projectEntry.id}`,
|
|
251
234
|
` title: ${projectEntry.title}`,
|
|
252
|
-
` tags: ${allTags.join(
|
|
253
|
-
...(techStack.length ? [` stack: ${techStack.join(
|
|
235
|
+
` tags: ${allTags.join(', ')}`,
|
|
236
|
+
...(techStack.length ? [` stack: ${techStack.join(', ')}`] : []),
|
|
254
237
|
...(repoUrl ? [` repo: ${repoUrl}`] : []),
|
|
255
238
|
];
|
|
256
239
|
|
|
257
240
|
if (bucketEntry) {
|
|
258
241
|
const bucketRelPath = bucketEntry.filePath
|
|
259
|
-
? bucketEntry.filePath.replace(config.vaultDir +
|
|
242
|
+
? bucketEntry.filePath.replace(config.vaultDir + '/', '')
|
|
260
243
|
: bucketEntry.filePath;
|
|
261
244
|
parts.push(``, `✓ Created bucket → ${bucketRelPath}`);
|
|
262
245
|
parts.push(` id: ${bucketEntry.id}`);
|
|
@@ -264,9 +247,6 @@ export async function handler(
|
|
|
264
247
|
parts.push(``, ` (bucket '${bucketTag}' already exists — skipped)`);
|
|
265
248
|
}
|
|
266
249
|
|
|
267
|
-
parts.push(
|
|
268
|
-
|
|
269
|
-
"_Use get_context with bucket tag to retrieve project-scoped entries._",
|
|
270
|
-
);
|
|
271
|
-
return ok(parts.join("\n"));
|
|
250
|
+
parts.push('', '_Use get_context with bucket tag to retrieve project-scoped entries._');
|
|
251
|
+
return ok(parts.join('\n'));
|
|
272
252
|
}
|
package/src/tools/ingest-url.js
CHANGED
|
@@ -1,23 +1,19 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import { captureAndIndex } from
|
|
3
|
-
import { ok, err, ensureVaultExists } from
|
|
4
|
-
import {
|
|
5
|
-
MAX_KIND_LENGTH,
|
|
6
|
-
MAX_TAG_LENGTH,
|
|
7
|
-
MAX_TAGS_COUNT,
|
|
8
|
-
} from "@context-vault/core/constants";
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { captureAndIndex } from '@context-vault/core/capture';
|
|
3
|
+
import { ok, err, ensureVaultExists } from '../helpers.js';
|
|
4
|
+
import { MAX_KIND_LENGTH, MAX_TAG_LENGTH, MAX_TAGS_COUNT } from '@context-vault/core/constants';
|
|
9
5
|
|
|
10
6
|
const MAX_URL_LENGTH = 2048;
|
|
11
7
|
|
|
12
|
-
export const name =
|
|
8
|
+
export const name = 'ingest_url';
|
|
13
9
|
|
|
14
10
|
export const description =
|
|
15
|
-
|
|
11
|
+
'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
12
|
|
|
17
13
|
export const inputSchema = {
|
|
18
|
-
url: z.string().describe(
|
|
19
|
-
kind: z.string().optional().describe(
|
|
20
|
-
tags: z.array(z.string()).optional().describe(
|
|
14
|
+
url: z.string().describe('The URL to fetch and save'),
|
|
15
|
+
kind: z.string().optional().describe('Entry kind (default: reference)'),
|
|
16
|
+
tags: z.array(z.string()).optional().describe('Tags for the entry'),
|
|
21
17
|
};
|
|
22
18
|
|
|
23
19
|
/**
|
|
@@ -25,39 +21,27 @@ export const inputSchema = {
|
|
|
25
21
|
* @param {import('../types.js').BaseCtx & Partial<import('../types.js').HostedCtxExtensions>} ctx
|
|
26
22
|
* @param {import('../types.js').ToolShared} shared
|
|
27
23
|
*/
|
|
28
|
-
export async function handler(
|
|
29
|
-
{ url: targetUrl, kind, tags },
|
|
30
|
-
ctx,
|
|
31
|
-
{ ensureIndexed },
|
|
32
|
-
) {
|
|
24
|
+
export async function handler({ url: targetUrl, kind, tags }, ctx, { ensureIndexed }) {
|
|
33
25
|
const { config } = ctx;
|
|
34
26
|
|
|
35
27
|
const vaultErr = ensureVaultExists(config);
|
|
36
28
|
if (vaultErr) return vaultErr;
|
|
37
29
|
|
|
38
|
-
if (!targetUrl?.trim())
|
|
39
|
-
return err("Required: url (non-empty string)", "INVALID_INPUT");
|
|
30
|
+
if (!targetUrl?.trim()) return err('Required: url (non-empty string)', 'INVALID_INPUT');
|
|
40
31
|
if (targetUrl.length > MAX_URL_LENGTH)
|
|
41
|
-
return err(`url must be under ${MAX_URL_LENGTH} chars`,
|
|
32
|
+
return err(`url must be under ${MAX_URL_LENGTH} chars`, 'INVALID_INPUT');
|
|
42
33
|
if (kind !== undefined && kind !== null) {
|
|
43
|
-
if (typeof kind !==
|
|
44
|
-
return err(
|
|
45
|
-
`kind must be a string, max ${MAX_KIND_LENGTH} chars`,
|
|
46
|
-
"INVALID_INPUT",
|
|
47
|
-
);
|
|
34
|
+
if (typeof kind !== 'string' || kind.length > MAX_KIND_LENGTH) {
|
|
35
|
+
return err(`kind must be a string, max ${MAX_KIND_LENGTH} chars`, 'INVALID_INPUT');
|
|
48
36
|
}
|
|
49
37
|
}
|
|
50
38
|
if (tags !== undefined && tags !== null) {
|
|
51
|
-
if (!Array.isArray(tags))
|
|
52
|
-
return err("tags must be an array of strings", "INVALID_INPUT");
|
|
39
|
+
if (!Array.isArray(tags)) return err('tags must be an array of strings', 'INVALID_INPUT');
|
|
53
40
|
if (tags.length > MAX_TAGS_COUNT)
|
|
54
|
-
return err(`tags: max ${MAX_TAGS_COUNT} tags allowed`,
|
|
41
|
+
return err(`tags: max ${MAX_TAGS_COUNT} tags allowed`, 'INVALID_INPUT');
|
|
55
42
|
for (const tag of tags) {
|
|
56
|
-
if (typeof tag !==
|
|
57
|
-
return err(
|
|
58
|
-
`each tag must be a string, max ${MAX_TAG_LENGTH} chars`,
|
|
59
|
-
"INVALID_INPUT",
|
|
60
|
-
);
|
|
43
|
+
if (typeof tag !== 'string' || tag.length > MAX_TAG_LENGTH) {
|
|
44
|
+
return err(`each tag must be a string, max ${MAX_TAG_LENGTH} chars`, 'INVALID_INPUT');
|
|
61
45
|
}
|
|
62
46
|
}
|
|
63
47
|
}
|
|
@@ -65,23 +49,23 @@ export async function handler(
|
|
|
65
49
|
await ensureIndexed();
|
|
66
50
|
|
|
67
51
|
try {
|
|
68
|
-
const { ingestUrl } = await import(
|
|
52
|
+
const { ingestUrl } = await import('../../capture/ingest-url.js');
|
|
69
53
|
const entryData = await ingestUrl(targetUrl, { kind, tags });
|
|
70
54
|
const entry = await captureAndIndex(ctx, { ...entryData });
|
|
71
55
|
const relPath = entry.filePath
|
|
72
|
-
? entry.filePath.replace(config.vaultDir +
|
|
56
|
+
? entry.filePath.replace(config.vaultDir + '/', '')
|
|
73
57
|
: entry.filePath;
|
|
74
58
|
const parts = [
|
|
75
59
|
`✓ Ingested URL → ${relPath}`,
|
|
76
60
|
` id: ${entry.id}`,
|
|
77
|
-
` title: ${entry.title ||
|
|
61
|
+
` title: ${entry.title || '(untitled)'}`,
|
|
78
62
|
` source: ${entry.source || targetUrl}`,
|
|
79
63
|
];
|
|
80
|
-
if (entry.tags?.length) parts.push(` tags: ${entry.tags.join(
|
|
64
|
+
if (entry.tags?.length) parts.push(` tags: ${entry.tags.join(', ')}`);
|
|
81
65
|
parts.push(` body: ${entry.body?.length || 0} chars`);
|
|
82
|
-
parts.push(
|
|
83
|
-
return ok(parts.join(
|
|
66
|
+
parts.push('', '_Use this id to update or delete later._');
|
|
67
|
+
return ok(parts.join('\n'));
|
|
84
68
|
} catch (e) {
|
|
85
|
-
return err(`Failed to ingest URL: ${e.message}`,
|
|
69
|
+
return err(`Failed to ingest URL: ${e.message}`, 'INGEST_FAILED');
|
|
86
70
|
}
|
|
87
71
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import { ok } from
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ok } from '../helpers.js';
|
|
3
3
|
|
|
4
|
-
export const name =
|
|
4
|
+
export const name = 'list_buckets';
|
|
5
5
|
|
|
6
6
|
export const description =
|
|
7
7
|
"List all registered bucket entities in the vault. Buckets are named scopes used to group entries via 'bucket:' prefixed tags. Returns each bucket's name, description, parent, and optional entry count.";
|
|
@@ -11,7 +11,7 @@ export const inputSchema = {
|
|
|
11
11
|
.boolean()
|
|
12
12
|
.optional()
|
|
13
13
|
.describe(
|
|
14
|
-
|
|
14
|
+
'Include count of entries tagged with each bucket (default true). Set false to skip the count queries for faster response.'
|
|
15
15
|
),
|
|
16
16
|
};
|
|
17
17
|
|
|
@@ -20,14 +20,10 @@ export const inputSchema = {
|
|
|
20
20
|
* @param {import('../types.js').BaseCtx & Partial<import('../types.js').HostedCtxExtensions>} ctx
|
|
21
21
|
* @param {import('../types.js').ToolShared} shared
|
|
22
22
|
*/
|
|
23
|
-
export async function handler(
|
|
24
|
-
{ include_counts = true },
|
|
25
|
-
ctx,
|
|
26
|
-
{ ensureIndexed, reindexFailed },
|
|
27
|
-
) {
|
|
23
|
+
export async function handler({ include_counts = true }, ctx, { ensureIndexed, reindexFailed }) {
|
|
28
24
|
await ensureIndexed();
|
|
29
25
|
|
|
30
|
-
const userClause =
|
|
26
|
+
const userClause = '';
|
|
31
27
|
const userParams = [];
|
|
32
28
|
|
|
33
29
|
const buckets = ctx.db
|
|
@@ -38,20 +34,20 @@ export async function handler(
|
|
|
38
34
|
AND (expires_at IS NULL OR expires_at > datetime('now'))
|
|
39
35
|
AND superseded_by IS NULL
|
|
40
36
|
${userClause}
|
|
41
|
-
ORDER BY title ASC
|
|
37
|
+
ORDER BY title ASC`
|
|
42
38
|
)
|
|
43
39
|
.all(...userParams);
|
|
44
40
|
|
|
45
41
|
if (!buckets.length) {
|
|
46
42
|
return ok(
|
|
47
|
-
'No buckets registered.\n\nCreate one with `save_context(kind: "bucket", identity_key: "bucket:myproject", title: "My Project", body: "...")` to register a bucket.'
|
|
43
|
+
'No buckets registered.\n\nCreate one with `save_context(kind: "bucket", identity_key: "bucket:myproject", title: "My Project", body: "...")` to register a bucket.'
|
|
48
44
|
);
|
|
49
45
|
}
|
|
50
46
|
|
|
51
47
|
const lines = [];
|
|
52
48
|
if (reindexFailed) {
|
|
53
49
|
lines.push(
|
|
54
|
-
`> **Warning:** Auto-reindex failed. Results may be stale. Run \`context-vault reindex\` to fix.\n
|
|
50
|
+
`> **Warning:** Auto-reindex failed. Results may be stale. Run \`context-vault reindex\` to fix.\n`
|
|
55
51
|
);
|
|
56
52
|
}
|
|
57
53
|
lines.push(`## Registered Buckets (${buckets.length})\n`);
|
|
@@ -60,21 +56,19 @@ export async function handler(
|
|
|
60
56
|
let meta = {};
|
|
61
57
|
if (b.meta) {
|
|
62
58
|
try {
|
|
63
|
-
meta = typeof b.meta ===
|
|
59
|
+
meta = typeof b.meta === 'string' ? JSON.parse(b.meta) : b.meta;
|
|
64
60
|
} catch {
|
|
65
61
|
meta = {};
|
|
66
62
|
}
|
|
67
63
|
}
|
|
68
64
|
|
|
69
65
|
const bucketTags = b.tags ? JSON.parse(b.tags) : [];
|
|
70
|
-
const name = b.identity_key
|
|
71
|
-
? b.identity_key.replace(/^bucket:/, "")
|
|
72
|
-
: b.title || b.id;
|
|
66
|
+
const name = b.identity_key ? b.identity_key.replace(/^bucket:/, '') : b.title || b.id;
|
|
73
67
|
const parent = meta.parent || null;
|
|
74
68
|
|
|
75
69
|
let entryCount = null;
|
|
76
70
|
if (include_counts && b.identity_key) {
|
|
77
|
-
const countUserClause =
|
|
71
|
+
const countUserClause = '';
|
|
78
72
|
const countParams = [];
|
|
79
73
|
const row = ctx.db
|
|
80
74
|
.prepare(
|
|
@@ -83,7 +77,7 @@ export async function handler(
|
|
|
83
77
|
AND kind != 'bucket'
|
|
84
78
|
AND (expires_at IS NULL OR expires_at > datetime('now'))
|
|
85
79
|
AND superseded_by IS NULL
|
|
86
|
-
${countUserClause}
|
|
80
|
+
${countUserClause}`
|
|
87
81
|
)
|
|
88
82
|
.get(`%"${b.identity_key}"%`, ...countParams);
|
|
89
83
|
entryCount = row ? row.c : 0;
|
|
@@ -94,20 +88,20 @@ export async function handler(
|
|
|
94
88
|
if (b.identity_key) headerParts.push(`\`${b.identity_key}\``);
|
|
95
89
|
if (parent) headerParts.push(`parent: ${parent}`);
|
|
96
90
|
if (entryCount !== null) headerParts.push(`${entryCount} entries`);
|
|
97
|
-
lines.push(`- ${headerParts.join(
|
|
91
|
+
lines.push(`- ${headerParts.join(' — ')}`);
|
|
98
92
|
|
|
99
93
|
if (b.body) {
|
|
100
|
-
const preview = b.body.replace(/\n+/g,
|
|
101
|
-
lines.push(` ${preview}${b.body.length > 120 ?
|
|
94
|
+
const preview = b.body.replace(/\n+/g, ' ').trim().slice(0, 120);
|
|
95
|
+
lines.push(` ${preview}${b.body.length > 120 ? '…' : ''}`);
|
|
102
96
|
}
|
|
103
97
|
if (bucketTags.length) {
|
|
104
|
-
lines.push(` tags: ${bucketTags.join(
|
|
98
|
+
lines.push(` tags: ${bucketTags.join(', ')}`);
|
|
105
99
|
}
|
|
106
100
|
}
|
|
107
101
|
|
|
108
102
|
lines.push(
|
|
109
|
-
'\n_Register a new bucket with `save_context(kind: "bucket", identity_key: "bucket:<name>", title: "...", body: "...")`_'
|
|
103
|
+
'\n_Register a new bucket with `save_context(kind: "bucket", identity_key: "bucket:<name>", title: "...", body: "...")`_'
|
|
110
104
|
);
|
|
111
105
|
|
|
112
|
-
return ok(lines.join(
|
|
106
|
+
return ok(lines.join('\n'));
|
|
113
107
|
}
|