Skip to content

feat(agents): add Gajae Code as a supported TUI agent - #13952

Open
nahyeongjin1 wants to merge 2 commits into
stablyai:mainfrom
nahyeongjin1:feat/register-gjc-agent
Open

nahyeongjin1 wants to merge 2 commits into
stablyai:mainfrom
nahyeongjin1:feat/register-gjc-agent

Conversation

@nahyeongjin1

@nahyeongjin1 nahyeongjin1 commented Aug 12, 2026 •

Copy link
Copy Markdown

What & Why

Registers the gjc CLI (Gajae Code, npm gajae-code) as a supported TUI agent, following the Trae/Prime Agent registration pattern.

ELI5: Orca can already run gjc in a terminal, but it doesn't know what it is — no entry in the agent picker, no process attribution, no prompt injection when you create a worktree. This adds the one catalog entry that makes it a first-class agent like the other 36.

Scope is registration only. Pi-style status hooks, prefill, and AI Vault session history are deliberately left out.

Two commits, and the first one is a prerequisite, not scope creep. On a rebase onto current main I found that src/shared/tui-agent-config.ts had reached 297 of the 300 counted lines oxlint allows for src/**/*.ts. A minimal agent entry is 7 code lines (5 fields plus braces), so pnpm lint now fails on any new agent registration, not just this one — and AGENTS.md forbids a max-lines disable. bdc9b61 therefore moves the four type declarations (TuiAgentConfig, AgentPromptInjectionMode, DraftPasteReadySignal, TuiAgentDetectionRuntime) into src/shared/tui-agent-config-types.ts, leaving the per-agent table at 273/300. It is a type-only move with no behavior change, four import sites repointed at the real module rather than a re-export (per #14447), and it lands before the registration so no commit on this branch is lint-red.

Field-by-field rationale

Gajae Code is a Pi fork, so the tempting move is to copy Pi's config. Every field was instead verified against the installed CLI and its source — and two of them turn out to differ from Pi:

Field Value Evidence
expectedProcess gjc Ships as a compiled single-file binary (file $(which gjc) → Mach-O 64-bit executable arm64), so the foreground process carries its own name. Unlike Pi and Prime Agent there is no Node/Bun shim launching a generic cli.js, so no entry is needed in agent-node-entrypoint-identities.ts.
promptInjectionMode argv gjc --help documents gjc [prompt] with the example gjc "List all .ts files in src/".
argvPromptSeparator -- Its arg parser skips a bare -- and pushes every following non-flag token into messages. Without the separator a prompt of exactly resume takes the gjc resume launch alias, and stats/models dispatch as subcommands.
windowsShiftEnterEncoding (omitted) Diverges from Pi. interactive-mode.ts binds newline to alt+enter/ctrl+j on win32 rather than shift+enter, so the default Esc+CR path is already what it expects; a csi-u override would be wrong here.
faviconDomain (omitted) gajae-code.com serves an inline data:image/svg+xml favicon, so https://www.google.com/s2/favicons?domain=gajae-code.com&sz=64 returns 404 with the generic globe placeholder. The AgentLetterIcon fallback is the honest icon — same reasoning already recorded for OMP.
skills-cli-agent-keys null Following that file's stated rule: an unconfirmed key is dropped rather than guessed.

I verified the separator behavior by running its parser directly rather than reading the help text:

["fix the bug"]              => messages: ["fix the bug"]
["--","fix the bug"]         => messages: ["fix the bug"]
["--","resume"]              => messages: ["resume"]     # without `--`: takes the resume alias
["--","/help me"]            => messages: ["/help me"]

Testing

  • I manually tested these changes locally
  • Automated tests added/updated

Added to src/renderer/src/lib/tui-agent-startup.test.ts, mirroring the existing Trae/Prime Agent cases:

  • positional argv behind --
  • subcommand-shaped prompt (resume) stays a prompt

Rebased onto main at 013eb53. The only conflict was src/shared/types.ts, deleted by #14447 (drop the shared/types barrel); the TuiAgent union member moved to its new home in src/shared/tui-agent.ts. The other fifteen files merged cleanly.

Re-run on the rebased branch, macOS arm64:

Check Result
pnpm typecheck (node + web) clean
npx oxlint exit 0, no findings
pnpm run audit:code-quality:native pass
pnpm run audit:code-quality:type-aware pass
pnpm run check:reliability-gates pass (87 gates)
pnpm run check:max-lines-ratchet pass, no new bypasses
pnpm run verify:localization-{catalog,extraction,coverage} pass
pnpm run verify:bundled-skill-guides, verify:skill-bundle-manifest pass
npx oxfmt --check on the touched files clean
vitest run --config config/vitest.config.ts src/shared src/renderer 29,692 passed, 44 skipped, 13 failed

The 13 failures are all in four terminal-ime-xterm-* preedit-overlay suites. I ran those same four files on a clean 013eb53 checkout in the same environment and got an identical 13 failed / 9 passed, so they are environmental here and untouched by this PR. The automation-schedules.test.ts locale failure I reported in the original description is gone on current main, so it is no longer a caveat.

pnpm test as a whole still could not run locally: ensure-native-runtime.mjs cannot bind node-pty for Node 24.16.0 on this machine, so I invoked vitest directly against config/vitest.config.ts. CI covers the native path.

The i18n key c851f042e2 was not hand-computed — I confirmed it by running npx i18next-cli extract -c config/i18next.config.ts and diffing the generated catalog. Added to all five locale files (en/es/ja/ko/zh) with the untranslated product name, as done for Prime Agent.

Mobile has no local node_modules here (separate Expo workspace), so in place of tsc --noEmit and vitest run I asserted the invariants those checks enforce:

  • the TuiAgent union, MOBILE_TUI_AGENT_LABELS keys, and MOBILE_TUI_AGENT_AUTO_PICK_ORDER are all 37 with no missing or extra entries;
  • mobile-agent-catalog.test.ts parses tui-agent-config.ts by regex for TUI_AGENT_CONFIG: Record<TuiAgent, TuiAgentConfig> = {…^}, which the type split could have broken — I ran that regex against the post-split file and it still yields all 37 keys, with mobile and desktop auto-pick order equal element-for-element and gjc at index 13 in both.

Not touched: README's Supported Agents badge list — the Prime Agent registration (#12935) did not touch it either, and doing so would pull in five translated READMEs.

Review

  • Security: no new I/O, network, or process-spawn surface; the entry is static data.
  • Cross-platform: gjc is the binary name on all three platforms, so no launchCmdByPlatform. The win32 newline divergence from Pi is called out above and is the reason the csi-u override is intentionally absent.
  • Remote SSH / mobile: detection rides the shared detectCmd path; mobile registry updated in the same commit.
  • Backwards compatibility: additive union member; agent-kind.ts keeps its 'other' runtime fallback for stale persisted settings.
  • Refactor risk: bdc9b61 moves types only — no value, function, or export shape changes, and TUI_AGENT_CONFIG itself stays in tui-agent-config.ts so the mobile test's source regex and tui-agent-detection-commands.ts still resolve it there. Reviewable as its own commit.
  • Performance: none.

Screenshots

N/A — no new UI. The agent picker renders the existing AgentLetterIcon ("G") through the unchanged catalog code path.

Checklist

  • This PR is small and focused
  • I explained what changed and why (including ELI5)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered
  • pnpm lint, pnpm typecheck, pnpm test, and pnpm build pass (or CI will cover; local preferred)

AI Disclosure

Authored with Claude (Sonnet 4.5) via Gajae Code. All CLI-contract claims above were verified against the installed binary and its source; none were inferred from the model's prior knowledge.

The rebase, the type split, and the re-verification above were done with Claude Opus 5 in Claude Code. Every result in the tables was run, not predicted; the IME baseline was measured on a clean main checkout rather than assumed.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds Gajae Code as the gjc TUI agent. The change updates shared types, telemetry mappings, launch configuration, selection order, display names, skills mapping, renderer catalog data, icon eligibility, localization files, startup tests, and mobile agent selection. Gajae Code launches with positional prompts separated by --, including prompts equal to resume.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 16 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely identifies the addition of Gajae Code as a supported TUI agent.
Description check ✅ Passed The description is detailed and covers the change, rationale, testing, risks, screenshots, checklist, and AI disclosure, but omits the required Linked Issue entry.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@nahyeongjin1

Copy link
Copy Markdown
Author

Rebased onto main — this is mergeable again.

The only conflict was src/shared/types.ts, deleted by #14447 (drop the shared/types barrel). The one-line TuiAgent union member moved to its new home in src/shared/tui-agent.ts; the other 15 files merged cleanly and the diff is unchanged at +74/-4 across 16 files.

Re-verified on the rebased branch (macOS arm64):

Check Result
pnpm typecheck clean
vitest run src/shared src/renderer/src/lib/tui-agent-startup.test.ts 5217 passed, 39 skipped, 0 failed
npx oxlint --config config/oxlint-code-quality-native-plugins.json ... --deny-warnings exit 0
verify:localization-{catalog,extraction,coverage} pass
check:reliability-gates, check:max-lines-ratchet pass

The pre-existing automation-schedules.test.ts locale failure I noted in the description is gone on current main, so this branch is now green locally with no caveats. Mobile still has no local node_modules, so in place of tsc --noEmit I asserted the invariant that check enforces: TuiAgent union, MOBILE_TUI_AGENT_LABELS keys, and MOBILE_TUI_AGENT_AUTO_PICK_ORDER are all 37 with no missing or extra entries.

@brennanb2025 only track-community-pr has ever run on this PR — the real workflows are still waiting on the fork-PR approval. Could you kick those off when you get a chance? Happy to address anything that falls out.

@nahyeongjin1

Copy link
Copy Markdown
Author

Why this one, and the one field the other argv PRs miss

There are now seven gjc registration PRs (#7579, #7805 (self-closed), #9319, #9326, #10476 (closed), this one, #15025). Six chose promptInjectionMode: 'argv'; only #15025 chose stdin-after-start. I re-verified the CLI contract today against gjc v0.14.2 rather than arguing it from docs:

$ gjc --help
USAGE
  $ gjc [COMMAND]
ARGUMENTS
  MESSAGES   Messages to send (prefix files with @)

$ script -q out.log gjc -- hi        # real pty
→ inline TUI renders, the prompt is processed, the composer stays live
  ("Type your message...")
→ the session survives until I SIGKILL it at 45s; on teardown it prints
  "Resume this session with: gjc --resume 01a0276d-…"

Positional MESSAGES keeps the hosted session interactive; only -p/--print is the one-shot mode. #7805 reached the same conclusion independently against v0.9.0.

The -- separator is what the other argv registrations miss. From gjc's own cli.ts:

/** Normalize the sole `gjc resume` alias into the value-less launch intent. */
export function normalizeResumeAlias(argv: readonly string[]): string[] {
  return argv.length === 1 && argv[0] === "resume" ? ["--resume"] : [...argv];
}

A worktree created with the prompt resume therefore opens gjc's session picker instead of sending the prompt, in every registration without argvPromptSeparator: '--'. Same collision class for stats / models, which dispatch as subcommands.

Field data for argv over stdin-after-start. gjc ships as a 134 MB single-file Mach-O. On a machine with an exec-hooking security agent, its first launch blocks in dyld before main:

$ sample <pid>
1786 Thread: Main Thread
  1786 _dyld_start (in dyld) + 0
  → dyld4::RuntimeState::notifyDebuggerLoad
  → dyld4::ExternallyViewableState::triggerNotifications
  → RemoteNotificationResponder::blockOnSynchronousEvent
  → mach_msg2_trap

~60 s from exec to first paint (RSS 32 KB → 570 MB); warm start ~4 s. stdin-after-start delivery is gated by FOREGROUND_AGENT_WRAPPER_RETRY_TIMEOUT_MS (6.5 s) and DRAFT_PASTE_READY_TIMEOUT_MS (8 s), so on a cold start the initial prompt is silently dropped with no error surface. argv carries the prompt in the exec itself and cannot lose it.

I've posted the same verification on #15025 with an offer to close this PR if that one adopts these two fields — it has the bundled icon and the telemetry coverage that mine deliberately skipped. Either registration is fine by me as long as the launch config is the one above; I'm not asking for a review race between seven duplicates.

nahyeongjin1 and others added 2 commits August 22, 2026 12:30
Why: tui-agent-config.ts sat at 297 of the 300 counted lines oxlint allows for
`src/**/*.ts`, so registering any further agent trips max-lines before the entry
is even written. The per-agent table now has 27 lines of headroom.

Type-only move — no behavior change. Consumers import from the new module
directly rather than through a re-export, per stablyai#14447.

Co-Authored-By: Claude <noreply@anthropic.com>
Register the `gjc` CLI (https://gajae-code.com, npm `gajae-code`) in the
desktop and mobile agent catalogs following the Trae/Prime Agent
registration pattern.

Every field was verified against the installed CLI and its source rather
than assumed from the Pi lineage it forked:

- `expectedProcess: 'gjc'` — it ships as a compiled single-file binary
  (Mach-O/ELF), so the foreground process carries its own name. Unlike Pi
  and Prime Agent there is no Node/Bun shim launching a generic `cli.js`,
  so no entrypoint identity is needed in
  `agent-node-entrypoint-identities.ts`.
- `promptInjectionMode: 'argv'` with `argvPromptSeparator: '--'` — its
  help documents `gjc [prompt]`, and its arg parser skips a bare `--`
  while pushing every following non-flag token into `messages`. Without
  the separator a prompt of exactly `resume` takes the `gjc resume`
  launch alias, and `stats`/`models` dispatch as subcommands.
- No `windowsShiftEnterEncoding` — this is where it diverges from Pi.
  `interactive-mode.ts` binds newline to `alt+enter`/`ctrl+j` on win32
  instead of `shift+enter`, so the default Esc+CR path is already the
  encoding it expects and a `csi-u` override would be wrong.
- No `faviconDomain` — gajae-code.com serves an inline data-URI favicon,
  so Google's favicon service returns 404 with its generic globe
  placeholder. The `AgentLetterIcon` fallback is the honest icon, same
  reasoning already recorded for OMP.
- `skills-cli-agent-keys`: `null`, following the file's stated rule that
  an unconfirmed key is dropped rather than guessed.

Scope is registration only. Pi-style status hooks, prefill, and AI Vault
session history are deliberately left out of this PR.

Co-Authored-By: Claude <noreply@anthropic.com>
@nahyeongjin1
nahyeongjin1 force-pushed the feat/register-gjc-agent branch from 6f1e200 to 2f92c36 Compare August 22, 2026 03:35
@nahyeongjin1

Copy link
Copy Markdown
Author

Follow-up to the rebase above: it surfaced a blocker that is worth flagging on its own, so the PR is now two commits and the description has been rewritten around it.

src/shared/tui-agent-config.ts had reached 297 of the 300 counted lines oxlint allows for src/**/*.ts. A minimal agent entry is 7 code lines, so npx oxlint — the first step of pnpm lint — fails on any new agent registration against current main, not just this one:

src/shared/tui-agent-config.ts:374:2: error eslint(max-lines):
  File has too many lines (304). help: Maximum allowed is 300.

AGENTS.md rules out a max-lines disable, so bdc9b61 moves the four type declarations into src/shared/tui-agent-config-types.ts and leaves the table at 273/300 — about four more registrations of headroom. Type-only move, four import sites repointed at the real module instead of a re-export (per #14447), and it is ordered first so no commit on the branch is lint-red. TUI_AGENT_CONFIG deliberately stays put, since mobile-agent-catalog.test.ts parses that file by regex.

Full verification is in the updated description. Two notes on what is not green: 13 tests fail in four terminal-ime-xterm-* preedit suites, and I measured an identical 13 failed / 9 passed on a clean 013eb53 checkout in the same environment, so they are not from this PR. Mobile is asserted by invariant rather than executed, since that workspace has no local node_modules.

Happy to split the refactor into its own PR if you would rather review it separately — say the word and I will reorder.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/shared/tui-agent-config.ts (1)

120-132: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Shorten the gjc configuration comments.

These comments restate configured fields and explain the default Windows behavior. Remove the expectedProcess and default-encoding rationale. Keep one short comment for argvPromptSeparator.

Proposed reduction
     expectedProcess: 'gjc',
     promptInjectionMode: 'argv',
-    // Why: separator so a prompt that is exactly a subcommand word (`resume`, `stats`,
-    // `models`) or starts with `/` reaches the parser as message text instead of
-    // dispatching. Verified against its arg parser: a bare `--` is skipped and every
-    // following non-flag token lands in `messages`.
+    // Keep command-like prompts as positional messages.
     argvPromptSeparator: '--'

As per coding guidelines, “Comments must be concise, non-obvious, and brief—prefer one line; do not explain obvious behavior or walk through code.”

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: aa9330d8-9e44-4894-b769-f4df20512b6a

📥 Commits

Reviewing files that changed from the base of the PR and between 6f1e200e01383ef854f20c20e8c3fa0e41c7959f and 2f92c36.

📒 Files selected for processing (6)
  • src/renderer/src/lib/agent-draft-readiness.ts
  • src/shared/draft-paste-ready-scanner-grok-trace-replay.test.ts
  • src/shared/draft-paste-ready-scanner.ts
  • src/shared/tui-agent-config-types.ts
  • src/shared/tui-agent-config.ts
  • src/shared/tui-agent-detection-commands.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants