diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ebd7afa59..78174258a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ on: release_name: description: "Release name to create when publish_release is true" required: true - default: "Agent Studio Code 0.0.2 Beta" + default: "Agent Code 0.0.2 Beta" concurrency: group: release-macos diff --git a/AGENTS.md b/AGENTS.md index a799063ee..d608c6e50 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,4 +1,4 @@ -# cc-shell +# Agent Code This codebase is written and developed by AI. Future you will pick up where past you left off, with no shared memory beyond what's in the repo. diff --git a/CLAUDE.md b/CLAUDE.md index a799063ee..d608c6e50 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,4 +1,4 @@ -# cc-shell +# Agent Code This codebase is written and developed by AI. Future you will pick up where past you left off, with no shared memory beyond what's in the repo. diff --git a/CODEX.md b/CODEX.md index a799063ee..d608c6e50 100644 --- a/CODEX.md +++ b/CODEX.md @@ -1,4 +1,4 @@ -# cc-shell +# Agent Code This codebase is written and developed by AI. Future you will pick up where past you left off, with no shared memory beyond what's in the repo. diff --git a/README.md b/README.md index fdd164d00..93421eea6 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,6 @@ -# cc-shell +# Agent Code -`cc-shell` is an agent-first editor for heavy users of Claude Code and Codex. - -The working name is `cc-shell`. The product is planned to be renamed to -**Agent Code**. Some repository paths, package names, and UI labels may still -use `cc-shell` during the transition. +Agent Code is an agent-first editor for heavy users of Claude Code and Codex. This is not another "make Claude Code look nicer" wrapper. It is an agentic development platform built on the opposite assumption: the agent @@ -22,7 +18,7 @@ of them hit the same two failure modes: That leads to tools that look polished but are materially weaker. -`cc-shell` takes a harder line: +Agent Code takes a harder line: - Claude Code should still feel like Claude Code. - Codex should still feel like Codex. @@ -39,7 +35,7 @@ The best agent editor is a platform that runs the real Claude Code and the real Codex under the hood, preserves their behavior, and then layers better workspace primitives on top for people who spend all day in agent loops. -That means `cc-shell` is built for: +That means Agent Code is built for: - parallel agent work - persistent sessions @@ -61,11 +57,11 @@ That approach is bad on two fronts: If you strip things down to "send message, get message back", you lose most of what makes those tools good in practice. -`cc-shell` is explicitly trying not to do that. +Agent Code is explicitly trying not to do that. ## How It Works -`cc-shell` runs Claude Code and Codex headlessly under the hood. +Agent Code runs Claude Code and Codex headlessly under the hood. But "headless" here does **not** mean using a minimal session API and pretending that is the product. @@ -82,7 +78,7 @@ The architecture combines: - transcript parsing - session management -This lets `cc-shell` surface the real commands, flows, and tools from Claude +This lets Agent Code surface the real commands, flows, and tools from Claude Code and Codex in an API-like way without collapsing them into a toy chat interface. @@ -95,7 +91,7 @@ In practice, the stack includes: ## What This Project Is -`cc-shell` is: +Agent Code is: - an Electron desktop shell for agent-heavy development - a multi-session workspace for Claude Code, Codex, and terminals diff --git a/docs/design/README.md b/docs/design/README.md index d69e5fecf..952e9dd8d 100644 --- a/docs/design/README.md +++ b/docs/design/README.md @@ -1,6 +1,6 @@ # `docs/design/` — sources of truth for non-obvious system designs -Files in this directory describe **what cc-shell's most complicated subsystems are and why they are shaped the way they are.** They are referenced by inline code comments and treated as authoritative. They are *not* changelogs, *not* running notes, *not* blow-by-blow histories of how a fix was derived. +Files in this directory describe **what Agent Code's most complicated subsystems are and why they are shaped the way they are.** They are referenced by inline code comments and treated as authoritative. They are *not* changelogs, *not* running notes, *not* blow-by-blow histories of how a fix was derived. ## DO NOT change these files lightly diff --git a/docs/design/ghost-system.md b/docs/design/ghost-system.md index 49209049f..51850d9d7 100644 --- a/docs/design/ghost-system.md +++ b/docs/design/ghost-system.md @@ -4,16 +4,16 @@ ## What ghost is, in plain English -cc-shell talks to the agent (Claude Code, Codex) two ways at the same time: +Agent Code talks to the agent (Claude Code, Codex) two ways at the same time: - It **reads the agent's authoritative transcript file** (Claude Code's `~/.claude/projects//.jsonl`, Codex's rollout JSONL). That file is the durable record of what happened — but it's written *lazily*. Claude Code batches transcript writes every 100 ms (10 ms for remote sessions); Codex queues writes through a tokio mpsc channel that drains on flush barriers. The file is structurally behind reality. - It **observes the agent's network traffic via a per-session mitmproxy.** The proxy emits a flat semantic stream — `turn_started`, `text_delta`, `tool_input_delta`, `tool_input_finalized`, `block_completed`, `tool_result`, `turn_completed`, etc. — as the model's reply streams in. The proxy is roughly real-time. The proxy is ahead. The file catches up. Eventually they agree. -A **ghost** is a provisional `ClaudeEntry` cc-shell mints from the proxy stream, tagged `_atp.origin = 'ghost'`, with a deterministic uuid (`g--`). It exists to bridge the gap between the two sources without producing duplicate-render bugs. When the authoritative JSONL entry lands, the ghost is *superseded* and dropped from the visible set. If it never lands, the ghost is *orphaned* and (sometimes — see the predicate) surfaces as the only record of what the proxy observed. +A **ghost** is a provisional `ClaudeEntry` Agent Code mints from the proxy stream, tagged `_atp.origin = 'ghost'`, with a deterministic uuid (`g--`). It exists to bridge the gap between the two sources without producing duplicate-render bugs. When the authoritative JSONL entry lands, the ghost is *superseded* and dropped from the visible set. If it never lands, the ghost is *orphaned* and (sometimes — see the predicate) surfaces as the only record of what the proxy observed. -The ghost primitive is general-purpose and lives in the standalone `agent-transcript-parser` package (`./packages/agent-transcript-parser/src/ghost.ts`, doc at `./packages/agent-transcript-parser/docs/ghost.md`). cc-shell's job is to wire it up: mint ghosts from semantic events, persist to disk, reconcile against JSONL, decide when to render. +The ghost primitive is general-purpose and lives in the standalone `agent-transcript-parser` package (`./packages/agent-transcript-parser/src/ghost.ts`, doc at `./packages/agent-transcript-parser/docs/ghost.md`). Agent Code's job is to wire it up: mint ghosts from semantic events, persist to disk, reconcile against JSONL, decide when to render. ## Why ghost exists @@ -22,7 +22,7 @@ The naive alternatives all fail: | Approach | Failure | |---|---| | Render only the JSONL file. | Visible 100 ms+ delay between the model talking and the screen showing anything. Worse during slow batched flushes or large tool_results. | -| Render only the proxy stream. | No durable record. Closing cc-shell mid-turn loses the partial turn. Resume after crash has nothing to show. | +| Render only the proxy stream. | No durable record. Closing Agent Code mid-turn loses the partial turn. Resume after crash has nothing to show. | | Render BOTH unconditionally — JSONL above, proxy below. | The same sentence appears twice during the lag window. Documented historical bug on Codex (`docs/superpowers/plans/2026-04-20-rendering-fixes.md`). | The design idea was: one merged feed. Authoritative entries from the file, plus provisional ghost rows when the proxy is ahead, with automatic supersedure when the file catches up. The user sees one row that transitions seamlessly from "live preview" to "committed." @@ -33,7 +33,7 @@ That plan was never fully completed (see [Phase 3: deliberately out of scope](#p This is the most-confusing part of the current architecture, and it's load-bearing for every rule below. -In cc-shell today, the **live current turn** (the assistant's in-flight reply) is rendered by `SemanticStreamingTurn`, which reads `runtime.semantic.currentTurn` directly off the semantic reducer. **Not from ghosts.** See `src/renderer/src/features/feed/ui/semantic/StreamingTurn.tsx` and the call site at `src/renderer/src/features/feed/ui/Feed.tsx:912-916`. +In Agent Code today, the **live current turn** (the assistant's in-flight reply) is rendered by `SemanticStreamingTurn`, which reads `runtime.semantic.currentTurn` directly off the semantic reducer. **Not from ghosts.** See `src/renderer/src/features/feed/ui/semantic/StreamingTurn.tsx` and the call site at `src/renderer/src/features/feed/ui/Feed.tsx:912-916`. Ghosts exist *in parallel* with that live render. Every semantic event that updates `currentTurn` ALSO mints / updates a corresponding ghost via `ghostsFromSemanticTurn` in `src/renderer/src/workspace/ghosts.ts`. The two paths don't collide because the render predicate (below) explicitly excludes any ghost whose `turnId === currentTurn.turnId` — `SemanticStreamingTurn` owns that turn, ghosts can't surface for it. @@ -42,7 +42,7 @@ So the practical question for the ghost system is: **what is rendering ghost row Answer: a small set of fallback cases where `SemanticStreamingTurn` can't help. 1. **JSONL stuck mid-turn.** The agent process gets wedged or its writer backlogs while the proxy keeps emitting events. `currentTurn` eventually clears (or doesn't), JSONL never lands. The ghost on disk is the only record of the turn. -2. **Crash + restart.** cc-shell exited mid-turn. Semantic state was in-memory and is gone. JSONL has a partial transcript that ends before the in-flight turn. The ghost log on disk preserves what the proxy saw; on resume it surfaces the lost partial turn. +2. **Crash + restart.** Agent Code exited mid-turn. Semantic state was in-memory and is gone. JSONL has a partial transcript that ends before the in-flight turn. The ghost log on disk preserves what the proxy saw; on resume it surfaces the lost partial turn. Both reduce to the same condition: **proxy state is past the JSONL tail with no recovery in sight.** The render predicate detects this structurally. @@ -121,8 +121,8 @@ The whole subsystem reads in one direction — semantic events flow in, the rend ### Components -- **atp ghost primitives** — `packages/agent-transcript-parser/src/ghost.ts`. Pure library: `createGhost`, `updateGhost`, `supersedeGhost`, `orphanGhost`, `reduceGhostLog`, `reduceGhostLogSansSuperseded`, `mergeWithUpstream`. No IO. Reusable outside cc-shell. -- **cc-shell renderer reducer** — `src/renderer/src/workspace/ghosts.ts`. Bridges semantic events to atp primitives: `ghostsFromSemanticTurn`, `reconcileUpstream`, `orphanStale`, `gcSupersededGhosts`, `ghostsToPersist`. Reference-stable on no-op. +- **atp ghost primitives** — `packages/agent-transcript-parser/src/ghost.ts`. Pure library: `createGhost`, `updateGhost`, `supersedeGhost`, `orphanGhost`, `reduceGhostLog`, `reduceGhostLogSansSuperseded`, `mergeWithUpstream`. No IO. Reusable outside Agent Code. +- **Agent Code renderer reducer** — `src/renderer/src/workspace/ghosts.ts`. Bridges semantic events to atp primitives: `ghostsFromSemanticTurn`, `reconcileUpstream`, `orphanStale`, `gcSupersededGhosts`, `ghostsToPersist`. Reference-stable on no-op. - **render predicate** — `src/renderer/src/workspace/mergedEntries.ts`. The five-rule selector. Source of truth for "does this ghost surface in the feed." - **IPC wiring** — `src/renderer/src/workspace/hook/ipc/useIpcSubscriptions.ts`. The orchestration layer: mint on semantic, reconcile on JSONL, sweep orphans on a 1s timer, stamp `lastJsonlEntryAt` from JSONL bursts, persist diffs. - **bootstrap on resume** — `src/renderer/src/workspace/hook/actions/session.ts` (ghost log read + reconcile against loaded JSONL tail) and `src/renderer/src/workspace/hook/actions/initialHistory.ts` (stamping `lastJsonlEntryAt` from the loaded tail). @@ -198,7 +198,7 @@ If you add a new code path that touches the ghost map, **uphold the contract**: The ghost journal lives at `/ghost-logs/.ghost.jsonl`. One file per session. Append-only JSONL. Owned by the main process; the renderer fires-and-forgets `window.api.ghostAppend`. -Why a separate file and not the agent's own JSONL: cc-shell never writes into Claude Code's `~/.claude/projects//.jsonl` or Codex's rollout file. Those belong to the agent and are actively written by its batched queue; two writers on the same file is a torn-line / lost-write disaster. Ghosts are also a cc-shell-internal concern — no external tool needs them. +Why a separate file and not the agent's own JSONL: Agent Code never writes into Claude Code's `~/.claude/projects//.jsonl` or Codex's rollout file. Those belong to the agent and are actively written by its batched queue; two writers on the same file is a torn-line / lost-write disaster. Ghosts are also an Agent Code-internal concern — no external tool needs them. Why batched at 100 ms: matches Claude Code's own transcript batch interval (`FLUSH_INTERVAL_MS` in `claude-code-src/full/utils/sessionStorage.ts`). Per-entry fsync during streaming would be tens of writes per second across every active pane. The trade-off — up to 100 ms of data loss on hard crash — is acceptable because ghost is provisional by definition and the agent's own JSONL survives independently. diff --git a/docs/plans_and_ideas/README.md b/docs/plans_and_ideas/README.md index 75d648041..7c699d6a5 100644 --- a/docs/plans_and_ideas/README.md +++ b/docs/plans_and_ideas/README.md @@ -2,4 +2,4 @@ This document is a placeholder for ideas, plans, and possible improvements we may want to implement in the future. -It will be used to capture early concepts, feature directions, experiments, and other notes that can help guide future work on `cc-shell`. +It will be used to capture early concepts, feature directions, experiments, and other notes that can help guide future work on Agent Code. diff --git a/electron-builder.yml b/electron-builder.yml index 88f2fbeb2..c96a7f580 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -1,5 +1,5 @@ -appId: com.agentstudiocode.app -productName: Agent Studio Code +appId: com.agentcode.app +productName: Agent Code artifactName: "${productName}-${version}-${arch}.${ext}" directories: output: release diff --git a/electron.vite.config.ts b/electron.vite.config.ts index 8227ea02b..59271783c 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -18,7 +18,7 @@ const headlessAlias = [ { find: /^agent-transcript-parser\/(.+)$/, replacement: `${resolve(__dirname, 'packages/agent-transcript-parser/src')}/$1` }, { find: 'agent-transcript-parser', replacement: resolve(__dirname, 'packages/agent-transcript-parser/src/index.ts') }, // `agent-voice-dictation` is a git submodule like the other local packages, - // so cc-shell must compile it from source. Relying on `dist` would make a + // so Agent Code must compile it from source. Relying on `dist` would make a // fresh clone fail unless someone remembered to build the submodule first, // and that hidden ordering dependency is exactly what the packages/ layout // is meant to avoid. @@ -56,7 +56,7 @@ function copyMainRuntimeResourcesPlugin(): Plugin { } return { - name: 'cc-shell-copy-main-runtime-resources', + name: 'agent-code-copy-main-runtime-resources', apply: 'build', async closeBundle() { // WHY this lives in the Vite main build instead of only in diff --git a/issues.md b/issues.md index b7860fba1..0d909d964 100644 --- a/issues.md +++ b/issues.md @@ -196,7 +196,7 @@ The order should be: ### Problem -Claude supports pasting images into input. `cc-shell` should support that too. +Claude supports pasting images into input. Agent Code should support that too. This should not just be a hidden transport feature. The UI should make the pasted image visible and obvious before send. diff --git a/native/macos-hotkey-helper/Sources/AgentVoiceHotkeyHelper/main.swift b/native/macos-hotkey-helper/Sources/AgentVoiceHotkeyHelper/main.swift index fd0fb253a..eb21191e0 100644 --- a/native/macos-hotkey-helper/Sources/AgentVoiceHotkeyHelper/main.swift +++ b/native/macos-hotkey-helper/Sources/AgentVoiceHotkeyHelper/main.swift @@ -20,7 +20,7 @@ let parts = binding let requestedModifiers = Set(parts.filter { modifierNames.contains($0) }) let requestedKey = parts.last.flatMap { modifierNames.contains($0) ? nil : $0 } -// These names match cc-shell's stored binding vocabulary. They are intentionally +// These names match Agent Code's stored binding vocabulary. They are intentionally // physical-ish key names, not localized glyphs: a dictation trigger should not // move just because the user has a Swedish/US/etc keyboard layout selected. let keyCodes: [String: CGKeyCode] = [ @@ -76,13 +76,16 @@ func emit(_ type: String, _ extra: String = "") { // helper saw Fn at all, but it now floods the terminal at every Cmd press // and buries the dictation logs the user actually cares about. Keep the // function so the call sites still compile, but make it a no-op unless -// CC_SHELL_HOTKEY_HELPER_DEBUG is set in the environment. Re-enable by -// running `CC_SHELL_HOTKEY_HELPER_DEBUG=1 npm run dev` if you ever need -// the raw modifier stream again. -let helperDebugEnabled = ProcessInfo.processInfo.environment["CC_SHELL_HOTKEY_HELPER_DEBUG"] == "1" +// AGENT_CODE_HOTKEY_HELPER_DEBUG is set in the environment. Re-enable by +// running `AGENT_CODE_HOTKEY_HELPER_DEBUG=1 npm run dev` if you ever need +// the raw modifier stream again. Keep the old CC_SHELL_* flag as a +// compatibility fallback for local scripts people may already have. +let helperDebugEnabled = + ProcessInfo.processInfo.environment["AGENT_CODE_HOTKEY_HELPER_DEBUG"] == "1" || + ProcessInfo.processInfo.environment["CC_SHELL_HOTKEY_HELPER_DEBUG"] == "1" func debug(_ message: String) { if !helperDebugEnabled { return } - fputs("[cc-shell-hotkey-helper-debug] \(message)\n", stderr) + fputs("[agent-code-hotkey-helper-debug] \(message)\n", stderr) fflush(stderr) } @@ -103,20 +106,20 @@ func changedModifierName(_ event: CGEvent) -> String? { } if binding.isEmpty { - fputs("[cc-shell-hotkey-helper] empty binding\n", stderr) + fputs("[agent-code-hotkey-helper] empty binding\n", stderr) exit(64) } debug("boot binding=\(binding) requestedModifiers=\(requestedModifiers.sorted().joined(separator: "+")) requestedKey=\(requestedKey ?? "nil")") if requestedKey != nil && requestedKeyCode == nil { - fputs("[cc-shell-hotkey-helper] unsupported key in binding: \(binding)\n", stderr) + fputs("[agent-code-hotkey-helper] unsupported key in binding: \(binding)\n", stderr) exit(65) } let promptOptions = [kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String: true] as CFDictionary if !AXIsProcessTrustedWithOptions(promptOptions) { - fputs("[cc-shell-hotkey-helper] accessibility permission is required\n", stderr) + fputs("[agent-code-hotkey-helper] accessibility permission is required\n", stderr) } let callback: CGEventTapCallBack = { _, type, event, _ in @@ -194,7 +197,7 @@ guard let tap = CGEvent.tapCreate( callback: callback, userInfo: nil ) else { - fputs("[cc-shell-hotkey-helper] failed to create event tap\n", stderr) + fputs("[agent-code-hotkey-helper] failed to create event tap\n", stderr) exit(66) } diff --git a/package-lock.json b/package-lock.json index 45d8f64d4..f8ece3170 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "cc-shell", + "name": "agent-code", "version": "0.0.2-beta.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "cc-shell", + "name": "agent-code", "version": "0.0.2-beta.1", "hasInstallScript": true, "dependencies": { diff --git a/package.json b/package.json index 7a2c012c0..065d2af82 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "cc-shell", + "name": "agent-code", "version": "0.0.2-beta.1", - "description": "Agent Studio Code desktop app", + "description": "Agent Code desktop app", "main": "out/main/index.js", "type": "module", "scripts": { diff --git a/packages/agent-transcript-parser b/packages/agent-transcript-parser index 5b6e1edc0..d0214f393 160000 --- a/packages/agent-transcript-parser +++ b/packages/agent-transcript-parser @@ -1 +1 @@ -Subproject commit 5b6e1edc0555d06612fdc18ca1a994f13f95653b +Subproject commit d0214f393c44dd43ee51913b3c21e2815abdd7a0 diff --git a/packages/agent-voice-dictation b/packages/agent-voice-dictation index cd0f87f35..491ddb42a 160000 --- a/packages/agent-voice-dictation +++ b/packages/agent-voice-dictation @@ -1 +1 @@ -Subproject commit cd0f87f3581d319ecec6d74f129941f39fada1fd +Subproject commit 491ddb42aa15a0d4095c2c7b0b3ff51667977a34 diff --git a/packages/claude-code-headless b/packages/claude-code-headless index 13e82be3e..76fc82ef4 160000 --- a/packages/claude-code-headless +++ b/packages/claude-code-headless @@ -1 +1 @@ -Subproject commit 13e82be3ea08f60b0ebfcad915447428da60c210 +Subproject commit 76fc82ef472280de2390367c9531fb542448190a diff --git a/packages/codex-headless b/packages/codex-headless index ffb6633bf..fcf4c06fe 160000 --- a/packages/codex-headless +++ b/packages/codex-headless @@ -1 +1 @@ -Subproject commit ffb6633bf26442f1fdd3656194cf5d1bb7bdcb1d +Subproject commit fcf4c06feacad1c7e2272af637e79d6f193cf63d diff --git a/scripts/test-dictation-imports.ts b/scripts/test-dictation-imports.ts index 7c8e35732..271fd1b8b 100644 --- a/scripts/test-dictation-imports.ts +++ b/scripts/test-dictation-imports.ts @@ -1,21 +1,21 @@ -// Phase 1 smoke test: prove cc-shell can resolve and use the +// Phase 1 smoke test: prove Agent Code can resolve and use the // agent-voice-dictation package without any actual network round-trip. // // Why a structural smoke test instead of a real Deepgram call: -// - No API key in this dev sandbox, no fixture audio in cc-shell's repo. +// - No API key in this dev sandbox, no fixture audio in Agent Code's repo. // - We want a fast, deterministic gate that catches the breakage modes // this integration is most likely to hit: misnamed exports, broken // subpath resolution, types that drift between the two repos. Real // network tests belong in flow-electron's live-providers suite, not -// here — cc-shell is a consumer, not a provider gateway. +// here — Agent Code is a consumer, not a provider gateway. // // What this verifies: -// 1. cc-shell's main-process tsx can import 'agent-voice-dictation' +// 1. Agent Code's main-process tsx can import 'agent-voice-dictation' // and the controller wrapping it. // 2. Both subpath exports we rely on resolve (composer + env). // 3. listSelectableProviders() agrees with the package's support map // (catches the case where the package quietly disables a provider -// and cc-shell's UI keeps offering it). +// and Agent Code's UI keeps offering it). // 4. The Deepgram streaming singleton constructs without throwing. // // Run with: npm run test:dictation diff --git a/scripts/test-prompt-templates.ts b/scripts/test-prompt-templates.ts index 483b19aa7..93c57aa55 100644 --- a/scripts/test-prompt-templates.ts +++ b/scripts/test-prompt-templates.ts @@ -1,3 +1,6 @@ +import { + PROMPT_TEMPLATES_STORAGE_KEY, +} from '@renderer/app-state/localStorageMigration' import { deleteCustomPromptTemplate, loadCustomPromptTemplates, @@ -5,7 +8,7 @@ import { updateCustomPromptTemplate, } from '@renderer/features/prompt-templates/templates' -const CUSTOM_TEMPLATES_KEY = 'cc-shell.promptTemplates.v1' +const CUSTOM_TEMPLATES_KEY = PROMPT_TEMPLATES_STORAGE_KEY function assert(condition: unknown, message: string): void { if (!condition) throw new Error(message) diff --git a/src/main/dictation/controller.ts b/src/main/dictation/controller.ts index cc271ba71..bb7d6ef06 100644 --- a/src/main/dictation/controller.ts +++ b/src/main/dictation/controller.ts @@ -1,6 +1,6 @@ -// Phase 1: minimal main-process dictation controller for cc-shell. +// Phase 1: minimal main-process dictation controller for Agent Code. // -// Purpose of this file is narrow on purpose. It proves cc-shell can call +// Purpose of this file is narrow on purpose. It proves Agent Code can call // `agent-voice-dictation` from its main process and shape the outputs into // the discriminated-union outcome type the renderer will eventually consume. // No IPC handlers, no global hotkey, no UI yet — those land in Phase 2 with @@ -13,7 +13,7 @@ // package converging on the same orchestration vocabulary makes it easier // to spot drift later — if the package gains a new primitive (say, // per-session diarisation), both hosts adopt it the same way. Every place -// where cc-shell's needs *intentionally* differ from flow-electron's gets +// where Agent Code's needs *intentionally* differ from flow-electron's gets // a comment explaining why. import { @@ -40,10 +40,10 @@ export type DictationBatchInput = { mimeType?: string /** ISO 639-1 / BCP-47 short code. v1 is English-only by product * decision (see agent-voice-dictation settings store comment) — leaving - * this configurable here keeps the option available for cc-shell to + * this configurable here keeps the option available for Agent Code to * expose multilingual dictation later without a controller refactor. */ language?: string - /** Optional polish step. cc-shell will eventually feed this with + /** Optional polish step. Agent Code will eventually feed this with * per-project glossary context (the MCP feedback loop), but Phase 1 * keeps it as a plain on/off toggle to stay scoped. */ polish?: { openRouterApiKey: string; model?: string } @@ -52,7 +52,7 @@ export type DictationBatchInput = { // Discriminated union, same shape as the cleanup we landed on the // agent-voice-dictation app side (PR #1, "treat empty transcripts as a -// normal outcome"). Adopted here from the start so cc-shell never has the +// normal outcome"). Adopted here from the start so Agent Code never has the // "no speech detected = thrown exception spamming the terminal" phase // flow-electron had to refactor out of. export type DictationBatchOutcome = @@ -119,7 +119,7 @@ function runProvider(input: DictationBatchInput): Promise { // Singleton streaming provider. The package's createDeepgramStreamingProvider // is intentionally session-keyed: one instance manages many concurrent // sessions identified by id. We construct it lazily on first use because -// the cc-shell main process should not pay the WebSocket-helper cost +// the Agent Code main process should not pay the WebSocket-helper cost // during boot — most users will never trigger dictation. let deepgramStreamingSingleton: ReturnType | null = null @@ -132,10 +132,10 @@ export function deepgramStreaming(): ReturnType isSpeechProviderSelectable(id)) diff --git a/src/main/dictation/index.ts b/src/main/dictation/index.ts index c3f98aab5..33efc6fd4 100644 --- a/src/main/dictation/index.ts +++ b/src/main/dictation/index.ts @@ -1,4 +1,4 @@ -// Public surface for the cc-shell dictation module. Phase 1 only exposes +// Public surface for the Agent Code dictation module. Phase 1 only exposes // the controller — IPC registration, settings/secrets wiring, and UI // integration (the composer-embedded pill) all land in Phase 2 and will // add their own modules under this directory. diff --git a/src/main/dictation/macHotkeyHelper.ts b/src/main/dictation/macHotkeyHelper.ts index 1ea0107d6..02b5677a8 100644 --- a/src/main/dictation/macHotkeyHelper.ts +++ b/src/main/dictation/macHotkeyHelper.ts @@ -88,7 +88,7 @@ async function ensureHelperBinary(): Promise { const sourceBytes = await readFile(source) const hash = createHash('sha256').update(sourceBytes).digest('hex').slice(0, 12) const dir = join(app.getPath('userData'), 'native-helpers') - const target = join(dir, `CcShellDictationHotkeyHelper-${hash}`) + const target = join(dir, `AgentCodeDictationHotkeyHelper-${hash}`) try { await access(target, constants.X_OK) diff --git a/src/main/ghostJournal.ts b/src/main/ghostJournal.ts index 99c018f6c..8b1e15a1e 100644 --- a/src/main/ghostJournal.ts +++ b/src/main/ghostJournal.ts @@ -3,7 +3,7 @@ // Ghost entries are minted, updated, superseded, and orphaned in the // renderer (via agent-transcript-parser + `src/renderer/src/workspace/ghosts.ts`). // This file handles the durable side: append each produced ghost to -// a cc-shell-owned JSONL file so the live view survives a crash, a +// an Agent Code-owned JSONL file so the live view survives a crash, a // window reload, or an Electron restart. // // See docs/design/ghost-system.md for the canonical explanation @@ -21,11 +21,11 @@ // `~/.codex/sessions/.../rollout-*.jsonl`. Those belong to the CLI // and are actively written by its own batched queue; two writers on // the same file is a lost-write / torn-line disaster. Native parsers -// also do not need to see our ghosts — ghosts are a cc-shell concern. +// also do not need to see our ghosts — ghosts are an Agent Code concern. // // Our ghost log sits under `app.getPath('userData')` which is the // standard Electron-managed profile dir (macOS: -// `~/Library/Application Support/cc-shell`). One file per sessionId: +// `~/Library/Application Support/Agent Code`). One file per sessionId: // // /ghost-logs/.ghost.jsonl // @@ -229,7 +229,7 @@ export class GhostJournalRegistry { * `(uuid, _atp.updatedAt)`, with equal timestamps resolved by later * file order. We can preserve that contract while holding only one * final object per uuid in memory. Superseded ghosts are dropped - * before returning because cc-shell imports `reduceGhostLogSansSuperseded` + * before returning because Agent Code imports `reduceGhostLogSansSuperseded` * on the renderer side for this same bootstrap path; doing the drop * here avoids shipping forensic-only entries across IPC. * diff --git a/src/main/index.ts b/src/main/index.ts index 5829ab6ab..8c2e62a54 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -33,6 +33,10 @@ import { performanceService } from '@main/performance/PerformanceService.js' import { startMainHeapWatchdog, stopMainHeapWatchdog } from '@main/performance/heapWatchdog.js' import { getToolPath, initializeToolchain } from '@main/setup/toolchain.js' import { WorktreeActivityIndex } from '@main/worktreeActivity/WorktreeActivityIndex.js' +import { + migrateLegacyStateDir, + migrateLegacyUserDataDirs, +} from '@main/storage/stateMigration.js' // Main process — thin Electron host. // @@ -94,16 +98,21 @@ if (!hasSingleInstanceLock) { focusMainWindow() }) - void performanceService.start().catch(err => { - console.warn('[performance] failed to start:', err) - }) - void app.whenReady().then(startApp) } // ---------- App lifecycle ---------- async function startApp(): Promise { + await migrateLegacyStateDir().catch(err => { + console.warn('[state] legacy state migration failed (continuing with current path):', err) + }) + await migrateLegacyUserDataDirs(app.getPath('userData')).catch(err => { + console.warn('[state] legacy userData migration failed (continuing with current path):', err) + }) + void performanceService.start().catch(err => { + console.warn('[performance] failed to start:', err) + }) performanceService.mark('app.main.whenReady.start') // Heap watchdog and debug-storage retention run as early as possible: // the watchdog so any pre-toolchain startup stall has forensic coverage, diff --git a/src/main/ipc/dictation.ts b/src/main/ipc/dictation.ts index 2d56ba7b1..0f85b405e 100644 --- a/src/main/ipc/dictation.ts +++ b/src/main/ipc/dictation.ts @@ -20,13 +20,15 @@ import { wrapWithSttTag, type SpeechTraceEvent } from 'agent-voice-dictation' // Opt-in chunk dump for diagnosing recorder/provider audio issues. Writes a // `.webm` per session under Electron's app temp dir so we don't pollute -// world-readable `/tmp`. Off unless `CC_SHELL_DICTATION_DUMP=1` is set — +// world-readable `/tmp`. Off unless `AGENT_CODE_DICTATION_DUMP=1` is set — // keeping mic audio off-disk by default is the right default for a privacy // surface, even if we trust the host machine. To use: -// CC_SHELL_DICTATION_DUMP=1 npm run dev +// AGENT_CODE_DICTATION_DUMP=1 npm run dev // then the path is logged at session start and finalize. -const DICTATION_DUMP_ENABLED = process.env.CC_SHELL_DICTATION_DUMP === '1' -const dictationDumpPath = (id: string) => join(app.getPath('temp'), `cc-shell-dictation-${id}.webm`) +const DICTATION_DUMP_ENABLED = + process.env.AGENT_CODE_DICTATION_DUMP === '1' || + process.env.CC_SHELL_DICTATION_DUMP === '1' +const dictationDumpPath = (id: string) => join(app.getPath('temp'), `agent-code-dictation-${id}.webm`) type ActiveDictationSession = { id: string @@ -121,7 +123,7 @@ export function registerDictationIpc(deps: { reason: 'non-deepgram-provider', provider: params.provider, }) - return { kind: 'error', message: 'Only Deepgram streaming is wired in cc-shell v1.' } + return { kind: 'error', message: 'Only Deepgram streaming is wired in Agent Code v1.' } } const apiKey = readDeepgramApiKey() @@ -131,7 +133,7 @@ export function registerDictationIpc(deps: { }) return { kind: 'error', - message: 'Missing DEEPGRAM_API_KEY. Add it to cc-shell .env or the shell environment.', + message: 'Missing DEEPGRAM_API_KEY. Add it to Agent Code .env or the shell environment.', } } @@ -151,7 +153,7 @@ export function registerDictationIpc(deps: { }) }, onTranscript: event => { - // cc-shell keeps the batch upload as the final authority because + // Agent Code keeps the batch upload as the final authority because // the WebM/Opus websocket path has had provider-side parser // failures. Streaming is still valuable as a preview side-channel: // if it emits interim text, the composer can paint live words; if diff --git a/src/main/ipc/fs.ts b/src/main/ipc/fs.ts index 3db3f8808..4700ad504 100644 --- a/src/main/ipc/fs.ts +++ b/src/main/ipc/fs.ts @@ -91,7 +91,7 @@ export function registerFsIpc(): void { // the renderer shows a text input modal where the user types a // path; we expand `~`, resolve to an absolute path, and check that // it exists and is a directory. Keyboard-first is faster for power - // users and matches cc-shell's terminal-native vibe. + // users and matches Agent Code's terminal-native vibe. ipcMain.handle( 'fs:expandCwd', async ( diff --git a/src/main/ipc/performance.ts b/src/main/ipc/performance.ts index 30f75dfad..d3a61dcf9 100644 --- a/src/main/ipc/performance.ts +++ b/src/main/ipc/performance.ts @@ -38,7 +38,8 @@ export function registerPerformanceIpc(manager: SessionManager): void { // immediately without touching disk. // // WHY gated on performanceService.getConfig().enabled (which - // mirrors CC_SHELL_PERF): the badge is an opt-in diagnostic, not + // mirrors AGENT_CODE_PERF, with CC_SHELL_PERF kept as a legacy alias: + // the badge is an opt-in diagnostic, not // a feature for end users. When disabled we still respond with a // valid shape (enabled: false plus zeros) so the renderer hook // can detect the gate on its first probe without throwing. The diff --git a/src/main/ipc/session.ts b/src/main/ipc/session.ts index 183b93607..dda504c52 100644 --- a/src/main/ipc/session.ts +++ b/src/main/ipc/session.ts @@ -78,7 +78,7 @@ export function registerSessionIpc( 'session:input', (_evt, sessionId: string, data: string, pasteId?: string) => { // Optional pasteId journals THIS write into the per-paste debug - // dump. Only set by the cc-shell paste flow (claudePaste.ts) — + // dump. Only set by the Agent Code paste flow (claudePaste.ts) — // never set on keystrokes, agent-pty bridging, or other normal // I/O. Pairs against the renderer's IPC:write:* events by sha8 // + byte count, same way dictation pairs renderer-produced diff --git a/src/main/ipc/workspace.ts b/src/main/ipc/workspace.ts index 603ed135c..986eb7524 100644 --- a/src/main/ipc/workspace.ts +++ b/src/main/ipc/workspace.ts @@ -30,9 +30,10 @@ export function registerWorkspaceIpc(): void { }) // Renderer calls this on first launch when there's no saved state - // and no user-picked cwd yet. CC_SHELL_CWD overrides — useful in + // and no user-picked cwd yet. AGENT_CODE_CWD overrides — useful in // dev for launching the app pointed at a specific test project. + // Keep CC_SHELL_CWD as a compatibility fallback for existing scripts. ipcMain.handle('workspace:defaultCwd', () => { - return process.env.CC_SHELL_CWD || process.cwd() + return process.env.AGENT_CODE_CWD || process.env.CC_SHELL_CWD || process.cwd() }) } diff --git a/src/main/loadEnv.ts b/src/main/loadEnv.ts index b2415ea06..851d64dbb 100644 --- a/src/main/loadEnv.ts +++ b/src/main/loadEnv.ts @@ -6,7 +6,7 @@ import { resolve } from 'node:path' // // WHY this exists: electron-vite's Vite layer only exposes `*_VITE_` // prefixed variables via `import.meta.env`. Unprefixed flags like -// `CC_SHELL_PERF` never reach `process.env` in the main process, so +// `AGENT_CODE_PERF` never reach `process.env` in the main process, so // modules that gate on them at import time (see // `@main/performance/PerformanceService.ts`) would always see them // as unset no matter what `.env` contained. @@ -49,7 +49,7 @@ function loadEnvFile(path: string): void { if (!parsed) continue const [key, value] = parsed // Don't clobber an explicit shell export — CLI-set values win - // so a `CC_SHELL_PERF=0 npm run dev` turns telemetry off even + // so an `AGENT_CODE_PERF=0 npm run dev` turns telemetry off even // if `.env` has it enabled. if (process.env[key] !== undefined) continue process.env[key] = value diff --git a/src/main/lspManager.ts b/src/main/lspManager.ts index 235659727..fb751f53f 100644 --- a/src/main/lspManager.ts +++ b/src/main/lspManager.ts @@ -103,7 +103,7 @@ function makeVirtualServerUri( const ext = languageFileExtension(language) const filePath = resolve( workspaceRoot, - '.cc-shell-lsp', + '.agent-code-lsp', `virtual-${hashText(clientUri)}.${ext}`, ) return pathToFileURL(filePath).href @@ -245,7 +245,7 @@ export class LspManager extends EventEmitter { ...process.env, // In packaged Electron, process.execPath is the app executable. // Without this flag, spawning the language server re-launches - // Agent Studio Code instead of running the CLI as a Node script. + // Agent Code instead of running the CLI as a Node script. ELECTRON_RUN_AS_NODE: '1', NODE_NO_WARNINGS: '1', }, diff --git a/src/main/performance/PerformanceService.ts b/src/main/performance/PerformanceService.ts index f51a1c45a..8294f5873 100644 --- a/src/main/performance/PerformanceService.ts +++ b/src/main/performance/PerformanceService.ts @@ -20,18 +20,19 @@ import { type PerformanceLogFile, } from '@main/storage/performanceLog.js' import { LocalJsonlSpanExporter } from '@main/performance/LocalJsonlSpanExporter.js' +import { APP_SLUG, LEGACY_APP_SLUG } from '@shared/appIdentity.js' const DEFAULT_SLOW_SPAN_MS = 50 const FLUSH_INTERVAL_MS = 500 const SAMPLE_INTERVAL_MS = 5000 -function envFlag(name: string): boolean { - const value = process.env[name] +function envFlag(name: string, legacyName?: string): boolean { + const value = process.env[name] ?? (legacyName ? process.env[legacyName] : undefined) return value === '1' || value === 'true' || value === 'yes' } -function envNumber(name: string, fallback: number): number { - const raw = process.env[name] +function envNumber(name: string, fallback: number, legacyName?: string): number { + const raw = process.env[name] ?? (legacyName ? process.env[legacyName] : undefined) if (!raw) return fallback const parsed = Number(raw) return Number.isFinite(parsed) && parsed >= 0 ? parsed : fallback @@ -60,13 +61,13 @@ function recordFile(record: PerformanceRecord): PerformanceLogFile { } export class PerformanceService { - private readonly enabled = envFlag('CC_SHELL_PERF') - private readonly verbose = envFlag('CC_SHELL_PERF_VERBOSE') - private readonly slowSpanMs = envNumber('CC_SHELL_PERF_SLOW_MS', DEFAULT_SLOW_SPAN_MS) + private readonly enabled = envFlag('AGENT_CODE_PERF', 'CC_SHELL_PERF') + private readonly verbose = envFlag('AGENT_CODE_PERF_VERBOSE', 'CC_SHELL_PERF_VERBOSE') + private readonly slowSpanMs = envNumber('AGENT_CODE_PERF_SLOW_MS', DEFAULT_SLOW_SPAN_MS, 'CC_SHELL_PERF_SLOW_MS') private readonly runId = this.enabled ? `${Date.now()}-${pid}` : null private runDir: string | null = null private tracerProvider: NodeTracerProvider | null = null - private tracer = trace.getTracer('cc-shell-main') + private tracer = trace.getTracer(`${APP_SLUG}-main`) private flushTimer: ReturnType | null = null private sampleTimer: ReturnType | null = null private eventLoopDelay: ReturnType | null = null @@ -88,7 +89,8 @@ export class PerformanceService { this.runDir = await ensurePerformanceRunDir(runFolderName(startedAt, pid)) this.tracerProvider = new NodeTracerProvider({ resource: resourceFromAttributes({ - 'service.name': 'cc-shell', + 'service.name': APP_SLUG, + 'service.legacy_name': LEGACY_APP_SLUG, 'service.namespace': 'desktop', 'process.runtime.name': 'electron-main', 'process.pid': pid, @@ -105,7 +107,7 @@ export class PerformanceService { ], }) this.tracerProvider.register() - this.tracer = trace.getTracer('cc-shell-main') + this.tracer = trace.getTracer(`${APP_SLUG}-main`) await writePerformanceManifest(this.runDir, { schemaVersion: 2, @@ -120,6 +122,9 @@ export class PerformanceService { electron: versions.electron, chrome: versions.chrome, env: { + AGENT_CODE_PERF: process.env.AGENT_CODE_PERF ?? null, + AGENT_CODE_PERF_VERBOSE: process.env.AGENT_CODE_PERF_VERBOSE ?? null, + AGENT_CODE_PERF_SLOW_MS: process.env.AGENT_CODE_PERF_SLOW_MS ?? null, CC_SHELL_PERF: process.env.CC_SHELL_PERF ?? null, CC_SHELL_PERF_VERBOSE: process.env.CC_SHELL_PERF_VERBOSE ?? null, CC_SHELL_PERF_SLOW_MS: process.env.CC_SHELL_PERF_SLOW_MS ?? null, diff --git a/src/main/performance/heapWatchdog.ts b/src/main/performance/heapWatchdog.ts index 01d30460f..6aec32853 100644 --- a/src/main/performance/heapWatchdog.ts +++ b/src/main/performance/heapWatchdog.ts @@ -2,7 +2,7 @@ // // Heap-pressure watchdog for the Electron main process. // -// WHY this exists: cc-shell long-running sessions have OOMed the main +// WHY this exists: Agent Code long-running sessions have OOMed the main // process (4 GB v8 cap on macOS). Reproductions take 20+ hours, so // catching them with a debugger is impractical. The watchdog samples // `v8.getHeapStatistics()` at a low duty cycle and writes a heap diff --git a/src/main/sessionIndex.ts b/src/main/sessionIndex.ts index 5028ad348..4d08dee00 100644 --- a/src/main/sessionIndex.ts +++ b/src/main/sessionIndex.ts @@ -22,7 +22,7 @@ import { performanceService } from '@main/performance/PerformanceService.js' // last-M user prompts) and search (query across ALL prompts with // matching-bubbles-to-top ranking). The UI toggles based on // whether the user has typed anything. -// - Linear scan over JSONL files is fine at cc-shell scale; we cap +// - Linear scan over JSONL files is fine at Agent Code scale; we cap // visible sessions and back the rest with search. A proper inverted // index would be overkill — a typical user has ≤200 sessions, each // with ≤100 user prompts, so we're scanning a few tens of thousands diff --git a/src/main/sessionManager.ts b/src/main/sessionManager.ts index 89f6bf001..6cc7e8f43 100644 --- a/src/main/sessionManager.ts +++ b/src/main/sessionManager.ts @@ -26,7 +26,7 @@ import type { ProviderConditionSnapshot } from '@shared/types/providerConditions // one IPC channel per event type anyway — the mux belongs here where // it's testable in isolation. // -// Multi-kind support: cc-shell can host two kinds of sessions per pane +// Multi-kind support: Agent Code can host two kinds of sessions per pane // today — a Claude Code session (ClaudeSession) or a plain shell // terminal (TerminalSession). The registry holds a union; spawn() // dispatches on options.kind; events from both are funnelled through @@ -506,7 +506,7 @@ export class SessionManager extends EventEmitter { (await reg.sessionExists(options.recoverTmuxName)) ) { // Reattach path — tmux owned this session through the previous - // cc-shell launch and it's still alive. Reuse the name; do + // Agent Code launch and it's still alive. Reuse the name; do // NOT createSession (that would error or duplicate). tmuxSessionName = options.recoverTmuxName } else { diff --git a/src/main/storage/claudeImageCache.ts b/src/main/storage/claudeImageCache.ts index 46f5d9daf..0b523e0fa 100644 --- a/src/main/storage/claudeImageCache.ts +++ b/src/main/storage/claudeImageCache.ts @@ -3,6 +3,8 @@ import { extname, join } from 'path' import { mkdir, rm, writeFile } from 'fs/promises' import { app } from 'electron' +import { APP_SLUG } from '@shared/appIdentity.js' + // Claude image paste cache. // // When the user pastes an image into the Claude composer, the @@ -68,7 +70,7 @@ function estimateBase64DecodedBytes(base64Data: string): number { } function getClaudeImageCacheDir(): string { - return join(app.getPath('temp'), 'cc-shell', 'claude-images') + return join(app.getPath('temp'), APP_SLUG, 'claude-images') } export async function cleanupClaudeImageCacheDir(): Promise { diff --git a/src/main/storage/debugBundle.ts b/src/main/storage/debugBundle.ts index 06a8f2dd6..92bebc26d 100644 --- a/src/main/storage/debugBundle.ts +++ b/src/main/storage/debugBundle.ts @@ -124,7 +124,7 @@ export async function saveDebugBundle( // are small (a few files, typically < 1MB total) and a serial loop // keeps error messages unambiguous — a failure names the file that // actually failed, not an aggregate rejection that's harder to act - // on when debugging cc-shell itself (which is the whole point of + // on when debugging Agent Code itself (which is the whole point of // this feature). for (const file of params.files) { const target = join(bundlePath, file.name) diff --git a/src/main/storage/debugRetention.ts b/src/main/storage/debugRetention.ts index 4eb48f54c..735590799 100644 --- a/src/main/storage/debugRetention.ts +++ b/src/main/storage/debugRetention.ts @@ -38,8 +38,8 @@ export type DebugStoragePruneResult = { let pruneInFlight: Promise | null = null let lastPruneStartedAt = 0 -function envNumber(name: string, fallback: number): number { - const raw = process.env[name]?.trim() +function envNumber(name: string, fallback: number, legacyName?: string): number { + const raw = (process.env[name] ?? (legacyName ? process.env[legacyName] : undefined))?.trim() if (!raw) return fallback const parsed = Number(raw) return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback @@ -64,11 +64,11 @@ async function defaultBudgetBytes(): Promise { } async function budgetBytes(): Promise { - return Math.floor(envNumber('CC_SHELL_DEBUG_MAX_GB', (await defaultBudgetBytes()) / GIB) * GIB) + return Math.floor(envNumber('AGENT_CODE_DEBUG_MAX_GB', (await defaultBudgetBytes()) / GIB, 'CC_SHELL_DEBUG_MAX_GB') * GIB) } function ttlHours(): number { - return envNumber('CC_SHELL_DEBUG_TTL_HOURS', DEFAULT_TTL_HOURS) + return envNumber('AGENT_CODE_DEBUG_TTL_HOURS', DEFAULT_TTL_HOURS, 'CC_SHELL_DEBUG_TTL_HOURS') } export function scheduleDebugStoragePrune(reason: string): void { diff --git a/src/main/storage/paths.ts b/src/main/storage/paths.ts index f428bb248..0d4388e51 100644 --- a/src/main/storage/paths.ts +++ b/src/main/storage/paths.ts @@ -1,14 +1,17 @@ import { join } from 'path' import { homedir } from 'os' +import { APP_SLUG, LEGACY_APP_SLUG } from '@shared/appIdentity.js' + // Disk paths for persisted state. // // STATE_DIR follows XDG on Linux but uses ~/.config on macOS too — // it's simpler than mirroring Electron's per-platform userData logic, // the file is tiny, and the user has explicit control over it. -// Everything persisted by cc-shell lives under this one directory. +// Everything persisted by Agent Code lives under this one directory. -export const STATE_DIR = join(homedir(), '.config', 'cc-shell') +export const STATE_DIR = join(homedir(), '.config', APP_SLUG) +export const LEGACY_STATE_DIR = join(homedir(), '.config', LEGACY_APP_SLUG) // Tile tree + session metadata, written atomically by workspace:save. // The renderer owns the JSON shape; main is a byte mover. @@ -25,7 +28,7 @@ export const FEED_DEBUG_DIR = join(STATE_DIR, 'feed-debug') // are user-triggered rather than streaming. // // Lives under STATE_DIR (not ~/Downloads or the project cwd) so bundles -// are colocated with the rest of cc-shell's on-disk state — one place to +// are colocated with the rest of Agent Code's on-disk state — one place to // purge, one place to back up. The Save command shows the resulting // path in a toast AND copies it to the clipboard, so discoverability // doesn't depend on the user knowing the filesystem layout. @@ -37,5 +40,6 @@ export const DEBUG_BUNDLE_DIR = join(STATE_DIR, 'debug-bundles') export const PROXY_EVENTS_DIR = join(STATE_DIR, 'proxy') // Environment-gated app performance traces. One folder per app run, -// written only when CC_SHELL_PERF=1. +// written only when AGENT_CODE_PERF=1. CC_SHELL_PERF remains accepted +// as a legacy compatibility alias. export const PERFORMANCE_RUNS_DIR = join(STATE_DIR, 'performance', 'runs') diff --git a/src/main/storage/proxyEventsReader.ts b/src/main/storage/proxyEventsReader.ts index 7a13792fc..47e7228af 100644 --- a/src/main/storage/proxyEventsReader.ts +++ b/src/main/storage/proxyEventsReader.ts @@ -10,7 +10,7 @@ import { canonicalizePath, sanitizePath } from '@shared/runtime/projectDir.js' // Both Claude (via mitmproxy + ProxyServer in claude-code-headless) // and Codex (via ResponsesProxy after PR feat/codex-proxy-capture) // write per-session-run JSONL logs under -// ~/.config/cc-shell/proxy////proxy-events.jsonl +// ~/.config/agent-code/proxy////proxy-events.jsonl // // These contain the wire-level capture of every API request and // response chunk: headers, request body (up to 2 MiB), pre-parsed @@ -45,7 +45,7 @@ const PROXY_ROOT = PROXY_EVENTS_DIR // are within the last few minutes of traffic. Older traffic is // useful for trend analysis but not bundle-immediacy. If the user // needs the full log they can grab it directly from -// ~/.config/cc-shell/proxy/.../proxy-events.jsonl — the run dir +// ~/.config/agent-code/proxy/.../proxy-events.jsonl — the run dir // path goes into the bundle's manifest so they know where to look. const PROXY_EVENTS_BUNDLE_MAX_BYTES = 5 * 1024 * 1024 diff --git a/src/main/storage/stateMigration.ts b/src/main/storage/stateMigration.ts new file mode 100644 index 000000000..78e03d58b --- /dev/null +++ b/src/main/storage/stateMigration.ts @@ -0,0 +1,93 @@ +import { cp, mkdir, readdir, rename, stat } from 'node:fs/promises' +import { dirname, join } from 'node:path' + +import { LEGACY_STATE_DIR, STATE_DIR } from '@main/storage/paths.js' + +const LEGACY_USER_DATA_DIR_NAMES = ['Agent Studio Code', 'cc-shell'] +const USER_DATA_SUBDIRS_TO_MIGRATE = [ + 'ghost-logs', + 'dictation-debug', + 'paste-debug', + 'native-helpers', +] + +async function exists(path: string): Promise { + try { + await stat(path) + return true + } catch { + return false + } +} + +async function copyMissingEntries(fromDir: string, toDir: string): Promise { + await mkdir(toDir, { recursive: true }) + const entries = await readdir(fromDir, { withFileTypes: true }).catch(() => []) + for (const entry of entries) { + const from = join(fromDir, entry.name) + const to = join(toDir, entry.name) + + if (entry.isDirectory() && (await exists(to))) { + await copyMissingEntries(from, to) + continue + } + + if (await exists(to)) continue + await cp(from, to, { recursive: entry.isDirectory(), force: false, errorOnExist: true }) + } +} + +export async function migrateLegacyStateDir(): Promise { + if (STATE_DIR === LEGACY_STATE_DIR) { + await mkdir(STATE_DIR, { recursive: true }) + return + } + + const hasLegacy = await exists(LEGACY_STATE_DIR) + const hasCurrent = await exists(STATE_DIR) + + if (!hasLegacy) { + await mkdir(STATE_DIR, { recursive: true }) + return + } + + if (!hasCurrent) { + // WHY rename first instead of copy: the old ~/.config/cc-shell directory can + // contain many GiB of proxy/feed/debug artifacts. A recursive copy would + // temporarily double disk usage, exactly the failure mode that forced us to + // add bounded retention. Same-volume rename is atomic and keeps all old + // workspace/debug state available at the new Agent Code path. + await rename(LEGACY_STATE_DIR, STATE_DIR) + return + } + + // Both directories existing means the user has already launched a newer + // build, or manually created the Agent Code state dir. In that case we merge + // only entries that do not already exist, preserving the newer directory as + // the source of truth. We intentionally leave the legacy directory behind: + // deleting user state after a partial merge would make rollback impossible. + await copyMissingEntries(LEGACY_STATE_DIR, STATE_DIR) +} + +export async function migrateLegacyUserDataDirs(currentUserDataDir: string): Promise { + const supportDir = dirname(currentUserDataDir) + await mkdir(currentUserDataDir, { recursive: true }) + + for (const legacyName of LEGACY_USER_DATA_DIR_NAMES) { + const legacyDir = join(supportDir, legacyName) + if (legacyDir === currentUserDataDir || !(await exists(legacyDir))) continue + + for (const subdir of USER_DATA_SUBDIRS_TO_MIGRATE) { + const from = join(legacyDir, subdir) + if (!(await exists(from))) continue + + // WHY copy subdirectories instead of renaming the whole legacy userData + // tree: Electron's userData location also contains framework-owned cache + // and per-build files whose shape can differ between dev and packaged + // runs. The Agent Code-owned durable pieces are known and small enough + // to merge safely, and leaving the old directory intact keeps rollback to + // pre-rename builds possible. + await copyMissingEntries(from, join(currentUserDataDir, subdir)) + } + } +} diff --git a/src/main/tmux/TmuxRegistry.ts b/src/main/tmux/TmuxRegistry.ts index ddf90bd7f..291511864 100644 --- a/src/main/tmux/TmuxRegistry.ts +++ b/src/main/tmux/TmuxRegistry.ts @@ -1,4 +1,4 @@ -// TmuxRegistry — main-side service that owns every cc-shell-managed +// TmuxRegistry — main-side service that owns every Agent Code-managed // tmux session. // // Why a registry rather than per-session methods: @@ -16,21 +16,26 @@ import { randomUUID } from 'node:crypto' import { TMUX_SESSION_FLAGS } from '@main/tmux/tmuxConfig.js' export type TmuxRegistryOptions = { - /** All session names this registry manages will start with this - * prefix. Production uses 'ccshell-'; tests use a different + /** All new session names this registry manages will start with this + * prefix. Production uses 'agentcode-'; tests use a different * prefix so they never touch the user's real sessions. */ namePrefix?: string + /** Previous production prefixes that should still be treated as ours + * during recovery. Used for the cc-shell -> Agent Code rename. */ + legacyNamePrefixes?: string[] /** Override for the tmux binary path. Defaults to 'tmux' on PATH. */ tmuxBinary?: string } export class TmuxRegistry { private readonly namePrefix: string + private readonly managedPrefixes: string[] private readonly tmuxBinary: string private availability: boolean | null = null constructor(options: TmuxRegistryOptions = {}) { - this.namePrefix = options.namePrefix ?? 'ccshell-' + this.namePrefix = options.namePrefix ?? 'agentcode-' + this.managedPrefixes = [this.namePrefix, ...(options.legacyNamePrefixes ?? ['ccshell-'])] this.tmuxBinary = options.tmuxBinary ?? 'tmux' } @@ -117,7 +122,7 @@ export class TmuxRegistry { /** * Return every tmux session whose name starts with this registry's * prefix. Used during launch reconciliation to discover sessions - * that survived a previous cc-shell run. + * that survived a previous Agent Code run. * * Returns [] if tmux is unavailable OR if there are no managed * sessions — callers shouldn't have to distinguish those cases here @@ -143,7 +148,7 @@ export class TmuxRegistry { const [name, createdStr] = line.split('|') return { name, createdAt: Number(createdStr) * 1000 } }) - .filter(s => s.name.startsWith(this.namePrefix)) + .filter(s => this.managedPrefixes.some(prefix => s.name.startsWith(prefix))) } /** Run a tmux command, resolving with stdout. Reject on non-zero. */ diff --git a/src/main/tmux/tmuxConfig.ts b/src/main/tmux/tmuxConfig.ts index fcbb13aba..23e5f8020 100644 --- a/src/main/tmux/tmuxConfig.ts +++ b/src/main/tmux/tmuxConfig.ts @@ -5,7 +5,7 @@ // doesn't shrink the primary view. // // Keep this list authoritative — every code path that creates a -// ccshell tmux session must apply these. Drift here would let the +// Agent Code tmux session must apply these. Drift here would let the // status bar leak into the renderer's xterm view. export const TMUX_SESSION_FLAGS: ReadonlyArray = [ diff --git a/src/main/tmux/tmuxRecovery.ts b/src/main/tmux/tmuxRecovery.ts index 230711066..c8f3c0431 100644 --- a/src/main/tmux/tmuxRecovery.ts +++ b/src/main/tmux/tmuxRecovery.ts @@ -24,7 +24,7 @@ export type RecoveryReport = { /** Persisted sessionIds whose tmuxName was NOT alive — caller * should treat these as fresh spawns. */ lost: string[] - /** Alive ccshell-* sessions that were NOT in persisted state. + /** Alive Agent Code-owned sessions that were NOT in persisted state. * P1 kills these silently; P2 will route them to the undo tray. */ orphans: string[] } @@ -51,7 +51,7 @@ export async function reconcile( .filter(s => !persistedNames.has(s.name)) .map(s => s.name) - // P1: silently kill orphans. They're stale ccshell sessions from + // P1: silently kill orphans. They're stale Agent Code sessions from // a previous run that failed to clean up. The registry's prefix // guarantees these are ours to kill. for (const name of orphans) { diff --git a/src/main/tmux/verify-tmux.ts b/src/main/tmux/verify-tmux.ts index e8caf9b5f..a426dbfd8 100644 --- a/src/main/tmux/verify-tmux.ts +++ b/src/main/tmux/verify-tmux.ts @@ -5,7 +5,7 @@ import { TmuxRegistry } from '@main/tmux/TmuxRegistry.js' import { reconcile } from '@main/tmux/tmuxRecovery.js' -const PREFIX = process.env.TMUX_TEST_PREFIX ?? 'ccshell-verify-' +const PREFIX = process.env.TMUX_TEST_PREFIX ?? 'agentcode-verify-' let failed = 0 function check(label: string, ok: boolean, detail?: string): void { diff --git a/src/main/worktreeActivity/transcriptParser.ts b/src/main/worktreeActivity/transcriptParser.ts index 0dbe21948..7f17bbbef 100644 --- a/src/main/worktreeActivity/transcriptParser.ts +++ b/src/main/worktreeActivity/transcriptParser.ts @@ -17,7 +17,7 @@ export async function parseTranscriptForActivity( // This parser stores only compact worktree facts, never rendered // transcript content. That keeps the persisted index small and keeps // privacy/blast-radius sane: the raw Claude/Codex JSONL files remain - // where the providers wrote them, while cc-shell stores enough + // where the providers wrote them, while Agent Code stores enough // metadata to answer workspace orchestration questions quickly. const text = await readFile(candidate.file, 'utf8') const events: WorktreeActivityEvent[] = [] diff --git a/src/preload/api/types.ts b/src/preload/api/types.ts index 65241231b..d0076ca39 100644 --- a/src/preload/api/types.ts +++ b/src/preload/api/types.ts @@ -280,10 +280,10 @@ export type DictationDebugEvent = DictationDebugEventInput & { // Per-paste debug dump. // // Direct mirror of the dictation-debug subsystem. Every Enter that -// triggers cc-shell's paste-submit code path writes one append-only +// triggers Agent Code's paste-submit code path writes one append-only // JSONL file under `/paste-debug/.paste.jsonl` // capturing the full renderer→IPC→main→PTY chain. The bug we are -// chasing is the "paste in cc-shell needs a second Enter to submit" +// chasing is the "paste in Agent Code needs a second Enter to submit" // intermittent — the PTY-isolated harness at // `vendor/in_progress/paste-submit-repro/` shows the production // 125 ms timer path works at 10/10 in isolation, so the failure must @@ -341,7 +341,7 @@ export type SaveDebugBundleResult = { // Section of a debug bundle filled by readProxyEvents IPC. // Carries the renderer-readable form of whatever -// ~/.config/cc-shell/proxy////proxy-events.jsonl +// ~/.config/agent-code/proxy////proxy-events.jsonl // existed for the target session at bundle-save time. Nulls signal // "no record found" — callers must tolerate them. See // main/storage/proxyEventsReader.ts for the search strategy and diff --git a/src/providers/claude/runtime/claudeSession.ts b/src/providers/claude/runtime/claudeSession.ts index ac050a3e2..b30642d79 100644 --- a/src/providers/claude/runtime/claudeSession.ts +++ b/src/providers/claude/runtime/claudeSession.ts @@ -1,7 +1,9 @@ import { EventEmitter } from 'events' +import { join } from 'path' import { spawn as ptySpawn } from 'node-pty' import type { SlashPickerState } from '@preload/index.js' +import { PROXY_EVENTS_DIR } from '@main/storage/paths.js' import { ClaudeCodeHeadless, createProxyServer, @@ -184,11 +186,11 @@ export class ClaudeSession extends EventEmitter { // Passing the workspace cwd into createProxyServer made the proxy layer // create timestamped runtime directories in whatever project the user had // open. That polluted repos with `proxy-events.jsonl`, mitmproxy CA - // state, and other runtime artifacts. We instead give the proxy helper - // enough identity to write under cc-shell's hidden app state directory, - // while still naming folders so humans can correlate them back to the - // shell session / resumed conversation. + // state, and other runtime artifacts. Agent Code owns the storage policy + // and passes its concrete app-state root into the reusable package. const proxy = await createProxyServer({ + storageRoot: PROXY_EVENTS_DIR, + confDir: join(PROXY_EVENTS_DIR, '_shared-conf'), cwd: this.cwd, sessionKey: this.resumeSessionId ? `resume-${this.resumeSessionId}` @@ -270,14 +272,14 @@ export class ClaudeSession extends EventEmitter { // We default to assuming the user is on a non-Haiku // primary model. Why this is OK as a default: // - // * The vast majority of cc-shell users today run + // * The vast majority of Agent Code users today run // Sonnet/Opus; the Anthropic Max / Pro defaults land // here. // * The filter only fires when BOTH the flow's model // matches `/haiku/i` AND the session model does NOT // — so if the user IS on Haiku as primary, they need // to opt out. The escape hatch is the - // `CC_SHELL_PRIMARY_MODEL` env var below; setting it + // `AGENT_CODE_PRIMARY_MODEL` env var below; setting it // to a Haiku model id disables filtering. // * The exact returned string doesn't matter as long as // it doesn't match `/haiku/i`. The adapter only uses @@ -289,7 +291,9 @@ export class ClaudeSession extends EventEmitter { // (1M context) …") so we always know the actual primary // model and the env-var escape hatch becomes redundant. getSessionModel: () => - process.env.CC_SHELL_PRIMARY_MODEL ?? 'claude-opus-4-7', + process.env.AGENT_CODE_PRIMARY_MODEL ?? + process.env.CC_SHELL_PRIMARY_MODEL ?? + 'claude-opus-4-7', } : undefined, }) diff --git a/src/providers/claude/runtime/sessionList.ts b/src/providers/claude/runtime/sessionList.ts index 31700ec20..c03a2eeff 100644 --- a/src/providers/claude/runtime/sessionList.ts +++ b/src/providers/claude/runtime/sessionList.ts @@ -5,7 +5,7 @@ import { getProjectDirForCwd, getProjectsDir } from '@shared/runtime/projectDir. import { performanceService } from '@main/performance/PerformanceService.js' // Session lister — reimplements the minimal subset of CC's -// utils/listSessionsImpl.ts needed to power the cc-shell resume UI. +// utils/listSessionsImpl.ts needed to power the Agent Code resume UI. // // We deliberately don't import from claude-code-src. CC's lister pulls // in a big transitive dependency graph (bootstrap state, analytics, @@ -265,7 +265,7 @@ export async function listSessionsForCwd( } /** - * List every Claude session across every project directory cc-shell + * List every Claude session across every project directory Agent Code * has ever recorded. Intended for the rendering-debug harness where * we want to pick any past session regardless of cwd. Walks * `~/.claude/projects/*` once, stats every `*.jsonl`, sorts by diff --git a/src/providers/codex/runtime/codexSession.ts b/src/providers/codex/runtime/codexSession.ts index c0cfc8a92..7d3885bf3 100644 --- a/src/providers/codex/runtime/codexSession.ts +++ b/src/providers/codex/runtime/codexSession.ts @@ -24,7 +24,7 @@ import { canonicalizePath, sanitizePath } from '@shared/runtime/projectDir.js' * provider's proxy events without branching. * * Path shape: - * ~/.config/cc-shell/proxy////proxy-events.jsonl + * ~/.config/agent-code/proxy////proxy-events.jsonl * * WHY a fresh run dir per call instead of reusing one per session: * A single CodexSession can be stopped + restarted (binary crash, @@ -81,8 +81,8 @@ function sanitiseSegment(value: string): string { // SessionManager can treat Claude and Codex sessions uniformly. // // Why this wrapper exists at all (instead of using CodexHeadless directly): -// - cc-shell needs to SPAWN the process (CodexHeadless takes an IPty) -// - cc-shell needs the SlashPickerState shape for IPC compatibility +// - Agent Code needs to SPAWN the process (CodexHeadless takes an IPty) +// - Agent Code needs the SlashPickerState shape for IPC compatibility // - The event shapes must match ClaudeSession's for SessionManager export type CodexSessionOptions = { @@ -201,7 +201,7 @@ export class CodexSession extends EventEmitter { // Mirror the Claude proxy's on-disk layout so a single // bundle-inspection tool can read either provider's // proxy-events.jsonl without branching: - // ~/.config/cc-shell/proxy////proxy-events.jsonl + // ~/.config/agent-code/proxy////proxy-events.jsonl // // The path discipline is identical to what // claude-code-headless' createProxyServer does (see ProxyServer diff --git a/src/providers/registry.main.ts b/src/providers/registry.main.ts index 9734979a8..db9fb93f8 100644 --- a/src/providers/registry.main.ts +++ b/src/providers/registry.main.ts @@ -24,9 +24,9 @@ const codexMain: MainProviderConfig = { // recorded in the user's current working directory. Without this // filter the codex picker silently returned every session globally // (Codex doesn't partition by cwd), which let the user pick a - // session whose underlying rollout cwd != cc-shell's spawn cwd. + // session whose underlying rollout cwd != Agent Code's spawn cwd. // That mismatch triggers Codex's upstream `cwd_prompt` modal, which - // cc-shell has no detector for — the modal then eats the user's + // Agent Code has no detector for — the modal then eats the user's // first bracketed-paste submission. See the matching change in // packages/codex-headless/src/transcript/SessionList.ts. listSessions: (cwd, limit) => listCodexSessions({ cwd, limit }), diff --git a/src/renderer/index.html b/src/renderer/index.html index 480ed0e03..c88e59188 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -7,7 +7,7 @@ http-equiv="Content-Security-Policy" content="default-src 'self'; img-src 'self' data: blob:; style-src 'self' 'unsafe-inline'; script-src 'self'" /> - Agent Studio Code + Agent Code
diff --git a/src/renderer/src/app-state/localStorageMigration.ts b/src/renderer/src/app-state/localStorageMigration.ts new file mode 100644 index 000000000..a2df14a21 --- /dev/null +++ b/src/renderer/src/app-state/localStorageMigration.ts @@ -0,0 +1,35 @@ +import { + APP_LOCAL_STORAGE_PREFIX, + LEGACY_LOCAL_STORAGE_PREFIX, +} from '@shared/appIdentity' + +type StorageKeyPair = { + current: string + legacy: string +} + +export const APP_STORE_STORAGE_KEY = `${APP_LOCAL_STORAGE_PREFIX}:app-store` +export const LEGACY_APP_STORE_STORAGE_KEY = `${LEGACY_LOCAL_STORAGE_PREFIX}:app-store` +export const APP_SETTINGS_STORAGE_KEY = `${APP_LOCAL_STORAGE_PREFIX}:settings` +export const LEGACY_SETTINGS_STORAGE_KEY = `${LEGACY_LOCAL_STORAGE_PREFIX}:settings` +export const PROMPT_TEMPLATES_STORAGE_KEY = `${APP_LOCAL_STORAGE_PREFIX}.promptTemplates.v1` +export const LEGACY_PROMPT_TEMPLATES_STORAGE_KEY = `${LEGACY_LOCAL_STORAGE_PREFIX}.promptTemplates.v1` + +const MIGRATED_KEYS: StorageKeyPair[] = [ + { current: APP_STORE_STORAGE_KEY, legacy: LEGACY_APP_STORE_STORAGE_KEY }, + { current: APP_SETTINGS_STORAGE_KEY, legacy: LEGACY_SETTINGS_STORAGE_KEY }, + { current: PROMPT_TEMPLATES_STORAGE_KEY, legacy: LEGACY_PROMPT_TEMPLATES_STORAGE_KEY }, +] + +export function migrateLegacyLocalStorageKeys(): void { + // WHY copy instead of move: localStorage is small, and keeping the legacy + // values makes rollback to a pre-rename build painless. The new keys become + // authoritative as soon as Agent Code writes them; old keys are just a + // read-once compatibility bridge for existing pre-rename installs. + for (const { current, legacy } of MIGRATED_KEYS) { + if (window.localStorage.getItem(current) !== null) continue + const value = window.localStorage.getItem(legacy) + if (value === null) continue + window.localStorage.setItem(current, value) + } +} diff --git a/src/renderer/src/app-state/settings/persistence.ts b/src/renderer/src/app-state/settings/persistence.ts index 13cadf7d1..cf8be58fa 100644 --- a/src/renderer/src/app-state/settings/persistence.ts +++ b/src/renderer/src/app-state/settings/persistence.ts @@ -7,8 +7,10 @@ import { type Settings, } from '@renderer/app-state/settings/types' import { coerceHotkeyBinding } from '@renderer/lib/hotkeyBinding' - -const LEGACY_STORAGE_KEY = 'cc-shell:settings' +import { + APP_SETTINGS_STORAGE_KEY, + LEGACY_SETTINGS_STORAGE_KEY, +} from '@renderer/app-state/localStorageMigration' export function coerceSettings(value: unknown): Settings { const parsed = value && typeof value === 'object' @@ -56,7 +58,9 @@ export function coerceSettings(value: unknown): Settings { export function loadInitialSettings(): Settings { try { - const raw = localStorage.getItem(LEGACY_STORAGE_KEY) + const raw = + localStorage.getItem(APP_SETTINGS_STORAGE_KEY) ?? + localStorage.getItem(LEGACY_SETTINGS_STORAGE_KEY) if (!raw) return DEFAULT_SETTINGS return coerceSettings(JSON.parse(raw)) } catch { diff --git a/src/renderer/src/app-state/settings/theme.ts b/src/renderer/src/app-state/settings/theme.ts index 6b6ac02c3..6a0f09f1c 100644 --- a/src/renderer/src/app-state/settings/theme.ts +++ b/src/renderer/src/app-state/settings/theme.ts @@ -1,4 +1,5 @@ import { ACCENTS, isDarkThemeMode, type Settings } from '@renderer/app-state/settings/types' +import { APP_SLUG } from '@shared/appIdentity' export function applyTheme(settings: Settings): void { const root = document.documentElement @@ -9,5 +10,5 @@ export function applyTheme(settings: Settings): void { const fg = isDarkThemeMode(settings.mode) ? accent.fgDark : accent.fgLight root.style.setProperty('--theme-accent', hex) root.style.setProperty('--theme-accent-fg', fg) - window.dispatchEvent(new CustomEvent('cc-shell:theme-changed', { detail: settings })) + window.dispatchEvent(new CustomEvent(`${APP_SLUG}:theme-changed`, { detail: settings })) } diff --git a/src/renderer/src/app-state/settings/types.ts b/src/renderer/src/app-state/settings/types.ts index 7ba529e76..e135362ee 100644 --- a/src/renderer/src/app-state/settings/types.ts +++ b/src/renderer/src/app-state/settings/types.ts @@ -104,14 +104,14 @@ export type Settings = { * new session picks up the new value. */ useProxyStreaming: boolean /** Inline voice dictation for the active composer. This is intentionally - * a cc-shell setting instead of an agent-voice-dictation setting: - * package code provides STT primitives, while cc-shell decides whether + * an Agent Code setting instead of an agent-voice-dictation setting: + * package code provides STT primitives, while Agent Code decides whether * voice belongs in its composer UI and which keyboard binding should * toggle recording. */ dictationEnabled: boolean dictationProvider: DictationProviderId /** Arbitrary keyboard binding captured by the settings UI. The standalone - * dictation app historically offered fixed choices, but cc-shell needs the + * dictation app historically offered fixed choices, but Agent Code needs the * same "press the key you want" model because composer bindings compete * with editor shortcuts. Empty string means "button only". */ dictationShortcut: string diff --git a/src/renderer/src/app-state/store.ts b/src/renderer/src/app-state/store.ts index 8b69912eb..ca8983f97 100644 --- a/src/renderer/src/app-state/store.ts +++ b/src/renderer/src/app-state/store.ts @@ -7,6 +7,13 @@ import { createWorkspaceSlice } from '@renderer/app-state/workspace/slice' import type { AppStore } from '@renderer/app-state/types' import type { Settings } from '@renderer/app-state/settings/types' import { coerceSettings } from '@renderer/app-state/settings/persistence' +import { + APP_STORE_STORAGE_KEY, + migrateLegacyLocalStorageKeys, +} from '@renderer/app-state/localStorageMigration' +import { APP_DISPLAY_NAME } from '@shared/appIdentity' + +migrateLegacyLocalStorageKeys() export const useAppStore = create()( devtools( @@ -17,7 +24,7 @@ export const useAppStore = create()( ...createWorkspaceSlice(...args), })), { - name: 'cc-shell:app-store', + name: APP_STORE_STORAGE_KEY, version: 2, storage: createJSONStorage(() => localStorage), partialize: state => ({ settings: state.settings }), @@ -29,6 +36,6 @@ export const useAppStore = create()( }, }, ), - { name: 'cc-shell' }, + { name: APP_DISPLAY_NAME }, ), ) diff --git a/src/renderer/src/app/App.tsx b/src/renderer/src/app/App.tsx index 6919bfed3..582a51782 100644 --- a/src/renderer/src/app/App.tsx +++ b/src/renderer/src/app/App.tsx @@ -392,7 +392,8 @@ export default function App() { {/* Always-visible main-process heap + RSS badge with a 60s - sparkline. Self-gates on CC_SHELL_PERF — renders null + sparkline. Self-gates on AGENT_CODE_PERF (or legacy + CC_SHELL_PERF) — renders null until the first IPC probe confirms telemetry is on. Click expands to a PerformancePanel-sized popover with the full buffered window and growth rates. diff --git a/src/renderer/src/features/debug/saveDebugBundle.ts b/src/renderer/src/features/debug/saveDebugBundle.ts index c3f68e196..6cebd1d03 100644 --- a/src/renderer/src/features/debug/saveDebugBundle.ts +++ b/src/renderer/src/features/debug/saveDebugBundle.ts @@ -268,7 +268,7 @@ export async function assembleAndSaveDebugBundle(params: { kind: string reason?: string /** Working directory of the session, used to locate the matching - * proxy-events run dir under ~/.config/cc-shell/proxy/. Optional + * proxy-events run dir under ~/.config/agent-code/proxy/. Optional * because sessions that ran without proxy capture (or callers * that don't have the cwd handy) should still produce a bundle — * the proxy section just gets omitted. */ @@ -291,7 +291,7 @@ export async function assembleAndSaveDebugBundle(params: { // Manual bundles include a bounded proxy tail because a human just // asked for a forensic snapshot. Autosave bundles intentionally skip - // that payload: the full proxy run remains under ~/.config/cc-shell/proxy + // that payload: the full proxy run remains under ~/.config/agent-code/proxy // and retention now budgets that root directly. Copying the same tail into // every minute-level autosave was one of the multipliers behind the 108 GB // debug-bundles directory; autosave should preserve orientation, not create diff --git a/src/renderer/src/features/feed/ui/semantic/renderUnits.ts b/src/renderer/src/features/feed/ui/semantic/renderUnits.ts index d429b275f..7b386d065 100644 --- a/src/renderer/src/features/feed/ui/semantic/renderUnits.ts +++ b/src/renderer/src/features/feed/ui/semantic/renderUnits.ts @@ -10,7 +10,7 @@ import type { SemanticRenderUnit } from '@renderer/features/feed/ui/semantic/typ // Claude Code does not render raw transcript/tool rows directly for // noisy low-signal activity. It groups read/search/tool churn into // summary units first, then the UI renders those summaries. -// cc-shell is not at full parity yet, but even this narrow pass +// Agent Code is not at full parity yet, but even this narrow pass // moves us away from "one semantic block = one visual row" and // toward the same safer architecture. // diff --git a/src/renderer/src/features/prompt-templates/templates.ts b/src/renderer/src/features/prompt-templates/templates.ts index 6ac890403..b4635ef9e 100644 --- a/src/renderer/src/features/prompt-templates/templates.ts +++ b/src/renderer/src/features/prompt-templates/templates.ts @@ -2,6 +2,10 @@ import { formatWorktreeDumpPrompt } from '@renderer/features/worktrees/lib/forma import { loadWorktreeDump } from '@renderer/features/worktrees/lib/loadWorktreeDump' import { collectLeaves } from '@renderer/workspace/tile-tree/treeOps' import type { Workspace } from '@renderer/workspace/workspaceStore' +import { + LEGACY_PROMPT_TEMPLATES_STORAGE_KEY, + PROMPT_TEMPLATES_STORAGE_KEY, +} from '@renderer/app-state/localStorageMigration' export type PromptTemplateContext = { workspace: Workspace @@ -19,7 +23,7 @@ export type PromptTemplate = { updatedAt?: number } -const CUSTOM_TEMPLATES_KEY = 'cc-shell.promptTemplates.v1' +const CUSTOM_TEMPLATES_KEY = PROMPT_TEMPLATES_STORAGE_KEY type AgentTranscriptRequest = { sessionId: string @@ -104,7 +108,7 @@ function buildActiveTabTranscriptPrompt( lines.push( `## ${index + 1}. ${label} agent`, '', - `cc-shell session id: \`${agent.sessionId}\``, + `Agent Code session id: \`${agent.sessionId}\``, `provider session id: \`${agent.providerSessionId}\``, `provider: \`${agent.kind}\``, 'cwd:', @@ -145,7 +149,7 @@ export const builtinPromptTemplates: PromptTemplate[] = [ title: 'Analyze Worktree Dump', description: 'Insert a live status dump for all Git worktrees in the focused project.', scope: 'builtin', - body: 'Please analyze this cc-shell worktree status dump.', + body: 'Please analyze this Agent Code worktree status dump.', buildBody: async ({ workspace, sessionId }) => { const cwd = workspace.state.sessions[sessionId]?.cwd ?? null const dump = await loadWorktreeDump({ cwd, workspace, forceActivityRefresh: false }) @@ -198,7 +202,9 @@ function saveCustomPromptTemplates(templates: PromptTemplate[]): void { export function loadCustomPromptTemplates(): PromptTemplate[] { try { - const raw = window.localStorage.getItem(CUSTOM_TEMPLATES_KEY) + const raw = + window.localStorage.getItem(CUSTOM_TEMPLATES_KEY) ?? + window.localStorage.getItem(LEGACY_PROMPT_TEMPLATES_STORAGE_KEY) if (!raw) return [] return normalizeCustomTemplates(JSON.parse(raw)) .sort((a, b) => (b.updatedAt ?? b.createdAt ?? 0) - (a.updatedAt ?? a.createdAt ?? 0)) diff --git a/src/renderer/src/features/settings/lib/settingsRegistry.ts b/src/renderer/src/features/settings/lib/settingsRegistry.ts index 9811ac245..e6a4b9bad 100644 --- a/src/renderer/src/features/settings/lib/settingsRegistry.ts +++ b/src/renderer/src/features/settings/lib/settingsRegistry.ts @@ -229,7 +229,7 @@ export function getSettingsRegistry(): SettingDefinition[] { category: 'experimental', title: 'Persistent Aggressive Debug Logs', description: - 'Periodically save full debug bundles for active agent panes, plus a best-effort final bundle on close. Expensive, intended for cc-shell development.', + 'Periodically save full debug bundles for active agent panes, plus a best-effort final bundle on close. Expensive, intended for Agent Code development.', keywords: ['debug', 'logs', 'persistent', 'aggressive', 'autosave', 'render', 'trace'], control: { type: 'toggle', diff --git a/src/renderer/src/features/setup/ui/SetupGate.tsx b/src/renderer/src/features/setup/ui/SetupGate.tsx index a6f153343..6a77bfbbf 100644 --- a/src/renderer/src/features/setup/ui/SetupGate.tsx +++ b/src/renderer/src/features/setup/ui/SetupGate.tsx @@ -82,7 +82,7 @@ export function SetupGate() {
-
Agent Studio Code Setup
+
Agent Code Setup
Required tools must be available before agent sessions can start.
diff --git a/src/renderer/src/features/system-perf/ui/SystemPerfBadge.tsx b/src/renderer/src/features/system-perf/ui/SystemPerfBadge.tsx index 6721a43b8..9d2fbe3a2 100644 --- a/src/renderer/src/features/system-perf/ui/SystemPerfBadge.tsx +++ b/src/renderer/src/features/system-perf/ui/SystemPerfBadge.tsx @@ -2,7 +2,7 @@ import type { SystemPerformanceStats } from '@shared/performance/types' // Color zones for the heap badge. // -// Thresholds chosen against observed cc-shell OOMs: +// Thresholds chosen against observed Agent Code OOMs: // - 2026-05-06: crashed at ~3.84 GiB / 4.09 GiB cap → 93% (deep red). // - 2026-05-11: crashed at ~2.69 GiB at an effective ~2.78 GiB cap // → 97% (deep red), but the heap-watchdog snapshot threshold is diff --git a/src/renderer/src/features/system-perf/ui/SystemPerfHeader.tsx b/src/renderer/src/features/system-perf/ui/SystemPerfHeader.tsx index 7d4dbb5f8..72a5c7b7c 100644 --- a/src/renderer/src/features/system-perf/ui/SystemPerfHeader.tsx +++ b/src/renderer/src/features/system-perf/ui/SystemPerfHeader.tsx @@ -16,7 +16,7 @@ import { SystemPerfPopover } from '@renderer/features/system-perf/ui/SystemPerfP // no command-palette entry yet (YAGNI), so it doesn't belong // on uiShell. // - Hiding the whole subtree behind one `enabled` check means -// when CC_SHELL_PERF is off, this entire feature compiles down +// when AGENT_CODE_PERF is off, this entire feature compiles down // to a no-op render with zero IPC traffic after the first probe. export function SystemPerfHeader() { const { enabled, current, buffer } = useSystemPerfPoller() diff --git a/src/renderer/src/features/system-perf/useSystemPerfPoller.ts b/src/renderer/src/features/system-perf/useSystemPerfPoller.ts index 4e4feadfc..060c44c2d 100644 --- a/src/renderer/src/features/system-perf/useSystemPerfPoller.ts +++ b/src/renderer/src/features/system-perf/useSystemPerfPoller.ts @@ -29,7 +29,7 @@ const POLL_INTERVAL_MS = 1000 export type SystemPerfPollerState = { // null until the first probe completes; thereafter true if - // CC_SHELL_PERF gating allows polling, false if it does not. + // AGENT_CODE_PERF gating allows polling, false if it does not. // The badge component renders nothing in either null or false // state — see SystemPerfHeader. enabled: boolean | null @@ -55,7 +55,7 @@ const INITIAL_STATE: SystemPerfPollerState = { // PerformancePanel manages its own polling (see // PerformancePanel.tsx — useEffect-owned setInterval). // -// WHY probe first, then start the interval: when CC_SHELL_PERF is +// WHY probe first, then start the interval: when AGENT_CODE_PERF is // off, the badge does not render. We want exactly zero recurring // work in that case — no setInterval, no allocation churn. The // first tick decides whether to schedule subsequent ticks. @@ -72,7 +72,7 @@ export function useSystemPerfPoller(): SystemPerfPollerState { if (cancelled) return if (!stats.enabled) { // Disabled: record the gate state once and stop. No - // interval is started; if the user toggles CC_SHELL_PERF + // interval is started; if the user toggles AGENT_CODE_PERF // they need to restart the app, which is consistent with // every other place that reads the flag. setState(prev => diff --git a/src/renderer/src/features/workspace/commands/sessionCommands.ts b/src/renderer/src/features/workspace/commands/sessionCommands.ts index 4f23fdc22..d2dac1b17 100644 --- a/src/renderer/src/features/workspace/commands/sessionCommands.ts +++ b/src/renderer/src/features/workspace/commands/sessionCommands.ts @@ -347,9 +347,9 @@ export const sessionCommands: CommandDef[] = [ { // Save Debug Logs — one-shot action (not a toggle). Snapshots the // focused pane's state/feed-debug/proxy-semantic/HTML into a single - // timestamped folder under ~/.config/cc-shell/debug-bundles/ and + // timestamped folder under ~/.config/agent-code/debug-bundles/ and // copies the path to the clipboard. Purpose is dev-time - // diagnostics of cc-shell itself — the four debug panels read the + // diagnostics of Agent Code itself — the four debug panels read the // same data live, this command preserves it for after-the-fact // inspection. // diff --git a/src/renderer/src/features/workspace/ui/RewindToPromptModal.tsx b/src/renderer/src/features/workspace/ui/RewindToPromptModal.tsx index a31c89b60..cb101c6ff 100644 --- a/src/renderer/src/features/workspace/ui/RewindToPromptModal.tsx +++ b/src/renderer/src/features/workspace/ui/RewindToPromptModal.tsx @@ -7,7 +7,7 @@ import type { SessionId } from '@renderer/workspace/types' // RewindToPromptModal — picker for the rewind-to-prompt flow. // // UX goal: let the user jump back to any past user-prompt in the -// focused session. On select, cc-shell writes a truncated provider +// focused session. On select, Agent Code writes a truncated provider // transcript, re-homes the focused pane onto it, and prefills the // composer with the anchored prompt in an UNSENT state. The source // session is never touched. diff --git a/src/renderer/src/features/worktrees/lib/formatWorktreeDump.ts b/src/renderer/src/features/worktrees/lib/formatWorktreeDump.ts index adf29d1fa..2f9b771dc 100644 --- a/src/renderer/src/features/worktrees/lib/formatWorktreeDump.ts +++ b/src/renderer/src/features/worktrees/lib/formatWorktreeDump.ts @@ -85,7 +85,7 @@ export function formatWorktreeDump(dump: WorktreeDump): string { export function formatWorktreeDumpPrompt(dump: WorktreeDump): string { return [ - 'Please analyze this cc-shell worktree status dump.', + 'Please analyze this Agent Code worktree status dump.', '', 'Help me decide:', '- which worktrees are active', diff --git a/src/renderer/src/lib/code/CodeBlock.tsx b/src/renderer/src/lib/code/CodeBlock.tsx index 43e0405de..1212e4306 100644 --- a/src/renderer/src/lib/code/CodeBlock.tsx +++ b/src/renderer/src/lib/code/CodeBlock.tsx @@ -6,6 +6,7 @@ import { normalizeCodeLanguage, supportsLsp, } from '@shared/code/language' +import { APP_PROTOCOL_SCHEME } from '@shared/appIdentity' type Props = { code: string @@ -23,10 +24,10 @@ function inferClientUri( path?: string | null, ): string { if (path) { - return `cc-shell://file/${encodeURIComponent(path)}#${encodeURIComponent(codeId)}` + return `${APP_PROTOCOL_SCHEME}://file/${encodeURIComponent(path)}#${encodeURIComponent(codeId)}` } const ext = languageFileExtension(language) - return `cc-shell://snippet/${encodeURIComponent(codeId)}.${ext}` + return `${APP_PROTOCOL_SCHEME}://snippet/${encodeURIComponent(codeId)}.${ext}` } export const CodeBlock = memo(function CodeBlock({ @@ -157,7 +158,7 @@ export const CodeBlock = memo(function CodeBlock({ if (event.clientUri !== clientUri) return monaco.editor.setModelMarkers( model, - 'cc-shell-lsp', + 'agent-code-lsp', event.diagnostics.map(diagnostic => ({ message: diagnostic.message, startLineNumber: diagnostic.startLine + 1, diff --git a/src/renderer/src/lib/code/monacoRuntime.ts b/src/renderer/src/lib/code/monacoRuntime.ts index 3ac51b2e3..d3a7d322e 100644 --- a/src/renderer/src/lib/code/monacoRuntime.ts +++ b/src/renderer/src/lib/code/monacoRuntime.ts @@ -4,6 +4,7 @@ import { normalizeCodeLanguage, supportsLsp, } from '@shared/code/language' +import { APP_SLUG } from '@shared/appIdentity' import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker' import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker' @@ -24,10 +25,10 @@ function currentThemeName(): string { const root = document.documentElement if (root.dataset.contrast === 'high') { return root.dataset.mode?.startsWith('light') - ? 'cc-shell-high-contrast-light' - : 'cc-shell-high-contrast-dark' + ? 'agent-code-high-contrast-light' + : 'agent-code-high-contrast-dark' } - return root.dataset.mode?.startsWith('light') ? 'cc-shell-light' : 'cc-shell-dark' + return root.dataset.mode?.startsWith('light') ? 'agent-code-light' : 'agent-code-dark' } function defineThemes(monaco: typeof Monaco): void { @@ -39,7 +40,7 @@ function defineThemes(monaco: typeof Monaco): void { const border = styles.getPropertyValue('--theme-code-border').trim() || '#262622' const accent = styles.getPropertyValue('--theme-accent').trim() || '#7dd3a0' - monaco.editor.defineTheme('cc-shell-dark', { + monaco.editor.defineTheme('agent-code-dark', { base: 'vs-dark', inherit: true, rules: [], @@ -57,7 +58,7 @@ function defineThemes(monaco: typeof Monaco): void { }, }) - monaco.editor.defineTheme('cc-shell-light', { + monaco.editor.defineTheme('agent-code-light', { base: 'vs', inherit: true, rules: [], @@ -75,7 +76,7 @@ function defineThemes(monaco: typeof Monaco): void { }, }) - monaco.editor.defineTheme('cc-shell-high-contrast-dark', { + monaco.editor.defineTheme('agent-code-high-contrast-dark', { base: 'hc-black', inherit: true, rules: [], @@ -86,7 +87,7 @@ function defineThemes(monaco: typeof Monaco): void { }, }) - monaco.editor.defineTheme('cc-shell-high-contrast-light', { + monaco.editor.defineTheme('agent-code-high-contrast-light', { base: 'hc-light', inherit: true, rules: [], @@ -103,7 +104,7 @@ function defineThemes(monaco: typeof Monaco): void { function installThemeListener(monaco: typeof Monaco): void { if (themeListenerInstalled) return themeListenerInstalled = true - window.addEventListener('cc-shell:theme-changed', () => { + window.addEventListener(`${APP_SLUG}:theme-changed`, () => { defineThemes(monaco) }) } diff --git a/src/renderer/src/performance/client.ts b/src/renderer/src/performance/client.ts index ed5377c13..5514dc721 100644 --- a/src/renderer/src/performance/client.ts +++ b/src/renderer/src/performance/client.ts @@ -25,7 +25,7 @@ let config: PerformanceConfig = { } let initialized = false let tracerProvider: BasicTracerProvider | null = null -let tracer = trace.getTracer('cc-shell-renderer') +let tracer = trace.getTracer('agent-code-renderer') let pending: PerformanceRecord[] = [] let flushTimer: ReturnType | null = null @@ -138,7 +138,7 @@ export async function initializePerformance(): Promise { ], }) trace.setGlobalTracerProvider(tracerProvider) - tracer = trace.getTracer('cc-shell-renderer') + tracer = trace.getTracer('agent-code-renderer') flushTimer = setInterval(() => { void flushPerformance() }, FLUSH_INTERVAL_MS) diff --git a/src/renderer/src/styles.css b/src/renderer/src/styles.css index 7fa95077f..8cef58923 100644 --- a/src/renderer/src/styles.css +++ b/src/renderer/src/styles.css @@ -3,7 +3,7 @@ @source "../../../src/shared"; /* - * cc-shell styles — v2: "native Claude Code, but typeset" + * Agent Code styles — v2: "native Claude Code, but typeset" * * Hard aesthetic rules (don't deviate without the user's explicit OK): * 1. No border-radius anywhere. Sharp rectangles. diff --git a/src/renderer/src/ui/PathInput.tsx b/src/renderer/src/ui/PathInput.tsx index f7a278af5..d992dc02f 100644 --- a/src/renderer/src/ui/PathInput.tsx +++ b/src/renderer/src/ui/PathInput.tsx @@ -7,7 +7,7 @@ import { type CSSProperties, } from 'react' -// PathInput — a generic path-with-completion input for anywhere in cc-shell +// PathInput — a generic path-with-completion input for anywhere in Agent Code // that needs the user to type a filesystem path (PathPickerModal today, // likely more places soon: file pickers, context attachments, cwd // changes within a pane, etc). diff --git a/src/renderer/src/workspace/claude/history.ts b/src/renderer/src/workspace/claude/history.ts index cf2724723..1b9ce3e71 100644 --- a/src/renderer/src/workspace/claude/history.ts +++ b/src/renderer/src/workspace/claude/history.ts @@ -14,7 +14,7 @@ import type { ConversationEntry } from '@shared/types/transcript' // compact_boundary) which don't participate in either path. // // `extractEmbeddedClaudeProgressEntry` produces a flat ConversationEntry -// from a progress wrapper — the shape the rest of cc-shell (Feed, +// from a progress wrapper — the shape the rest of Agent Code (Feed, // reconciler, ghost ops) expects. Returns null for anything that // isn't a progress wrapper holding a conversation entry. diff --git a/src/renderer/src/workspace/codex/rollout.ts b/src/renderer/src/workspace/codex/rollout.ts index 6ce8aad00..c2c7e5581 100644 --- a/src/renderer/src/workspace/codex/rollout.ts +++ b/src/renderer/src/workspace/codex/rollout.ts @@ -191,7 +191,7 @@ export function codexTurnIdFromRollout(entry: Record): string | /** * Stamp a mapped Codex feed entry with the rollout turn id so the * ghost reconciler can supersede by turn id. The field is added as - * a cc-shell-local extension to the shared `Entry` type via cast — + * an Agent Code-local extension to the shared `Entry` type via cast — * consumers that don't care about it ignore it, and * `reconcileUpstream` reads it defensively. */ diff --git a/src/renderer/src/workspace/ghosts.ts b/src/renderer/src/workspace/ghosts.ts index 0280336d4..d5ded76a6 100644 --- a/src/renderer/src/workspace/ghosts.ts +++ b/src/renderer/src/workspace/ghosts.ts @@ -3,7 +3,7 @@ // persistence. // // ----------------------------------------------------------------------------- -// What ghost is in cc-shell today +// What ghost is in Agent Code today // ----------------------------------------------------------------------------- // // Ghost is a parallel disk-backed ledger of semantic events. As @@ -69,7 +69,7 @@ // See also `agent-transcript-parser/docs/ghost.md` for the // underlying primitive's semantics, and // docs/superpowers/plans/2026-05-07-ghost-system-findings.md for -// the long-form diagnostic of how cc-shell got here. +// the long-form diagnostic of how Agent Code got here. // // No function here performs IO. No function here subscribes to // events. Input goes in, new `Map` comes out. @@ -367,7 +367,7 @@ function ghostContextForBlock( * * Codex: rollout emits one entry per content block with its own * uuid. We match by (turnId, blockIndex). turnId for Codex - * comes from the response id when available; cc-shell's + * comes from the response id when available; Agent Code's * Codex ingest path already exposes it on the mapped * entry. When the mapping is not available we fall back * to tool_use id pairing for tool blocks. diff --git a/src/renderer/src/workspace/hook/actions/provider.ts b/src/renderer/src/workspace/hook/actions/provider.ts index 0a7929b90..113f78e4b 100644 --- a/src/renderer/src/workspace/hook/actions/provider.ts +++ b/src/renderer/src/workspace/hook/actions/provider.ts @@ -193,7 +193,7 @@ export function useProviderActions( // prompt in unsent form so I can edit/re-send it". // // Bash mode: Claude Code exposes a `bash` input mode that - // prefixes `!` when composing. cc-shell's composer doesn't + // prefixes `!` when composing. Agent Code's composer doesn't // have a discrete bash mode yet, but it DOES treat a // leading `!` as bash. Mirroring CC's behavior means // "rewinding to a /bash-input prompt rehydrates as diff --git a/src/renderer/src/workspace/hook/actions/session.ts b/src/renderer/src/workspace/hook/actions/session.ts index 6bc342c59..2cb8cc04f 100644 --- a/src/renderer/src/workspace/hook/actions/session.ts +++ b/src/renderer/src/workspace/hook/actions/session.ts @@ -202,7 +202,7 @@ export function useSessionActions( } // Ghost log bootstrap — fire-and-forget, no await. If a prior - // run of cc-shell persisted ghosts for this sessionId, replay + // run of Agent Code persisted ghosts for this sessionId, replay // them through the atp reducer and merge into the runtime's // ghost map. The renderer then sees the same merged feed after // reload as it saw before. A missing file is not an error. diff --git a/src/renderer/src/workspace/hook/persistence/rehydrate.ts b/src/renderer/src/workspace/hook/persistence/rehydrate.ts index 51e581f5c..77c91a25b 100644 --- a/src/renderer/src/workspace/hook/persistence/rehydrate.ts +++ b/src/renderer/src/workspace/hook/persistence/rehydrate.ts @@ -33,7 +33,7 @@ import { loadInitialHistoryForSession } from '@renderer/workspace/hook/actions/i // `providerSessionId`, we pass it to the spawn call as // `resumeSessionId` so claude boots with `--resume ` and the // full conversation history — tool calls, transcript, queue state, -// the lot — comes back. The cc-shell SessionId we mint here is a +// the lot — comes back. The Agent Code SessionId we mint here is a // fresh routing key; CC's own session UUID is the thing we care // about preserving. // diff --git a/src/renderer/src/workspace/layout/helpers.ts b/src/renderer/src/workspace/layout/helpers.ts index 9dfe08549..e3612e3d3 100644 --- a/src/renderer/src/workspace/layout/helpers.ts +++ b/src/renderer/src/workspace/layout/helpers.ts @@ -12,7 +12,7 @@ import { collectLeaves } from '@renderer/workspace/tile-tree/treeOps' // or session-state context. /** Derive a tab title from a cwd — use the last path segment - * ("cc-shell"), falling back to the full cwd if none. */ + * ("agent-code"), falling back to the full cwd if none. */ export function titleFromCwd(cwd: string): string { const parts = cwd.split('/').filter(Boolean) return parts[parts.length - 1] ?? cwd diff --git a/src/renderer/src/workspace/mergedEntries.ts b/src/renderer/src/workspace/mergedEntries.ts index 27aaed1e5..7526bf8ca 100644 --- a/src/renderer/src/workspace/mergedEntries.ts +++ b/src/renderer/src/workspace/mergedEntries.ts @@ -2,7 +2,7 @@ // merged into the rendered feed. // // ----------------------------------------------------------------------------- -// Where ghost rendering fits in cc-shell today +// Where ghost rendering fits in Agent Code today // ----------------------------------------------------------------------------- // // The live current turn is rendered by `SemanticStreamingTurn` @@ -99,7 +99,7 @@ // Each prior simplification of these rules regressed in // production (see the Warning section in the design doc for the // commit-by-commit history). Changing this predicate without -// re-reading that doc is how cc-shell ends up with stale ghost +// re-reading that doc is how Agent Code ends up with stale ghost // rows at the bottom of every feed again. import type { Entry } from '@shared/types/transcript' diff --git a/src/renderer/src/workspace/persistence.ts b/src/renderer/src/workspace/persistence.ts index feb6a6b9c..8e599e52b 100644 --- a/src/renderer/src/workspace/persistence.ts +++ b/src/renderer/src/workspace/persistence.ts @@ -10,7 +10,7 @@ import type { import type { TileTabsState } from '@renderer/workspace/workspaceState' // --------------------------------------------------------------------------- -// Persisted state shape (serialized to ~/.config/cc-shell/workspace.json) +// Persisted state shape (serialized to ~/.config/agent-code/workspace.json) // --------------------------------------------------------------------------- /** diff --git a/src/renderer/src/workspace/runtime/feedDebug.ts b/src/renderer/src/workspace/runtime/feedDebug.ts index d36abd4ab..45b02c30c 100644 --- a/src/renderer/src/workspace/runtime/feedDebug.ts +++ b/src/renderer/src/workspace/runtime/feedDebug.ts @@ -10,7 +10,7 @@ import type { // appends one entry here, capped at FEED_DEBUG_LOG_CAP. The // FeedDebugPanel renders this in realtime; the same entries are // shipped to main/storage/feedDebugLog.ts every tick to be written -// to disk (per-session JSONL under ~/.config/cc-shell/feed-debug/). +// to disk (per-session JSONL under ~/.config/agent-code/feed-debug/). // // Why cap the in-memory array: long-running sessions could // accumulate tens of thousands of entries, bloating the runtime map diff --git a/src/renderer/src/workspace/semantic/helpers.ts b/src/renderer/src/workspace/semantic/helpers.ts index 206172bf6..f0a2141b2 100644 --- a/src/renderer/src/workspace/semantic/helpers.ts +++ b/src/renderer/src/workspace/semantic/helpers.ts @@ -209,7 +209,7 @@ export function emptySemanticLookupSnapshot(): SemanticLiveTurn['lookups'] { * from raw transcript rows. It builds a relationship layer first * (`toolUseByToolUseID`, `toolResultByToolUseID`, `resolvedToolUseIDs`, * sibling sets, progress maps) and then renders from that. This smaller - * semantic lookup snapshot is the same idea for cc-shell's live turn: + * semantic lookup snapshot is the same idea for Agent Code's live turn: * keep the expensive / correctness-sensitive "which tool is still live, * which one errored, which tools were siblings in this turn?" logic in * the shared reducer so every surface reads the same answer. diff --git a/src/renderer/src/workspace/tile-tree/TileLeaf.tsx b/src/renderer/src/workspace/tile-tree/TileLeaf.tsx index b5b01e039..d336ee50b 100644 --- a/src/renderer/src/workspace/tile-tree/TileLeaf.tsx +++ b/src/renderer/src/workspace/tile-tree/TileLeaf.tsx @@ -31,7 +31,7 @@ import { recordHtmlTraceSnapshot } from '@renderer/features/debug/renderTrace' // the paste debounce, the image size/format gates, and the pane // header shortening. -// TileLeaf — one pane. A "mini cc-shell" self-contained in a box: +// TileLeaf — one pane. A "mini Agent Code" self-contained in a box: // header strip (project dir + status) // Feed (structured JSONL + streaming preview) // composer (input box routing keystrokes to this pane's session) diff --git a/src/renderer/src/workspace/tile-tree/TileLeaf/useComposerDictation.ts b/src/renderer/src/workspace/tile-tree/TileLeaf/useComposerDictation.ts index ac30c9bd8..09efdea47 100644 --- a/src/renderer/src/workspace/tile-tree/TileLeaf/useComposerDictation.ts +++ b/src/renderer/src/workspace/tile-tree/TileLeaf/useComposerDictation.ts @@ -156,7 +156,7 @@ async function pickAudioConstraints( // "what mics were even available at this moment?" The labels are // user-data-ish (e.g., "Julius's AirPods Pro Microphone") but the // dump file is local 0o600 — same privacy posture as the existing - // CC_SHELL_DICTATION_DUMP webm output. See dictationJournal.ts. + // AGENT_CODE_DICTATION_DUMP webm output. See dictationJournal.ts. window.api.recordDictationDebugEvent(debugSessionId, { layer: 'DEVICE', event: 'enumerate:audioinput', @@ -212,7 +212,7 @@ const VOICE_BANDS_HZ: Array<[number, number]> = [ // Composer dictation is intentionally pane-local. The standalone // agent-voice-dictation app uses an OS overlay because it has to paste -// into arbitrary apps; cc-shell already owns the target composer, so a +// into arbitrary apps; Agent Code already owns the target composer, so a // floating knob would be the wrong abstraction here. Keeping state in this // hook lets the transcript land as normal editable draft text and keeps // every provider/secret decision behind the preload IPC boundary. @@ -845,7 +845,7 @@ export function useComposerDictation({ recorderState: recording.recorder.state, }, 'IPC') // Keep the provider socket lifecycle behind the recorder lifecycle. - // The first cc-shell port opened Deepgram before the MediaRecorder was + // The first Agent Code port opened Deepgram before the MediaRecorder was // definitely running, so a release during startup closed a CONNECTING // WebSocket and left the composer stuck on "listening". The standalone // app avoids that by letting the recorder own the lifecycle and by @@ -973,7 +973,7 @@ export function useComposerDictation({ void ensureStreamStarted() } else if (recording.streamStartTimer === null) { // Do not open Deepgram for the accidental-tap window. The - // standalone app discards <180ms presses; in cc-shell the + // standalone app discards <180ms presses; in Agent Code the // provider socket was being created early enough that those // discarded taps still produced "WebSocket was closed before // the connection was established". Queue locally until the diff --git a/src/renderer/src/workspace/tile-tree/TileLeaf/usePromptHistory.ts b/src/renderer/src/workspace/tile-tree/TileLeaf/usePromptHistory.ts index d5f95015c..9931ad33e 100644 --- a/src/renderer/src/workspace/tile-tree/TileLeaf/usePromptHistory.ts +++ b/src/renderer/src/workspace/tile-tree/TileLeaf/usePromptHistory.ts @@ -6,7 +6,7 @@ import type { SessionKind } from '@renderer/workspace/types' // ---- Prompt history state ---- // -// cc-shell keeps its own bash-style history for the composer +// Agent Code keeps its own bash-style history for the composer // instead of forwarding Up/Down to CC. Two reasons: // 1. CC's own history updates CC's own input box in the terminal // buffer, but our composer is a React textarea — the two diff --git a/src/renderer/src/workspace/tile-tree/useKeybinds.ts b/src/renderer/src/workspace/tile-tree/useKeybinds.ts index d0564fa14..64febca79 100644 --- a/src/renderer/src/workspace/tile-tree/useKeybinds.ts +++ b/src/renderer/src/workspace/tile-tree/useKeybinds.ts @@ -152,7 +152,7 @@ export function useKeybinds( // 1. Consume Escape so it closes the modal even when the // modal's inner div has lost focus. // 2. preventDefault on shortcut chords (cmd / alt). Without - // this, returning early stops cc-shell's handler but + // this, returning early stops Agent Code's handler but // not the macOS / Electron defaults — cmd+W would still // close the window, cmd+T would still open a new tab in // Electron, alt+number cycles tab focus on some setups. diff --git a/src/renderer/src/workspace/types.ts b/src/renderer/src/workspace/types.ts index e297f454f..877d7a5e0 100644 --- a/src/renderer/src/workspace/types.ts +++ b/src/renderer/src/workspace/types.ts @@ -50,13 +50,13 @@ export type Tab = { * Which kind of backend a session drives. * * 'claude' — a Claude Code child process. The pane renders the - * full cc-shell UI (feed, composer, slash picker, …) + * full Agent Code UI (feed, composer, slash picker, …) * driven by the JSONL transcript + headless terminal * screen scrape. * 'terminal' — a plain shell child process. The pane renders an * xterm.js instance that receives raw PTY bytes and * forwards keystrokes back. VS Code-style integrated - * terminal with no cc-shell chrome. + * terminal with no Agent Code chrome. * * Persisted in SessionMeta so a reload restores each pane to the * right component. Absent (= undefined) in pre-terminal workspace.json @@ -76,7 +76,7 @@ export type SessionMeta = { */ kind?: SessionKind /** - * CC's own session UUID (distinct from cc-shell's SessionId which is + * CC's own session UUID (distinct from Agent Code's SessionId which is * a per-launch routing key). Captured from the `sessionId` field on * the first JSONL entry that lands for this session, and persisted * to disk so we can pass `--resume ` on the next launch and diff --git a/src/renderer/src/workspace/workspaceState.ts b/src/renderer/src/workspace/workspaceState.ts index c53071c92..62de7cf69 100644 --- a/src/renderer/src/workspace/workspaceState.ts +++ b/src/renderer/src/workspace/workspaceState.ts @@ -154,7 +154,7 @@ export type SemanticToolCallSnapshot = { // Per-turn tool-call index. Kept intentionally minimal: only fields // consumed by a rendered surface live here. Two fields (hasParseError, // siblingToolUseIds) were removed after confirming no reader used -// them — the upstream Claude render layer needs them, cc-shell does +// them — the upstream Claude render layer needs them, Agent Code does // not yet. Add back only when a real caller appears. export type SemanticLookupSnapshot = { toolCallsById: Record diff --git a/src/shared/appIdentity.ts b/src/shared/appIdentity.ts new file mode 100644 index 000000000..107d28d9c --- /dev/null +++ b/src/shared/appIdentity.ts @@ -0,0 +1,10 @@ +export const APP_DISPLAY_NAME = 'Agent Code' +export const APP_PACKAGE_NAME = 'agent-code' +export const APP_SLUG = 'agent-code' +export const LEGACY_APP_SLUG = 'cc-shell' + +export const APP_PROTOCOL_SCHEME = 'agent-code' +export const LEGACY_PROTOCOL_SCHEME = 'cc-shell' + +export const APP_LOCAL_STORAGE_PREFIX = 'agent-code' +export const LEGACY_LOCAL_STORAGE_PREFIX = 'cc-shell' diff --git a/src/shared/paths/displayPath.ts b/src/shared/paths/displayPath.ts index 35cd465d8..3cf5b77b8 100644 --- a/src/shared/paths/displayPath.ts +++ b/src/shared/paths/displayPath.ts @@ -4,7 +4,7 @@ // paths. Showing only the basename is too lossy — a repo can easily // have a dozen files called `index.tsx` and the user can't tell which // one is being edited. Showing the full absolute path is too noisy — -// the prefix `/Users/alice/Desktop/Development/cc-shell/` repeats on +// the prefix `/Users/alice/Desktop/Development/agent-code/` repeats on // every single tool row. // // Compromise: when the path is inside the session's workspace root diff --git a/src/shared/runtime/jsonlTailer.ts b/src/shared/runtime/jsonlTailer.ts index 740da7ee2..0218258c1 100644 --- a/src/shared/runtime/jsonlTailer.ts +++ b/src/shared/runtime/jsonlTailer.ts @@ -19,7 +19,7 @@ import { basename } from 'path' * chokidar on macOS defaults to fs.watch-based change detection for * single files, which is known to silently miss rapid appends from * non-editor writers (append-only files that don't atomic-rename). - * Users saw it concretely: submit a prompt in cc-shell, CC writes + * Users saw it concretely: submit a prompt in Agent Code, CC writes * the user entry + a bunch of attachments to the JSONL, and the * feed wouldn't update until some unrelated later write nudged * chokidar into re-reading. "The prompt didn't appear." @@ -170,7 +170,7 @@ export type JsonlEntry = Record * Watches a CC project directory for the JSONL file CC creates when the * session starts, then tails it. Use case: * - * 1. cc-shell spawns `claude` with cwd=X + * 1. Agent Code spawns `claude` with cwd=X * 2. Before/right after spawn, we call `tailNewSessionFile(projectDir, ...)` * 3. CC creates ~/.claude/projects//.jsonl * 4. The tailer notices the new .jsonl, opens it, and starts emitting entries diff --git a/src/shared/runtime/terminalSession.ts b/src/shared/runtime/terminalSession.ts index 1f922ab87..9abde9ce1 100644 --- a/src/shared/runtime/terminalSession.ts +++ b/src/shared/runtime/terminalSession.ts @@ -4,7 +4,7 @@ import { spawn as ptySpawn, type IPty } from 'node-pty' // TerminalSession — a plain shell session that the user drives directly. // // Counterpart to ClaudeSession. Where ClaudeSession spawns `claude` -// and owns a headless terminal parser + JSONL tailer because cc-shell +// and owns a headless terminal parser + JSONL tailer because Agent Code // renders its own UI over the top, TerminalSession is the simpler // case: spawn the user's login shell, pipe raw bytes to whoever's // subscribed, accept raw bytes back, and let the renderer's xterm.js @@ -37,7 +37,7 @@ export type TerminalSessionOptions = { /** * Shell binary to spawn. Defaults to $SHELL, then /bin/zsh, then * /bin/bash, then 'sh' as a last resort. We don't try to be clever - * about Windows — if/when cc-shell runs there we'll add a branch. + * about Windows — if/when Agent Code runs there we'll add a branch. */ shell?: string /** Extra environment overrides for the spawned shell. */ @@ -46,7 +46,7 @@ export type TerminalSessionOptions = { * Backend used to host the shell. * * 'direct' — node-pty spawns the shell directly. Original behavior; - * terminal dies with cc-shell. + * terminal dies with Agent Code. * * 'tmux' — node-pty spawns `tmux attach -t `. * The tmux session must already exist (TmuxRegistry diff --git a/src/shared/types/session.ts b/src/shared/types/session.ts index 8946b7591..6a49bb104 100644 --- a/src/shared/types/session.ts +++ b/src/shared/types/session.ts @@ -16,7 +16,7 @@ export type SessionOptions = { snapshotIntervalMs?: number resumeSessionId?: string dangerousMode?: boolean - /** Stable cc-shell session id assigned by SessionManager. Provider + /** Stable Agent Code session id assigned by SessionManager. Provider * runtimes can use this for app-owned artifact storage paths * without coupling themselves to the manager implementation. */ shellSessionId?: string diff --git a/testing/ghost-demo/bridge.mts b/testing/ghost-demo/bridge.mts index 079cd23f6..b99ee40ed 100644 --- a/testing/ghost-demo/bridge.mts +++ b/testing/ghost-demo/bridge.mts @@ -1,4 +1,4 @@ -// Smoke test for the cc-shell ghost bridge. +// Smoke test for the Agent Code ghost bridge. // // Exercises the pure-function surface area used by `workspaceStore`: // 1. `ghostsFromSemanticTurn` — builds a ghost map from a fabricated diff --git a/testing/rendering/README.md b/testing/rendering/README.md index e3a8128ce..7d12e4283 100644 --- a/testing/rendering/README.md +++ b/testing/rendering/README.md @@ -1,16 +1,16 @@ -# cc-shell rendering harness +# Agent Code rendering harness Standalone Electron app for **working on the rendering engine in -isolation**. The harness reproduces cc-shell's full rendering pipeline +isolation**. The harness reproduces Agent Code's full rendering pipeline — PTY → JSONL → proxy SSE → mapper → Feed component tree — against real, live agent sessions, with every layer exposed side by side so visual regressions can be traced to the layer that broke. If you are debugging or extending **only** the rendering layer, work here. Edits to vendored render components in this folder do **not** -touch cc-shell. Edits to data-plumbing modules under `src/` (parsers, +touch Agent Code. Edits to data-plumbing modules under `src/` (parsers, transcript types, workspaceStore mappers, settings store) DO affect -cc-shell — by design, so a regression in one app reproduces in the +Agent Code — by design, so a regression in one app reproduces in the other. No tabs. No command palette. No workspace. One session, one window, @@ -20,7 +20,7 @@ every stream visible at once. ## Isolation model -| Layer | Location | Shared with cc-shell? | +| Layer | Location | Shared with Agent Code? | |--------------------------------------|----------------------------------------------------------|------------------------| | Render UI (Feed, rows, code, git) | `testing/rendering/renderer/components/` | **No** — vendored copy | | Harness shell + debug panels | `testing/rendering/renderer/RenderingHarnessApp.tsx` | No | @@ -33,10 +33,10 @@ every stream visible at once. | Headless PTY runtimes / providers | `src/providers/`, `claude-code-headless`, `codex-headless` | Yes | The vendored render layer is a **one-time copy** with manual sync. -When cc-shell ships a real Feed/row fix, port it into the vendored +When Agent Code ships a real Feed/row fix, port it into the vendored copy by hand. When you want to experiment with rendering only here, edit only the files under `testing/rendering/renderer/components/` -and cc-shell is untouched. +and Agent Code is untouched. ## Run @@ -46,7 +46,7 @@ npm run testing:rendering:build # build only npm run testing:rendering:preview # launch from existing build ``` -Output lives under `testing/rendering/out/` — separate from cc-shell's +Output lives under `testing/rendering/out/` — separate from Agent Code's `out/`, so the two builds never clobber each other. ## What you see @@ -82,7 +82,7 @@ Right column, stacked: ## Bug-fix registry -Tickets here track **cc-shell rendering bugs** that the harness +Tickets here track **Agent Code rendering bugs** that the harness helped us reproduce or fix. Harness setup mistakes (wrong CSS, hook order, etc.) are NOT in this list — see "Harness architecture notes" below for those. @@ -90,17 +90,17 @@ below for those. Every ticket is referenced from a thick WHY-comment at the patch site so future-you can jump from code to context and back. -| Ticket | Bug in cc-shell rendering | Harness patch site | cc-shell status | +| Ticket | Bug in Agent Code rendering | Harness patch site | Agent Code status | |----------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------| | REND-1 | Bootstrap race: bulk `session:jsonl-entries` burst (~200 entries) fires inside `await spawnSession()`. Any consumer that subscribes AFTER spawn returns drops the entire bootstrap. Visually: only the very last message appears. | `RenderingHarnessApp.tsx` — subscriptions hoisted to App level, refs reset before `spawnSession` | Fixed: `workspaceStore` subscribes at module mount, before any spawn. | | REND-2 | "Double-rendered last message" on resume. The screen still contains the last assistant message, so `` extracts and renders it AT THE SAME TIME the JSONL bootstrap renders it as an ``. Both visible. | `RenderingHarnessApp.tsx` — `isLive` gate on `streamingScreen` prop (mirror of `TileLeaf.isSessionLive`) | Fixed: `TileLeaf.isSessionLive` zeroes `streamingScreen` when no turn is in flight. | -| REND-3 | After submitting a prompt, the previous assistant text rendered below the user message until the new turn's first text_delta. Two root causes: (a) no `streamingBaseline` captured at submit, (b) proxy semantic events were never folded into a `SemanticLiveTurn` so Feed always fell back to `` (screen scrape) instead of `` (parsed events). | `RenderingHarnessApp.tsx` — `foldSemanticEvent` fold + `onBeforeSubmit` baseline capture | **PARTIAL.** cc-shell captures the baseline (`TileLeaf.tsx` onSubmit) and folds events, but `Feed.tsx isStaleStreamingExtract` early-returns false for Claude — only Codex gets the prefix-containment heuristic. Claude can still flicker the previous turn's text during the submit→first-delta gap. | +| REND-3 | After submitting a prompt, the previous assistant text rendered below the user message until the new turn's first text_delta. Two root causes: (a) no `streamingBaseline` captured at submit, (b) proxy semantic events were never folded into a `SemanticLiveTurn` so Feed always fell back to `` (screen scrape) instead of `` (parsed events). | `RenderingHarnessApp.tsx` — `foldSemanticEvent` fold + `onBeforeSubmit` baseline capture | **PARTIAL.** Agent Code captures the baseline (`TileLeaf.tsx` onSubmit) and folds events, but `Feed.tsx isStaleStreamingExtract` early-returns false for Claude — only Codex gets the prefix-containment heuristic. Claude can still flicker the previous turn's text during the submit→first-delta gap. | ### Adding a new ticket 1. Pick the next `REND-N`. 2. Add a row to the table above (one-line summary + harness patch site - + cc-shell status: fixed / partial / not fixed). + + Agent Code status: fixed / partial / not fixed). 3. At the harness patch site, write a thick WHY-comment that **starts** with the ticket id, e.g. ``` @@ -109,13 +109,13 @@ site so future-you can jump from code to context and back. // Reason: the prepend loop only ran the conversation/compact filter // once, but compact_summary is wrapped in a `progress` envelope that // had to be unwrapped first. Mirror of workspaceStore's loadOlder - // path in cc-shell. + // path in Agent Code. ``` 4. Commit with the ticket id in the subject: `REND-4: …`. ## Harness architecture notes -Items below are **NOT** cc-shell bugs — they are harness setup +Items below are **NOT** Agent Code bugs — they are harness setup decisions / first-cut mistakes documented here so future maintainers don't re-litigate them. None of these have a ticket id; they're just constraints of the harness itself. @@ -140,7 +140,7 @@ just constraints of the harness itself. `CodeBlock`, `GitRows`, `monacoRuntime` are vendored at `renderer/components/`. Edits stay in the harness; data plumbing is shared. Bottom padding inside the vendored Feed (`pb-24`) is - larger than cc-shell's (`pb-8`) because the harness has a tighter + larger than Agent Code's (`pb-8`) because the harness has a tighter pane. - **FAT debug stream**: per-layer debug log (`JSONL` / `MAP` / `SEM` / `STATE` / `RENDER`) so a regression can @@ -159,10 +159,10 @@ To add e.g. a `PTY` layer: ``` 4. The filter chip and copy buttons pick it up automatically. -## Required cc-shell additions +## Required Agent Code additions Some additions in `src/` exist purely to support this harness. They -are pure additions — no existing cc-shell behavior changed. +are pure additions — no existing Agent Code behavior changed. - `session:list-all` IPC handler in `src/main/index.ts` - `listAllSessions` API in `src/preload/index.ts` @@ -170,6 +170,6 @@ are pure additions — no existing cc-shell behavior changed. - `mapCodexRolloutToFeedEntries`, `extractEmbeddedClaudeProgressEntry`, `claudeHistoryMarker`, `codexHistoryMarker`, `foldSemanticEvent` exported from `src/renderer/src/workspace/workspaceStore.ts` If the harness build complains about a missing export, that export -needs to be added to cc-shell — never duplicate the implementation +needs to be added to Agent Code — never duplicate the implementation into the harness; the whole point is that the data layer stays shared. diff --git a/testing/rendering/main.ts b/testing/rendering/main.ts index d97a290b9..7cf667bf5 100644 --- a/testing/rendering/main.ts +++ b/testing/rendering/main.ts @@ -1,6 +1,6 @@ // Rendering-debug harness — dedicated Electron main process. // -// Setup constraint (NOT a cc-shell bug; see README → "Harness +// Setup constraint (NOT an Agent Code bug; see README → "Harness // architecture notes"). The harness does NOT load `src/main/index.ts` // because that entrypoint boots tmux detection (noisy startup logs: // "[tmux] available", "[tmux] recovery: 2 recoverable"), workspace @@ -357,7 +357,7 @@ function createWindow(): void { minWidth: 1200, minHeight: 760, backgroundColor: '#111111', - title: 'cc-shell rendering debug', + title: 'Agent Code rendering debug', autoHideMenuBar: true, webPreferences: { // Reuse the existing preload bridge so the renderer's diff --git a/testing/rendering/renderer/RenderingHarnessApp.tsx b/testing/rendering/renderer/RenderingHarnessApp.tsx index 1f225cd30..5456ab601 100644 --- a/testing/rendering/renderer/RenderingHarnessApp.tsx +++ b/testing/rendering/renderer/RenderingHarnessApp.tsx @@ -2,10 +2,10 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' // Vendored Feed — see testing/rendering/renderer/components/. // The harness owns its own copy of every renderer component so -// padding / margin / row tweaks here can't bleed into cc-shell. +// padding / margin / row tweaks here can't bleed into Agent Code. // Data plumbing (transcript types, parsers, mappers, settings) is // still imported from src/ — those are stable and the harness wants -// to reproduce cc-shell behavior, not diverge from it. +// to reproduce Agent Code behavior, not diverge from it. import { Feed } from './components/Feed' import { claudeHistoryMarker, @@ -49,7 +49,7 @@ import { DEFAULT_SETTINGS } from '@renderer/app-state/settings/types' // permanently lost — visually presents as "only the very last // message rendered after I picked the session". // -// cc-shell sidesteps this in `workspaceStore` by subscribing at +// Agent Code sidesteps this in `workspaceStore` by subscribing at // module mount, before any spawn happens. The harness mirrors // the same pattern: subscribe at the OUTER component // (RenderingHarnessApp), not inside DebugSession which only mounts @@ -134,7 +134,7 @@ type ScreenState = { const EMPTY_SCREEN: ScreenState = { plain: '', markdown: '', recent: '', recentMarkdown: '' } -// Setup constraint (NOT a cc-shell bug). `process` is not defined +// Setup constraint (NOT an Agent Code bug). `process` is not defined // in the renderer when contextIsolation is on. First version of this // constant was `process?.env?.HOME ?? '/'` and threw a ReferenceError // at module load → black screen on first launch. Hardcoded `/` is @@ -688,7 +688,7 @@ export function RenderingHarnessApp() { setSpawned(null) }, [spawned]) - // Setup constraint (NOT a cc-shell bug): every hook in this + // Setup constraint (NOT an Agent Code bug): every hook in this // component must run on every render. First version of this // `useCallback` was declared AFTER the `if (!spawned) return …` // early return — phase-1 render skipped it, phase-2 called it, @@ -720,7 +720,7 @@ export function RenderingHarnessApp() { // still contained the last assistant message AND the JSONL // bootstrap brought the same message in as an Entry → both // rendered, "double-rendered last message". Mirror of - // TileLeaf.isSessionLive in cc-shell. See README → REND-2. + // TileLeaf.isSessionLive in Agent Code. See README → REND-2. const isLive = activityStatus !== null || awaitingAssistant || @@ -785,7 +785,7 @@ function SessionPicker({ onPick }: { onPick: (info: HarnessSessionInfo) => void return (
-
cc-shell rendering debug
+
Agent Code rendering debug
Pick a session to resume. Spawns the real agent, shows every pipeline layer side by side. @@ -977,7 +977,7 @@ function DebugSession({
- {/* Setup constraint (NOT a cc-shell bug): split MUST be + {/* Setup constraint (NOT an Agent Code bug): split MUST be * flex-row, not grid-cols. * * First implementation used `grid grid-cols-[3fr_2fr]` with no diff --git a/testing/rendering/renderer/components/CodeBlock.tsx b/testing/rendering/renderer/components/CodeBlock.tsx index d65e55bd2..299bceeb1 100644 --- a/testing/rendering/renderer/components/CodeBlock.tsx +++ b/testing/rendering/renderer/components/CodeBlock.tsx @@ -23,10 +23,10 @@ function inferClientUri( path?: string | null, ): string { if (path) { - return `cc-shell://file/${encodeURIComponent(path)}#${encodeURIComponent(codeId)}` + return `agent-code://file/${encodeURIComponent(path)}#${encodeURIComponent(codeId)}` } const ext = languageFileExtension(language) - return `cc-shell://snippet/${encodeURIComponent(codeId)}.${ext}` + return `agent-code://snippet/${encodeURIComponent(codeId)}.${ext}` } export const CodeBlock = memo(function CodeBlock({ @@ -157,7 +157,7 @@ export const CodeBlock = memo(function CodeBlock({ if (event.clientUri !== clientUri) return monaco.editor.setModelMarkers( model, - 'cc-shell-lsp', + 'agent-code-lsp', event.diagnostics.map(diagnostic => ({ message: diagnostic.message, startLineNumber: diagnostic.startLine + 1, diff --git a/testing/rendering/renderer/components/Feed.tsx b/testing/rendering/renderer/components/Feed.tsx index 4734df564..5a22b4d76 100644 --- a/testing/rendering/renderer/components/Feed.tsx +++ b/testing/rendering/renderer/components/Feed.tsx @@ -842,9 +842,9 @@ function FeedImpl({ ref={scrollerRef} className="h-full overflow-auto" > - {/* Bigger pb than cc-shell so the last message doesn't sit + {/* Bigger pb than Agent Code so the last message doesn't sit flush against the harness composer bar. Vendored Feed — - cc-shell's copy still uses pb-8. */} + Agent Code's copy still uses pb-8. */}
{visible.map((e, i) => { const uuid = (e as Entry).uuid @@ -1065,7 +1065,7 @@ function buildSemanticRenderUnits(turn: SemanticLiveTurn): SemanticRenderUnit[] // // Claude Code does not render raw transcript/tool rows directly for noisy // low-signal activity. It groups read/search/tool churn into summary units - // first, then the UI renders those summaries. cc-shell is not at full parity + // first, then the UI renders those summaries. Agent Code is not at full parity // yet, but even this narrow pass moves us away from "one semantic block = // one visual row" and toward the same safer architecture. for (const block of blocks) { @@ -1207,7 +1207,7 @@ const SemanticTaskSummary = memo(function SemanticTaskSummary({ // // Upstream Claude renders task/agent progress from dedicated task state, not // by hoping the user can mentally reconstruct it from scattered tool rows. - // cc-shell is not at full upstream parity yet, but exposing the derived task + // Agent Code is not at full upstream parity yet, but exposing the derived task // snapshot here gives the feed one stable place to surface "what is the // session working on right now?" without re-parsing markdown or screen text. return ( diff --git a/testing/rendering/renderer/components/monacoRuntime.ts b/testing/rendering/renderer/components/monacoRuntime.ts index 3ac51b2e3..17d2d671c 100644 --- a/testing/rendering/renderer/components/monacoRuntime.ts +++ b/testing/rendering/renderer/components/monacoRuntime.ts @@ -24,10 +24,10 @@ function currentThemeName(): string { const root = document.documentElement if (root.dataset.contrast === 'high') { return root.dataset.mode?.startsWith('light') - ? 'cc-shell-high-contrast-light' - : 'cc-shell-high-contrast-dark' + ? 'agent-code-high-contrast-light' + : 'agent-code-high-contrast-dark' } - return root.dataset.mode?.startsWith('light') ? 'cc-shell-light' : 'cc-shell-dark' + return root.dataset.mode?.startsWith('light') ? 'agent-code-light' : 'agent-code-dark' } function defineThemes(monaco: typeof Monaco): void { @@ -39,7 +39,7 @@ function defineThemes(monaco: typeof Monaco): void { const border = styles.getPropertyValue('--theme-code-border').trim() || '#262622' const accent = styles.getPropertyValue('--theme-accent').trim() || '#7dd3a0' - monaco.editor.defineTheme('cc-shell-dark', { + monaco.editor.defineTheme('agent-code-dark', { base: 'vs-dark', inherit: true, rules: [], @@ -57,7 +57,7 @@ function defineThemes(monaco: typeof Monaco): void { }, }) - monaco.editor.defineTheme('cc-shell-light', { + monaco.editor.defineTheme('agent-code-light', { base: 'vs', inherit: true, rules: [], @@ -75,7 +75,7 @@ function defineThemes(monaco: typeof Monaco): void { }, }) - monaco.editor.defineTheme('cc-shell-high-contrast-dark', { + monaco.editor.defineTheme('agent-code-high-contrast-dark', { base: 'hc-black', inherit: true, rules: [], @@ -86,7 +86,7 @@ function defineThemes(monaco: typeof Monaco): void { }, }) - monaco.editor.defineTheme('cc-shell-high-contrast-light', { + monaco.editor.defineTheme('agent-code-high-contrast-light', { base: 'hc-light', inherit: true, rules: [], @@ -103,7 +103,7 @@ function defineThemes(monaco: typeof Monaco): void { function installThemeListener(monaco: typeof Monaco): void { if (themeListenerInstalled) return themeListenerInstalled = true - window.addEventListener('cc-shell:theme-changed', () => { + window.addEventListener('agent-code:theme-changed', () => { defineThemes(monaco) }) } diff --git a/testing/rendering/renderer/index.html b/testing/rendering/renderer/index.html index 45218a93a..1f41dbaf1 100644 --- a/testing/rendering/renderer/index.html +++ b/testing/rendering/renderer/index.html @@ -7,7 +7,7 @@ content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:;" /> - cc-shell Rendering Harness + Agent Code Rendering Harness