preflight-mcp 0.1.7 → 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.
@@ -7,7 +7,7 @@ function nowIso() {
7
7
  }
8
8
  function githubHeaders(cfg) {
9
9
  const headers = {
10
- 'User-Agent': 'preflight-mcp/0.1.7',
10
+ 'User-Agent': 'preflight-mcp/0.1.8',
11
11
  Accept: 'application/vnd.github+json',
12
12
  };
13
13
  if (cfg.githubToken) {
@@ -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) {
@@ -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.7' });
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
@@ -46,7 +46,8 @@ export class BundleNotFoundError extends PreflightError {
46
46
  ? '\nUse preflight_list_bundles to see available bundles.'
47
47
  : `\nHint: bundleId must be a UUID (e.g., 025c6dcb-1234-5678-9abc-def012345678).\n` +
48
48
  ` "${bundleId}" looks like a displayName, not a bundleId.\n` +
49
- ` Use preflight_list_bundles to find the correct bundleId.`;
49
+ ` Use preflight_list_bundles to find the correct bundleId.\n` +
50
+ ` DO NOT automatically create a new bundle - ASK the user first!`;
50
51
  super(`Bundle not found: ${bundleId}${hint}`, 'BUNDLE_NOT_FOUND', {
51
52
  context: { bundleId },
52
53
  });
@@ -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
- return generateGlobalDependencyGraph({
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
@@ -100,7 +100,7 @@ export async function startServer() {
100
100
  startHttpServer(cfg);
101
101
  const server = new McpServer({
102
102
  name: 'preflight-mcp',
103
- version: '0.1.7',
103
+ version: '0.1.8',
104
104
  description: 'Create evidence-based preflight bundles for repositories (docs + code) with SQLite FTS search.',
105
105
  }, {
106
106
  capabilities: {
@@ -380,7 +380,7 @@ export async function startServer() {
380
380
  });
381
381
  server.registerTool('preflight_create_bundle', {
382
382
  title: 'Create bundle',
383
- description: 'Create a new bundle from GitHub repos or local directories (or update an existing one if ifExists=updateExisting). Use when: "index this repo", "create bundle for", "add repo to preflight", "索引这个仓库", "创建bundle", "添加GitHub项目", "学习这个项目". NOTE: If the bundle contains code files, consider asking user if they want to generate dependency graph (preflight_evidence_dependency_graph) or establish trace links (preflight_trace_upsert).',
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.',
384
384
  inputSchema: CreateBundleInputSchema,
385
385
  outputSchema: {
386
386
  // Normal completion fields
@@ -793,11 +793,10 @@ export async function startServer() {
793
793
  });
794
794
  server.registerTool('preflight_evidence_dependency_graph', {
795
795
  title: 'Evidence: dependency graph (callers + imports)',
796
- description: 'Generate an evidence-based dependency graph. Two modes: ' +
797
- '(1) TARGET MODE: provide target.file to analyze a specific file\'s imports and callers. ' +
798
- '(2) GLOBAL MODE: omit target to generate a project-wide import graph of all code files. ' +
799
- 'For target mode, file path must be bundle-relative: repos/{owner}/{repo}/norm/{path}. ' +
800
- '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}.',
801
800
  inputSchema: DependencyGraphInputSchema,
802
801
  outputSchema: {
803
802
  meta: z.any(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "preflight-mcp",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "MCP server that creates evidence-based preflight bundles for GitHub repositories and library docs.",
5
5
  "type": "module",
6
6
  "license": "MIT",