preflight-mcp 0.5.1 → 0.5.2

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.
@@ -72,11 +72,21 @@ async function buildIgnore(repoRoot) {
72
72
  }
73
73
  return ig;
74
74
  }
75
- async function* walkFiles(repoRoot, ig) {
75
+ async function* walkFiles(repoRoot, ig, onSkip) {
76
76
  const stack = [repoRoot];
77
77
  while (stack.length) {
78
78
  const dir = stack.pop();
79
- const entries = await fs.readdir(dir, { withFileTypes: true });
79
+ let entries;
80
+ try {
81
+ entries = await fs.readdir(dir, { withFileTypes: true });
82
+ }
83
+ catch (err) {
84
+ // Skip directories that cannot be read (broken symlinks, permission issues, etc.)
85
+ const rel = path.relative(repoRoot, dir);
86
+ const relPosix = toPosix(rel);
87
+ onSkip?.(relPosix, `cannot read directory: ${err.code ?? 'unknown'}`);
88
+ continue;
89
+ }
80
90
  for (const ent of entries) {
81
91
  const abs = path.join(dir, ent.name);
82
92
  const rel = path.relative(repoRoot, abs);
@@ -85,12 +95,29 @@ async function* walkFiles(repoRoot, ig) {
85
95
  if (ig.ignores(relPosix)) {
86
96
  continue;
87
97
  }
88
- if (ent.isDirectory()) {
89
- stack.push(abs);
90
- continue;
98
+ // Handle symlinks and other special entries
99
+ try {
100
+ if (ent.isSymbolicLink()) {
101
+ // Check if symlink target exists
102
+ try {
103
+ await fs.stat(abs);
104
+ }
105
+ catch {
106
+ onSkip?.(relPosix, 'broken symlink');
107
+ continue;
108
+ }
109
+ }
110
+ if (ent.isDirectory()) {
111
+ stack.push(abs);
112
+ continue;
113
+ }
114
+ if (!ent.isFile())
115
+ continue;
91
116
  }
92
- if (!ent.isFile())
117
+ catch (err) {
118
+ onSkip?.(relPosix, `cannot stat: ${err.code ?? 'unknown'}`);
93
119
  continue;
120
+ }
94
121
  yield { absPath: abs, relPosix };
95
122
  }
96
123
  }
@@ -101,7 +128,9 @@ export async function ingestRepoToBundle(params) {
101
128
  const files = [];
102
129
  const skipped = [];
103
130
  const decoder = new TextDecoder('utf-8', { fatal: true });
104
- for await (const f of walkFiles(params.repoRoot, ig)) {
131
+ for await (const f of walkFiles(params.repoRoot, ig, (relPosix, reason) => {
132
+ skipped.push(`${relPosix} (${reason})`);
133
+ })) {
105
134
  // ignore check already done in walkFiles
106
135
  const st = await fs.stat(f.absPath);
107
136
  if (st.size > params.options.maxFileBytes) {
package/dist/server.js CHANGED
@@ -147,7 +147,7 @@ export async function startServer() {
147
147
  startHttpServer(cfg);
148
148
  const server = new McpServer({
149
149
  name: 'preflight-mcp',
150
- version: '0.5.1',
150
+ version: '0.5.2',
151
151
  description: 'Create evidence-based preflight bundles for repositories (docs + code) with SQLite FTS search.',
152
152
  }, {
153
153
  capabilities: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "preflight-mcp",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
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",