@bastani/atomic 0.5.24-0 → 0.5.25-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/sdk/components/attached-statusline.d.ts +17 -4
  2. package/dist/sdk/components/attached-statusline.d.ts.map +1 -1
  3. package/dist/sdk/components/graph-theme.d.ts +1 -0
  4. package/dist/sdk/components/graph-theme.d.ts.map +1 -1
  5. package/dist/sdk/components/statusline.d.ts.map +1 -1
  6. package/dist/sdk/providers/claude.d.ts.map +1 -1
  7. package/dist/sdk/runtime/attached-footer.d.ts +17 -0
  8. package/dist/sdk/runtime/attached-footer.d.ts.map +1 -0
  9. package/dist/sdk/runtime/cc-debounce.d.ts +29 -0
  10. package/dist/sdk/runtime/cc-debounce.d.ts.map +1 -0
  11. package/dist/sdk/runtime/executor.d.ts.map +1 -1
  12. package/dist/sdk/runtime/theme.d.ts +1 -0
  13. package/dist/sdk/runtime/theme.d.ts.map +1 -1
  14. package/dist/sdk/runtime/tmux.d.ts +10 -45
  15. package/dist/sdk/runtime/tmux.d.ts.map +1 -1
  16. package/dist/sdk/workflows/index.d.ts +1 -1
  17. package/dist/sdk/workflows/index.d.ts.map +1 -1
  18. package/package.json +1 -1
  19. package/src/cli.ts +5 -2
  20. package/src/commands/cli/chat/index.ts +8 -1
  21. package/src/commands/cli/footer.tsx +18 -4
  22. package/src/sdk/components/attached-statusline.tsx +58 -5
  23. package/src/sdk/components/connectors.test.ts +1 -0
  24. package/src/sdk/components/graph-theme.ts +2 -0
  25. package/src/sdk/components/statusline.tsx +3 -0
  26. package/src/sdk/providers/claude.ts +7 -10
  27. package/src/sdk/runtime/attached-footer.ts +66 -0
  28. package/src/sdk/runtime/cc-debounce.ts +104 -0
  29. package/src/sdk/runtime/executor.ts +1 -28
  30. package/src/sdk/runtime/theme.ts +3 -0
  31. package/src/sdk/runtime/tmux.conf +21 -16
  32. package/src/sdk/runtime/tmux.ts +26 -179
  33. package/src/sdk/workflows/index.ts +0 -7
@@ -1,13 +1,26 @@
1
1
  /** @jsxImportSource @opentui/react */
2
2
  /**
3
3
  * Footer rendered inside each agent tmux window. Lives in a 1-row bottom
4
- * pane created by the executor after the agent window is spawned. Mirrors
5
- * the orchestrator Statusline style colored badge on the left, dimmed
6
- * keyboard hints on the right.
4
+ * pane created by the executor (workflow) or the chat command, spawned via
5
+ * `atomic _footer`. Mirrors the orchestrator Statusline style: a colored
6
+ * pill on the left and right-aligned context on the right.
7
+ *
8
+ * Two variants:
9
+ * - Workflow: the window name is the left badge; right side shows the
10
+ * navigation hints (ctrl+g graph · ctrl+\ next). The `ctrl+b d
11
+ * detach` hint is surfaced in the orchestrator-window Statusline
12
+ * only, not duplicated into every agent pane footer.
13
+ * - Chat (agentType set): the provider name becomes the left pill
14
+ * (CLAUDE / COPILOT / OPENCODE, colored to match the workflow
15
+ * picker); right side shows pane name · ctrl+b d detach
16
+ * (tmux's default detach binding — spelled out because many Atomic
17
+ * users have never used tmux directly).
7
18
  */
19
+ import type { AgentType } from "../types.ts";
8
20
  import type { GraphTheme } from "./graph-theme.ts";
