Skip to content

Commit 9ba3bb1

Browse files
James-Chapmanclaude
andcommitted
Fix review issues: stem guard, cache drives, dedup isDir, typo, null check
- Raise stripSuffix minimum length from 3 to 4 to prevent "rules"→"rul" - Cache getDriveRoots() result to avoid double I/O when skipDrives=false - Deduplicate isDir: export from system-scan-ides, import in system-scan - Fix typo: probeIdegGroup → probeIdeGroup - Use explicit null check for OPPORTUNITY_FILES entries - Add TODO documenting sync countSkillFiles/listSkillNames dependency Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 5848afe commit 9ba3bb1

3 files changed

Lines changed: 13 additions & 18 deletions

File tree

server/lib/system-scan-ides.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,4 @@ async function probeAIExtensions() {
113113
return perIde;
114114
}
115115

116-
module.exports = { probeIDEs, probeAIExtensions };
116+
module.exports = { probeIDEs, probeAIExtensions, isDir };

server/lib/system-scan.js

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
const fs = require('fs');
66
const path = require('path');
77
const { HOMEDIR, DATA_DIR, SKILLS_DIR } = require('./config');
8+
// TODO: countSkillFiles and listSkillNames are synchronous and block the event loop.
9+
// Converting them to async (fs.promises) would require changes across skills.js and
10+
// all callers. Until then, calls here are the main latency source in scanSystem.
811
const { countSkillFiles, listSkillNames } = require('./skills');
912
const {
1013
HOSTS,
@@ -13,7 +16,7 @@ const {
1316
CONFIG_FILE_NAMES,
1417
OPPORTUNITY_FILES,
1518
} = require('./system-scan-definitions');
16-
const { probeIDEs, probeAIExtensions } = require('./system-scan-ides');
19+
const { probeIDEs, probeAIExtensions, isDir } = require('./system-scan-ides');
1720

1821
// ---- Helpers ----
1922

@@ -44,14 +47,6 @@ async function isFile(p) {
4447
return false;
4548
}
4649
}
47-
/** @param {string} p */
48-
async function isDir(p) {
49-
try {
50-
return (await fs.promises.stat(p)).isDirectory();
51-
} catch {
52-
return false;
53-
}
54-
}
5550

5651
/** @param {string} p */
5752
async function readJsonSafe(p) {
@@ -307,7 +302,7 @@ async function probeHostDir(hostDef, homedir) {
307302

308303
// Opportunities (missing global config)
309304
const expected = OPPORTUNITY_FILES[/** @type {keyof typeof OPPORTUNITY_FILES} */ (hostDef.id)];
310-
if (expected) {
305+
if (expected != null) {
311306
const filePath = path.join(hostPath, expected);
312307
const homedirFile = path.join(homedir, expected);
313308
// If config doesn't exist inside host dir or at homedir root
@@ -324,7 +319,7 @@ async function probeHostDir(hostDef, homedir) {
324319
}
325320

326321
/** @param {Array<{id: string, label: string, path: string, exe: string}>} ideList */
327-
async function probeIdegGroup(ideList) {
322+
async function probeIdeGroup(ideList) {
328323
if (!ideList.length) return null;
329324
const perIde = await probeAIExtensions();
330325
return {
@@ -362,8 +357,8 @@ async function scanSystem(customPaths = [], opts = {}) {
362357
}
363358

364359
// Probe host dirs from drives
360+
const drives = !skipDrives ? await getDriveRoots() : [];
365361
if (!skipDrives) {
366-
const drives = await getDriveRoots();
367362
const driveResults = await Promise.all(
368363
drives.flatMap((drive) =>
369364
HOSTS.map(async (h) => {
@@ -445,7 +440,6 @@ async function scanSystem(customPaths = [], opts = {}) {
445440
await scanStandaloneFiles(HOMEDIR, hosts);
446441
}
447442
if (!skipDrives) {
448-
const drives = await getDriveRoots();
449443
await Promise.all(drives.map((d) => scanStandaloneFiles(d, hosts)));
450444
}
451445
await Promise.all(workspaces.map((/** @type {string} */ ws) => scanStandaloneFiles(ws, hosts)));
@@ -462,7 +456,7 @@ async function scanSystem(customPaths = [], opts = {}) {
462456
);
463457

464458
// IDE group
465-
const ides = await probeIdegGroup(ideList);
459+
const ides = await probeIdeGroup(ideList);
466460

467461
return {
468462
hosts: populated,

server/lib/vectorstore.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,14 +225,15 @@ function bareSkillId(skillId) {
225225
}
226226

227227
/**
228-
* Strip common suffixes for loose matching, but never shrink below 3 chars
229-
* to avoid false matches (e.g. "string" → "str", "processed" → "process").
228+
* Strip common suffixes for loose matching, but preserve enough characters
229+
* to avoid false matches (e.g. "string" → "str", "rules" → "rul").
230+
* Minimum remaining length of 4 ensures stems like "make" stay intact.
230231
* @param {string} word
231232
*/
232233
function stripSuffix(word) {
233234
let stem = word;
234235
for (const suffix of ['ing', 'ed', 'es', 's']) {
235-
if (stem.endsWith(suffix) && stem.length - suffix.length >= 3) {
236+
if (stem.endsWith(suffix) && stem.length - suffix.length >= 4) {
236237
stem = stem.slice(0, -suffix.length);
237238
break;
238239
}

0 commit comments

Comments
 (0)