fullcourtdefense-cli 1.26.10 → 1.26.12
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.
|
@@ -76,3 +76,8 @@ export declare function buildFolderCatalog(options?: {
|
|
|
76
76
|
export declare function resolveScanRoots(flag: string | undefined): string[];
|
|
77
77
|
/** Include config paths for apps that exist on disk even when the MCP file is not created yet. */
|
|
78
78
|
export declare function discoverScanTargets(cwd: string, extra?: string): ConfigPathCandidate[];
|
|
79
|
+
/**
|
|
80
|
+
* True when the AI *app* is on disk — not when a leftover config folder exists.
|
|
81
|
+
* `~/.codex` / `~/.cursor` without Cursor.exe must not count as installed.
|
|
82
|
+
*/
|
|
83
|
+
export declare function detectIdeAppInstalled(clientKey: string, env?: NodeJS.ProcessEnv, home?: string, platform?: NodeJS.Platform): boolean;
|
|
@@ -49,6 +49,7 @@ exports.scanRootsForProjectConfigs = scanRootsForProjectConfigs;
|
|
|
49
49
|
exports.buildFolderCatalog = buildFolderCatalog;
|
|
50
50
|
exports.resolveScanRoots = resolveScanRoots;
|
|
51
51
|
exports.discoverScanTargets = discoverScanTargets;
|
|
52
|
+
exports.detectIdeAppInstalled = detectIdeAppInstalled;
|
|
52
53
|
const fs = __importStar(require("fs"));
|
|
53
54
|
const os = __importStar(require("os"));
|
|
54
55
|
const path = __importStar(require("path"));
|
|
@@ -461,3 +462,76 @@ function discoverScanTargets(cwd, extra) {
|
|
|
461
462
|
}
|
|
462
463
|
return [...existing, ...implicit];
|
|
463
464
|
}
|
|
465
|
+
function fileExists(file) {
|
|
466
|
+
try {
|
|
467
|
+
return fs.existsSync(file) && fs.statSync(file).isFile();
|
|
468
|
+
}
|
|
469
|
+
catch {
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
function dirHasFile(dir, names) {
|
|
474
|
+
return names.some(name => fileExists(path.join(dir, name)));
|
|
475
|
+
}
|
|
476
|
+
function firstExistingCodexBin(localAppData) {
|
|
477
|
+
const binRoot = path.join(localAppData, 'OpenAI', 'Codex', 'bin');
|
|
478
|
+
try {
|
|
479
|
+
if (!fs.statSync(binRoot).isDirectory())
|
|
480
|
+
return false;
|
|
481
|
+
for (const name of fs.readdirSync(binRoot)) {
|
|
482
|
+
if (fileExists(path.join(binRoot, name, 'codex.exe')) || fileExists(path.join(binRoot, name, 'codex')))
|
|
483
|
+
return true;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
catch { /* missing */ }
|
|
487
|
+
return fileExists(path.join(localAppData, 'OpenAI', 'Codex', 'codex.exe'));
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* True when the AI *app* is on disk — not when a leftover config folder exists.
|
|
491
|
+
* `~/.codex` / `~/.cursor` without Cursor.exe must not count as installed.
|
|
492
|
+
*/
|
|
493
|
+
function detectIdeAppInstalled(clientKey, env = process.env, home = os.homedir(), platform = process.platform) {
|
|
494
|
+
const localAppData = env.LOCALAPPDATA || path.join(home, 'AppData', 'Local');
|
|
495
|
+
const programFiles = env.ProgramFiles || 'C:\\Program Files';
|
|
496
|
+
const programFilesX86 = env['ProgramFiles(x86)'] || 'C:\\Program Files (x86)';
|
|
497
|
+
if (clientKey === 'cursor') {
|
|
498
|
+
if (platform === 'darwin' && fs.existsSync('/Applications/Cursor.app'))
|
|
499
|
+
return true;
|
|
500
|
+
return dirHasFile(path.join(localAppData, 'Programs', 'cursor'), ['Cursor.exe'])
|
|
501
|
+
|| dirHasFile(path.join(localAppData, 'Programs', 'Cursor'), ['Cursor.exe'])
|
|
502
|
+
|| dirHasFile(path.join(programFiles, 'Cursor'), ['Cursor.exe']);
|
|
503
|
+
}
|
|
504
|
+
if (clientKey === 'vscode') {
|
|
505
|
+
if (platform === 'darwin' && fs.existsSync('/Applications/Visual Studio Code.app'))
|
|
506
|
+
return true;
|
|
507
|
+
return dirHasFile(path.join(localAppData, 'Programs', 'Microsoft VS Code'), ['Code.exe'])
|
|
508
|
+
|| dirHasFile(path.join(programFiles, 'Microsoft VS Code'), ['Code.exe'])
|
|
509
|
+
|| dirHasFile(path.join(programFilesX86, 'Microsoft VS Code'), ['Code.exe']);
|
|
510
|
+
}
|
|
511
|
+
if (clientKey === 'windsurf') {
|
|
512
|
+
if (platform === 'darwin' && fs.existsSync('/Applications/Windsurf.app'))
|
|
513
|
+
return true;
|
|
514
|
+
return dirHasFile(path.join(localAppData, 'Programs', 'Windsurf'), ['Windsurf.exe'])
|
|
515
|
+
|| dirHasFile(path.join(localAppData, 'Programs', 'windsurf'), ['Windsurf.exe']);
|
|
516
|
+
}
|
|
517
|
+
if (clientKey === 'claude_desktop') {
|
|
518
|
+
if (platform === 'darwin' && fs.existsSync('/Applications/Claude.app'))
|
|
519
|
+
return true;
|
|
520
|
+
if (platform === 'win32' && claudeDesktopMsixCandidates().length > 0)
|
|
521
|
+
return true;
|
|
522
|
+
if (platform === 'win32' && claudeDesktopWindowsInstallDirs(env, home).some(dir => fs.existsSync(dir)))
|
|
523
|
+
return true;
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
526
|
+
if (clientKey === 'claude_code') {
|
|
527
|
+
return fileExists(path.join(home, '.local', 'bin', 'claude.exe'))
|
|
528
|
+
|| fileExists(path.join(home, '.local', 'bin', 'claude'))
|
|
529
|
+
|| dirHasFile(path.join(localAppData, 'Programs', 'claude'), ['claude.exe']);
|
|
530
|
+
}
|
|
531
|
+
if (clientKey === 'codex') {
|
|
532
|
+
if (platform === 'darwin' && (fs.existsSync('/Applications/Codex.app') || fs.existsSync('/Applications/ChatGPT Codex.app')))
|
|
533
|
+
return true;
|
|
534
|
+
return firstExistingCodexBin(localAppData);
|
|
535
|
+
}
|
|
536
|
+
return false;
|
|
537
|
+
}
|
|
@@ -13,6 +13,8 @@ export interface ClientCoverageRow {
|
|
|
13
13
|
cursorHooksInstalled: boolean;
|
|
14
14
|
cursorHookEvents: string[];
|
|
15
15
|
cursorHookShadow: boolean;
|
|
16
|
+
/** False when only leftover config exists — the app binary is not on disk. */
|
|
17
|
+
appInstalled: boolean;
|
|
16
18
|
}
|
|
17
19
|
export interface ProxyClassifiableServer {
|
|
18
20
|
serverName: string;
|
|
@@ -45,6 +45,7 @@ exports.buildClientCoverage = buildClientCoverage;
|
|
|
45
45
|
const fs = __importStar(require("fs"));
|
|
46
46
|
const os = __importStar(require("os"));
|
|
47
47
|
const path = __importStar(require("path"));
|
|
48
|
+
const discoverPaths_1 = require("./discoverPaths");
|
|
48
49
|
/**
|
|
49
50
|
* Config sources that no AI client actually loads. A plain `mcp.json` at a
|
|
50
51
|
* repo root is documentation/sample material — Cursor, Claude Code, VS Code,
|
|
@@ -258,6 +259,12 @@ function buildClientCoverage(scanned, servers, cwd) {
|
|
|
258
259
|
list.push(server);
|
|
259
260
|
serversByConfig.set(key, list);
|
|
260
261
|
}
|
|
262
|
+
const appInstalledByKey = new Map();
|
|
263
|
+
const appInstalledFor = (clientKey) => {
|
|
264
|
+
if (!appInstalledByKey.has(clientKey))
|
|
265
|
+
appInstalledByKey.set(clientKey, (0, discoverPaths_1.detectIdeAppInstalled)(clientKey));
|
|
266
|
+
return appInstalledByKey.get(clientKey) === true;
|
|
267
|
+
};
|
|
261
268
|
const rows = [];
|
|
262
269
|
for (const item of scanned) {
|
|
263
270
|
const list = serversByConfig.get(item.path.toLowerCase()) || [];
|
|
@@ -273,6 +280,7 @@ function buildClientCoverage(scanned, servers, cwd) {
|
|
|
273
280
|
cursorHooksInstalled: clientKey === 'cursor' ? hooks.installed : false,
|
|
274
281
|
cursorHookEvents: clientKey === 'cursor' ? hooks.events : [],
|
|
275
282
|
cursorHookShadow: clientKey === 'cursor' ? hooks.shadow : false,
|
|
283
|
+
appInstalled: appInstalledFor(clientKey),
|
|
276
284
|
});
|
|
277
285
|
}
|
|
278
286
|
// Cursor hooks apply globally even if only project mcp.json was scanned
|
|
@@ -287,6 +295,7 @@ function buildClientCoverage(scanned, servers, cwd) {
|
|
|
287
295
|
cursorHooksInstalled: true,
|
|
288
296
|
cursorHookEvents: hooks.events,
|
|
289
297
|
cursorHookShadow: hooks.shadow,
|
|
298
|
+
appInstalled: appInstalledFor('cursor'),
|
|
290
299
|
});
|
|
291
300
|
}
|
|
292
301
|
return rows.sort((a, b) => a.client.localeCompare(b.client));
|
|
@@ -2656,12 +2656,16 @@ function protectAllTargetFiles(extra, allowedClientKeys) {
|
|
|
2656
2656
|
seen.add(key);
|
|
2657
2657
|
files.push(candidate);
|
|
2658
2658
|
}
|
|
2659
|
-
//
|
|
2660
|
-
//
|
|
2661
|
-
|
|
2659
|
+
// Same trees discovery scores (`--scan-root auto` + admin extras). MSI
|
|
2660
|
+
// onboard cwd is the install folder, so without this walk project MCP
|
|
2661
|
+
// under C:\dev stays direct while the fleet matrix paints the IDE Exposed.
|
|
2662
|
+
// Skip clientKey `other` — repo-root mcp.json is reference inventory, not
|
|
2663
|
+
// a live client config.
|
|
2664
|
+
const protectionRoots = [...extraScanRoots, ...(0, discoverPaths_1.defaultScanRoots)()];
|
|
2665
|
+
const recursive = (0, discoverPaths_1.scanRootsForProjectConfigs)(protectionRoots, { excludedRoots: disabledScanRoots });
|
|
2662
2666
|
for (const candidate of recursive.candidates) {
|
|
2663
2667
|
const key = (0, discoverPaths_1.normalizedPath)(candidate.path);
|
|
2664
|
-
if (seen.has(key) || !allow(candidate.clientKey) || (0, discoverPaths_1.isExcludedPath)(candidate.path, disabledScanRoots))
|
|
2668
|
+
if (seen.has(key) || candidate.clientKey === 'other' || !allow(candidate.clientKey) || (0, discoverPaths_1.isExcludedPath)(candidate.path, disabledScanRoots))
|
|
2665
2669
|
continue;
|
|
2666
2670
|
seen.add(key);
|
|
2667
2671
|
files.push(candidate);
|
package/dist/version.json
CHANGED