Files
NetMesh/domain/codingCliProviderMatch.ts
zhaolei 3c72efcb7f
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
[Init] Initial commit - NetMesh terminal manager
2026-09-13 18:24:01 +08:00

130 lines
3.8 KiB
TypeScript

import { CODING_CLI_PROVIDERS, getCodingCliProvider, type CodingCliProvider } from './codingCliProviders';
import {
inferCodingCliProviderFromTitleSignals,
titleIncludesPhrase,
} from './codingCliTitleParse';
import type { Host, TerminalSession } from '../types';
export type SessionCodingCliSource = Pick<
TerminalSession,
| 'dynamicTitle'
| 'startupCommand'
| 'customName'
| 'hostLabel'
| 'localShell'
| 'localShellName'
| 'codingCliProviderId'
> & {
hostStartupCommand?: Host['startupCommand'];
};
export function getCodingCliCommandBasename(commandLine: string): string {
const trimmed = commandLine.trim();
if (!trimmed) return '';
const firstToken = trimmed.split(/\s+/)[0] ?? '';
const segments = firstToken.split(/[\\/]/);
const basename = (segments.pop() || '').toLowerCase();
return basename.replace(/\.(exe|cmd|bat|ps1)$/i, '');
}
export function matchCodingCliProviderFromCommand(commandLine: string): CodingCliProvider | undefined {
const basename = getCodingCliCommandBasename(commandLine);
if (!basename) return undefined;
return CODING_CLI_PROVIDERS.find((provider) => (
provider.command === basename
|| provider.aliases?.some((alias) => alias === basename)
));
}
export function matchCodingCliProviderFromTitle(title: string): CodingCliProvider | undefined {
const inferredId = inferCodingCliProviderFromTitleSignals(title);
if (inferredId) {
return getCodingCliProvider(inferredId);
}
const normalized = title.toLowerCase();
if (!normalized.trim()) return undefined;
const ranked = [...CODING_CLI_PROVIDERS].sort((left, right) => {
const leftHints = [
...(left.titleHints ?? []),
left.label,
left.command,
...(left.aliases ?? []),
];
const rightHints = [
...(right.titleHints ?? []),
right.label,
right.command,
...(right.aliases ?? []),
];
const leftMax = Math.max(...leftHints.map((hint) => hint.length), 0);
const rightMax = Math.max(...rightHints.map((hint) => hint.length), 0);
return rightMax - leftMax;
});
for (const provider of ranked) {
const hints = [
...(provider.titleHints ?? []),
provider.label,
provider.command,
...(provider.aliases ?? []),
];
if (hints.some((hint) => titleIncludesPhrase(normalized, hint))) {
return provider;
}
}
return undefined;
}
/**
* Resolve the active coding CLI for a terminal session from launch commands
* and shell-reported window titles.
*/
export function resolveCodingCliProviderFromCommandCandidates(
source: Pick<SessionCodingCliSource, 'startupCommand' | 'localShell'>,
host?: Pick<Host, 'startupCommand'>,
): CodingCliProvider | undefined {
const commandCandidates = [
source.startupCommand,
host?.startupCommand,
source.localShell,
].filter((value): value is string => Boolean(value?.trim()));
for (const commandLine of commandCandidates) {
const provider = matchCodingCliProviderFromCommand(commandLine);
if (provider) return provider;
}
return undefined;
}
export function resolveSessionCodingCliProvider(
source: SessionCodingCliSource,
host?: Pick<Host, 'startupCommand'>,
): CodingCliProvider | undefined {
if (source.codingCliProviderId) {
const sticky = getCodingCliProvider(source.codingCliProviderId);
if (sticky) return sticky;
}
const commandProvider = resolveCodingCliProviderFromCommandCandidates(source, host);
if (commandProvider) return commandProvider;
if (!source.customName) {
const dynamicTitle = source.dynamicTitle?.trim();
if (dynamicTitle) {
const provider = matchCodingCliProviderFromTitle(dynamicTitle);
if (provider) return provider;
}
}
if (source.localShellName) {
return matchCodingCliProviderFromTitle(source.localShellName);
}
return undefined;
}