preflight-mcp 0.1.6 → 0.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.
- package/dist/bundle/githubArchive.js +1 -1
- package/dist/bundle/paths.js +3 -0
- package/dist/bundle/service.js +3 -2
- package/dist/context7/client.js +1 -1
- package/dist/errors.js +13 -1
- package/dist/evidence/dependencyGraph.js +30 -1
- package/dist/server.js +11 -11
- package/package.json +1 -1
package/dist/bundle/paths.js
CHANGED
|
@@ -25,6 +25,7 @@ export function getBundlePaths(storageDir, bundleId) {
|
|
|
25
25
|
validateBundleId(bundleId);
|
|
26
26
|
const rootDir = path.join(storageDir, bundleId);
|
|
27
27
|
const indexesDir = path.join(rootDir, 'indexes');
|
|
28
|
+
const depsDir = path.join(rootDir, 'deps');
|
|
28
29
|
return {
|
|
29
30
|
bundleId,
|
|
30
31
|
rootDir,
|
|
@@ -36,6 +37,8 @@ export function getBundlePaths(storageDir, bundleId) {
|
|
|
36
37
|
searchDbPath: path.join(indexesDir, 'search.sqlite3'),
|
|
37
38
|
reposDir: path.join(rootDir, 'repos'),
|
|
38
39
|
librariesDir: path.join(rootDir, 'libraries'),
|
|
40
|
+
depsDir,
|
|
41
|
+
depsGraphPath: path.join(depsDir, 'dependency-graph.json'),
|
|
39
42
|
};
|
|
40
43
|
}
|
|
41
44
|
export function repoRootDir(paths, owner, repo) {
|
package/dist/bundle/service.js
CHANGED
|
@@ -15,6 +15,7 @@ import { analyzeBundleStatic } from './analysis.js';
|
|
|
15
15
|
import { autoDetectTags, generateDisplayName, generateDescription } from './tagging.js';
|
|
16
16
|
import { bundleCreationLimiter } from '../core/concurrency-limiter.js';
|
|
17
17
|
import { getProgressTracker, calcPercent } from '../jobs/progressTracker.js';
|
|
18
|
+
import { BundleNotFoundError } from '../errors.js';
|
|
18
19
|
const DEDUP_INDEX_FILE = '.preflight-dedup-index.json';
|
|
19
20
|
function sha256Hex(text) {
|
|
20
21
|
return crypto.createHash('sha256').update(text, 'utf8').digest('hex');
|
|
@@ -425,7 +426,7 @@ async function validateBundleCompleteness(bundleRoot) {
|
|
|
425
426
|
export async function assertBundleComplete(cfg, bundleId) {
|
|
426
427
|
const storageDir = await findBundleStorageDir(cfg.storageDirs, bundleId);
|
|
427
428
|
if (!storageDir) {
|
|
428
|
-
throw new
|
|
429
|
+
throw new BundleNotFoundError(bundleId);
|
|
429
430
|
}
|
|
430
431
|
const bundleRoot = getBundlePaths(storageDir, bundleId).rootDir;
|
|
431
432
|
const { isValid, missingComponents } = await validateBundleCompleteness(bundleRoot);
|
|
@@ -1316,7 +1317,7 @@ export async function repairBundle(cfg, bundleId, options) {
|
|
|
1316
1317
|
const rebuildOverviewOpt = options?.rebuildOverview ?? true;
|
|
1317
1318
|
const storageDir = await findBundleStorageDir(cfg.storageDirs, bundleId);
|
|
1318
1319
|
if (!storageDir) {
|
|
1319
|
-
throw new
|
|
1320
|
+
throw new BundleNotFoundError(bundleId);
|
|
1320
1321
|
}
|
|
1321
1322
|
const paths = getBundlePaths(storageDir, bundleId);
|
|
1322
1323
|
const before = await validateBundleCompleteness(paths.rootDir);
|
package/dist/context7/client.js
CHANGED
|
@@ -20,7 +20,7 @@ export async function connectContext7(cfg) {
|
|
|
20
20
|
maxRetries: 1,
|
|
21
21
|
},
|
|
22
22
|
});
|
|
23
|
-
const client = new Client({ name: 'preflight-context7', version: '0.1.
|
|
23
|
+
const client = new Client({ name: 'preflight-context7', version: '0.1.8' });
|
|
24
24
|
await client.connect(transport);
|
|
25
25
|
return {
|
|
26
26
|
client,
|
package/dist/errors.js
CHANGED
|
@@ -31,12 +31,24 @@ export class PreflightError extends Error {
|
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Check if a string looks like a valid UUID v4.
|
|
36
|
+
*/
|
|
37
|
+
function isUuidFormat(id) {
|
|
38
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id);
|
|
39
|
+
}
|
|
34
40
|
/**
|
|
35
41
|
* Error thrown when a bundle is not found.
|
|
36
42
|
*/
|
|
37
43
|
export class BundleNotFoundError extends PreflightError {
|
|
38
44
|
constructor(bundleId) {
|
|
39
|
-
|
|
45
|
+
const hint = isUuidFormat(bundleId)
|
|
46
|
+
? '\nUse preflight_list_bundles to see available bundles.'
|
|
47
|
+
: `\nHint: bundleId must be a UUID (e.g., 025c6dcb-1234-5678-9abc-def012345678).\n` +
|
|
48
|
+
` "${bundleId}" looks like a displayName, not a bundleId.\n` +
|
|
49
|
+
` Use preflight_list_bundles to find the correct bundleId.\n` +
|
|
50
|
+
` DO NOT automatically create a new bundle - ASK the user first!`;
|
|
51
|
+
super(`Bundle not found: ${bundleId}${hint}`, 'BUNDLE_NOT_FOUND', {
|
|
40
52
|
context: { bundleId },
|
|
41
53
|
});
|
|
42
54
|
this.name = 'BundleNotFoundError';
|
|
@@ -21,6 +21,8 @@ export const DependencyGraphInputSchema = {
|
|
|
21
21
|
.describe('Optional symbol name (function/class). If omitted, graph is file-level.'),
|
|
22
22
|
}).optional().describe('Target file/symbol to analyze. If omitted, generates a GLOBAL dependency graph of all code files in the bundle. ' +
|
|
23
23
|
'Global mode shows import relationships between all files but may be truncated for large projects.'),
|
|
24
|
+
force: z.boolean().default(false).describe('If true, regenerate the dependency graph even if cached. ' +
|
|
25
|
+
'Global mode results are cached in the bundle; use force=true to refresh.'),
|
|
24
26
|
options: z
|
|
25
27
|
.object({
|
|
26
28
|
maxFiles: z.number().int().min(1).max(500).default(200),
|
|
@@ -200,6 +202,24 @@ export async function generateDependencyGraph(cfg, rawArgs) {
|
|
|
200
202
|
}
|
|
201
203
|
const paths = getBundlePathsForId(storageDir, args.bundleId);
|
|
202
204
|
const manifest = await readManifest(paths.manifestPath);
|
|
205
|
+
// Global mode: check for cached result (unless force=true)
|
|
206
|
+
const isGlobalMode = !args.target;
|
|
207
|
+
if (isGlobalMode && !args.force) {
|
|
208
|
+
try {
|
|
209
|
+
const cached = await fs.readFile(paths.depsGraphPath, 'utf8');
|
|
210
|
+
const parsed = JSON.parse(cached);
|
|
211
|
+
// Add note that this is from cache
|
|
212
|
+
parsed.signals.warnings = parsed.signals.warnings || [];
|
|
213
|
+
parsed.signals.warnings.unshift({
|
|
214
|
+
code: 'from_cache',
|
|
215
|
+
message: `Loaded from cache (generated at ${parsed.meta.generatedAt}). Use force=true to regenerate.`,
|
|
216
|
+
});
|
|
217
|
+
return parsed;
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
// Cache miss - generate fresh
|
|
221
|
+
}
|
|
222
|
+
}
|
|
203
223
|
const limits = {
|
|
204
224
|
maxFiles: args.options.maxFiles,
|
|
205
225
|
maxNodes: args.options.maxNodes,
|
|
@@ -248,7 +268,7 @@ export async function generateDependencyGraph(cfg, rawArgs) {
|
|
|
248
268
|
const bundleFileUri = (p) => toBundleFileUri({ bundleId: args.bundleId, relativePath: p });
|
|
249
269
|
// Global mode: no target specified
|
|
250
270
|
if (!args.target) {
|
|
251
|
-
|
|
271
|
+
const result = await generateGlobalDependencyGraph({
|
|
252
272
|
cfg,
|
|
253
273
|
args,
|
|
254
274
|
paths,
|
|
@@ -265,6 +285,15 @@ export async function generateDependencyGraph(cfg, rawArgs) {
|
|
|
265
285
|
addEdge,
|
|
266
286
|
bundleFileUri,
|
|
267
287
|
});
|
|
288
|
+
// Save to cache
|
|
289
|
+
try {
|
|
290
|
+
await fs.mkdir(paths.depsDir, { recursive: true });
|
|
291
|
+
await fs.writeFile(paths.depsGraphPath, JSON.stringify(result, null, 2));
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
// Ignore cache write errors
|
|
295
|
+
}
|
|
296
|
+
return result;
|
|
268
297
|
}
|
|
269
298
|
const targetFile = args.target.file.replaceAll('\\', '/');
|
|
270
299
|
const targetRepo = parseRepoNormPath(targetFile);
|
package/dist/server.js
CHANGED
|
@@ -8,6 +8,7 @@ import { getProgressTracker } from './jobs/progressTracker.js';
|
|
|
8
8
|
import { readManifest } from './bundle/manifest.js';
|
|
9
9
|
import { safeJoin, toBundleFileUri } from './mcp/uris.js';
|
|
10
10
|
import { wrapPreflightError } from './mcp/errorKinds.js';
|
|
11
|
+
import { BundleNotFoundError } from './errors.js';
|
|
11
12
|
import { searchIndex } from './search/sqliteFts.js';
|
|
12
13
|
import { logger } from './logging/logger.js';
|
|
13
14
|
import { runSearchByTags } from './tools/searchByTags.js';
|
|
@@ -99,7 +100,7 @@ export async function startServer() {
|
|
|
99
100
|
startHttpServer(cfg);
|
|
100
101
|
const server = new McpServer({
|
|
101
102
|
name: 'preflight-mcp',
|
|
102
|
-
version: '0.1.
|
|
103
|
+
version: '0.1.8',
|
|
103
104
|
description: 'Create evidence-based preflight bundles for repositories (docs + code) with SQLite FTS search.',
|
|
104
105
|
}, {
|
|
105
106
|
capabilities: {
|
|
@@ -280,7 +281,7 @@ export async function startServer() {
|
|
|
280
281
|
try {
|
|
281
282
|
const storageDir = await findBundleStorageDir(cfg.storageDirs, args.bundleId);
|
|
282
283
|
if (!storageDir) {
|
|
283
|
-
throw new
|
|
284
|
+
throw new BundleNotFoundError(args.bundleId);
|
|
284
285
|
}
|
|
285
286
|
const paths = getBundlePathsForId(storageDir, args.bundleId);
|
|
286
287
|
const bundleRoot = paths.rootDir;
|
|
@@ -364,7 +365,7 @@ export async function startServer() {
|
|
|
364
365
|
try {
|
|
365
366
|
const deleted = await clearBundleMulti(cfg.storageDirs, args.bundleId);
|
|
366
367
|
if (!deleted) {
|
|
367
|
-
throw new
|
|
368
|
+
throw new BundleNotFoundError(args.bundleId);
|
|
368
369
|
}
|
|
369
370
|
server.sendResourceListChanged();
|
|
370
371
|
const out = { deleted: true, bundleId: args.bundleId };
|
|
@@ -379,7 +380,7 @@ export async function startServer() {
|
|
|
379
380
|
});
|
|
380
381
|
server.registerTool('preflight_create_bundle', {
|
|
381
382
|
title: 'Create bundle',
|
|
382
|
-
description: 'Create a new bundle from GitHub repos or local directories
|
|
383
|
+
description: 'Create a new bundle from GitHub repos or local directories. IMPORTANT: Only call this tool when the user EXPLICITLY asks to create/index a repo. Do NOT automatically create bundles when search fails or bundle is not found - ASK the user first! Use when user says: "index this repo", "create bundle for", "创建bundle", "添加GitHub项目". If ifExists=updateExisting, updates an existing bundle.',
|
|
383
384
|
inputSchema: CreateBundleInputSchema,
|
|
384
385
|
outputSchema: {
|
|
385
386
|
// Normal completion fields
|
|
@@ -594,7 +595,7 @@ export async function startServer() {
|
|
|
594
595
|
try {
|
|
595
596
|
const storageDir = await findBundleStorageDir(cfg.storageDirs, args.bundleId);
|
|
596
597
|
if (!storageDir) {
|
|
597
|
-
throw new
|
|
598
|
+
throw new BundleNotFoundError(args.bundleId);
|
|
598
599
|
}
|
|
599
600
|
// checkOnly mode: just check for updates without applying
|
|
600
601
|
if (args.checkOnly) {
|
|
@@ -759,7 +760,7 @@ export async function startServer() {
|
|
|
759
760
|
// Resolve bundle location across storageDirs (more robust than a single effectiveDir).
|
|
760
761
|
const storageDir = await findBundleStorageDir(cfg.storageDirs, args.bundleId);
|
|
761
762
|
if (!storageDir) {
|
|
762
|
-
throw new
|
|
763
|
+
throw new BundleNotFoundError(args.bundleId);
|
|
763
764
|
}
|
|
764
765
|
if (args.ensureFresh) {
|
|
765
766
|
throw new Error('ensureFresh is deprecated and not supported in this tool. This tool is strictly read-only. ' +
|
|
@@ -792,11 +793,10 @@ export async function startServer() {
|
|
|
792
793
|
});
|
|
793
794
|
server.registerTool('preflight_evidence_dependency_graph', {
|
|
794
795
|
title: 'Evidence: dependency graph (callers + imports)',
|
|
795
|
-
description: 'Generate an evidence-based dependency graph.
|
|
796
|
-
'(1) TARGET MODE: provide target.file
|
|
797
|
-
'
|
|
798
|
-
'
|
|
799
|
-
'Use preflight_search_bundle to find file paths, or check OVERVIEW.md.',
|
|
796
|
+
description: 'Generate an evidence-based dependency graph. IMPORTANT: Before running, ASK the user which bundle and which file/mode they want! ' +
|
|
797
|
+
'Two modes: (1) TARGET MODE: analyze a specific file (provide target.file). (2) GLOBAL MODE: project-wide graph (omit target). ' +
|
|
798
|
+
'Do NOT automatically choose bundle or mode - confirm with user first! ' +
|
|
799
|
+
'File path must be bundle-relative: repos/{owner}/{repo}/norm/{path}.',
|
|
800
800
|
inputSchema: DependencyGraphInputSchema,
|
|
801
801
|
outputSchema: {
|
|
802
802
|
meta: z.any(),
|