9
- export declare function AttachedStatusline({ name, theme }: {
21
+ export declare function AttachedStatusline({ name, theme, agentType, }: {
10
22
  name: string;
11
23
  theme: GraphTheme;
24
+ agentType?: AgentType;
12
25
  }): import("react").ReactNode;
13
26
  //# sourceMappingURL=attached-statusline.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"attached-statusline.d.ts","sourceRoot":"","sources":["../../../src/sdk/components/attached-statusline.tsx"],"names":[],"mappings":"AAAA,sCAAsC;AACtC;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,wBAAgB,kBAAkB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,6BAsBtF"}
1
+ {"version":3,"file":"attached-statusline.d.ts","sourceRoot":"","sources":["../../../src/sdk/components/attached-statusline.tsx"],"names":[],"mappings":"AAAA,sCAAsC;AACtC;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAWnD,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,KAAK,EACL,SAAS,GACV,EAAE;IACD,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,6BA8CA"}
@@ -10,6 +10,7 @@ export interface GraphTheme {
10
10
  error: string;
11
11
  warning: string;
12
12
  info: string;
13
+ mauve: string;
13
14
  border: string;
14
15
  borderActive: string;
15
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"graph-theme.d.ts","sourceRoot":"","sources":["../../../src/sdk/components/graph-theme.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,aAAa,GAAG,UAAU,CAe7D"}
1
+ {"version":3,"file":"graph-theme.d.ts","sourceRoot":"","sources":["../../../src/sdk/components/graph-theme.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,aAAa,GAAG,UAAU,CAgB7D"}
@@ -1 +1 @@
1
- {"version":3,"file":"statusline.d.ts","sourceRoot":"","sources":["../../../src/sdk/components/statusline.tsx"],"names":[],"mappings":"AAAA,sCAAsC;AAItC,wBAAgB,UAAU,CAAC,EACzB,SAAS,GACV,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;CACnB,6BAoDA"}
1
+ {"version":3,"file":"statusline.d.ts","sourceRoot":"","sources":["../../../src/sdk/components/statusline.tsx"],"names":[],"mappings":"AAAA,sCAAsC;AAItC,wBAAgB,UAAU,CAAC,EACzB,SAAS,GACV,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;CACnB,6BAuDA"}
@@ -1 +1 @@
1
- {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../../src/sdk/providers/claude.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../../src/sdk/providers/claude.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Helper for spawning the attached `atomic _footer` pane inside an agent
3
+ * tmux window.
4
+ *
5
+ * Shared between the workflow executor (per-agent windows) and the chat
6
+ * command (single-agent window). Splits the target pane vertically so the
7
+ * top pane keeps running the agent CLI and the bottom pane hosts the
8
+ * React footer.
9
+ *
10
+ * Resolves the CLI entrypoint relative to this module (runtime/ lives at
11
+ * src/sdk/runtime/, so ../../cli.ts is the CLI). `process.argv[1]` points
12
+ * at the orchestrator's executor-entry.ts when called from the executor,
13
+ * so it can't be used here.
14
+ */
15
+ import type { AgentType } from "../types.ts";
16
+ export declare function spawnAttachedFooter(windowName: string, paneId: string, agentType?: AgentType): void;
17
+ //# sourceMappingURL=attached-footer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attached-footer.d.ts","sourceRoot":"","sources":["../../../src/sdk/runtime/attached-footer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAiB7C,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,SAAS,GACpB,IAAI,CA4BN"}
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Ctrl+C debounce helper for Atomic-managed tmux panes.
4
+ *
5
+ * Invoked from `tmux.conf` on every root-table Ctrl+C:
6
+ *
7
+ * bind -n C-c run-shell -b '"#{@atomic-bun}" "#{@atomic-cc-debounce}" "#{pane_id}"'
8
+ *
9
+ * The binding sits on the shared atomic tmux server, so the debounce
10
+ * applies uniformly to every pane — Claude Code, OpenCode, and Copilot
11
+ * CLI, in both chat and workflow sessions.
12
+ *
13
+ * Rule: forward Ctrl+C only if the previous press is more than QUIET_MS
14
+ * ago. The state file is touched on *every* press (forwarded or
15
+ * swallowed) so sustained spam keeps extending the cooldown instead of
16
+ * letting a press leak through every QUIET_MS interval — which is what
17
+ * would otherwise still trigger an agent CLI's "double-tap to exit"
18
+ * confirmation.
19
+ */
20
+ /** Quiet period (ms) the user must leave between presses for the next
21
+ * one to be forwarded. Must exceed every integrated agent's exit-confirm
22
+ * window — Claude Code's is the widest (~1.5 s), so 1200 ms is a safe
23
+ * margin that still feels responsive for legitimate double-interrupts. */
24
+ export declare const QUIET_MS = 1200;
25
+ /** Pure decision helper — exported so tests can exercise it without
26
+ * touching the filesystem or spawning tmux. Returns `true` when the
27
+ * press should be forwarded, `false` when it should be swallowed. */
28
+ export declare function shouldForward(nowMs: number, lastMs: number, quietMs?: number): boolean;
29
+ //# sourceMappingURL=cc-debounce.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cc-debounce.d.ts","sourceRoot":"","sources":["../../../src/sdk/runtime/cc-debounce.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;GAiBG;AAMH;;;2EAG2E;AAC3E,eAAO,MAAM,QAAQ,OAAO,CAAC;AAE7B;;sEAEsE;AACtE,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,MAAiB,GACzB,OAAO,CAET"}
@@ -1 +1 @@
1
- {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../../src/sdk/runtime/executor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../../src/sdk/runtime/executor.ts"],"names":[],"mappings":"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"}
@@ -19,6 +19,7 @@ export interface TerminalTheme {
19
19
  success: string;
20
20
  error: string;
21
21
  warning: string;
22
+ mauve: string;
22
23
  }
23
24
  /**
24
25
  * Resolve the terminal theme from the renderer's detected theme mode.
@@ -1 +1 @@
1
- {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../../src/sdk/runtime/theme.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAM/C,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AA0CD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,GAAG,aAAa,CAElE"}
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../../src/sdk/runtime/theme.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAM/C,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AA4CD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,GAAG,aAAa,CAElE"}
@@ -82,6 +82,16 @@ export declare function createWindow(sessionName: string, windowName: string, co
82
82
  * @returns The pane ID of the new pane
83
83
  */
84
84
  export declare function createPane(sessionName: string, command: string): string;
85
+ /**
86
+ * Replace the running command in an existing pane with a new one.
87
+ *
88
+ * `-k` kills whatever is currently running in the pane (e.g. a still-initializing
89
+ * shell) before tmux spawns the new command. Because tmux execs the command
90
+ * itself rather than forwarding keystrokes through a shell line editor, there
91
+ * is no shell-ready race and no ZLE TCSAFLUSH drop — callers can invoke this
92
+ * immediately after pane creation without waiting for a prompt to appear.
93
+ */
94
+ export declare function respawnPane(paneId: string, command: string): void;
85
95
  /**
86
96
  * Send literal text to a tmux pane using `-l` flag (no special key interpretation).
87
97
  * Uses `--` to prevent text starting with `-` from being parsed as flags.
@@ -106,14 +116,6 @@ export declare function sendViaPasteBuffer(paneId: string, text: string): void;
106
116
  * Send a special key (C-m, C-c, C-u, Tab, etc.) to a tmux pane.
107
117
  */
108
118
  export declare function sendSpecialKey(paneId: string, key: string): void;
109
- /**
110
- * Send literal text and submit with C-m (carriage return).
111
- * Uses C-m instead of Enter for raw-mode TUI compatibility.
112
- *
113
- * @param presses - Number of C-m presses (default: 1)
114
- * @param delayMs - Delay between presses in ms (default: 100)
115
- */
116
- export declare function sendKeysAndSubmit(paneId: string, text: string, presses?: number, delayMs?: number): Promise<void>;
117
119
  /**
118
120
  * Capture the visible content of a tmux pane.
119
121
  *
@@ -248,41 +250,4 @@ export declare function normalizeTmuxCapture(text: string): string;
248
250
  * Normalize captured text preserving line structure (for display output).
249
251
  */
250
252
  export declare function normalizeTmuxLines(text: string): string;
251
- /**
252
- * Returns true when the pane shows an agent prompt ready for input.
253
- * Detects Claude Code (❯), Codex (›), and generic (>) prompts.
254
- */
255
- export declare function paneLooksReady(captured: string): boolean;
256
- /**
257
- * Returns true when the agent has an active task in progress.
258
- * Checks last 40 lines for known busy indicators.
259
- */
260
- export declare function paneHasActiveTask(captured: string): boolean;
261
- /**
262
- * Returns true when the pane is idle — showing a prompt and not processing.
263
- * Uses visible-only capture to avoid stale scrollback matches.
264
- */
265
- export declare function paneIsIdle(paneId: string): boolean;
266
- /**
267
- * Wait for the pane to be idle (prompt visible, no active task) with
268
- * exponential backoff. Returns the time spent waiting (ms).
269
- */
270
- export declare function waitForPaneReady(paneId: string, timeoutMs?: number): Promise<number>;
271
- /**
272
- * Attempt to submit by pressing C-m, verifying after each round.
273
- * Returns true as soon as the trigger text disappears from the visible
274
- * capture or an active task is detected.
275
- */
276
- export declare function attemptSubmitRounds(paneId: string, normalizedPrompt: string, rounds: number, pressesPerRound?: number): Promise<boolean>;
277
- /**
278
- * Wait for a pattern to appear in a tmux pane's output.
279
- * Polls the pane content at the given interval until the pattern matches
280
- * or the timeout is reached.
281
- *
282
- * @returns The full pane content when the pattern was found
283
- */
284
- export declare function waitForOutput(paneId: string, pattern: RegExp, options?: {
285
- timeoutMs?: number;
286
- pollIntervalMs?: number;
287
- }): Promise<string>;
288
253
  //# sourceMappingURL=tmux.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tmux.d.ts","sourceRoot":"","sources":["../../../src/sdk/runtime/tmux.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tmux.d.ts","sourceRoot":"","sources":["../../../src/sdk/runtime/tmux.ts"],"names":[],"mappings":"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"}
@@ -15,7 +15,7 @@ export type { ClaudeSessionOptions, ClaudeQueryOptions } from "../providers/clau
15
15
  export { validateCopilotWorkflow } from "../providers/copilot.ts";
16
16
  export { validateOpenCodeWorkflow } from "../providers/opencode.ts";
17
17
  export type { TmuxResult, TmuxSession, SessionType } from "../runtime/tmux.ts";
18
- export { SOCKET_NAME, isTmuxInstalled, getMuxBinary, resetMuxBinaryCache, isInsideTmux, isInsideAtomicSocket, createSession, createWindow, createPane, sendLiteralText, sendSpecialKey, sendKeysAndSubmit, capturePane, capturePaneVisible, capturePaneScrollback, killSession, killWindow, sessionExists, listSessions, attachSession, spawnMuxAttach, switchClient, getCurrentSession, attachOrSwitch, detachAndAttachAtomic, selectWindow, setSessionEnv, getSessionEnv, parseSessionName, waitForOutput, tmuxRun, normalizeTmuxCapture, normalizeTmuxLines, paneLooksReady, paneHasActiveTask, paneIsIdle, waitForPaneReady, attemptSubmitRounds, } from "../runtime/tmux.ts";
18
+ export { SOCKET_NAME, isTmuxInstalled, getMuxBinary, resetMuxBinaryCache, isInsideTmux, isInsideAtomicSocket, createSession, createWindow, createPane, sendLiteralText, sendSpecialKey, capturePane, capturePaneVisible, capturePaneScrollback, killSession, killWindow, sessionExists, listSessions, attachSession, spawnMuxAttach, switchClient, getCurrentSession, attachOrSwitch, detachAndAttachAtomic, selectWindow, setSessionEnv, getSessionEnv, parseSessionName, tmuxRun, normalizeTmuxCapture, normalizeTmuxLines, } from "../runtime/tmux.ts";
19
19
  export { AGENTS, discoverWorkflows, findWorkflow, loadWorkflowsMetadata, WORKFLOWS_GITIGNORE, } from "../runtime/discovery.ts";
20
20
  export type { DiscoveredWorkflow, WorkflowWithMetadata, WorkflowMetadataStatus, } from "../runtime/discovery.ts";
21
21
  export { WorkflowLoader } from "../runtime/loader.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/sdk/workflows/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExE,YAAY,EACV,SAAS,EACT,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,cAAc,EACd,cAAc,EACd,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,aAAa,EACb,oBAAoB,EACpB,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAGrB,YAAY,EAAE,YAAY,IAAI,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,YAAY,EAAE,qBAAqB,IAAI,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC3F,YAAY,EAAE,cAAc,IAAI,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAG7F,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAC5I,YAAY,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEvF,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAElE,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAGpE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EACL,WAAW,EACX,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,oBAAoB,EACpB,aAAa,EACb,YAAY,EACZ,UAAU,EACV,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,WAAW,EACX,kBAAkB,EAClB,qBAAqB,EACrB,WAAW,EACX,UAAU,EACV,aAAa,EACb,YAAY,EACZ,aAAa,EACb,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,OAAO,EACP,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,EACd,iBAAiB,EACjB,UAAU,EACV,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,MAAM,EACN,iBAAiB,EACjB,YAAY,EACZ,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,kBAAkB,EAClB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,YAAY,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/sdk/workflows/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExE,YAAY,EACV,SAAS,EACT,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,cAAc,EACd,cAAc,EACd,UAAU,EACV,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,eAAe,EACf,aAAa,EACb,oBAAoB,EACpB,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAGrB,YAAY,EAAE,YAAY,IAAI,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,YAAY,EAAE,qBAAqB,IAAI,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC3F,YAAY,EAAE,cAAc,IAAI,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAG7F,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAC5I,YAAY,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEvF,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAElE,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAGpE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EACL,WAAW,EACX,eAAe,EACf,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,oBAAoB,EACpB,aAAa,EACb,YAAY,EACZ,UAAU,EACV,eAAe,EACf,cAAc,EACd,WAAW,EACX,kBAAkB,EAClB,qBAAqB,EACrB,WAAW,EACX,UAAU,EACV,aAAa,EACb,YAAY,EACZ,aAAa,EACb,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,OAAO,EACP,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,MAAM,EACN,iBAAiB,EACjB,YAAY,EACZ,qBAAqB,EACrB,mBAAmB,GACpB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,kBAAkB,EAClB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,YAAY,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bastani/atomic",
3
- "version": "0.5.24-0",
3
+ "version": "0.5.25-0",
4
4
  "description": "Configuration management CLI and SDK for coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/cli.ts CHANGED
@@ -331,9 +331,12 @@ Examples:
331
331
  .command("_footer", { hidden: true })
332
332
  .description("Internal: render the attached-mode footer for an agent window")
333
333
  .requiredOption("--name <name>", "Agent window name")
334
- .action(async (opts: { name: string }) => {
334
+ .option("--agent <agent>", "Agent type renders provider pill in the footer")
335
+ .action(async (opts: { name: string; agent?: string }) => {
335
336
  const { footerCommand } = await import("./commands/cli/footer.tsx");
336
- const exitCode = await footerCommand(opts.name);
337
+ const { isValidAgent } = await import("./services/config/definitions.ts");
338
+ const agentType = opts.agent && isValidAgent(opts.agent) ? opts.agent : undefined;
339
+ const exitCode = await footerCommand(opts.name, agentType);
337
340
  process.exit(exitCode);
338
341
  });
339
342
 
@@ -39,6 +39,7 @@ import {
39
39
  spawnMuxAttach,
40
40
  switchClient,
41
41
  } from "../../../sdk/workflows/index.ts";
42
+ import { spawnAttachedFooter } from "../../../sdk/runtime/attached-footer.ts";
42
43
  import { ensureTmuxInstalled } from "../../../lib/spawn.ts";
43
44
 
44
45
  // ============================================================================
@@ -241,9 +242,15 @@ export async function chatCommand(options: ChatCommandOptions = {}): Promise<num
241
242
 
242
243
  // ── Create session on the atomic socket and attach ──
243
244
  try {
244
- createSession(windowName, shellCmd, undefined, projectRoot);
245
+ const paneId = createSession(windowName, shellCmd, undefined, projectRoot);
245
246
  setSessionEnv(windowName, "ATOMIC_AGENT", agentType);
246
247
 
248
+ // Split the pane so the agent CLI runs on top and the React footer
249
+ // (provider pill + window name) runs in a 5% bottom pane. Matches
250
+ // the workflow-window layout, minus the keyboard hints (chat sessions
251
+ // only host a single agent, so ctrl+g / ctrl+\ navigation is moot).
252
+ spawnAttachedFooter(windowName, paneId, agentType);
253
+
247
254
  if (isInsideAtomicSocket()) {
248
255
  // Already on the atomic server — just switch to the new session.
249
256
  switchClient(windowName);
@@ -20,6 +20,7 @@ import {
20
20
  type GraphTheme,
21
21
  } from "../../sdk/components/graph-theme.ts";
22
22
  import { AttachedStatusline } from "../../sdk/components/attached-statusline.tsx";
23
+ import type { AgentType } from "../../sdk/types.ts";
23
24
 
24
25
  const PARENT_WATCHDOG_MS = 2000;
25
26
 
@@ -58,7 +59,15 @@ const EXIT_SIGNALS: NodeJS.Signals[] =
58
59
  ? ["SIGTERM", "SIGINT", "SIGBREAK", "SIGHUP"]
59
60
  : ["SIGHUP", "SIGTERM", "SIGINT", "SIGPIPE"];
60
61
 
61
- function FooterShell({ name, theme }: { name: string; theme: GraphTheme }) {
62
+ function FooterShell({
63
+ name,
64
+ theme,
65
+ agentType,
66
+ }: {
67
+ name: string;
68
+ theme: GraphTheme;
69
+ agentType?: AgentType;
70
+ }) {
62
71
  const renderer = useRenderer();
63
72
 
64
73
  useEffect(() => {
@@ -101,17 +110,22 @@ function FooterShell({ name, theme }: { name: string; theme: GraphTheme }) {
101
110
  justifyContent="flex-end"
102
111
  backgroundColor={theme.background}
103
112
  >
104
- <AttachedStatusline name={name} theme={theme} />
113
+ <AttachedStatusline name={name} theme={theme} agentType={agentType} />
105
114
  </box>
106
115
  );
107
116
  }
108
117
 
109
- export async function footerCommand(name: string): Promise<number> {
118
+ export async function footerCommand(
119
+ name: string,
120
+ agentType?: AgentType,
121
+ ): Promise<number> {
110
122
  const renderer = await createCliRenderer({
111
123
  exitOnCtrlC: false,
112
124
  });
113
125
  const theme = deriveGraphTheme(resolveTheme(renderer.themeMode));
114
- createRoot(renderer).render(<FooterShell name={name} theme={theme} />);
126
+ createRoot(renderer).render(
127
+ <FooterShell name={name} theme={theme} agentType={agentType} />,
128
+ );
115
129
 
116
130
  await new Promise<void>(() => {});
117
131
  return 0;
@@ -1,14 +1,67 @@
1
1
  /** @jsxImportSource @opentui/react */
2
2
  /**
3
3
  * Footer rendered inside each agent tmux window. Lives in a 1-row bottom
4
- * pane created by the executor after the agent window is spawned. Mirrors
5
- * the orchestrator Statusline style colored badge on the left, dimmed
6
- * keyboard hints on the right.
4
+ * pane created by the executor (workflow) or the chat command, spawned via
5
+ * `atomic _footer`. Mirrors the orchestrator Statusline style: a colored
6
+ * pill on the left and right-aligned context on the right.
7
+ *
8
+ * Two variants:
9
+ * - Workflow: the window name is the left badge; right side shows the
10
+ * navigation hints (ctrl+g graph · ctrl+\ next). The `ctrl+b d
11
+ * detach` hint is surfaced in the orchestrator-window Statusline
12
+ * only, not duplicated into every agent pane footer.
13
+ * - Chat (agentType set): the provider name becomes the left pill
14
+ * (CLAUDE / COPILOT / OPENCODE, colored to match the workflow
15
+ * picker); right side shows pane name · ctrl+b d detach
16
+ * (tmux's default detach binding — spelled out because many Atomic
17
+ * users have never used tmux directly).
7
18
  */
8
19
 
20
+ import type { AgentType } from "../types.ts";
9
21
  import type { GraphTheme } from "./graph-theme.ts";
10
22
 
11
- export function AttachedStatusline({ name, theme }: { name: string; theme: GraphTheme }) {
23
+ /** Per-agent brand color, matching the workflow picker pill hues. */
24
+ const AGENT_PILL_COLOR: Record<AgentType, keyof GraphTheme> = {
25
+ claude: "warning",
26
+ copilot: "success",
27
+ opencode: "mauve",
28
+ };
29
+
30
+ const DOT = "\u00B7";
31
+
32
+ export function AttachedStatusline({
33
+ name,
34
+ theme,
35
+ agentType,
36
+ }: {
37
+ name: string;
38
+ theme: GraphTheme;
39
+ agentType?: AgentType;
40
+ }) {
41
+ if (agentType) {
42
+ const pillBg = theme[AGENT_PILL_COLOR[agentType]];
43
+ return (
44
+ <box height={1} flexDirection="row" backgroundColor={theme.backgroundElement}>
45
+ <box backgroundColor={pillBg} paddingLeft={1} paddingRight={1} alignItems="center">
46
+ <text fg={theme.backgroundElement}>
47
+ <strong>{agentType.toUpperCase()}</strong>
48
+ </text>
49
+ </box>
50
+
51
+ <box flexGrow={1} />
52
+
53
+ <box paddingRight={2} alignItems="center">
54
+ <text>
55
+ <span fg={theme.textMuted}>{name}</span>
56
+ <span fg={theme.textDim}>{" " + DOT + " "}</span>
57
+ <span fg={theme.text}>ctrl+b d</span>
58
+ <span fg={theme.textMuted}> detach</span>
59
+ </text>
60
+ </box>
61
+ </box>
62
+ );
63
+ }
64
+
12
65
  return (
13
66
  <box height={1} flexDirection="row" backgroundColor={theme.backgroundElement}>
14
67
  <box backgroundColor={theme.primary} paddingLeft={1} paddingRight={1} alignItems="center">
@@ -23,7 +76,7 @@ export function AttachedStatusline({ name, theme }: { name: string; theme: Graph
23
76
  <text>
24
77
  <span fg={theme.text}>ctrl+g</span>
25
78
  <span fg={theme.textMuted}> graph</span>
26
- <span fg={theme.textDim}> {"\u00B7"} </span>
79
+ <span fg={theme.textDim}>{" " + DOT + " "}</span>
27
80
  <span fg={theme.text}>{"ctrl+\\"}</span>
28
81
  <span fg={theme.textMuted}> next</span>
29
82
  </text>
@@ -19,6 +19,7 @@ const mockTheme: GraphTheme = {
19
19
  error: "#ff2244",
20
20
  warning: "#ffaa00",
21
21
  info: "#00aaff",
22
+ mauve: "#cc88ff",
22
23
  border: "#334455",
23
24
  borderActive: "#aabbcc",
24
25
  };
@@ -14,6 +14,7 @@ export interface GraphTheme {
14
14
  error: string;
15
15
  warning: string;
16
16
  info: string;
17
+ mauve: string;
17
18
  border: string;
18
19
  borderActive: string;
19
20
  }
@@ -30,6 +31,7 @@ export function deriveGraphTheme(t: TerminalTheme): GraphTheme {
30
31
  error: t.error,
31
32
  warning: t.warning,
32
33
  info: t.accent,
34
+ mauve: t.mauve,
33
35
  border: t.borderDim,
34
36
  borderActive: t.border,
35
37
  };
@@ -51,6 +51,9 @@ export function Statusline({
51
51
  <span fg={theme.text}>/</span>
52
52
  <span fg={theme.textMuted}> stages</span>
53
53
  <span fg={theme.textDim}> {"\u00B7"} </span>
54
+ <span fg={theme.text}>ctrl+b d</span>
55
+ <span fg={theme.textMuted}> detach</span>
56
+ <span fg={theme.textDim}> {"\u00B7"} </span>
54
57
  <span fg={theme.text}>q</span>
55
58
  <span fg={theme.textMuted}> quit</span>
56
59
  </text>
@@ -24,7 +24,7 @@ import {
24
24
  type SDKUserMessage,
25
25
  type Options as SDKOptions,
26
26
  } from "@anthropic-ai/claude-agent-sdk";
27
- import { sendKeysAndSubmit, waitForPaneReady } from "../runtime/tmux.ts";
27
+ import { respawnPane } from "../runtime/tmux.ts";
28
28
  import { escBash } from "../runtime/executor.ts";
29
29
  import { watch, unlink, mkdir, writeFile } from "node:fs/promises";
30
30
  import { existsSync, writeFileSync } from "node:fs";
@@ -283,15 +283,12 @@ async function spawnClaudeWithPrompt(
283
283
  argvPrompt,
284
284
  ].join(" ");
285
285
 
286
- // Wait for the pane's shell to finish init and activate its line editor
287
- // (starship `❯` / bare zsh `>`). Sending keys before this point lets zsh's
288
- // TCSAFLUSH on ZLE startup discard the buffered `\r`, so the command ends
289
- // up displayed at the prompt but never submitted. This wait was dropped in
290
- // eca267b0 alongside the post-submit pane-scrape we only needed to drop
291
- // the latter.
292
- await waitForPaneReady(paneId, readyTimeoutMs);
293
-
294
- await sendKeysAndSubmit(paneId, cmd);
286
+ // Replace the pane's shell with `claude` directly. tmux execs the command
287
+ // itself, so there's no shell line editor to race with the previous
288
+ // approach keystroked into a zsh that hadn't finished ZLE init yet, and
289
+ // zsh's TCSAFLUSH during startup would discard the buffered `\r`, leaving
290
+ // the command typed at the prompt but never submitted.
291
+ respawnPane(paneId, cmd);
295
292
 
296
293
  // SDK-native readiness signal: wait for Claude to create its JSONL file
297
294
  // at the known UUID path.
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Helper for spawning the attached `atomic _footer` pane inside an agent
3
+ * tmux window.
4
+ *
5
+ * Shared between the workflow executor (per-agent windows) and the chat
6
+ * command (single-agent window). Splits the target pane vertically so the
7
+ * top pane keeps running the agent CLI and the bottom pane hosts the
8
+ * React footer.
9
+ *
10
+ * Resolves the CLI entrypoint relative to this module (runtime/ lives at
11
+ * src/sdk/runtime/, so ../../cli.ts is the CLI). `process.argv[1]` points
12
+ * at the orchestrator's executor-entry.ts when called from the executor,
13
+ * so it can't be used here.
14
+ */
15
+
16
+ import { join } from "node:path";
17
+ import type { AgentType } from "../types.ts";
18
+ import { tmuxRun } from "./tmux.ts";
19
+
20
+ /**
21
+ * Rows reserved for the footer pane. Matches the single-row height of
22
+ * `AttachedStatusline` so the agent pane absorbs all remaining space.
23
+ */
24
+ const FOOTER_PANE_LINES = 1;
25
+
26
+ /** Escape a string for safe interpolation inside a bash double-quoted string. */
27
+ function escBash(s: string): string {
28
+ return s
29
+ .replace(/\x00/g, "")
30
+ .replace(/[\n\r]+/g, " ")
31
+ .replace(/[\\"$`!]/g, "\\$&");
32
+ }
33
+
34
+ export function spawnAttachedFooter(
35
+ windowName: string,
36
+ paneId: string,
37
+ agentType?: AgentType,
38
+ ): void {
39
+ const runtime = process.execPath;
40
+ if (!runtime) return;
41
+ const cliPath = join(import.meta.dir, "..", "..", "cli.ts");
42
+ const agentFlag = agentType ? ` --agent "${escBash(agentType)}"` : "";
43
+ const cmd =
44
+ `"${escBash(runtime)}" "${escBash(cliPath)}" _footer ` +
45
+ `--name "${escBash(windowName)}"${agentFlag}`;
46
+ const split = tmuxRun([
47
+ "split-window",
48
+ "-t", paneId,
49
+ "-v", "-l", String(FOOTER_PANE_LINES), "-d",
50
+ "-P", "-F", "#{pane_id}",
51
+ cmd,
52
+ ]);
53
+ if (!split.ok) return;
54
+ const footerPaneId = split.stdout.trim();
55
+ if (!footerPaneId) return;
56
+ // Pin the footer to FOOTER_PANE_LINES on every resize so the agent pane
57
+ // absorbs all new space. Tmux's default proportional redistribution
58
+ // would otherwise grow the footer on larger windows. Window-scoped
59
+ // (`-w`) so other windows (e.g. the orchestrator graph) are unaffected.
60
+ tmuxRun([
61
+ "set-hook",
62
+ "-w", "-t", footerPaneId,
63
+ "window-resized",
64
+ `resize-pane -t ${footerPaneId} -y ${FOOTER_PANE_LINES}`,
65
+ ]);
66
+ }
@@ -0,0 +1,104 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Ctrl+C debounce helper for Atomic-managed tmux panes.
4
+ *
5
+ * Invoked from `tmux.conf` on every root-table Ctrl+C:
6
+ *
7
+ * bind -n C-c run-shell -b '"#{@atomic-bun}" "#{@atomic-cc-debounce}" "#{pane_id}"'
8
+ *
9
+ * The binding sits on the shared atomic tmux server, so the debounce
10
+ * applies uniformly to every pane — Claude Code, OpenCode, and Copilot
11
+ * CLI, in both chat and workflow sessions.
12
+ *
13
+ * Rule: forward Ctrl+C only if the previous press is more than QUIET_MS
14
+ * ago. The state file is touched on *every* press (forwarded or
15
+ * swallowed) so sustained spam keeps extending the cooldown instead of
16
+ * letting a press leak through every QUIET_MS interval — which is what
17
+ * would otherwise still trigger an agent CLI's "double-tap to exit"
18
+ * confirmation.
19
+ */
20
+
21
+ import { tmpdir } from "node:os";
22
+ import { join } from "node:path";
23
+ import { readFileSync, writeFileSync } from "node:fs";
24
+
25
+ /** Quiet period (ms) the user must leave between presses for the next
26
+ * one to be forwarded. Must exceed every integrated agent's exit-confirm
27
+ * window — Claude Code's is the widest (~1.5 s), so 1200 ms is a safe
28
+ * margin that still feels responsive for legitimate double-interrupts. */
29
+ export const QUIET_MS = 1200;
30
+
31
+ /** Pure decision helper — exported so tests can exercise it without
32
+ * touching the filesystem or spawning tmux. Returns `true` when the
33
+ * press should be forwarded, `false` when it should be swallowed. */
34
+ export function shouldForward(
35
+ nowMs: number,
36
+ lastMs: number,
37
+ quietMs: number = QUIET_MS,
38
+ ): boolean {
39
+ return nowMs - lastMs > quietMs;
40
+ }
41
+
42
+ /** Filesystem-safe transform for a tmux pane id (typically `%0`, `%12`).
43
+ * A defensive sanitise keeps the path portable if tmux ever hands us
44
+ * something with shell metacharacters. */
45
+ function stateFileFor(paneId: string): string {
46
+ const safe = paneId.replace(/[^a-zA-Z0-9_%-]/g, "_");
47
+ return join(tmpdir(), `atomic-cc-${safe}`);
48
+ }
49
+
50
+ function readLastPress(stateFile: string): number {
51
+ try {
52
+ const parsed = Number.parseInt(readFileSync(stateFile, "utf8").trim(), 10);
53
+ return Number.isFinite(parsed) ? parsed : 0;
54
+ } catch {
55
+ return 0;
56
+ }
57
+ }
58
+
59
+ /** Resolve the multiplexer binary for `tmux send-keys`. When `run-shell`
60
+ * spawns this script the `TMUX` / `PSMUX` env var is already set to the
61
+ * atomic socket, so plain `tmux` / `psmux` routes correctly without
62
+ * `-L`. We only need to pick the right executable name for the host. */
63
+ function resolveMuxBinary(): string {
64
+ if (process.platform === "win32") {
65
+ for (const candidate of ["psmux", "pmux", "tmux"]) {
66
+ if (Bun.which(candidate)) return candidate;
67
+ }
68
+ return "psmux";
69
+ }
70
+ return "tmux";
71
+ }
72
+
73
+ function main(): number {
74
+ const paneId = process.argv[2];
75
+ if (!paneId) return 0;
76
+
77
+ const stateFile = stateFileFor(paneId);
78
+ const now = Date.now();
79
+ const last = readLastPress(stateFile);
80
+
81
+ // Always bump the timestamp so held-down or spammed presses keep
82
+ // extending the cooldown — this is what turns the single-press gate
83
+ // into a true quiet-period debounce.
84
+ try {
85
+ writeFileSync(stateFile, String(now));
86
+ } catch {
87
+ // Best-effort: if the tmp dir is read-only we'd rather forward every
88
+ // press than drop them silently.
89
+ }
90
+
91
+ if (!shouldForward(now, last)) return 0;
92
+
93
+ const proc = Bun.spawnSync({
94
+ cmd: [resolveMuxBinary(), "send-keys", "-t", paneId, "C-c"],
95
+ stdin: "ignore",
96
+ stdout: "ignore",
97
+ stderr: "ignore",
98
+ });
99
+ return proc.exitCode ?? 0;
100
+ }
101
+
102
+ if (import.meta.main) {
103
+ process.exit(main());
104
+ }
@@ -45,6 +45,7 @@ import type { SessionPromptResponse } from "@opencode-ai/sdk/v2";
45
45
  import type { SessionMessage } from "@anthropic-ai/claude-agent-sdk";
46
46
  import * as tmux from "./tmux.ts";
47
47
  import { spawnMuxAttach } from "./tmux.ts";
48
+ import { spawnAttachedFooter } from "./attached-footer.ts";
48
49
  import { WorkflowLoader } from "./loader.ts";
49
50
  import {
50
51
  clearClaudeSession,
@@ -276,34 +277,6 @@ export function applyContainerEnvDefaults(): void {
276
277
  if (bin) process.env.COPILOT_CLI_PATH = bin;
277
278
  }
278
279
 
279
- /** Percent of the agent window's vertical space allocated to the React footer pane. */
280
- const FOOTER_PANE_PERCENT = 5;
281
-
282
- /**
283
- * Split the agent window so the top pane runs the agent CLI and the bottom
284
- * pane runs the React footer (via `atomic _footer`). Allocating a percentage
285
- * of the vertical layout — rather than an absolute cell count — lets tmux
286
- * enforce its minimum-pane-size constraints and keeps the split readable on
287
- * both tall and short terminals.
288
- *
289
- * Resolves the CLI entrypoint relative to this file (executor.ts lives at
290
- * src/sdk/runtime/, so ../../cli.ts is the CLI). `process.argv[1]` points
291
- * to executor-entry.ts here — a separate process that has no `_footer`
292
- * subcommand — so we can't use it.
293
- */
294
- function spawnAttachedFooter(windowName: string, paneId: string): void {
295
- const runtime = process.execPath;
296
- if (!runtime) return;
297
- const cliPath = join(import.meta.dir, "..", "..", "cli.ts");
298
- const cmd = `"${escBash(runtime)}" "${escBash(cliPath)}" _footer --name "${escBash(windowName)}"`;
299
- tmux.tmuxRun([
300
- "split-window",
301
- "-t", paneId,
302
- "-v", "-l", `${FOOTER_PANE_PERCENT}%`, "-d",
303
- cmd,
304
- ]);
305
- }
306
-
307
280
  function buildPaneCommand(
308
281
  agent: AgentType,
309
282
  port: number,
@@ -25,6 +25,7 @@ export interface TerminalTheme {
25
25
  success: string;
26
26
  error: string;
27
27
  warning: string;
28
+ mauve: string;
28
29
  }
29
30
 
30
31
  // ---------------------------------------------------------------------------
@@ -43,6 +44,7 @@ const CATPPUCCIN_MOCHA: TerminalTheme = {
43
44
  success: "#a6e3a1", // Green
44
45
  error: "#f38ba8", // Red
45
46
  warning: "#f9e2af", // Yellow
47
+ mauve: "#cba6f7", // Mauve
46
48
  };
47
49
 
48
50
  // ---------------------------------------------------------------------------
@@ -61,6 +63,7 @@ const CATPPUCCIN_LATTE: TerminalTheme = {
61
63
  success: "#40a02b", // Green
62
64
  error: "#d20f39", // Red
63
65
  warning: "#df8e1d", // Yellow
66
+ mauve: "#8839ef", // Mauve
64
67
  };
65
68
 
66
69
  // ---------------------------------------------------------------------------
@@ -20,14 +20,10 @@ set -g status-interval 5
20
20
  set -g focus-events on
21
21
  setw -g aggressive-resize on
22
22
 
23
- # Status bar — minimal
24
- # These defaults are mirrored by TMUX_DEFAULT_STATUS_* constants in tmux.ts.
25
- # Keep both in sync when changing.
26
- set -g status-left " "
27
- set -g status-right " #{session_name} | %H:%M "
28
- set -g status-right-length 60
29
- set -g status-style "bg=#1e1e2e,fg=#cdd6f4"
30
- set -g status-right-style "fg=#6c7086"
23
+ # Status bar — hidden. The React-rendered AttachedStatusline footer
24
+ # (spawned via `atomic _footer`) already shows the provider pill / pane
25
+ # name, so the default tmux status line would visually compete with it.
26
+ set -g status off
31
27
 
32
28
  # Pane resizing
33
29
  bind -r l resize-pane -R 5
@@ -57,6 +53,15 @@ bind -n C-g select-window -t :0
57
53
  # Ctrl+\: cycle to next agent window from anywhere
58
54
  bind -n C-\\ next-window
59
55
 
56
+ # Ctrl+C debounce — forward the first press of a burst and swallow the
57
+ # rest while the user is still pressing, preventing the "double-tap to
58
+ # exit" behaviour of Claude Code / OpenCode / Copilot from firing on
59
+ # accidental spam. Runs via bun so the same TypeScript helper works on
60
+ # Linux, macOS, and Windows psmux. @atomic-bun and @atomic-cc-debounce
61
+ # are set to absolute paths by tmux.ts on session creation — no PATH
62
+ # lookup needed inside run-shell.
63
+ bind -n C-c run-shell -b '"#{@atomic-bun}" "#{@atomic-cc-debounce}" "#{pane_id}"'
64
+
60
65
  # Escape exits copy-mode (clear selection first if one exists, otherwise cancel)
61
66
  bind-key -T copy-mode-vi Escape if-shell -F "#{selection_present}" "send-keys -X clear-selection" "send-keys -X cancel"
62
67
 
@@ -68,7 +73,7 @@ bind -T copy-mode-vi MouseDrag1Pane select-pane \; send-keys -X begin-selecti
68
73
  bind -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-and-cancel
69
74
  bind -T copy-mode-vi MouseDown1Pane send-keys -X clear-selection \; select-pane
70
75
 
71
- # ── Workflow protection: unbind destructive defaults ──────────────
76
+ # ── Workflow protection: unbind destructive and layout-changing defaults ──
72
77
  # Atomic manages the tmux session lifecycle programmatically. Users should
73
78
  # interact with agents and navigate via Ctrl+G / Ctrl+\ / the graph panel.
74
79
  # The prefix key (C-b) is kept only for copy-mode entry (C-b [).
@@ -84,21 +89,21 @@ bind -T copy-mode-vi MouseDown1Pane send-keys -X clear-selection \; select-pa
84
89
  # C-b [ copy-mode entry (keyboard fallback for mouse scroll)
85
90
  # C-b : command prompt (requires typing a full command — low accidental risk)
86
91
  # C-b d detach (non-destructive; just disconnects the client)
87
- # C-b c new window (non-destructive; doesn't affect existing windows)
88
- # C-b - custom split vertical (replaces default C-b ")
89
- # C-b | custom split horizontal (replaces default C-b %)
90
92
 
91
93
  # Prevent window/pane destruction
92
94
  unbind & # kill-window
93
95
  unbind x # kill-pane
94
96
 
97
+ # Prevent window/pane creation — Atomic owns the layout. User-initiated
98
+ # splits or new windows would desync the executor's pane registry and
99
+ # leave orphaned shells inside workflow/chat sessions.
100
+ unbind c # new-window
101
+ unbind '"' # default split vertical
102
+ unbind % # default split horizontal
103
+
95
104
  # Prevent renaming
96
105
  unbind , # rename-window
97
106
  unbind '$' # rename-session
98
107
 
99
108
  # Prevent automatic window renaming (complements allow-rename off above)
100
109
  setw -g automatic-rename off
101
-
102
- # Pane splitting
103
- bind - split-window -v -c "#{pane_current_path}"
104
- bind | split-window -h -c "#{pane_current_path}"
@@ -21,6 +21,11 @@ export const SOCKET_NAME = "atomic";
21
21
  /** Path to the bundled tmux config (shared by tmux and psmux). */
22
22
  const CONFIG_PATH = join(import.meta.dir, "tmux.conf");
23
23
 
24
+ /** Path to the bundled Ctrl+C debounce script (TypeScript, run via bun
25
+ * so the same file handles Linux, macOS, and Windows without shell
26
+ * dialect gymnastics). Referenced from tmux.conf. */
27
+ const CC_DEBOUNCE_PATH = join(import.meta.dir, "cc-debounce.ts");
28
+
24
29
  /** Discriminated result from a tmux command execution. */
25
30
  export type TmuxResult =
26
31
  | { ok: true; stdout: string }
@@ -204,6 +209,14 @@ export function createSession(
204
209
  // Reload config into the running server so keybindings are always current
205
210
  // (tmux only loads -f on first server start; source-file updates a running server).
206
211
  tmuxRun(["source-file", CONFIG_PATH]);
212
+ // Expose the bun binary and debounce-script paths as server-wide user
213
+ // options so tmux.conf's Ctrl+C binding can invoke them without
214
+ // hardcoding an install path or relying on the user's PATH — which
215
+ // tmux's run-shell does not always inherit in full, especially on
216
+ // Windows psmux. `process.execPath` is the exact bun interpreter
217
+ // currently running atomic, guaranteeing it's executable.
218
+ tmuxRun(["set-option", "-g", "@atomic-bun", process.execPath]);
219
+ tmuxRun(["set-option", "-g", "@atomic-cc-debounce", CC_DEBOUNCE_PATH]);
207
220
  return paneId || tmux(["list-panes", "-t", sessionName, "-F", "#{pane_id}"]).split("\n")[0]!;
208
221
  }
209
222
 
@@ -253,6 +266,19 @@ export function createPane(sessionName: string, command: string): string {
253
266
  ]);
254
267
  }
255
268
 
269
+ /**
270
+ * Replace the running command in an existing pane with a new one.
271
+ *
272
+ * `-k` kills whatever is currently running in the pane (e.g. a still-initializing
273
+ * shell) before tmux spawns the new command. Because tmux execs the command
274
+ * itself rather than forwarding keystrokes through a shell line editor, there
275
+ * is no shell-ready race and no ZLE TCSAFLUSH drop — callers can invoke this
276
+ * immediately after pane creation without waiting for a prompt to appear.
277
+ */
278
+ export function respawnPane(paneId: string, command: string): void {
279
+ tmuxExec(["respawn-pane", "-k", "-t", paneId, command]);
280
+ }
281
+
256
282
  // ---------------------------------------------------------------------------
257
283
  // Keystroke sending
258
284
  // ---------------------------------------------------------------------------
@@ -305,29 +331,6 @@ export function sendSpecialKey(paneId: string, key: string): void {
305
331
  tmuxExec(["send-keys", "-t", paneId, key]);
306
332
  }
307
333
 
308
- /**
309
- * Send literal text and submit with C-m (carriage return).
310
- * Uses C-m instead of Enter for raw-mode TUI compatibility.
311
- *
312
- * @param presses - Number of C-m presses (default: 1)
313
- * @param delayMs - Delay between presses in ms (default: 100)
314
- */
315
- export async function sendKeysAndSubmit(
316
- paneId: string,
317
- text: string,
318
- presses = 1,
319
- delayMs = 100
320
- ): Promise<void> {
321
- sendLiteralText(paneId, text);
322
-
323
- for (let i = 0; i < presses; i++) {
324
- if (i > 0 && delayMs > 0) {
325
- await Bun.sleep(delayMs);
326
- }
327
- sendSpecialKey(paneId, "C-m");
328
- }
329
- }
330
-
331
334
  // ---------------------------------------------------------------------------
332
335
  // Pane capture
333
336
  // ---------------------------------------------------------------------------
@@ -655,159 +658,3 @@ export function normalizeTmuxLines(text: string): string {
655
658
  .trim();
656
659
  }
657
660
 
658
- /** Split capture into cleaned, non-empty lines. */
659
- function toPaneLines(captured: string): string[] {
660
- return captured
661
- .split("\n")
662
- .map((l) => l.replace(/\r/g, "").trimEnd())
663
- .filter((l) => l.trim() !== "");
664
- }
665
-
666
- // ---------------------------------------------------------------------------
667
- // Pane state detection (ported from oh-my-codex's tmux-hook-engine.ts)
668
- // ---------------------------------------------------------------------------
669
-
670
- /** Returns true when the pane is still bootstrapping (loading/initializing). */
671
- function paneIsBootstrapping(lines: string[]): boolean {
672
- return lines.some(
673
- (line) =>
674
- /\b(loading|initializing|starting up)\b/i.test(line) ||
675
- /\bmodel:\s*loading\b/i.test(line) ||
676
- /\bconnecting\s+to\b/i.test(line),
677
- );
678
- }
679
-
680
- /**
681
- * Returns true when the pane shows an agent prompt ready for input.
682
- * Detects Claude Code (❯), Codex (›), and generic (>) prompts.
683
- */
684
- export function paneLooksReady(captured: string): boolean {
685
- const content = captured.trimEnd();
686
- if (content === "") return false;
687
-
688
- const lines = toPaneLines(content);
689
- if (paneIsBootstrapping(lines)) return false;
690
-
691
- if (lines.some((line) => /^\s*[›>❯]\s*/u.test(line))) return true;
692
- if (lines.some((line) => /\bhow can i help(?: you)?\b/i.test(line))) return true;
693
-
694
- return false;
695
- }
696
-
697
- /**
698
- * Returns true when the agent has an active task in progress.
699
- * Checks last 40 lines for known busy indicators.
700
- */
701
- export function paneHasActiveTask(captured: string): boolean {
702
- const tail = toPaneLines(captured)
703
- .map((line) => line.trim())
704
- .slice(-40);
705
-
706
- return tail.some((l) =>
707
- /\b\d+\s+background terminal running\b/i.test(l) ||
708
- /esc to interrupt/i.test(l) ||
709
- /\bbackground terminal running\b/i.test(l) ||
710
- /^[·✻]\s+[A-Za-z][A-Za-z0-9''-]*(?:\s+[A-Za-z][A-Za-z0-9''-]*){0,3}(?:…|\.{3})$/u.test(l),
711
- );
712
- }
713
-
714
- /**
715
- * Returns true when the pane is idle — showing a prompt and not processing.
716
- * Uses visible-only capture to avoid stale scrollback matches.
717
- */
718
- export function paneIsIdle(paneId: string): boolean {
719
- const visible = capturePaneVisible(paneId);
720
- return paneLooksReady(visible) && !paneHasActiveTask(visible);
721
- }
722
-
723
- // ---------------------------------------------------------------------------
724
- // Readiness wait
725
- // ---------------------------------------------------------------------------
726
-
727
- /**
728
- * Wait for the pane to be idle (prompt visible, no active task) with
729
- * exponential backoff. Returns the time spent waiting (ms).
730
- */
731
- export async function waitForPaneReady(paneId: string, timeoutMs: number = 30_000): Promise<number> {
732
- const startedAt = Date.now();
733
- let delayMs = 150;
734
- const maxDelayMs = 8_000;
735
-
736
- while (Date.now() - startedAt < timeoutMs) {
737
- if (paneIsIdle(paneId)) return Date.now() - startedAt;
738
-
739
- const remaining = timeoutMs - (Date.now() - startedAt);
740
- if (remaining <= 0) break;
741
- await Bun.sleep(Math.min(delayMs, remaining));
742
- delayMs = Math.min(maxDelayMs, delayMs * 2);
743
- }
744
-
745
- return Date.now() - startedAt;
746
- }
747
-
748
- // ---------------------------------------------------------------------------
749
- // Submit rounds with per-round verification
750
- // ---------------------------------------------------------------------------
751
-
752
- /**
753
- * Attempt to submit by pressing C-m, verifying after each round.
754
- * Returns true as soon as the trigger text disappears from the visible
755
- * capture or an active task is detected.
756
- */
757
- export async function attemptSubmitRounds(
758
- paneId: string,
759
- normalizedPrompt: string,
760
- rounds: number,
761
- pressesPerRound: number = 1,
762
- ): Promise<boolean> {
763
- const presses = Math.max(1, Math.floor(pressesPerRound));
764
-
765
- for (let round = 0; round < rounds; round++) {
766
- await Bun.sleep(100);
767
-
768
- for (let press = 0; press < presses; press++) {
769
- sendSpecialKey(paneId, "C-m");
770
- if (press < presses - 1) await Bun.sleep(200);
771
- }
772
-
773
- await Bun.sleep(140);
774
-
775
- const visible = capturePaneVisible(paneId);
776
- if (!normalizeTmuxCapture(visible).includes(normalizedPrompt)) return true;
777
- if (paneHasActiveTask(visible)) return true;
778
-
779
- await Bun.sleep(140);
780
- }
781
-
782
- return false;
783
- }
784
-
785
- // ---------------------------------------------------------------------------
786
- // Output waiting
787
- // ---------------------------------------------------------------------------
788
-
789
- /**
790
- * Wait for a pattern to appear in a tmux pane's output.
791
- * Polls the pane content at the given interval until the pattern matches
792
- * or the timeout is reached.
793
- *
794
- * @returns The full pane content when the pattern was found
795
- */
796
- export async function waitForOutput(
797
- paneId: string,
798
- pattern: RegExp,
799
- options: { timeoutMs?: number; pollIntervalMs?: number } = {}
800
- ): Promise<string> {
801
- const { timeoutMs = 30_000, pollIntervalMs = 500 } = options;
802
- const deadline = Date.now() + timeoutMs;
803
-
804
- while (Date.now() < deadline) {
805
- const content = capturePane(paneId);
806
- if (pattern.test(content)) {
807
- return content;
808
- }
809
- await Bun.sleep(pollIntervalMs);
810
- }
811
-
812
- throw new Error(`Timed out waiting for pattern ${pattern} in pane ${paneId}`);
813
- }
@@ -64,7 +64,6 @@ export {
64
64
  createPane,
65
65
  sendLiteralText,
66
66
  sendSpecialKey,
67
- sendKeysAndSubmit,
68
67
  capturePane,
69
68
  capturePaneVisible,
70
69
  capturePaneScrollback,
@@ -82,15 +81,9 @@ export {
82
81
  setSessionEnv,
83
82
  getSessionEnv,
84
83
  parseSessionName,
85
- waitForOutput,
86
84
  tmuxRun,
87
85
  normalizeTmuxCapture,
88
86
  normalizeTmuxLines,
89
- paneLooksReady,
90
- paneHasActiveTask,
91
- paneIsIdle,
92
- waitForPaneReady,
93
- attemptSubmitRounds,
94
87
  } from "../runtime/tmux.ts";
95
88
 
96
89
  // Runtime — workflow discovery