feat(settings): improve tool detector display layout (#12906)

*  feat(settings): improve tool detector display layout

- Move version to left side with Name, display as Tag
- Right side: two lines (Available status + path), right-aligned
- Unavailable: single line centered
- Add runtime environment detectors (Node, Python, npm)
- Add i18n for system tools settings

Made-with: Cursor

* 🔧 fix(toolDetectors): ensure successful version check for Python runtime

- Update pythonDetector to enforce successful invocation of `--version` for confirming usable runtime.
- Removed redundant version handling logic to streamline the detection process.

Signed-off-by: Innei <tukon479@gmail.com>

---------

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei
2026-03-11 19:55:36 +08:00
committed by GitHub
parent bc50db6a8b
commit 21d1f0e472
24 changed files with 239 additions and 56 deletions

View File

@@ -18,6 +18,9 @@ import {
browserAutomationDetectors,
contentSearchDetectors,
fileSearchDetectors,
type IToolDetector,
runtimeEnvironmentDetectors,
type ToolCategory,
} from '@/modules/toolDetectors';
import type { IServiceModule } from '@/services';
import { createLogger } from '@/utils/logger';
@@ -187,24 +190,20 @@ export class App {
private registerBuiltinToolDetectors() {
logger.debug('Registering built-in tool detectors');
// Register content search tools (rg, ag, grep)
for (const detector of contentSearchDetectors) {
this.toolDetectorManager.register(detector, 'content-search');
}
const detectorCategories: Partial<Record<ToolCategory, IToolDetector[]>> = {
'runtime-environment': runtimeEnvironmentDetectors,
'ast-search': astSearchDetectors,
'browser-automation': browserAutomationDetectors,
'content-search': contentSearchDetectors,
'file-search': fileSearchDetectors,
};
// Register AST-based code search tools (ast-grep)
for (const detector of astSearchDetectors) {
this.toolDetectorManager.register(detector, 'ast-search');
}
// Register file search tools (mdfind, fd, find)
for (const detector of fileSearchDetectors) {
this.toolDetectorManager.register(detector, 'file-search');
}
// Register browser automation tools (agent-browser)
for (const detector of browserAutomationDetectors) {
this.toolDetectorManager.register(detector, 'browser-automation');
for (const [category, detectors] of Object.entries(detectorCategories)) {
if (detectors) {
for (const detector of detectors) {
this.toolDetectorManager.register(detector, category as ToolCategory);
}
}
}
logger.info(

View File

@@ -40,6 +40,7 @@ export type ToolCategory =
| 'ast-search'
| 'file-search'
| 'browser-automation'
| 'runtime-environment'
| 'system'
| 'custom';

View File

@@ -8,6 +8,7 @@
export { browserAutomationDetectors } from './agentBrowserDetectors';
export { astSearchDetectors, contentSearchDetectors } from './contentSearchDetectors';
export { fileSearchDetectors } from './fileSearchDetectors';
export { runtimeEnvironmentDetectors } from './runtimeEnvironmentDetectors';
// Re-export types for convenience
export type {

View File

@@ -0,0 +1,73 @@
import { exec } from 'node:child_process';
import { platform } from 'node:os';
import { promisify } from 'node:util';
import type { IToolDetector, ToolStatus } from '@/core/infrastructure/ToolDetectorManager';
import { createCommandDetector } from '@/core/infrastructure/ToolDetectorManager';
const execPromise = promisify(exec);
/**
* Node.js runtime detector
*/
export const nodeDetector: IToolDetector = createCommandDetector('node', {
description: 'Node.js - JavaScript runtime',
priority: 1,
});
/**
* NPM package manager detector
*/
export const npmDetector: IToolDetector = createCommandDetector('npm', {
description: 'npm - Node.js package manager',
priority: 2,
});
/**
* Python runtime detector
* Tries python3 (Unix) first, then python (cross-platform)
*/
export const pythonDetector: IToolDetector = {
description: 'Python - programming language runtime',
async detect(): Promise<ToolStatus> {
const commands = platform() === 'win32' ? ['python', 'py'] : ['python3', 'python'];
for (const cmd of commands) {
try {
const whichCmd = platform() === 'win32' ? 'where' : 'which';
const { stdout: pathOut } = await execPromise(`${whichCmd} ${cmd}`, { timeout: 3000 });
const toolPath = pathOut.trim().split('\n')[0];
// Must successfully invoke --version to confirm usable runtime (e.g. avoid
// Windows Microsoft Store alias which is found by where but fails to run)
const { stdout: versionOut } = await execPromise(`${cmd} --version`, {
timeout: 3000,
});
const version = versionOut.trim().split('\n')[0];
return {
available: true,
path: toolPath,
version,
};
} catch {
continue;
}
}
return {
available: false,
};
},
name: 'python',
priority: 3,
};
/**
* All runtime environment detectors
*/
export const runtimeEnvironmentDetectors: IToolDetector[] = [
nodeDetector,
npmDetector,
pythonDetector,
];