@bastani/atomic 0.5.5 → 0.5.6-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.
- package/README.md +60 -34
- package/dist/sdk/components/compact-switcher.d.ts +10 -0
- package/dist/sdk/components/compact-switcher.d.ts.map +1 -0
- package/dist/sdk/components/orchestrator-panel-store.d.ts +21 -1
- package/dist/sdk/components/orchestrator-panel-store.d.ts.map +1 -1
- package/dist/sdk/components/orchestrator-panel-types.d.ts +1 -0
- package/dist/sdk/components/orchestrator-panel-types.d.ts.map +1 -1
- package/dist/sdk/components/session-graph-panel.d.ts.map +1 -1
- package/dist/sdk/components/statusline.d.ts.map +1 -1
- package/dist/sdk/runtime/executor.d.ts +3 -2
- package/dist/sdk/runtime/executor.d.ts.map +1 -1
- package/dist/sdk/runtime/tmux.d.ts +82 -2
- package/dist/sdk/runtime/tmux.d.ts.map +1 -1
- package/dist/sdk/workflows/index.d.ts +2 -2
- package/dist/sdk/workflows/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cli.ts +150 -27
- package/src/commands/cli/chat/index.ts +25 -14
- package/src/commands/cli/completions.ts +24 -0
- package/src/commands/cli/session.test.ts +491 -0
- package/src/commands/cli/session.ts +265 -0
- package/src/commands/cli/workflow.ts +1 -1
- package/src/completions/bash.ts +107 -0
- package/src/completions/fish.ts +126 -0
- package/src/completions/index.ts +7 -0
- package/src/completions/powershell.ts +184 -0
- package/src/completions/zsh.ts +144 -0
- package/src/sdk/components/compact-switcher.tsx +73 -0
- package/src/sdk/components/orchestrator-panel-store.test.ts +124 -0
- package/src/sdk/components/orchestrator-panel-store.ts +36 -1
- package/src/sdk/components/orchestrator-panel-types.ts +2 -0
- package/src/sdk/components/session-graph-panel.tsx +138 -10
- package/src/sdk/components/statusline.tsx +13 -8
- package/src/sdk/runtime/executor.ts +18 -27
- package/src/sdk/runtime/tmux.conf +18 -0
- package/src/sdk/runtime/tmux.ts +198 -24
- package/src/sdk/workflows/index.ts +7 -1
package/src/cli.ts
CHANGED
|
@@ -5,20 +5,86 @@
|
|
|
5
5
|
* Built with Commander.js for robust argument parsing and type-safe options.
|
|
6
6
|
*
|
|
7
7
|
* Usage:
|
|
8
|
-
* atomic
|
|
9
|
-
* atomic init
|
|
10
|
-
* atomic init -a <agent>
|
|
11
|
-
* atomic init -s <scm>
|
|
12
|
-
* atomic chat -a <agent>
|
|
13
|
-
* atomic
|
|
14
|
-
* atomic
|
|
15
|
-
* atomic
|
|
8
|
+
* atomic Interactive setup (same as 'atomic init')
|
|
9
|
+
* atomic init Interactive setup with agent selection
|
|
10
|
+
* atomic init -a <agent> Setup specific agent (skip selection)
|
|
11
|
+
* atomic init -s <scm> Setup specific SCM (github, sapling)
|
|
12
|
+
* atomic chat -a <agent> Start interactive chat with an agent
|
|
13
|
+
* atomic chat session list List running chat/workflow sessions
|
|
14
|
+
* atomic chat session connect <id> Attach to a session
|
|
15
|
+
* atomic workflow list List available workflows
|
|
16
|
+
* atomic workflow session list List running sessions
|
|
17
|
+
* atomic workflow session connect <id> Attach to a session
|
|
18
|
+
* atomic session list List all running sessions
|
|
19
|
+
* atomic session connect [id] Interactive session picker
|
|
20
|
+
* atomic config set <key> <value> Set configuration value
|
|
21
|
+
* atomic --version Show version
|
|
22
|
+
* atomic --help Show help
|
|
16
23
|
*/
|
|
17
24
|
|
|
18
25
|
import { Command } from "@commander-js/extra-typings";
|
|
19
26
|
import { VERSION } from "./version.ts";
|
|
20
27
|
import { COLORS } from "./theme/colors.ts";
|
|
21
28
|
import { AGENT_CONFIG, type AgentKey, SCM_CONFIG, type SourceControlType } from "./services/config/index.ts";
|
|
29
|
+
import { SUPPORTED_SHELLS, type Shell } from "./completions/index.ts";
|
|
30
|
+
|
|
31
|
+
// ─── Session subcommand factory ─────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Build a `session` subcommand group with `list` and `connect` children.
|
|
35
|
+
* Reused under `chat`, `workflow`, and at the top level.
|
|
36
|
+
*/
|
|
37
|
+
/** Commander collect helper: accumulates repeated `-a` values into an array. */
|
|
38
|
+
function collectAgent(value: string, previous: string[]): string[] {
|
|
39
|
+
return [...previous, value];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function addSessionSubcommand(parent: Command, scope: "chat" | "workflow" | "all" = "all") {
|
|
43
|
+
const sessionCmd = parent
|
|
44
|
+
.command("session")
|
|
45
|
+
.description("Manage running tmux sessions");
|
|
46
|
+
|
|
47
|
+
sessionCmd
|
|
48
|
+
.command("list")
|
|
49
|
+
.description("List running sessions on the atomic tmux socket")
|
|
50
|
+
.option(
|
|
51
|
+
"-a, --agent <name>",
|
|
52
|
+
`Filter by agent backend (${Object.keys(AGENT_CONFIG).join(", ")}); repeatable`,
|
|
53
|
+
collectAgent,
|
|
54
|
+
[] as string[],
|
|
55
|
+
)
|
|
56
|
+
.action(async (localOpts) => {
|
|
57
|
+
const { sessionListCommand } = await import("./commands/cli/session.ts");
|
|
58
|
+
const exitCode = await sessionListCommand(localOpts.agent, scope);
|
|
59
|
+
process.exit(exitCode);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
sessionCmd
|
|
63
|
+
.command("connect")
|
|
64
|
+
.description("Attach to a running session (interactive picker when no id given)")
|
|
65
|
+
.argument("[session_id]", "Session name to connect to")
|
|
66
|
+
.option(
|
|
67
|
+
"-a, --agent <name>",
|
|
68
|
+
`Filter picker by agent backend (${Object.keys(AGENT_CONFIG).join(", ")}); repeatable`,
|
|
69
|
+
collectAgent,
|
|
70
|
+
[] as string[],
|
|
71
|
+
)
|
|
72
|
+
.action(async (sessionId, localOpts) => {
|
|
73
|
+
if (sessionId) {
|
|
74
|
+
const { sessionConnectCommand } = await import("./commands/cli/session.ts");
|
|
75
|
+
const exitCode = await sessionConnectCommand(sessionId);
|
|
76
|
+
process.exit(exitCode);
|
|
77
|
+
} else {
|
|
78
|
+
const { sessionPickerCommand } = await import("./commands/cli/session.ts");
|
|
79
|
+
const exitCode = await sessionPickerCommand(localOpts.agent, scope);
|
|
80
|
+
process.exit(exitCode);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return sessionCmd;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ─── Program ────────────────────────────────────────────────────────────────
|
|
22
88
|
|
|
23
89
|
/**
|
|
24
90
|
* Create and configure the main CLI program
|
|
@@ -28,6 +94,9 @@ export function createProgram() {
|
|
|
28
94
|
.name("atomic")
|
|
29
95
|
.description("Configuration management CLI for coding agents")
|
|
30
96
|
.version(VERSION, "-v, --version", "Show version number")
|
|
97
|
+
// Required so subcommands (workflow list, session connect) can define
|
|
98
|
+
// their own options without the parent absorbing them first.
|
|
99
|
+
.enablePositionalOptions()
|
|
31
100
|
|
|
32
101
|
// Global options available to all commands
|
|
33
102
|
.option("-y, --yes", "Auto-confirm all prompts (non-interactive mode)")
|
|
@@ -71,13 +140,15 @@ export function createProgram() {
|
|
|
71
140
|
});
|
|
72
141
|
});
|
|
73
142
|
|
|
74
|
-
//
|
|
75
|
-
program
|
|
143
|
+
// ── Chat command (default) ──────────────────────────────────────────────
|
|
144
|
+
const chatCmd = program
|
|
76
145
|
.command("chat", { isDefault: true })
|
|
77
146
|
.description("Start an interactive chat session with a coding agent")
|
|
78
147
|
.option("-a, --agent <name>", `Agent to chat with (${agentChoices})`)
|
|
79
148
|
.allowUnknownOption()
|
|
80
149
|
.allowExcessArguments(true)
|
|
150
|
+
.enablePositionalOptions()
|
|
151
|
+
.passThroughOptions()
|
|
81
152
|
.addHelpText(
|
|
82
153
|
"after",
|
|
83
154
|
`
|
|
@@ -88,8 +159,8 @@ Examples:
|
|
|
88
159
|
$ atomic chat -a copilot Start Copilot interactively
|
|
89
160
|
$ atomic chat -a opencode Start OpenCode interactively
|
|
90
161
|
$ atomic chat -a claude "fix the bug" Claude with initial prompt
|
|
91
|
-
$ atomic chat
|
|
92
|
-
$ atomic chat
|
|
162
|
+
$ atomic chat session list List running sessions
|
|
163
|
+
$ atomic chat session connect <id> Attach to a session`,
|
|
93
164
|
)
|
|
94
165
|
.action(async (localOpts, cmd) => {
|
|
95
166
|
const validAgents = Object.keys(AGENT_CONFIG);
|
|
@@ -126,48 +197,72 @@ Examples:
|
|
|
126
197
|
process.exit(exitCode);
|
|
127
198
|
});
|
|
128
199
|
|
|
129
|
-
//
|
|
200
|
+
// Chat session subcommands: atomic chat session list / connect
|
|
201
|
+
addSessionSubcommand(chatCmd, "chat");
|
|
202
|
+
|
|
203
|
+
// ── Workflow command ─────────────────────────────────────────────────────
|
|
130
204
|
//
|
|
131
|
-
//
|
|
205
|
+
// Three shapes:
|
|
132
206
|
// 1. `atomic workflow -a <agent>` — interactive picker
|
|
133
|
-
// 2. `atomic workflow -n <name> -a <agent> ...` — named run
|
|
134
|
-
//
|
|
135
|
-
// `--<field>=<value>` flags (structured-input workflows).
|
|
207
|
+
// 2. `atomic workflow -n <name> -a <agent> ...` — named run
|
|
208
|
+
// 3. `atomic workflow list [-a <agent>]` — list workflows
|
|
136
209
|
//
|
|
137
|
-
// `allowUnknownOption` + `allowExcessArguments`
|
|
138
|
-
//
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
program
|
|
210
|
+
// `allowUnknownOption` + `allowExcessArguments` let unknown flags and
|
|
211
|
+
// positional tokens land in `cmd.args`, forwarded as `passthroughArgs`
|
|
212
|
+
// so the command layer can parse them against the workflow's schema.
|
|
213
|
+
const workflowCmd = program
|
|
142
214
|
.command("workflow")
|
|
143
215
|
.description("Run a multi-session agent workflow")
|
|
144
216
|
.option("-n, --name <name>", "Workflow name (matches directory under .atomic/workflows/<name>/)")
|
|
145
217
|
.option("-a, --agent <name>", `Agent to use (${agentChoices})`)
|
|
146
|
-
.option("-l, --list", "List available workflows")
|
|
147
218
|
.allowUnknownOption()
|
|
148
219
|
.allowExcessArguments(true)
|
|
220
|
+
.enablePositionalOptions()
|
|
221
|
+
.passThroughOptions()
|
|
149
222
|
.addHelpText(
|
|
150
223
|
"after",
|
|
151
224
|
`
|
|
152
225
|
Examples:
|
|
153
|
-
$ atomic workflow
|
|
226
|
+
$ atomic workflow list List available workflows
|
|
227
|
+
$ atomic workflow list -a claude List Claude workflows only
|
|
154
228
|
$ atomic workflow -a claude Open the interactive picker
|
|
155
229
|
$ atomic workflow -n ralph -a claude "fix bug" Run a free-form workflow
|
|
156
230
|
$ atomic workflow -n gen-spec -a claude --research_doc=notes.md --focus=standard
|
|
157
|
-
Run a structured-input workflow
|
|
231
|
+
Run a structured-input workflow
|
|
232
|
+
$ atomic workflow session list List running sessions
|
|
233
|
+
$ atomic workflow session connect <id> Attach to a session`,
|
|
158
234
|
)
|
|
159
235
|
.action(async (localOpts, cmd) => {
|
|
160
236
|
const { workflowCommand } = await import("./commands/cli/workflow.ts");
|
|
161
237
|
const exitCode = await workflowCommand({
|
|
162
238
|
name: localOpts.name,
|
|
163
239
|
agent: localOpts.agent,
|
|
164
|
-
list: localOpts.list,
|
|
165
240
|
passthroughArgs: cmd.args,
|
|
166
241
|
});
|
|
167
242
|
process.exit(exitCode);
|
|
168
243
|
});
|
|
169
244
|
|
|
170
|
-
//
|
|
245
|
+
// Workflow list subcommand: atomic workflow list [-a <agent>]
|
|
246
|
+
workflowCmd
|
|
247
|
+
.command("list")
|
|
248
|
+
.description("List available workflows")
|
|
249
|
+
.option("-a, --agent <name>", `Filter by agent (${agentChoices})`)
|
|
250
|
+
.action(async (localOpts) => {
|
|
251
|
+
const { workflowCommand } = await import("./commands/cli/workflow.ts");
|
|
252
|
+
const exitCode = await workflowCommand({
|
|
253
|
+
list: true,
|
|
254
|
+
agent: localOpts.agent,
|
|
255
|
+
});
|
|
256
|
+
process.exit(exitCode);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// Workflow session subcommands: atomic workflow session list / connect
|
|
260
|
+
addSessionSubcommand(workflowCmd, "workflow");
|
|
261
|
+
|
|
262
|
+
// ── Top-level session command ───────────────────────────────────────────
|
|
263
|
+
addSessionSubcommand(program);
|
|
264
|
+
|
|
265
|
+
// ── Config command ──────────────────────────────────────────────────────
|
|
171
266
|
const configCmd = program
|
|
172
267
|
.command("config")
|
|
173
268
|
.description("Manage atomic configuration");
|
|
@@ -184,6 +279,34 @@ Examples:
|
|
|
184
279
|
process.exit(exitCode);
|
|
185
280
|
});
|
|
186
281
|
|
|
282
|
+
// ── Completions command ────────────────────────────────────────────────
|
|
283
|
+
program
|
|
284
|
+
.command("completions")
|
|
285
|
+
.description("Output shell completion script")
|
|
286
|
+
.argument("<shell>", `Shell type (${SUPPORTED_SHELLS.join(", ")})`)
|
|
287
|
+
.addHelpText(
|
|
288
|
+
"after",
|
|
289
|
+
`
|
|
290
|
+
Install completions for your shell:
|
|
291
|
+
|
|
292
|
+
Bash eval "$(atomic completions bash)" # add to ~/.bashrc
|
|
293
|
+
Zsh eval "$(atomic completions zsh)" # add to ~/.zshrc
|
|
294
|
+
Fish atomic completions fish | source # or save to ~/.config/fish/completions/atomic.fish
|
|
295
|
+
PowerShell atomic completions powershell | Invoke-Expression # add to $PROFILE`,
|
|
296
|
+
)
|
|
297
|
+
.action(async (shell) => {
|
|
298
|
+
if (!SUPPORTED_SHELLS.includes(shell as Shell)) {
|
|
299
|
+
console.error(
|
|
300
|
+
`${COLORS.red}Error: Unknown shell '${shell}'${COLORS.reset}`,
|
|
301
|
+
);
|
|
302
|
+
console.error(`Supported shells: ${SUPPORTED_SHELLS.join(", ")}`);
|
|
303
|
+
process.exit(1);
|
|
304
|
+
}
|
|
305
|
+
const { completionsCommand } = await import("./commands/cli/completions.ts");
|
|
306
|
+
const exitCode = completionsCommand(shell as Shell);
|
|
307
|
+
process.exit(exitCode);
|
|
308
|
+
});
|
|
309
|
+
|
|
187
310
|
return program;
|
|
188
311
|
}
|
|
189
312
|
|
|
@@ -24,15 +24,18 @@ import {
|
|
|
24
24
|
} from "../../../services/config/atomic-global-config.ts";
|
|
25
25
|
import { getConfigRoot } from "../../../services/config/config-path.ts";
|
|
26
26
|
import {
|
|
27
|
+
isInsideAtomicSocket,
|
|
27
28
|
isInsideTmux,
|
|
28
29
|
isTmuxInstalled,
|
|
29
30
|
resetMuxBinaryCache,
|
|
30
31
|
} from "../../../sdk/workflows/index.ts";
|
|
31
32
|
import {
|
|
32
33
|
createSession,
|
|
34
|
+
detachAndAttachAtomic,
|
|
33
35
|
killSession,
|
|
36
|
+
setSessionEnv,
|
|
34
37
|
spawnMuxAttach,
|
|
35
|
-
|
|
38
|
+
switchClient,
|
|
36
39
|
} from "../../../sdk/workflows/index.ts";
|
|
37
40
|
import { ensureTmuxInstalled } from "../../../lib/spawn.ts";
|
|
38
41
|
|
|
@@ -137,11 +140,10 @@ function buildLauncherScript(
|
|
|
137
140
|
/**
|
|
138
141
|
* Spawn the native agent CLI as an interactive subprocess.
|
|
139
142
|
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* attached so the agent benefits from multiplexer features.
|
|
143
|
+
* Always creates a new session in the atomic tmux socket and attaches
|
|
144
|
+
* to it, regardless of whether the user is already inside tmux.
|
|
145
|
+
* Falls back to direct spawn only when no TTY is available or tmux
|
|
146
|
+
* cannot be installed.
|
|
145
147
|
*
|
|
146
148
|
* @param options - Chat command configuration options
|
|
147
149
|
* @returns Exit code from the agent process
|
|
@@ -175,11 +177,6 @@ export async function chatCommand(options: ChatCommandOptions = {}): Promise<num
|
|
|
175
177
|
const cmd = [config.cmd, ...args];
|
|
176
178
|
const envVars = config.env_vars;
|
|
177
179
|
|
|
178
|
-
// ── Inside tmux: spawn inline in the current pane ──
|
|
179
|
-
if (isInsideTmux()) {
|
|
180
|
-
return spawnDirect(cmd, projectRoot, envVars);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
180
|
// ── No TTY: tmux attach requires a real terminal ──
|
|
184
181
|
if (!process.stdin.isTTY) {
|
|
185
182
|
return spawnDirect(cmd, projectRoot, envVars);
|
|
@@ -202,7 +199,7 @@ export async function chatCommand(options: ChatCommandOptions = {}): Promise<num
|
|
|
202
199
|
|
|
203
200
|
// ── Build launcher script for safe arg/cwd handling ──
|
|
204
201
|
const chatId = generateChatId();
|
|
205
|
-
const windowName = `atomic-chat-${chatId}`;
|
|
202
|
+
const windowName = `atomic-chat-${agentType}-${chatId}`;
|
|
206
203
|
|
|
207
204
|
const sessionsDir = join(homedir(), ".atomic", "sessions", "chat");
|
|
208
205
|
await mkdir(sessionsDir, { recursive: true });
|
|
@@ -219,11 +216,25 @@ export async function chatCommand(options: ChatCommandOptions = {}): Promise<num
|
|
|
219
216
|
? `pwsh -NoProfile -File "${launcherPath}"`
|
|
220
217
|
: `bash "${launcherPath}"`;
|
|
221
218
|
|
|
222
|
-
// ──
|
|
219
|
+
// ── Create session on the atomic socket and attach ──
|
|
223
220
|
try {
|
|
224
221
|
createSession(windowName, shellCmd, undefined, projectRoot);
|
|
222
|
+
setSessionEnv(windowName, "ATOMIC_AGENT", agentType);
|
|
225
223
|
|
|
226
|
-
|
|
224
|
+
if (isInsideAtomicSocket()) {
|
|
225
|
+
// Already on the atomic server — just switch to the new session.
|
|
226
|
+
switchClient(windowName);
|
|
227
|
+
try { await rm(launcherPath, { force: true }); } catch {}
|
|
228
|
+
return 0;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (isInsideTmux()) {
|
|
232
|
+
// Inside a different tmux server — detach and replace the client
|
|
233
|
+
// with an attach to the atomic socket (no nesting).
|
|
234
|
+
detachAndAttachAtomic(windowName);
|
|
235
|
+
try { await rm(launcherPath, { force: true }); } catch {}
|
|
236
|
+
return 0;
|
|
237
|
+
}
|
|
227
238
|
|
|
228
239
|
const attachProc = spawnMuxAttach(windowName);
|
|
229
240
|
const exitCode = await attachProc.exited;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
bashCompletionScript,
|
|
3
|
+
zshCompletionScript,
|
|
4
|
+
fishCompletionScript,
|
|
5
|
+
powershellCompletionScript,
|
|
6
|
+
type Shell,
|
|
7
|
+
} from "../../completions/index.ts";
|
|
8
|
+
|
|
9
|
+
const SCRIPTS: Record<Shell, string> = {
|
|
10
|
+
bash: bashCompletionScript,
|
|
11
|
+
zsh: zshCompletionScript,
|
|
12
|
+
fish: fishCompletionScript,
|
|
13
|
+
powershell: powershellCompletionScript,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Print the shell completion script for the given shell to stdout.
|
|
18
|
+
* Returns 0 on success, 1 on unknown shell.
|
|
19
|
+
*/
|
|
20
|
+
export function completionsCommand(shell: Shell): number {
|
|
21
|
+
const script = SCRIPTS[shell];
|
|
22
|
+
process.stdout.write(script);
|
|
23
|
+
return 0;
|
|
24
|
+
}
|