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:
Neil
2026-10-03 01:49:40 -07:00
committed by Neil
co-authored by czzczz
parent 84d246b9f0
commit af4c3e2962
11 changed files with 602 additions and 475 deletions
@@ -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()]
}
+2 -332
View File
@@ -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()
+80
View File
@@ -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
}
}
+2 -75
View File
@@ -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'