mirror of
https://github.com/stablyai/orca.git
synced 2026-10-07 16:02:29 +00:00
fix(jcode): decompose the four files jcode pushed over max-lines
Adding an agent tipped four modules past their line budget. AGENTS.md forbids a `max-lines` disable or a per-file bump, so each is split on a real seam rather than silenced: - agent-catalog.tsx keeps `AgentIcon`, which 70+ files import, and the rows move out. The rows alone exceed the 300-line budget a `.ts` file gets, so they follow the primary/secondary split this repo already uses for commit-message agent specs. - getAgentResumeArgv -> agent-resume-argv.ts, re-exported so the 18 call sites keep one import path. - isDiscoverableSessionFile/pathSegments -> session-file-discovery.ts. - remoteCodexSources -> remote-session-scanner-codex-sources.ts; Codex is the one remote agent with two CODEX_HOME roots. Also: - Records the readiness-census baseline jcode now needs. main added that gate while this branch was out; the fixture is the recorded 74-case matrix, not a hand-written one. - Restores two entries a rebase resolution silently dropped from config/tsconfig.cli.json (gitlab/project-ref-parser, startup/shell-path-probe). Nothing to do with jcode; losing them was a conflict-resolution mistake on this branch. Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
This commit is contained in:
committed by
Neil
co-authored by
czzczz
parent
84d246b9f0
commit
af4c3e2962
@@ -0,0 +1,51 @@
|
||||
import { joinRemotePath, type RemoteHostPlatform } from '../ssh/ssh-remote-platform'
|
||||
import { parseCodexSessionContent } from './session-scanner-codex-parser'
|
||||
import { remoteCodexIndexedTitleReader } from './remote-session-scanner-codex-index'
|
||||
import type { RemoteSessionContent } from './remote-session-content-lines'
|
||||
import type { FileWithMtime } from './session-scanner-types'
|
||||
import type { RemoteScannerContext, RemoteSessionSource } from './remote-session-scanner-types'
|
||||
|
||||
/** Codex is the one remote agent whose sessions live under two CODEX_HOME roots, so it
|
||||
* yields a source per root. Split from `remote-session-scanner-sources.ts` so adding an
|
||||
* agent does not push that module past its line budget. */
|
||||
export function remoteCodexSources(
|
||||
remoteHome: string,
|
||||
hostPlatform: RemoteHostPlatform
|
||||
): RemoteSessionSource[] {
|
||||
return [
|
||||
joinRemotePath(hostPlatform, remoteHome, '.codex'),
|
||||
joinRemotePath(
|
||||
hostPlatform,
|
||||
remoteHome,
|
||||
'.local',
|
||||
'share',
|
||||
'orca',
|
||||
'codex-runtime-home',
|
||||
'home'
|
||||
)
|
||||
].map((codexHome) => {
|
||||
const parse = (
|
||||
file: FileWithMtime,
|
||||
content: RemoteSessionContent,
|
||||
context: RemoteScannerContext
|
||||
) =>
|
||||
parseCodexSessionContent({
|
||||
file,
|
||||
content,
|
||||
platform: context.hostPlatform.os,
|
||||
codexHome,
|
||||
executionHostId: context.executionHostId,
|
||||
executionHostPlatform: context.hostPlatform.os,
|
||||
signal: context.signal,
|
||||
readIndexedTitle: remoteCodexIndexedTitleReader(codexHome, context)
|
||||
})
|
||||
return {
|
||||
agent: 'codex',
|
||||
rootDir: joinRemotePath(hostPlatform, codexHome, 'sessions'),
|
||||
codexHome,
|
||||
extensions: ['.jsonl'],
|
||||
parse,
|
||||
parseLines: parse
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -10,7 +10,6 @@ import type { RemoteHostPlatform } from '../ssh/ssh-remote-platform'
|
||||
import { joinRemotePath } from '../ssh/ssh-remote-platform'
|
||||
import { parseAntigravitySessionContent } from './session-scanner-antigravity-parser'
|
||||
import { isAntigravityTranscriptPath } from './session-scanner-antigravity-paths'
|
||||
import { parseCodexSessionContent } from './session-scanner-codex-parser'
|
||||
import { parseDroidSessionContent } from './session-scanner-droid-parser'
|
||||
import { parseClaudeSessionContent } from './session-scanner-primary-parsers'
|
||||
import { parseCodebuddySessionContent } from './session-scanner-codebuddy-parser'
|
||||
@@ -33,7 +32,7 @@ import {
|
||||
remotePrimeAgentSessionsSegments
|
||||
} from './remote-session-scanner-source-parsers'
|
||||
import type { FileWithMtime } from './session-scanner-types'
|
||||
import { remoteCodexIndexedTitleReader } from './remote-session-scanner-codex-index'
|
||||
import { remoteCodexSources } from './remote-session-scanner-codex-sources'
|
||||
import { remoteClineSource } from './remote-session-scanner-cline-source'
|
||||
import { remoteDevinSource } from './remote-session-scanner-devin-source'
|
||||
import type {
|
||||
@@ -258,48 +257,6 @@ function jsonlSource(
|
||||
}
|
||||
}
|
||||
|
||||
function remoteCodexSources(
|
||||
remoteHome: string,
|
||||
hostPlatform: RemoteHostPlatform
|
||||
): RemoteSessionSource[] {
|
||||
return [
|
||||
joinRemotePath(hostPlatform, remoteHome, '.codex'),
|
||||
joinRemotePath(
|
||||
hostPlatform,
|
||||
remoteHome,
|
||||
'.local',
|
||||
'share',
|
||||
'orca',
|
||||
'codex-runtime-home',
|
||||
'home'
|
||||
)
|
||||
].map((codexHome) => {
|
||||
const parse = (
|
||||
file: FileWithMtime,
|
||||
content: RemoteSessionContent,
|
||||
context: RemoteScannerContext
|
||||
) =>
|
||||
parseCodexSessionContent({
|
||||
file,
|
||||
content,
|
||||
platform: context.hostPlatform.os,
|
||||
codexHome,
|
||||
executionHostId: context.executionHostId,
|
||||
executionHostPlatform: context.hostPlatform.os,
|
||||
signal: context.signal,
|
||||
readIndexedTitle: remoteCodexIndexedTitleReader(codexHome, context)
|
||||
})
|
||||
return {
|
||||
agent: 'codex',
|
||||
rootDir: joinRemotePath(hostPlatform, codexHome, 'sessions'),
|
||||
codexHome,
|
||||
extensions: ['.jsonl'],
|
||||
parse,
|
||||
parseLines: parse
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function remoteOpenClawSources(
|
||||
remoteHome: string,
|
||||
hostPlatform: RemoteHostPlatform
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { dirname, extname, relative } from 'node:path'
|
||||
import type { AiVaultAgentSource } from './session-scanner-agent-sources'
|
||||
|
||||
/** Whether a walked path is a session file this source owns. Split from the source table
|
||||
* so adding an agent does not push that module past its line budget. */
|
||||
export function isDiscoverableSessionFile(
|
||||
source: AiVaultAgentSource,
|
||||
rootDir: string,
|
||||
filePath: string
|
||||
): boolean {
|
||||
if (!source.extensions.includes(extname(filePath).toLowerCase())) {
|
||||
return false
|
||||
}
|
||||
if (source.filePredicate && !source.filePredicate(filePath)) {
|
||||
return false
|
||||
}
|
||||
const { directoryPredicate } = source
|
||||
if (!directoryPredicate) {
|
||||
return true
|
||||
}
|
||||
// Indexed like walkSessionFiles: depth 0 is a child of rootDir.
|
||||
return pathSegments(relative(rootDir, dirname(filePath)))
|
||||
.filter(Boolean)
|
||||
.every((name, depth) => directoryPredicate(name, depth))
|
||||
}
|
||||
|
||||
export function pathSegments(filePath: string): string[] {
|
||||
return filePath.split(/[\\/]/)
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { homedir } from 'node:os'
|
||||
import { basename, dirname, extname, join, relative } from 'node:path'
|
||||
import { basename, dirname, join } from 'node:path'
|
||||
import { pathSegments } from './session-file-discovery'
|
||||
import { resolveAbsoluteDirOverride } from '../../shared/absolute-dir-override'
|
||||
import type { AiVaultAgent } from '../../shared/ai-vault-types'
|
||||
import type { AiVaultDeletableAgent } from '../../shared/ai-vault-session-deletion'
|
||||
@@ -341,27 +342,5 @@ export const AI_VAULT_AGENT_SOURCES: AiVaultAgentSourceTable = {
|
||||
* predicate. This is the delete validator's accept rule, so a path no scan
|
||||
* would ever list can't become a delete target either.
|
||||
*/
|
||||
export function isDiscoverableSessionFile(
|
||||
source: AiVaultAgentSource,
|
||||
rootDir: string,
|
||||
filePath: string
|
||||
): boolean {
|
||||
if (!source.extensions.includes(extname(filePath).toLowerCase())) {
|
||||
return false
|
||||
}
|
||||
if (source.filePredicate && !source.filePredicate(filePath)) {
|
||||
return false
|
||||
}
|
||||
const { directoryPredicate } = source
|
||||
if (!directoryPredicate) {
|
||||
return true
|
||||
}
|
||||
// Indexed like walkSessionFiles: depth 0 is a child of rootDir.
|
||||
return pathSegments(relative(rootDir, dirname(filePath)))
|
||||
.filter(Boolean)
|
||||
.every((name, depth) => directoryPredicate(name, depth))
|
||||
}
|
||||
|
||||
function pathSegments(filePath: string): string[] {
|
||||
return filePath.split(/[\\/]/)
|
||||
}
|
||||
export { isDiscoverableSessionFile } from './session-file-discovery'
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"description": "jcode: title x first-party status on a painted screen; screen x foreground under the titles that leave the low lanes open; dialog order x title. Each read clocked and clockless.",
|
||||
"cases": {
|
||||
"title=working-spinner status=none screen=present fg=agent clock=clocked": "now=working edge=working quiet=working wait=pending",
|
||||
"title=working-spinner status=none screen=present fg=agent clock=clockless": "verdict=working wait=pending",
|
||||
"title=working-spinner status=done-fresh screen=present fg=agent clock=clocked": "now=working edge=working quiet=working wait=pending",
|
||||
"title=working-spinner status=done-fresh screen=present fg=agent clock=clockless": "verdict=working wait=pending",
|
||||
"title=working-spinner status=done-stale screen=present fg=agent clock=clocked": "now=working edge=working quiet=working wait=pending",
|
||||
"title=working-spinner status=done-stale screen=present fg=agent clock=clockless": "verdict=working wait=pending",
|
||||
"title=working-spinner status=working-fresh screen=present fg=agent clock=clocked": "now=working edge=working quiet=working wait=pending",
|
||||
"title=working-spinner status=working-fresh screen=present fg=agent clock=clockless": "verdict=working wait=pending",
|
||||
"title=working-spinner status=working-stale screen=present fg=agent clock=clocked": "now=working edge=working quiet=working wait=pending",
|
||||
"title=working-spinner status=working-stale screen=present fg=agent clock=clockless": "verdict=working wait=pending",
|
||||
"title=working-spinner status=blocked-fresh screen=present fg=agent clock=clocked": "now=pending:closed edge=pending:closed quiet=pending:closed wait=pending",
|
||||
"title=working-spinner status=blocked-fresh screen=present fg=agent clock=clockless": "verdict=pending:closed wait=pending",
|
||||
"title=working-spinner status=blocked-stale screen=present fg=agent clock=clocked": "now=working edge=working quiet=working wait=pending",
|
||||
"title=working-spinner status=blocked-stale screen=present fg=agent clock=clockless": "verdict=working wait=pending",
|
||||
"title=name-only status=none screen=present fg=agent clock=clocked": "now=ready-weak edge=ready-weak quiet=ready-weak wait=ready@start",
|
||||
"title=name-only status=none screen=present fg=agent clock=clockless": "verdict=ready-weak wait=ready@start",
|
||||
"title=name-only status=done-fresh screen=present fg=agent clock=clocked": "now=ready-weak edge=ready-weak quiet=ready-weak wait=ready@start",
|
||||
"title=name-only status=done-fresh screen=present fg=agent clock=clockless": "verdict=ready-weak wait=ready@start",
|
||||
"title=name-only status=done-stale screen=present fg=agent clock=clocked": "now=ready-weak edge=ready-weak quiet=ready-weak wait=ready@start",
|
||||
"title=name-only status=done-stale screen=present fg=agent clock=clockless": "verdict=ready-weak wait=ready@start",
|
||||
"title=name-only status=working-fresh screen=present fg=agent clock=clocked": "now=working edge=working quiet=working wait=pending",
|
||||
"title=name-only status=working-fresh screen=present fg=agent clock=clockless": "verdict=working wait=pending",
|
||||
"title=name-only status=working-stale screen=present fg=agent clock=clocked": "now=ready-weak edge=ready-weak quiet=ready-weak wait=ready@start",
|
||||
"title=name-only status=working-stale screen=present fg=agent clock=clockless": "verdict=ready-weak wait=ready@start",
|
||||
"title=name-only status=blocked-fresh screen=present fg=agent clock=clocked": "now=pending:closed edge=pending:closed quiet=pending:closed wait=pending",
|
||||
"title=name-only status=blocked-fresh screen=present fg=agent clock=clockless": "verdict=pending:closed wait=pending",
|
||||
"title=name-only status=blocked-stale screen=present fg=agent clock=clocked": "now=ready-weak edge=ready-weak quiet=ready-weak wait=ready@start",
|
||||
"title=name-only status=blocked-stale screen=present fg=agent clock=clockless": "verdict=ready-weak wait=ready@start",
|
||||
"title=none status=none screen=present fg=agent clock=clocked": "now=pending:closed edge=pending:closed quiet=pending:closed wait=pending",
|
||||
"title=none status=none screen=present fg=agent clock=clockless": "verdict=pending:closed wait=pending",
|
||||
"title=none status=done-fresh screen=present fg=agent clock=clocked": "now=pending:closed edge=pending:closed quiet=pending:closed wait=pending",
|
||||
"title=none status=done-fresh screen=present fg=agent clock=clockless": "verdict=pending:closed wait=pending",
|
||||
"title=none status=done-stale screen=present fg=agent clock=clocked": "now=pending:closed edge=pending:closed quiet=pending:closed wait=pending",
|
||||
"title=none status=done-stale screen=present fg=agent clock=clockless": "verdict=pending:closed wait=pending",
|
||||
"title=none status=working-fresh screen=present fg=agent clock=clocked": "now=working edge=working quiet=working wait=pending",
|
||||
"title=none status=working-fresh screen=present fg=agent clock=clockless": "verdict=working wait=pending",
|
||||
"title=none status=working-stale screen=present fg=agent clock=clocked": "now=pending:closed edge=pending:closed quiet=pending:closed wait=pending",
|
||||
"title=none status=working-stale screen=present fg=agent clock=clockless": "verdict=pending:closed wait=pending",
|
||||
"title=none status=blocked-fresh screen=present fg=agent clock=clocked": "now=pending:closed edge=pending:closed quiet=pending:closed wait=pending",
|
||||
"title=none status=blocked-fresh screen=present fg=agent clock=clockless": "verdict=pending:closed wait=pending",
|
||||
"title=none status=blocked-stale screen=present fg=agent clock=clocked": "now=pending:closed edge=pending:closed quiet=pending:closed wait=pending",
|
||||
"title=none status=blocked-stale screen=present fg=agent clock=clockless": "verdict=pending:closed wait=pending",
|
||||
"title=name-only status=none screen=present fg=shell clock=clocked": "now=ready-weak edge=ready-weak quiet=ready-weak wait=ready@start",
|
||||
"title=name-only status=none screen=present fg=shell clock=clockless": "verdict=ready-weak wait=ready@start",
|
||||
"title=name-only status=none screen=untrusted fg=agent clock=clocked": "now=ready-weak edge=ready-weak quiet=ready-weak wait=ready@start",
|
||||
"title=name-only status=none screen=untrusted fg=agent clock=clockless": "verdict=ready-weak wait=ready@start",
|
||||
"title=name-only status=none screen=untrusted fg=shell clock=clocked": "now=ready-weak edge=ready-weak quiet=ready-weak wait=ready@start",
|
||||
"title=name-only status=none screen=untrusted fg=shell clock=clockless": "verdict=ready-weak wait=ready@start",
|
||||
"title=name-only status=none screen=absent fg=agent clock=clocked": "now=ready-weak edge=ready-weak quiet=ready-weak wait=ready@start",
|
||||
"title=name-only status=none screen=absent fg=agent clock=clockless": "verdict=ready-weak wait=ready@start",
|
||||
"title=name-only status=none screen=absent fg=shell clock=clocked": "now=ready-weak edge=ready-weak quiet=ready-weak wait=ready@start",
|
||||
"title=name-only status=none screen=absent fg=shell clock=clockless": "verdict=ready-weak wait=ready@start",
|
||||
"title=none status=none screen=present fg=shell clock=clocked": "now=pending:closed edge=pending:closed quiet=pending:closed wait=pending",
|
||||
"title=none status=none screen=present fg=shell clock=clockless": "verdict=pending:closed wait=pending",
|
||||
"title=none status=none screen=untrusted fg=agent clock=clocked": "now=pending:closed edge=pending:closed quiet=pending:closed wait=pending",
|
||||
"title=none status=none screen=untrusted fg=agent clock=clockless": "verdict=pending:closed wait=pending",
|
||||
"title=none status=none screen=untrusted fg=shell clock=clocked": "now=pending:closed edge=pending:closed quiet=pending:closed wait=pending",
|
||||
"title=none status=none screen=untrusted fg=shell clock=clockless": "verdict=pending:closed wait=pending",
|
||||
"title=none status=none screen=absent fg=agent clock=clocked": "now=pending:closed edge=pending:closed quiet=pending:closed wait=pending",
|
||||
"title=none status=none screen=absent fg=agent clock=clockless": "verdict=pending:closed wait=pending",
|
||||
"title=none status=none screen=absent fg=shell clock=clocked": "now=pending:closed edge=pending:closed quiet=pending:closed wait=pending",
|
||||
"title=none status=none screen=absent fg=shell clock=clockless": "verdict=pending:closed wait=pending",
|
||||
"title=working-spinner status=none screen=dialog-last fg=agent clock=clocked": "now=blocked:agent-trust-workspace edge=blocked:agent-trust-workspace quiet=blocked:agent-trust-workspace wait=blocked:agent-trust-workspace@start",
|
||||
"title=working-spinner status=none screen=dialog-last fg=agent clock=clockless": "verdict=blocked:agent-trust-workspace wait=blocked:agent-trust-workspace@start",
|
||||
"title=working-spinner status=none screen=ready-last fg=agent clock=clocked": "now=blocked:agent-trust-workspace edge=blocked:agent-trust-workspace quiet=blocked:agent-trust-workspace wait=blocked:agent-trust-workspace@start",
|
||||
"title=working-spinner status=none screen=ready-last fg=agent clock=clockless": "verdict=blocked:agent-trust-workspace wait=blocked:agent-trust-workspace@start",
|
||||
"title=name-only status=none screen=dialog-last fg=agent clock=clocked": "now=blocked:agent-trust-workspace edge=blocked:agent-trust-workspace quiet=blocked:agent-trust-workspace wait=blocked:agent-trust-workspace@start",
|
||||
"title=name-only status=none screen=dialog-last fg=agent clock=clockless": "verdict=blocked:agent-trust-workspace wait=blocked:agent-trust-workspace@start",
|
||||
"title=name-only status=none screen=ready-last fg=agent clock=clocked": "now=blocked:agent-trust-workspace edge=blocked:agent-trust-workspace quiet=blocked:agent-trust-workspace wait=blocked:agent-trust-workspace@start",
|
||||
"title=name-only status=none screen=ready-last fg=agent clock=clockless": "verdict=blocked:agent-trust-workspace wait=blocked:agent-trust-workspace@start",
|
||||
"title=none status=none screen=dialog-last fg=agent clock=clocked": "now=blocked:agent-trust-workspace edge=blocked:agent-trust-workspace quiet=blocked:agent-trust-workspace wait=blocked:agent-trust-workspace@start",
|
||||
"title=none status=none screen=dialog-last fg=agent clock=clockless": "verdict=blocked:agent-trust-workspace wait=blocked:agent-trust-workspace@start",
|
||||
"title=none status=none screen=ready-last fg=agent clock=clocked": "now=blocked:agent-trust-workspace edge=blocked:agent-trust-workspace quiet=blocked:agent-trust-workspace wait=blocked:agent-trust-workspace@start",
|
||||
"title=none status=none screen=ready-last fg=agent clock=clockless": "verdict=blocked:agent-trust-workspace wait=blocked:agent-trust-workspace@start"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
import { getCatalogPlatform } from './agent-catalog-platform'
|
||||
import openClaudeLogoUrl from '../../../../resources/openclaude-logo.png?url'
|
||||
import { getTuiAgentLaunchCommand, TUI_AGENT_CONFIG } from '../../../shared/tui-agent-config'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
import type { AgentCatalogEntry } from './agent-catalog'
|
||||
|
||||
/** The agents Orca drives first-party. Split from the community tail so adding an agent
|
||||
* does not push either module past its line budget; the two are concatenated in order
|
||||
* by `buildAgentCatalogEntries`. */
|
||||
export function primaryAgentCatalogEntries(): AgentCatalogEntry[] {
|
||||
return [
|
||||
{
|
||||
id: 'claude',
|
||||
label: translate('auto.lib.agent.catalog.0708ed89f1', 'Claude'),
|
||||
cmd: 'claude',
|
||||
homepageUrl: 'https://code.claude.com/docs'
|
||||
},
|
||||
{
|
||||
id: 'claude-agent-teams',
|
||||
label: translate('auto.lib.agent.catalog.bf53f09bf8', 'Claude Agent Teams'),
|
||||
cmd: getTuiAgentLaunchCommand(TUI_AGENT_CONFIG['claude-agent-teams'], getCatalogPlatform()),
|
||||
homepageUrl: 'https://code.claude.com/docs/en/agent-teams'
|
||||
},
|
||||
{
|
||||
id: 'openclaude',
|
||||
label: translate('auto.lib.agent.catalog.a5fc0cb622', 'OpenClaude'),
|
||||
cmd: 'openclaude',
|
||||
// Why: OpenClaude's published favicon has a padded 500px canvas; Orca
|
||||
// uses a cropped derivative of that official asset so 12px tab icons stay legible.
|
||||
iconUrl: openClaudeLogoUrl,
|
||||
homepageUrl: 'https://openclaude.gitlawb.com/'
|
||||
},
|
||||
{
|
||||
id: 'codex',
|
||||
label: translate('auto.lib.agent.catalog.760bc6883d', 'Codex'),
|
||||
cmd: 'codex',
|
||||
homepageUrl: 'https://github.com/openai/codex'
|
||||
},
|
||||
{
|
||||
id: 'grok',
|
||||
label: translate('auto.lib.agent.catalog.0baad2d5d2', 'Grok'),
|
||||
cmd: 'grok',
|
||||
faviconDomain: 'x.ai',
|
||||
homepageUrl: 'https://x.ai/cli'
|
||||
},
|
||||
{
|
||||
id: 'copilot',
|
||||
label: translate('auto.lib.agent.catalog.706b0fe68b', 'GitHub Copilot'),
|
||||
cmd: 'copilot',
|
||||
homepageUrl: 'https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli'
|
||||
},
|
||||
{
|
||||
id: 'opencode2',
|
||||
label: translate('auto.lib.agent.catalog.opencode2_label', 'OpenCode 2'),
|
||||
cmd: 'opencode2',
|
||||
homepageUrl: 'https://opencode.ai/v2/docs/'
|
||||
},
|
||||
{
|
||||
id: 'opencode',
|
||||
label: translate('auto.lib.agent.catalog.e7a4ca5103', 'OpenCode'),
|
||||
cmd: 'opencode',
|
||||
homepageUrl: 'https://opencode.ai/docs/cli/'
|
||||
},
|
||||
{
|
||||
id: 'mimo-code',
|
||||
label: translate('auto.lib.agent.catalog.mimo_code_label', 'MiMo Code'),
|
||||
cmd: 'mimo',
|
||||
faviconDomain: 'mimo.xiaomi.com',
|
||||
homepageUrl: 'https://mimo.xiaomi.com/coder'
|
||||
},
|
||||
{
|
||||
id: 'ante',
|
||||
label: translate('auto.lib.agent.catalog.da41abbdd4', 'Ante'),
|
||||
cmd: 'ante',
|
||||
faviconDomain: 'antigma.ai',
|
||||
homepageUrl: 'https://github.com/AntigmaLabs/ante-preview'
|
||||
},
|
||||
{
|
||||
id: 'trae',
|
||||
label: translate('auto.lib.agent.catalog.060d152fb5', 'Trae'),
|
||||
// Why: matches TUI_AGENT_CONFIG.trae.detectCmd, not the ambiguous `trae-cli` — see the Why there.
|
||||
cmd: 'traecli',
|
||||
// Why: bare `trae.cn` 404s on Google's favicon service.
|
||||
faviconDomain: 'www.trae.cn',
|
||||
homepageUrl: 'https://docs.trae.cn/cli_get-started-with-trae-cli'
|
||||
},
|
||||
{
|
||||
id: 'muse',
|
||||
label: translate('auto.lib.agent.catalog.muse_label', 'Muse'),
|
||||
cmd: 'muse',
|
||||
faviconDomain: 'dev.meta.ai',
|
||||
homepageUrl: 'https://dev.meta.ai/docs/muse-code'
|
||||
},
|
||||
{
|
||||
id: 'dsh',
|
||||
label: translate('auto.lib.agent.catalog.dsh_label', 'DeepSeek Harness'),
|
||||
cmd: 'dsh-tui',
|
||||
searchAliases: ['deepseek', 'dsh', 'dst', 'deepseek harness'],
|
||||
homepageUrl: 'https://deepseek-harness.github.io/deepseek-harness/'
|
||||
},
|
||||
{
|
||||
id: 'qoder',
|
||||
label: translate('auto.lib.agent.catalog.qoder_label', 'Qoder CLI'),
|
||||
cmd: 'qodercli',
|
||||
faviconDomain: 'qoder.com',
|
||||
homepageUrl: 'https://docs.qoder.com/cli/overview'
|
||||
},
|
||||
{
|
||||
id: 'qoder-cn',
|
||||
label: translate('auto.lib.agent.catalog.qoder_cn_label', 'Qoder CLI China'),
|
||||
cmd: 'qoderclicn',
|
||||
faviconDomain: 'qoder.cn',
|
||||
homepageUrl: 'https://docs.qoder.cn/cli/overview'
|
||||
},
|
||||
{
|
||||
id: 'zcode',
|
||||
label: translate('auto.lib.agent.catalog.zcode_label', 'ZCode'),
|
||||
cmd: 'zcode',
|
||||
faviconDomain: 'zcode.z.ai',
|
||||
homepageUrl: 'https://zcode.z.ai/en/docs'
|
||||
},
|
||||
{
|
||||
id: 'pi',
|
||||
label: translate('auto.lib.agent.catalog.302934c5d9', 'Pi'),
|
||||
cmd: 'pi',
|
||||
homepageUrl: 'https://pi.dev'
|
||||
},
|
||||
{
|
||||
id: 'omp',
|
||||
label: translate('auto.lib.agent.catalog.09973b4d84', 'OMP'),
|
||||
cmd: 'omp',
|
||||
searchAliases: ['oh-my-pi', 'oh my pi'],
|
||||
// Why: no faviconDomain — omp renders the hand-authored OmpIcon glyph, so a
|
||||
// favicon fallback would never be reached.
|
||||
homepageUrl: 'https://omp.sh'
|
||||
},
|
||||
{
|
||||
id: 'prime-agent',
|
||||
label: translate('auto.lib.agent.catalog.d443a47995', 'Prime Agent'),
|
||||
cmd: 'prime-agent',
|
||||
faviconDomain: 'primeintellect.ai',
|
||||
homepageUrl: 'https://github.com/PrimeIntellect-ai/prime-agent'
|
||||
},
|
||||
{
|
||||
id: 'gemini',
|
||||
label: translate('auto.lib.agent.catalog.12e6baa4f7', 'Gemini'),
|
||||
cmd: 'gemini',
|
||||
faviconDomain: 'gemini.google.com',
|
||||
homepageUrl: 'https://github.com/google-gemini/gemini-cli'
|
||||
},
|
||||
{
|
||||
id: 'antigravity',
|
||||
label: translate('auto.lib.agent.catalog.691dd11789', 'Antigravity'),
|
||||
cmd: 'agy',
|
||||
faviconDomain: 'antigravity.google',
|
||||
homepageUrl: 'https://antigravity.google/docs/cli-overview'
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
import { translate } from '@/i18n/i18n'
|
||||
import type { AgentCatalogEntry } from './agent-catalog'
|
||||
|
||||
/** The community tail of the catalog; see `primaryAgentCatalogEntries`. */
|
||||
export function secondaryAgentCatalogEntries(): AgentCatalogEntry[] {
|
||||
return [
|
||||
{
|
||||
id: 'aider',
|
||||
label: translate('auto.lib.agent.catalog.b32627f09b', 'Aider'),
|
||||
cmd: 'aider',
|
||||
homepageUrl: 'https://aider.chat/docs/'
|
||||
},
|
||||
{
|
||||
id: 'goose',
|
||||
label: translate('auto.lib.agent.catalog.8da11d876c', 'Goose'),
|
||||
cmd: 'goose',
|
||||
faviconDomain: 'goose-docs.ai',
|
||||
homepageUrl: 'https://block.github.io/goose/docs/quickstart/'
|
||||
},
|
||||
{
|
||||
id: 'amp',
|
||||
label: translate('auto.lib.agent.catalog.c73c573939', 'Amp'),
|
||||
cmd: 'amp',
|
||||
faviconDomain: 'ampcode.com',
|
||||
homepageUrl: 'https://ampcode.com/manual#install'
|
||||
},
|
||||
{
|
||||
id: 'kilo',
|
||||
label: translate('auto.lib.agent.catalog.918ba4ffed', 'Kilocode'),
|
||||
cmd: 'kilo',
|
||||
homepageUrl: 'https://kilo.ai/docs/cli'
|
||||
},
|
||||
{
|
||||
id: 'kiro',
|
||||
label: translate('auto.lib.agent.catalog.e0247254f2', 'Kiro'),
|
||||
// Why: the Kiro installer (https://cli.kiro.dev/install) ships a binary
|
||||
// named `kiro-cli`, not `kiro`. Match TUI_AGENT_CONFIG.kiro.detectCmd so
|
||||
// the settings pane's "default command" hint aligns with what Orca
|
||||
// actually looks for on PATH.
|
||||
cmd: 'kiro-cli',
|
||||
faviconDomain: 'kiro.dev',
|
||||
homepageUrl: 'https://kiro.dev/docs/cli/'
|
||||
},
|
||||
{
|
||||
id: 'crush',
|
||||
label: translate('auto.lib.agent.catalog.9477377a2a', 'Charm'),
|
||||
cmd: 'crush',
|
||||
faviconDomain: 'charm.sh',
|
||||
homepageUrl: 'https://github.com/charmbracelet/crush'
|
||||
},
|
||||
{
|
||||
id: 'aug',
|
||||
label: translate('auto.lib.agent.catalog.5e8eff11b3', 'Auggie'),
|
||||
cmd: 'auggie',
|
||||
faviconDomain: 'augmentcode.com',
|
||||
homepageUrl: 'https://docs.augmentcode.com/cli/overview'
|
||||
},
|
||||
{
|
||||
id: 'autohand',
|
||||
label: translate('auto.lib.agent.catalog.1f8a19e9ad', 'Autohand Code'),
|
||||
cmd: 'autohand',
|
||||
faviconDomain: 'autohand.ai',
|
||||
homepageUrl: 'https://github.com/autohandai/code-cli'
|
||||
},
|
||||
{
|
||||
id: 'cline',
|
||||
label: translate('auto.lib.agent.catalog.cbaf0c2e0b', 'Cline'),
|
||||
cmd: 'cline',
|
||||
faviconDomain: 'cline.bot',
|
||||
homepageUrl: 'https://docs.cline.bot/cline-cli/overview'
|
||||
},
|
||||
{
|
||||
id: 'codebuff',
|
||||
label: translate('auto.lib.agent.catalog.4238b771b5', 'Codebuff'),
|
||||
cmd: 'codebuff',
|
||||
faviconDomain: 'codebuff.com',
|
||||
homepageUrl: 'https://www.codebuff.com/docs/help/quick-start'
|
||||
},
|
||||
{
|
||||
id: 'freebuff',
|
||||
label: translate('auto.lib.agent.catalog.freebuff_label', 'Freebuff'),
|
||||
cmd: 'freebuff',
|
||||
faviconDomain: 'freebuff.com',
|
||||
homepageUrl: 'https://freebuff.com/cli'
|
||||
},
|
||||
{
|
||||
id: 'command-code',
|
||||
label: translate('auto.lib.agent.catalog.6f8056a565', 'Command Code'),
|
||||
// Why: `npm i -g command-code` installs both `command-code` and the
|
||||
// shorter alias `cmd`. Show the full name in the settings hint so it
|
||||
// matches TUI_AGENT_CONFIG['command-code'].detectCmd and avoids any
|
||||
// suggestion that Orca is looking for Windows' built-in `cmd.exe`.
|
||||
cmd: 'command-code',
|
||||
faviconDomain: 'commandcode.ai',
|
||||
homepageUrl: 'https://commandcode.ai/docs/quickstart'
|
||||
},
|
||||
{
|
||||
id: 'continue',
|
||||
label: translate('auto.lib.agent.catalog.9e2a9bb87b', 'Continue'),
|
||||
// Why: Continue's terminal agent installs as `cn`; `continue` resolves to
|
||||
// a shell builtin in common shells and is not a reliable executable hint.
|
||||
cmd: 'cn',
|
||||
faviconDomain: 'continue.dev',
|
||||
homepageUrl: 'https://docs.continue.dev/guides/cli'
|
||||
},
|
||||
{
|
||||
id: 'cursor',
|
||||
label: translate('auto.lib.agent.catalog.667c104cff', 'Cursor'),
|
||||
cmd: 'cursor-agent',
|
||||
faviconDomain: 'cursor.com',
|
||||
homepageUrl: 'https://cursor.com/cli'
|
||||
},
|
||||
{
|
||||
id: 'droid',
|
||||
label: translate('auto.lib.agent.catalog.739a930554', 'Droid'),
|
||||
cmd: 'droid',
|
||||
homepageUrl: 'https://docs.factory.ai/cli/getting-started/quickstart'
|
||||
},
|
||||
{
|
||||
id: 'kimi',
|
||||
label: translate('auto.lib.agent.catalog.28810273af', 'Kimi'),
|
||||
cmd: 'kimi',
|
||||
faviconDomain: 'moonshot.cn',
|
||||
homepageUrl: 'https://www.kimi.com/code/docs/en/kimi-code-cli/getting-started.html'
|
||||
},
|
||||
{
|
||||
id: 'mistral-vibe',
|
||||
label: translate('auto.lib.agent.catalog.ca73055bd0', 'Mistral Vibe'),
|
||||
// Why: `uv tool install mistral-vibe` exposes the interactive CLI as
|
||||
// `vibe`; the package name is not the executable users put on PATH.
|
||||
cmd: 'vibe',
|
||||
faviconDomain: 'mistral.ai',
|
||||
homepageUrl: 'https://github.com/mistralai/mistral-vibe'
|
||||
},
|
||||
{
|
||||
id: 'qwen-code',
|
||||
label: translate('auto.lib.agent.catalog.bee242fe3d', 'Qwen Code'),
|
||||
// Why: QwenLM/qwen-code installs its CLI executable as `qwen`; the package
|
||||
// name is not the binary users put on PATH. Keep `id` for stable identity.
|
||||
cmd: 'qwen',
|
||||
faviconDomain: 'qwenlm.github.io',
|
||||
homepageUrl: 'https://github.com/QwenLM/qwen-code'
|
||||
},
|
||||
{
|
||||
id: 'rovo',
|
||||
label: translate('auto.lib.agent.catalog.4e63c7b956', 'Rovo Dev'),
|
||||
cmd: 'rovo',
|
||||
faviconDomain: 'atlassian.com',
|
||||
homepageUrl:
|
||||
'https://support.atlassian.com/rovo/docs/install-and-run-rovo-dev-cli-on-your-device/'
|
||||
},
|
||||
{
|
||||
id: 'hermes',
|
||||
label: translate('auto.lib.agent.catalog.8a9ba743cc', 'Hermes'),
|
||||
cmd: 'hermes',
|
||||
faviconDomain: 'nousresearch.com',
|
||||
homepageUrl: 'https://hermes-agent.nousresearch.com/docs/'
|
||||
},
|
||||
{
|
||||
id: 'devin',
|
||||
label: translate('auto.lib.agent.catalog.fc80296033', 'Devin'),
|
||||
cmd: 'devin',
|
||||
faviconDomain: 'devin.ai',
|
||||
homepageUrl: 'https://devin.ai/cli'
|
||||
},
|
||||
{
|
||||
id: 'openclaw',
|
||||
label: translate('auto.lib.agent.catalog.5dff448636', 'OpenClaw'),
|
||||
cmd: 'openclaw',
|
||||
faviconDomain: 'openclaw.ai',
|
||||
homepageUrl: 'https://github.com/openclaw/openclaw'
|
||||
},
|
||||
{
|
||||
id: 'codebuddy',
|
||||
label: translate('auto.lib.agent.catalog.codebuddy_label', 'CodeBuddy'),
|
||||
cmd: 'codebuddy',
|
||||
faviconDomain: 'codebuddy.ai',
|
||||
homepageUrl: 'https://www.codebuddy.ai/cli'
|
||||
},
|
||||
{
|
||||
id: 'jcode',
|
||||
label: translate('auto.lib.agent.catalog.jcode_label', 'Jcode'),
|
||||
cmd: 'jcode',
|
||||
faviconDomain: 'jcode.sh',
|
||||
homepageUrl: 'https://github.com/1jehuang/jcode'
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { primaryAgentCatalogEntries } from './agent-catalog-entries-primary'
|
||||
import { secondaryAgentCatalogEntries } from './agent-catalog-entries-secondary'
|
||||
import type { AgentCatalogEntry } from './agent-catalog'
|
||||
|
||||
/** The catalog rows, in display order. */
|
||||
export function buildAgentCatalogEntries(): AgentCatalogEntry[] {
|
||||
return [...primaryAgentCatalogEntries(), ...secondaryAgentCatalogEntries()]
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
import { getCatalogPlatform } from './agent-catalog-platform'
|
||||
import type React from 'react'
|
||||
import { ClaudeIcon, DroidIcon, OpenAIIcon } from '@/components/status-bar/icons'
|
||||
import openClaudeLogoUrl from '../../../../resources/openclaude-logo.png?url'
|
||||
import type { TuiAgent } from '../../../shared/tui-agent'
|
||||
import { getTuiAgentLaunchCommand, TUI_AGENT_CONFIG } from '../../../shared/tui-agent-config'
|
||||
import {
|
||||
AgentLetterIcon,
|
||||
AiderIcon,
|
||||
@@ -13,9 +10,9 @@ import {
|
||||
OpenCodeIcon,
|
||||
PiIcon
|
||||
} from './agent-icon-glyphs'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
import { createLocalizedCatalog } from '@/i18n/localized-catalog'
|
||||
import { AGENT_FAVICON_ASSETS } from './agent-favicon-assets'
|
||||
import { buildAgentCatalogEntries } from './agent-catalog-entries'
|
||||
|
||||
export type AgentCatalogEntry = {
|
||||
id: TuiAgent
|
||||
@@ -31,334 +28,7 @@ export type AgentCatalogEntry = {
|
||||
homepageUrl: string
|
||||
}
|
||||
|
||||
export const getAgentCatalog = createLocalizedCatalog((): AgentCatalogEntry[] => [
|
||||
{
|
||||
id: 'claude',
|
||||
label: translate('auto.lib.agent.catalog.0708ed89f1', 'Claude'),
|
||||
cmd: 'claude',
|
||||
homepageUrl: 'https://code.claude.com/docs'
|
||||
},
|
||||
{
|
||||
id: 'claude-agent-teams',
|
||||
label: translate('auto.lib.agent.catalog.bf53f09bf8', 'Claude Agent Teams'),
|
||||
cmd: getTuiAgentLaunchCommand(TUI_AGENT_CONFIG['claude-agent-teams'], getCatalogPlatform()),
|
||||
homepageUrl: 'https://code.claude.com/docs/en/agent-teams'
|
||||
},
|
||||
{
|
||||
id: 'openclaude',
|
||||
label: translate('auto.lib.agent.catalog.a5fc0cb622', 'OpenClaude'),
|
||||
cmd: 'openclaude',
|
||||
// Why: OpenClaude's published favicon has a padded 500px canvas; Orca
|
||||
// uses a cropped derivative of that official asset so 12px tab icons stay legible.
|
||||
iconUrl: openClaudeLogoUrl,
|
||||
homepageUrl: 'https://openclaude.gitlawb.com/'
|
||||
},
|
||||
{
|
||||
id: 'codex',
|
||||
label: translate('auto.lib.agent.catalog.760bc6883d', 'Codex'),
|
||||
cmd: 'codex',
|
||||
homepageUrl: 'https://github.com/openai/codex'
|
||||
},
|
||||
{
|
||||
id: 'grok',
|
||||
label: translate('auto.lib.agent.catalog.0baad2d5d2', 'Grok'),
|
||||
cmd: 'grok',
|
||||
faviconDomain: 'x.ai',
|
||||
homepageUrl: 'https://x.ai/cli'
|
||||
},
|
||||
{
|
||||
id: 'copilot',
|
||||
label: translate('auto.lib.agent.catalog.706b0fe68b', 'GitHub Copilot'),
|
||||
cmd: 'copilot',
|
||||
homepageUrl: 'https://docs.github.com/en/copilot/how-tos/set-up/install-copilot-cli'
|
||||
},
|
||||
{
|
||||
id: 'opencode2',
|
||||
label: translate('auto.lib.agent.catalog.opencode2_label', 'OpenCode 2'),
|
||||
cmd: 'opencode2',
|
||||
homepageUrl: 'https://opencode.ai/v2/docs/'
|
||||
},
|
||||
{
|
||||
id: 'opencode',
|
||||
label: translate('auto.lib.agent.catalog.e7a4ca5103', 'OpenCode'),
|
||||
cmd: 'opencode',
|
||||
homepageUrl: 'https://opencode.ai/docs/cli/'
|
||||
},
|
||||
{
|
||||
id: 'mimo-code',
|
||||
label: translate('auto.lib.agent.catalog.mimo_code_label', 'MiMo Code'),
|
||||
cmd: 'mimo',
|
||||
faviconDomain: 'mimo.xiaomi.com',
|
||||
homepageUrl: 'https://mimo.xiaomi.com/coder'
|
||||
},
|
||||
{
|
||||
id: 'ante',
|
||||
label: translate('auto.lib.agent.catalog.da41abbdd4', 'Ante'),
|
||||
cmd: 'ante',
|
||||
faviconDomain: 'antigma.ai',
|
||||
homepageUrl: 'https://github.com/AntigmaLabs/ante-preview'
|
||||
},
|
||||
{
|
||||
id: 'trae',
|
||||
label: translate('auto.lib.agent.catalog.060d152fb5', 'Trae'),
|
||||
// Why: matches TUI_AGENT_CONFIG.trae.detectCmd, not the ambiguous `trae-cli` — see the Why there.
|
||||
cmd: 'traecli',
|
||||
// Why: bare `trae.cn` 404s on Google's favicon service.
|
||||
faviconDomain: 'www.trae.cn',
|
||||
homepageUrl: 'https://docs.trae.cn/cli_get-started-with-trae-cli'
|
||||
},
|
||||
{
|
||||
id: 'muse',
|
||||
label: translate('auto.lib.agent.catalog.muse_label', 'Muse'),
|
||||
cmd: 'muse',
|
||||
faviconDomain: 'dev.meta.ai',
|
||||
homepageUrl: 'https://dev.meta.ai/docs/muse-code'
|
||||
},
|
||||
{
|
||||
id: 'dsh',
|
||||
label: translate('auto.lib.agent.catalog.dsh_label', 'DeepSeek Harness'),
|
||||
cmd: 'dsh-tui',
|
||||
searchAliases: ['deepseek', 'dsh', 'dst', 'deepseek harness'],
|
||||
homepageUrl: 'https://deepseek-harness.github.io/deepseek-harness/'
|
||||
},
|
||||
{
|
||||
id: 'qoder',
|
||||
label: translate('auto.lib.agent.catalog.qoder_label', 'Qoder CLI'),
|
||||
cmd: 'qodercli',
|
||||
faviconDomain: 'qoder.com',
|
||||
homepageUrl: 'https://docs.qoder.com/cli/overview'
|
||||
},
|
||||
{
|
||||
id: 'qoder-cn',
|
||||
label: translate('auto.lib.agent.catalog.qoder_cn_label', 'Qoder CLI China'),
|
||||
cmd: 'qoderclicn',
|
||||
faviconDomain: 'qoder.cn',
|
||||
homepageUrl: 'https://docs.qoder.cn/cli/overview'
|
||||
},
|
||||
{
|
||||
id: 'zcode',
|
||||
label: translate('auto.lib.agent.catalog.zcode_label', 'ZCode'),
|
||||
cmd: 'zcode',
|
||||
faviconDomain: 'zcode.z.ai',
|
||||
homepageUrl: 'https://zcode.z.ai/en/docs'
|
||||
},
|
||||
{
|
||||
id: 'pi',
|
||||
label: translate('auto.lib.agent.catalog.302934c5d9', 'Pi'),
|
||||
cmd: 'pi',
|
||||
homepageUrl: 'https://pi.dev'
|
||||
},
|
||||
{
|
||||
id: 'omp',
|
||||
label: translate('auto.lib.agent.catalog.09973b4d84', 'OMP'),
|
||||
cmd: 'omp',
|
||||
searchAliases: ['oh-my-pi', 'oh my pi'],
|
||||
// Why: no faviconDomain — omp renders the hand-authored OmpIcon glyph, so a
|
||||
// favicon fallback would never be reached.
|
||||
homepageUrl: 'https://omp.sh'
|
||||
},
|
||||
{
|
||||
id: 'prime-agent',
|
||||
label: translate('auto.lib.agent.catalog.d443a47995', 'Prime Agent'),
|
||||
cmd: 'prime-agent',
|
||||
faviconDomain: 'primeintellect.ai',
|
||||
homepageUrl: 'https://github.com/PrimeIntellect-ai/prime-agent'
|
||||
},
|
||||
{
|
||||
id: 'gemini',
|
||||
label: translate('auto.lib.agent.catalog.12e6baa4f7', 'Gemini'),
|
||||
cmd: 'gemini',
|
||||
faviconDomain: 'gemini.google.com',
|
||||
homepageUrl: 'https://github.com/google-gemini/gemini-cli'
|
||||
},
|
||||
{
|
||||
id: 'antigravity',
|
||||
label: translate('auto.lib.agent.catalog.691dd11789', 'Antigravity'),
|
||||
cmd: 'agy',
|
||||
faviconDomain: 'antigravity.google',
|
||||
homepageUrl: 'https://antigravity.google/docs/cli-overview'
|
||||
},
|
||||
{
|
||||
id: 'aider',
|
||||
label: translate('auto.lib.agent.catalog.b32627f09b', 'Aider'),
|
||||
cmd: 'aider',
|
||||
homepageUrl: 'https://aider.chat/docs/'
|
||||
},
|
||||
{
|
||||
id: 'goose',
|
||||
label: translate('auto.lib.agent.catalog.8da11d876c', 'Goose'),
|
||||
cmd: 'goose',
|
||||
faviconDomain: 'goose-docs.ai',
|
||||
homepageUrl: 'https://block.github.io/goose/docs/quickstart/'
|
||||
},
|
||||
{
|
||||
id: 'amp',
|
||||
label: translate('auto.lib.agent.catalog.c73c573939', 'Amp'),
|
||||
cmd: 'amp',
|
||||
faviconDomain: 'ampcode.com',
|
||||
homepageUrl: 'https://ampcode.com/manual#install'
|
||||
},
|
||||
{
|
||||
id: 'kilo',
|
||||
label: translate('auto.lib.agent.catalog.918ba4ffed', 'Kilocode'),
|
||||
cmd: 'kilo',
|
||||
homepageUrl: 'https://kilo.ai/docs/cli'
|
||||
},
|
||||
{
|
||||
id: 'kiro',
|
||||
label: translate('auto.lib.agent.catalog.e0247254f2', 'Kiro'),
|
||||
// Why: the Kiro installer (https://cli.kiro.dev/install) ships a binary
|
||||
// named `kiro-cli`, not `kiro`. Match TUI_AGENT_CONFIG.kiro.detectCmd so
|
||||
// the settings pane's "default command" hint aligns with what Orca
|
||||
// actually looks for on PATH.
|
||||
cmd: 'kiro-cli',
|
||||
faviconDomain: 'kiro.dev',
|
||||
homepageUrl: 'https://kiro.dev/docs/cli/'
|
||||
},
|
||||
{
|
||||
id: 'crush',
|
||||
label: translate('auto.lib.agent.catalog.9477377a2a', 'Charm'),
|
||||
cmd: 'crush',
|
||||
faviconDomain: 'charm.sh',
|
||||
homepageUrl: 'https://github.com/charmbracelet/crush'
|
||||
},
|
||||
{
|
||||
id: 'aug',
|
||||
label: translate('auto.lib.agent.catalog.5e8eff11b3', 'Auggie'),
|
||||
cmd: 'auggie',
|
||||
faviconDomain: 'augmentcode.com',
|
||||
homepageUrl: 'https://docs.augmentcode.com/cli/overview'
|
||||
},
|
||||
{
|
||||
id: 'autohand',
|
||||
label: translate('auto.lib.agent.catalog.1f8a19e9ad', 'Autohand Code'),
|
||||
cmd: 'autohand',
|
||||
faviconDomain: 'autohand.ai',
|
||||
homepageUrl: 'https://github.com/autohandai/code-cli'
|
||||
},
|
||||
{
|
||||
id: 'cline',
|
||||
label: translate('auto.lib.agent.catalog.cbaf0c2e0b', 'Cline'),
|
||||
cmd: 'cline',
|
||||
faviconDomain: 'cline.bot',
|
||||
homepageUrl: 'https://docs.cline.bot/cline-cli/overview'
|
||||
},
|
||||
{
|
||||
id: 'codebuff',
|
||||
label: translate('auto.lib.agent.catalog.4238b771b5', 'Codebuff'),
|
||||
cmd: 'codebuff',
|
||||
faviconDomain: 'codebuff.com',
|
||||
homepageUrl: 'https://www.codebuff.com/docs/help/quick-start'
|
||||
},
|
||||
{
|
||||
id: 'freebuff',
|
||||
label: translate('auto.lib.agent.catalog.freebuff_label', 'Freebuff'),
|
||||
cmd: 'freebuff',
|
||||
faviconDomain: 'freebuff.com',
|
||||
homepageUrl: 'https://freebuff.com/cli'
|
||||
},
|
||||
{
|
||||
id: 'command-code',
|
||||
label: translate('auto.lib.agent.catalog.6f8056a565', 'Command Code'),
|
||||
// Why: `npm i -g command-code` installs both `command-code` and the
|
||||
// shorter alias `cmd`. Show the full name in the settings hint so it
|
||||
// matches TUI_AGENT_CONFIG['command-code'].detectCmd and avoids any
|
||||
// suggestion that Orca is looking for Windows' built-in `cmd.exe`.
|
||||
cmd: 'command-code',
|
||||
faviconDomain: 'commandcode.ai',
|
||||
homepageUrl: 'https://commandcode.ai/docs/quickstart'
|
||||
},
|
||||
{
|
||||
id: 'continue',
|
||||
label: translate('auto.lib.agent.catalog.9e2a9bb87b', 'Continue'),
|
||||
// Why: Continue's terminal agent installs as `cn`; `continue` resolves to
|
||||
// a shell builtin in common shells and is not a reliable executable hint.
|
||||
cmd: 'cn',
|
||||
faviconDomain: 'continue.dev',
|
||||
homepageUrl: 'https://docs.continue.dev/guides/cli'
|
||||
},
|
||||
{
|
||||
id: 'cursor',
|
||||
label: translate('auto.lib.agent.catalog.667c104cff', 'Cursor'),
|
||||
cmd: 'cursor-agent',
|
||||
faviconDomain: 'cursor.com',
|
||||
homepageUrl: 'https://cursor.com/cli'
|
||||
},
|
||||
{
|
||||
id: 'droid',
|
||||
label: translate('auto.lib.agent.catalog.739a930554', 'Droid'),
|
||||
cmd: 'droid',
|
||||
homepageUrl: 'https://docs.factory.ai/cli/getting-started/quickstart'
|
||||
},
|
||||
{
|
||||
id: 'kimi',
|
||||
label: translate('auto.lib.agent.catalog.28810273af', 'Kimi'),
|
||||
cmd: 'kimi',
|
||||
faviconDomain: 'moonshot.cn',
|
||||
homepageUrl: 'https://www.kimi.com/code/docs/en/kimi-code-cli/getting-started.html'
|
||||
},
|
||||
{
|
||||
id: 'mistral-vibe',
|
||||
label: translate('auto.lib.agent.catalog.ca73055bd0', 'Mistral Vibe'),
|
||||
// Why: `uv tool install mistral-vibe` exposes the interactive CLI as
|
||||
// `vibe`; the package name is not the executable users put on PATH.
|
||||
cmd: 'vibe',
|
||||
faviconDomain: 'mistral.ai',
|
||||
homepageUrl: 'https://github.com/mistralai/mistral-vibe'
|
||||
},
|
||||
{
|
||||
id: 'qwen-code',
|
||||
label: translate('auto.lib.agent.catalog.bee242fe3d', 'Qwen Code'),
|
||||
// Why: QwenLM/qwen-code installs its CLI executable as `qwen`; the package
|
||||
// name is not the binary users put on PATH. Keep `id` for stable identity.
|
||||
cmd: 'qwen',
|
||||
faviconDomain: 'qwenlm.github.io',
|
||||
homepageUrl: 'https://github.com/QwenLM/qwen-code'
|
||||
},
|
||||
{
|
||||
id: 'rovo',
|
||||
label: translate('auto.lib.agent.catalog.4e63c7b956', 'Rovo Dev'),
|
||||
cmd: 'rovo',
|
||||
faviconDomain: 'atlassian.com',
|
||||
homepageUrl:
|
||||
'https://support.atlassian.com/rovo/docs/install-and-run-rovo-dev-cli-on-your-device/'
|
||||
},
|
||||
{
|
||||
id: 'hermes',
|
||||
label: translate('auto.lib.agent.catalog.8a9ba743cc', 'Hermes'),
|
||||
cmd: 'hermes',
|
||||
faviconDomain: 'nousresearch.com',
|
||||
homepageUrl: 'https://hermes-agent.nousresearch.com/docs/'
|
||||
},
|
||||
{
|
||||
id: 'devin',
|
||||
label: translate('auto.lib.agent.catalog.fc80296033', 'Devin'),
|
||||
cmd: 'devin',
|
||||
faviconDomain: 'devin.ai',
|
||||
homepageUrl: 'https://devin.ai/cli'
|
||||
},
|
||||
{
|
||||
id: 'openclaw',
|
||||
label: translate('auto.lib.agent.catalog.5dff448636', 'OpenClaw'),
|
||||
cmd: 'openclaw',
|
||||
faviconDomain: 'openclaw.ai',
|
||||
homepageUrl: 'https://github.com/openclaw/openclaw'
|
||||
},
|
||||
{
|
||||
id: 'codebuddy',
|
||||
label: translate('auto.lib.agent.catalog.codebuddy_label', 'CodeBuddy'),
|
||||
cmd: 'codebuddy',
|
||||
faviconDomain: 'codebuddy.ai',
|
||||
homepageUrl: 'https://www.codebuddy.ai/cli'
|
||||
},
|
||||
{
|
||||
id: 'jcode',
|
||||
label: translate('auto.lib.agent.catalog.jcode_label', 'Jcode'),
|
||||
cmd: 'jcode',
|
||||
faviconDomain: 'jcode.sh',
|
||||
homepageUrl: 'https://github.com/1jehuang/jcode'
|
||||
}
|
||||
])
|
||||
export const getAgentCatalog = createLocalizedCatalog(buildAgentCatalogEntries)
|
||||
|
||||
// Why: tests and a few legacy call sites still import a catalog snapshot.
|
||||
export const AGENT_CATALOG: AgentCatalogEntry[] = getAgentCatalog()
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import type { AgentProviderSessionMetadata, ResumableTuiAgent } from './agent-session-resume'
|
||||
|
||||
/** The argv that re-enters an existing session, per agent. Split from
|
||||
* `agent-session-resume.ts` so adding an agent does not push that module past its line
|
||||
* budget; type-only import back, so there is no runtime cycle. */
|
||||
export function getAgentResumeArgv(
|
||||
agent: ResumableTuiAgent,
|
||||
providerSession: AgentProviderSessionMetadata,
|
||||
ompResumeFilePath?: string | null
|
||||
): string[] | null {
|
||||
const id = providerSession.id
|
||||
switch (agent) {
|
||||
case 'codebuddy':
|
||||
return providerSession.key === 'session_id' ? ['codebuddy', '--resume', id] : null
|
||||
case 'claude':
|
||||
return providerSession.key === 'session_id' ? ['claude', '--resume', id] : null
|
||||
case 'cursor':
|
||||
return providerSession.key === 'conversation_id' ? ['cursor-agent', '--resume', id] : null
|
||||
case 'codex':
|
||||
return providerSession.key === 'session_id' ? ['codex', 'resume', id] : null
|
||||
case 'qoder-cn':
|
||||
return providerSession.key === 'session_id' ? ['qoderclicn', '--resume', id] : null
|
||||
case 'qwen-code':
|
||||
return providerSession.key === 'session_id' ? ['qwen', '--resume', id] : null
|
||||
case 'qoder':
|
||||
return providerSession.key === 'session_id' ? ['qodercli', '--resume', id] : null
|
||||
case 'gemini':
|
||||
return providerSession.key === 'session_id' ? ['gemini', '--resume', id] : null
|
||||
case 'antigravity':
|
||||
return providerSession.key === 'conversation_id' ? ['agy', '--conversation', id] : null
|
||||
case 'opencode':
|
||||
return providerSession.key === 'session_id' ? ['opencode', '--session', id] : null
|
||||
case 'opencode2':
|
||||
return providerSession.key === 'session_id'
|
||||
? ['opencode2', '--standalone', '--session', id]
|
||||
: null
|
||||
case 'pi':
|
||||
return providerSession.key === 'session_id' && providerSession.transcriptPath
|
||||
? ['pi', '--session', providerSession.transcriptPath]
|
||||
: null
|
||||
case 'prime-agent':
|
||||
return providerSession.key === 'session_id' && providerSession.transcriptPath
|
||||
? ['prime-agent', '--resume', providerSession.transcriptPath]
|
||||
: null
|
||||
case 'mimo-code':
|
||||
return providerSession.key === 'session_id' ? ['mimo', '--session', id] : null
|
||||
case 'droid':
|
||||
return providerSession.key === 'session_id' ? ['droid', '--resume', id] : null
|
||||
case 'grok':
|
||||
return providerSession.key === 'session_id' ? ['grok', '--resume', id] : null
|
||||
case 'devin':
|
||||
return providerSession.key === 'session_id' ? ['devin', '--resume', id] : null
|
||||
case 'omp':
|
||||
return providerSession.key === 'session_id'
|
||||
? [
|
||||
'omp',
|
||||
'--resume',
|
||||
ompResumeFilePath?.trim() || providerSession.transcriptPath?.trim() || id
|
||||
]
|
||||
: null
|
||||
// Why: the joined form is the only one Copilot documents, and it matches the
|
||||
// flag spelling buildAgentResumeInvocation bakes into persisted AI Vault
|
||||
// resume commands, so local and remote resumes agree on one spelling.
|
||||
case 'copilot':
|
||||
return providerSession.key === 'session_id' ? ['copilot', `--resume=${id}`] : null
|
||||
// Why: Kimi resumes by id with --session; sessions are work-dir-scoped (enforced by callers).
|
||||
case 'kimi':
|
||||
return providerSession.key === 'session_id' ? ['kimi', '--session', id] : null
|
||||
case 'muse':
|
||||
return providerSession.key === 'session_id' ? ['muse', 'resume', id] : null
|
||||
case 'zcode':
|
||||
return providerSession.key === 'session_id' ? ['zcode', '--resume', id] : null
|
||||
// Why: `dsh-tui --resume <id>` re-enters the session the launcher recorded for this
|
||||
// workspace. DSH keys sessions by workspace path, so callers must keep the cwd.
|
||||
case 'dsh':
|
||||
return providerSession.key === 'session_id' ? ['dsh-tui', '--resume', id] : null
|
||||
case 'jcode':
|
||||
return providerSession.key === 'session_id' ? ['jcode', '--resume', id] : null
|
||||
}
|
||||
}
|
||||
@@ -285,78 +285,5 @@ export function extractAgentProviderSession(
|
||||
}
|
||||
}
|
||||
|
||||
export function getAgentResumeArgv(
|
||||
agent: ResumableTuiAgent,
|
||||
providerSession: AgentProviderSessionMetadata,
|
||||
ompResumeFilePath?: string | null
|
||||
): string[] | null {
|
||||
const id = providerSession.id
|
||||
switch (agent) {
|
||||
case 'codebuddy':
|
||||
return providerSession.key === 'session_id' ? ['codebuddy', '--resume', id] : null
|
||||
case 'claude':
|
||||
return providerSession.key === 'session_id' ? ['claude', '--resume', id] : null
|
||||
case 'cursor':
|
||||
return providerSession.key === 'conversation_id' ? ['cursor-agent', '--resume', id] : null
|
||||
case 'codex':
|
||||
return providerSession.key === 'session_id' ? ['codex', 'resume', id] : null
|
||||
case 'qoder-cn':
|
||||
return providerSession.key === 'session_id' ? ['qoderclicn', '--resume', id] : null
|
||||
case 'qwen-code':
|
||||
return providerSession.key === 'session_id' ? ['qwen', '--resume', id] : null
|
||||
case 'qoder':
|
||||
return providerSession.key === 'session_id' ? ['qodercli', '--resume', id] : null
|
||||
case 'gemini':
|
||||
return providerSession.key === 'session_id' ? ['gemini', '--resume', id] : null
|
||||
case 'antigravity':
|
||||
return providerSession.key === 'conversation_id' ? ['agy', '--conversation', id] : null
|
||||
case 'opencode':
|
||||
return providerSession.key === 'session_id' ? ['opencode', '--session', id] : null
|
||||
case 'opencode2':
|
||||
return providerSession.key === 'session_id'
|
||||
? ['opencode2', '--standalone', '--session', id]
|
||||
: null
|
||||
case 'pi':
|
||||
return providerSession.key === 'session_id' && providerSession.transcriptPath
|
||||
? ['pi', '--session', providerSession.transcriptPath]
|
||||
: null
|
||||
case 'prime-agent':
|
||||
return providerSession.key === 'session_id' && providerSession.transcriptPath
|
||||
? ['prime-agent', '--resume', providerSession.transcriptPath]
|
||||
: null
|
||||
case 'mimo-code':
|
||||
return providerSession.key === 'session_id' ? ['mimo', '--session', id] : null
|
||||
case 'droid':
|
||||
return providerSession.key === 'session_id' ? ['droid', '--resume', id] : null
|
||||
case 'grok':
|
||||
return providerSession.key === 'session_id' ? ['grok', '--resume', id] : null
|
||||
case 'devin':
|
||||
return providerSession.key === 'session_id' ? ['devin', '--resume', id] : null
|
||||
case 'omp':
|
||||
return providerSession.key === 'session_id'
|
||||
? [
|
||||
'omp',
|
||||
'--resume',
|
||||
ompResumeFilePath?.trim() || providerSession.transcriptPath?.trim() || id
|
||||
]
|
||||
: null
|
||||
// Why: the joined form is the only one Copilot documents, and it matches the
|
||||
// flag spelling buildAgentResumeInvocation bakes into persisted AI Vault
|
||||
// resume commands, so local and remote resumes agree on one spelling.
|
||||
case 'copilot':
|
||||
return providerSession.key === 'session_id' ? ['copilot', `--resume=${id}`] : null
|
||||
// Why: Kimi resumes by id with --session; sessions are work-dir-scoped (enforced by callers).
|
||||
case 'kimi':
|
||||
return providerSession.key === 'session_id' ? ['kimi', '--session', id] : null
|
||||
case 'muse':
|
||||
return providerSession.key === 'session_id' ? ['muse', 'resume', id] : null
|
||||
case 'zcode':
|
||||
return providerSession.key === 'session_id' ? ['zcode', '--resume', id] : null
|
||||
// Why: `dsh-tui --resume <id>` re-enters the session the launcher recorded for this
|
||||
// workspace. DSH keys sessions by workspace path, so callers must keep the cwd.
|
||||
case 'dsh':
|
||||
return providerSession.key === 'session_id' ? ['dsh-tui', '--resume', id] : null
|
||||
case 'jcode':
|
||||
return providerSession.key === 'session_id' ? ['jcode', '--resume', id] : null
|
||||
}
|
||||
}
|
||||
// Re-exported so the 18 existing call sites keep one import path.
|
||||
export { getAgentResumeArgv } from './agent-resume-argv'
|
||||
|
||||
Reference in New Issue
Block a user