@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
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session CLI commands — shared between `atomic chat session` and
|
|
3
|
+
* `atomic workflow session`, and the top-level `atomic session` picker.
|
|
4
|
+
*
|
|
5
|
+
* Wraps tmux -L atomic list-sessions / attach-session so users can
|
|
6
|
+
* inspect and reconnect to running atomic sessions without touching
|
|
7
|
+
* tmux directly.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { select, isCancel, cancel } from "@clack/prompts";
|
|
11
|
+
import { createPainter, type PaletteKey } from "../../theme/colors.ts";
|
|
12
|
+
import {
|
|
13
|
+
listSessions as _listSessions,
|
|
14
|
+
isTmuxInstalled as _isTmuxInstalled,
|
|
15
|
+
isInsideAtomicSocket as _isInsideAtomicSocket,
|
|
16
|
+
isInsideTmux as _isInsideTmux,
|
|
17
|
+
sessionExists as _sessionExists,
|
|
18
|
+
switchClient as _switchClient,
|
|
19
|
+
spawnMuxAttach as _spawnMuxAttach,
|
|
20
|
+
detachAndAttachAtomic as _detachAndAttachAtomic,
|
|
21
|
+
SOCKET_NAME,
|
|
22
|
+
} from "../../sdk/workflows/index.ts";
|
|
23
|
+
import type { TmuxSession, SessionType } from "../../sdk/runtime/tmux.ts";
|
|
24
|
+
import type { Subprocess } from "bun";
|
|
25
|
+
|
|
26
|
+
/** Scope controls which session types a command shows. */
|
|
27
|
+
export type SessionScope = "chat" | "workflow" | "all";
|
|
28
|
+
|
|
29
|
+
/** Injectable tmux dependencies for command functions. */
|
|
30
|
+
export interface SessionDeps {
|
|
31
|
+
isTmuxInstalled: () => boolean;
|
|
32
|
+
sessionExists: (name: string) => boolean;
|
|
33
|
+
listSessions: () => TmuxSession[];
|
|
34
|
+
isInsideAtomicSocket: () => boolean;
|
|
35
|
+
isInsideTmux: () => boolean;
|
|
36
|
+
switchClient: (name: string) => void;
|
|
37
|
+
spawnMuxAttach: (name: string) => Subprocess;
|
|
38
|
+
detachAndAttachAtomic: (name: string) => void;
|
|
39
|
+
/** Prompt function for the session picker — defaults to @clack/prompts select. */
|
|
40
|
+
select: typeof select;
|
|
41
|
+
isCancel: typeof isCancel;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Default deps — wire through to the real implementations. */
|
|
45
|
+
const defaultDeps: SessionDeps = {
|
|
46
|
+
isTmuxInstalled: _isTmuxInstalled,
|
|
47
|
+
sessionExists: _sessionExists,
|
|
48
|
+
listSessions: _listSessions,
|
|
49
|
+
isInsideAtomicSocket: _isInsideAtomicSocket,
|
|
50
|
+
isInsideTmux: _isInsideTmux,
|
|
51
|
+
switchClient: _switchClient,
|
|
52
|
+
spawnMuxAttach: _spawnMuxAttach,
|
|
53
|
+
detachAndAttachAtomic: _detachAndAttachAtomic,
|
|
54
|
+
select,
|
|
55
|
+
isCancel,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// ─── Rendering ──────────────────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Render the session list as a printable string.
|
|
62
|
+
*
|
|
63
|
+
* Layout mirrors the workflow list style — data-first count header,
|
|
64
|
+
* session rows with metadata, dim footer hint.
|
|
65
|
+
*/
|
|
66
|
+
export function renderSessionList(sessions: TmuxSession[]): string {
|
|
67
|
+
const paint = createPainter();
|
|
68
|
+
const lines: string[] = [];
|
|
69
|
+
|
|
70
|
+
if (sessions.length === 0) {
|
|
71
|
+
lines.push("");
|
|
72
|
+
lines.push(" " + paint("text", "no sessions running", { bold: true }));
|
|
73
|
+
lines.push("");
|
|
74
|
+
lines.push(" " + paint("dim", "start one with"));
|
|
75
|
+
lines.push(" " + paint("accent", "atomic chat -a <agent>"));
|
|
76
|
+
lines.push(" " + paint("accent", "atomic workflow -n <name> -a <agent>"));
|
|
77
|
+
lines.push("");
|
|
78
|
+
return lines.join("\n") + "\n";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const count = sessions.length;
|
|
82
|
+
const noun = count === 1 ? "session" : "sessions";
|
|
83
|
+
lines.push("");
|
|
84
|
+
lines.push(
|
|
85
|
+
" " + paint("text", String(count), { bold: true }) + " " + paint("dim", noun) +
|
|
86
|
+
paint("dim", ` on tmux -L ${SOCKET_NAME}`),
|
|
87
|
+
);
|
|
88
|
+
lines.push("");
|
|
89
|
+
|
|
90
|
+
for (const s of sessions) {
|
|
91
|
+
const status: PaletteKey = s.attached ? "success" : "dim";
|
|
92
|
+
const indicator = s.attached ? "●" : "○";
|
|
93
|
+
const age = formatAge(s.created);
|
|
94
|
+
const agentBadge = s.agent ? " " + paint("accent", `[${s.agent}]`) : "";
|
|
95
|
+
|
|
96
|
+
lines.push(
|
|
97
|
+
" " +
|
|
98
|
+
paint(status, indicator) + " " +
|
|
99
|
+
paint("text", s.name, { bold: true }) +
|
|
100
|
+
agentBadge +
|
|
101
|
+
paint("dim", " " + age) +
|
|
102
|
+
(s.attached ? " " + paint("success", "attached") : ""),
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
lines.push("");
|
|
107
|
+
lines.push(" " + paint("dim", "connect: atomic session connect <name>"));
|
|
108
|
+
lines.push("");
|
|
109
|
+
|
|
110
|
+
return lines.join("\n") + "\n";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Format an ISO timestamp (or raw string) as a human-readable relative age.
|
|
115
|
+
*/
|
|
116
|
+
function formatAge(isoOrRaw: string): string {
|
|
117
|
+
const d = new Date(isoOrRaw);
|
|
118
|
+
if (Number.isNaN(d.getTime())) return isoOrRaw;
|
|
119
|
+
|
|
120
|
+
const diffMs = Date.now() - d.getTime();
|
|
121
|
+
if (diffMs < 0) return "just now";
|
|
122
|
+
|
|
123
|
+
const seconds = Math.floor(diffMs / 1000);
|
|
124
|
+
if (seconds < 60) return "just now";
|
|
125
|
+
const minutes = Math.floor(seconds / 60);
|
|
126
|
+
if (minutes < 60) return `${minutes}m ago`;
|
|
127
|
+
const hours = Math.floor(minutes / 60);
|
|
128
|
+
if (hours < 24) return `${hours}h ago`;
|
|
129
|
+
const days = Math.floor(hours / 24);
|
|
130
|
+
return `${days}d ago`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ─── Filtering ─────────────────────────────────────────────────────────────
|
|
134
|
+
|
|
135
|
+
/** Map a SessionScope to the SessionType it allows (undefined = no filter). */
|
|
136
|
+
const SCOPE_TO_TYPE: Record<SessionScope, SessionType | undefined> = {
|
|
137
|
+
chat: "chat",
|
|
138
|
+
workflow: "workflow",
|
|
139
|
+
all: undefined,
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/** Filter sessions by scope (chat-only, workflow-only, or all). */
|
|
143
|
+
export function filterByScope(sessions: TmuxSession[], scope: SessionScope): TmuxSession[] {
|
|
144
|
+
const required = SCOPE_TO_TYPE[scope];
|
|
145
|
+
if (!required) return sessions;
|
|
146
|
+
return sessions.filter((s) => s.type === required);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Filter sessions to only those matching at least one of the given agents. */
|
|
150
|
+
export function filterByAgent(sessions: TmuxSession[], agents: string[]): TmuxSession[] {
|
|
151
|
+
if (agents.length === 0) return sessions;
|
|
152
|
+
const allowed = new Set(agents.map((a) => a.toLowerCase()));
|
|
153
|
+
return sessions.filter((s) => s.agent !== undefined && allowed.has(s.agent.toLowerCase()));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ─── Session list command ───────────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
export async function sessionListCommand(agents: string[] = [], scope: SessionScope = "all", deps: SessionDeps = defaultDeps): Promise<number> {
|
|
159
|
+
if (!deps.isTmuxInstalled()) {
|
|
160
|
+
const paint = createPainter();
|
|
161
|
+
process.stdout.write(
|
|
162
|
+
"\n " + paint("text", "no sessions running", { bold: true }) +
|
|
163
|
+
"\n\n " + paint("dim", "tmux is not installed") + "\n\n",
|
|
164
|
+
);
|
|
165
|
+
return 0;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const sessions = filterByAgent(filterByScope(deps.listSessions(), scope), agents);
|
|
169
|
+
process.stdout.write(renderSessionList(sessions));
|
|
170
|
+
return 0;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ─── Session connect command ────────────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Connect to a named session. Handles the three tmux contexts:
|
|
177
|
+
* already on atomic socket → switch-client, inside other tmux → detach+attach,
|
|
178
|
+
* outside tmux → spawn attach.
|
|
179
|
+
*/
|
|
180
|
+
export async function sessionConnectCommand(sessionName: string, deps: SessionDeps = defaultDeps): Promise<number> {
|
|
181
|
+
const paint = createPainter();
|
|
182
|
+
|
|
183
|
+
if (!deps.isTmuxInstalled()) {
|
|
184
|
+
process.stderr.write(
|
|
185
|
+
paint("error", "Error: tmux is not installed.") + "\n",
|
|
186
|
+
);
|
|
187
|
+
return 1;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!deps.sessionExists(sessionName)) {
|
|
191
|
+
process.stderr.write(
|
|
192
|
+
paint("error", `Error: session '${sessionName}' not found.`) + "\n",
|
|
193
|
+
);
|
|
194
|
+
const sessions = deps.listSessions();
|
|
195
|
+
if (sessions.length > 0) {
|
|
196
|
+
process.stderr.write(
|
|
197
|
+
"\n" + paint("dim", "Available sessions:") + "\n",
|
|
198
|
+
);
|
|
199
|
+
for (const s of sessions) {
|
|
200
|
+
process.stderr.write(
|
|
201
|
+
" " + paint("dim", "○") + " " + paint("text", s.name) + "\n",
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
process.stderr.write("\n");
|
|
205
|
+
}
|
|
206
|
+
return 1;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (deps.isInsideAtomicSocket()) {
|
|
210
|
+
deps.switchClient(sessionName);
|
|
211
|
+
return 0;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (deps.isInsideTmux()) {
|
|
215
|
+
deps.detachAndAttachAtomic(sessionName);
|
|
216
|
+
return 0;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const proc = deps.spawnMuxAttach(sessionName);
|
|
220
|
+
return await proc.exited;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// ─── Interactive session picker ─────────────────────────────────────────────
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Show an fzf-style interactive picker for all running atomic sessions.
|
|
227
|
+
* Used by `atomic session connect` (no args).
|
|
228
|
+
*/
|
|
229
|
+
export async function sessionPickerCommand(agents: string[] = [], scope: SessionScope = "all", deps: SessionDeps = defaultDeps): Promise<number> {
|
|
230
|
+
const paint = createPainter();
|
|
231
|
+
|
|
232
|
+
if (!deps.isTmuxInstalled()) {
|
|
233
|
+
process.stderr.write(
|
|
234
|
+
paint("error", "Error: tmux is not installed.") + "\n",
|
|
235
|
+
);
|
|
236
|
+
return 1;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const sessions = filterByAgent(filterByScope(deps.listSessions(), scope), agents);
|
|
240
|
+
|
|
241
|
+
if (sessions.length === 0) {
|
|
242
|
+
process.stdout.write(renderSessionList(sessions));
|
|
243
|
+
return 0;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const selected = await deps.select({
|
|
247
|
+
message: "Connect to session",
|
|
248
|
+
options: sessions.map((s) => {
|
|
249
|
+
const age = formatAge(s.created);
|
|
250
|
+
const tag = s.attached ? " (attached)" : "";
|
|
251
|
+
return {
|
|
252
|
+
value: s.name,
|
|
253
|
+
label: s.name,
|
|
254
|
+
hint: `${age}${tag}`,
|
|
255
|
+
};
|
|
256
|
+
}),
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
if (deps.isCancel(selected)) {
|
|
260
|
+
cancel("Cancelled.");
|
|
261
|
+
return 0;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return sessionConnectCommand(selected as string, deps);
|
|
265
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* atomic workflow -n <name> -a <agent> <prompt> free-form workflow
|
|
7
7
|
* atomic workflow -n <name> -a <agent> --<field>=<value> ...
|
|
8
8
|
* structured-input workflow
|
|
9
|
-
* atomic workflow
|
|
9
|
+
* atomic workflow list [-a <agent>] list discoverable workflows
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { AGENT_CONFIG, type AgentKey } from "../../services/config/index.ts";
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bash completion script for the atomic CLI.
|
|
3
|
+
*
|
|
4
|
+
* Install: eval "$(atomic completions bash)"
|
|
5
|
+
*/
|
|
6
|
+
export const bashCompletionScript = `
|
|
7
|
+
_atomic_completions() {
|
|
8
|
+
local cur prev words cword
|
|
9
|
+
_init_completion || return
|
|
10
|
+
|
|
11
|
+
local commands="init chat workflow session config completions"
|
|
12
|
+
local agents="claude opencode copilot"
|
|
13
|
+
local scms="github sapling"
|
|
14
|
+
local global_opts="-y --yes --no-banner -v --version -h --help"
|
|
15
|
+
|
|
16
|
+
# Walk the words to find the command chain (skip flags and their values)
|
|
17
|
+
local cmd1="" cmd2="" cmd3=""
|
|
18
|
+
local i=1
|
|
19
|
+
while [[ $i -lt $cword ]]; do
|
|
20
|
+
local w="\${words[$i]}"
|
|
21
|
+
case "$w" in
|
|
22
|
+
-a|--agent|-s|--scm|-n|--name) (( i++ )) ;; # skip flag value
|
|
23
|
+
-*) ;; # skip other flags
|
|
24
|
+
*)
|
|
25
|
+
if [[ -z "$cmd1" ]]; then cmd1="$w"
|
|
26
|
+
elif [[ -z "$cmd2" ]]; then cmd2="$w"
|
|
27
|
+
elif [[ -z "$cmd3" ]]; then cmd3="$w"
|
|
28
|
+
fi
|
|
29
|
+
;;
|
|
30
|
+
esac
|
|
31
|
+
(( i++ ))
|
|
32
|
+
done
|
|
33
|
+
|
|
34
|
+
# Complete flag values
|
|
35
|
+
case "$prev" in
|
|
36
|
+
-a|--agent)
|
|
37
|
+
COMPREPLY=( $(compgen -W "$agents" -- "$cur") )
|
|
38
|
+
return
|
|
39
|
+
;;
|
|
40
|
+
-s|--scm)
|
|
41
|
+
COMPREPLY=( $(compgen -W "$scms" -- "$cur") )
|
|
42
|
+
return
|
|
43
|
+
;;
|
|
44
|
+
esac
|
|
45
|
+
|
|
46
|
+
# Top-level (no subcommand yet)
|
|
47
|
+
if [[ -z "$cmd1" ]]; then
|
|
48
|
+
COMPREPLY=( $(compgen -W "$commands $global_opts" -- "$cur") )
|
|
49
|
+
return
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
case "$cmd1" in
|
|
53
|
+
init)
|
|
54
|
+
COMPREPLY=( $(compgen -W "-a --agent -s --scm -h --help" -- "$cur") )
|
|
55
|
+
;;
|
|
56
|
+
chat)
|
|
57
|
+
if [[ -z "$cmd2" ]]; then
|
|
58
|
+
COMPREPLY=( $(compgen -W "session -a --agent -h --help" -- "$cur") )
|
|
59
|
+
elif [[ "$cmd2" == "session" ]]; then
|
|
60
|
+
if [[ -z "$cmd3" ]]; then
|
|
61
|
+
COMPREPLY=( $(compgen -W "list connect -h --help" -- "$cur") )
|
|
62
|
+
else
|
|
63
|
+
COMPREPLY=( $(compgen -W "-a --agent -h --help" -- "$cur") )
|
|
64
|
+
fi
|
|
65
|
+
fi
|
|
66
|
+
;;
|
|
67
|
+
workflow)
|
|
68
|
+
if [[ -z "$cmd2" ]]; then
|
|
69
|
+
COMPREPLY=( $(compgen -W "list session -n --name -a --agent -h --help" -- "$cur") )
|
|
70
|
+
elif [[ "$cmd2" == "list" ]]; then
|
|
71
|
+
COMPREPLY=( $(compgen -W "-a --agent -h --help" -- "$cur") )
|
|
72
|
+
elif [[ "$cmd2" == "session" ]]; then
|
|
73
|
+
if [[ -z "$cmd3" ]]; then
|
|
74
|
+
COMPREPLY=( $(compgen -W "list connect -h --help" -- "$cur") )
|
|
75
|
+
else
|
|
76
|
+
COMPREPLY=( $(compgen -W "-a --agent -h --help" -- "$cur") )
|
|
77
|
+
fi
|
|
78
|
+
fi
|
|
79
|
+
;;
|
|
80
|
+
session)
|
|
81
|
+
if [[ -z "$cmd2" ]]; then
|
|
82
|
+
COMPREPLY=( $(compgen -W "list connect -h --help" -- "$cur") )
|
|
83
|
+
else
|
|
84
|
+
COMPREPLY=( $(compgen -W "-a --agent -h --help" -- "$cur") )
|
|
85
|
+
fi
|
|
86
|
+
;;
|
|
87
|
+
config)
|
|
88
|
+
if [[ -z "$cmd2" ]]; then
|
|
89
|
+
COMPREPLY=( $(compgen -W "set -h --help" -- "$cur") )
|
|
90
|
+
elif [[ "$cmd2" == "set" ]]; then
|
|
91
|
+
if [[ -z "$cmd3" ]]; then
|
|
92
|
+
COMPREPLY=( $(compgen -W "telemetry" -- "$cur") )
|
|
93
|
+
else
|
|
94
|
+
case "$cmd3" in
|
|
95
|
+
telemetry) COMPREPLY=( $(compgen -W "true false" -- "$cur") ) ;;
|
|
96
|
+
esac
|
|
97
|
+
fi
|
|
98
|
+
fi
|
|
99
|
+
;;
|
|
100
|
+
completions)
|
|
101
|
+
COMPREPLY=( $(compgen -W "bash zsh fish powershell" -- "$cur") )
|
|
102
|
+
;;
|
|
103
|
+
esac
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
complete -F _atomic_completions atomic
|
|
107
|
+
`;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fish completion script for the atomic CLI.
|
|
3
|
+
*
|
|
4
|
+
* Install: atomic completions fish | source
|
|
5
|
+
* or: atomic completions fish > ~/.config/fish/completions/atomic.fish
|
|
6
|
+
*/
|
|
7
|
+
export const fishCompletionScript = `
|
|
8
|
+
# Disable file completions by default
|
|
9
|
+
complete -c atomic -f
|
|
10
|
+
|
|
11
|
+
# ── Helpers ─────────────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
set -l agents claude opencode copilot
|
|
14
|
+
set -l scms github sapling
|
|
15
|
+
|
|
16
|
+
# Condition helpers — true when the command line matches a specific depth.
|
|
17
|
+
# "__fish_seen_subcommand_from X" is true once token X has appeared.
|
|
18
|
+
|
|
19
|
+
function __atomic_no_subcommand
|
|
20
|
+
not __fish_seen_subcommand_from init chat workflow session config completions
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
function __atomic_using_cmd
|
|
24
|
+
set -l cmds $argv
|
|
25
|
+
set -l tokens (commandline -opc)
|
|
26
|
+
# Check that the command sequence matches
|
|
27
|
+
set -l idx 2 # start after 'atomic'
|
|
28
|
+
for cmd in $cmds
|
|
29
|
+
if test $idx -gt (count $tokens)
|
|
30
|
+
return 1
|
|
31
|
+
end
|
|
32
|
+
# Skip flags and their values
|
|
33
|
+
while test $idx -le (count $tokens)
|
|
34
|
+
switch $tokens[$idx]
|
|
35
|
+
case '-*'
|
|
36
|
+
set idx (math $idx + 1)
|
|
37
|
+
# Skip flag value for known value-flags
|
|
38
|
+
switch $tokens[(math $idx - 1)]
|
|
39
|
+
case -a --agent -s --scm -n --name
|
|
40
|
+
set idx (math $idx + 1)
|
|
41
|
+
end
|
|
42
|
+
case '*'
|
|
43
|
+
break
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
if test $idx -gt (count $tokens)
|
|
47
|
+
return 1
|
|
48
|
+
end
|
|
49
|
+
if test "$tokens[$idx]" != "$cmd"
|
|
50
|
+
return 1
|
|
51
|
+
end
|
|
52
|
+
set idx (math $idx + 1)
|
|
53
|
+
end
|
|
54
|
+
return 0
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
function __atomic_needs_subcmd_of
|
|
58
|
+
__atomic_using_cmd $argv
|
|
59
|
+
and not __fish_seen_subcommand_from list connect set
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# ── Global options ──────────────────────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
complete -c atomic -n __atomic_no_subcommand -s y -l yes -d 'Auto-confirm all prompts'
|
|
65
|
+
complete -c atomic -n __atomic_no_subcommand -l no-banner -d 'Skip ASCII banner display'
|
|
66
|
+
complete -c atomic -n __atomic_no_subcommand -s v -l version -d 'Show version number'
|
|
67
|
+
complete -c atomic -n __atomic_no_subcommand -s h -l help -d 'Show help'
|
|
68
|
+
|
|
69
|
+
# ── Top-level subcommands ───────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
complete -c atomic -n __atomic_no_subcommand -a init -d 'Interactive setup with agent selection'
|
|
72
|
+
complete -c atomic -n __atomic_no_subcommand -a chat -d 'Start an interactive chat session'
|
|
73
|
+
complete -c atomic -n __atomic_no_subcommand -a workflow -d 'Run a multi-session agent workflow'
|
|
74
|
+
complete -c atomic -n __atomic_no_subcommand -a session -d 'Manage running tmux sessions'
|
|
75
|
+
complete -c atomic -n __atomic_no_subcommand -a config -d 'Manage atomic configuration'
|
|
76
|
+
complete -c atomic -n __atomic_no_subcommand -a completions -d 'Output shell completion script'
|
|
77
|
+
|
|
78
|
+
# ── init ────────────────────────────────────────────────────────────────────
|
|
79
|
+
|
|
80
|
+
complete -c atomic -n '__atomic_using_cmd init' -s a -l agent -d 'Agent to configure' -r -a "$agents"
|
|
81
|
+
complete -c atomic -n '__atomic_using_cmd init' -s s -l scm -d 'Source control system' -r -a "$scms"
|
|
82
|
+
|
|
83
|
+
# ── chat ────────────────────────────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
complete -c atomic -n '__atomic_using_cmd chat; and not __fish_seen_subcommand_from session' -s a -l agent -d 'Agent to chat with' -r -a "$agents"
|
|
86
|
+
complete -c atomic -n '__atomic_using_cmd chat; and not __fish_seen_subcommand_from session' -a session -d 'Manage running chat sessions'
|
|
87
|
+
|
|
88
|
+
# chat session
|
|
89
|
+
complete -c atomic -n '__atomic_using_cmd chat session; and not __fish_seen_subcommand_from list connect' -a list -d 'List running sessions'
|
|
90
|
+
complete -c atomic -n '__atomic_using_cmd chat session; and not __fish_seen_subcommand_from list connect' -a connect -d 'Attach to a running session'
|
|
91
|
+
complete -c atomic -n '__atomic_using_cmd chat session list' -s a -l agent -d 'Filter by agent' -r -a "$agents"
|
|
92
|
+
complete -c atomic -n '__atomic_using_cmd chat session connect' -s a -l agent -d 'Filter by agent' -r -a "$agents"
|
|
93
|
+
|
|
94
|
+
# ── workflow ────────────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
complete -c atomic -n '__atomic_using_cmd workflow; and not __fish_seen_subcommand_from list session' -s n -l name -d 'Workflow name' -r
|
|
97
|
+
complete -c atomic -n '__atomic_using_cmd workflow; and not __fish_seen_subcommand_from list session' -s a -l agent -d 'Agent to use' -r -a "$agents"
|
|
98
|
+
complete -c atomic -n '__atomic_using_cmd workflow; and not __fish_seen_subcommand_from list session' -a list -d 'List available workflows'
|
|
99
|
+
complete -c atomic -n '__atomic_using_cmd workflow; and not __fish_seen_subcommand_from list session' -a session -d 'Manage running workflow sessions'
|
|
100
|
+
|
|
101
|
+
# workflow list
|
|
102
|
+
complete -c atomic -n '__atomic_using_cmd workflow list' -s a -l agent -d 'Filter by agent' -r -a "$agents"
|
|
103
|
+
|
|
104
|
+
# workflow session
|
|
105
|
+
complete -c atomic -n '__atomic_using_cmd workflow session; and not __fish_seen_subcommand_from list connect' -a list -d 'List running sessions'
|
|
106
|
+
complete -c atomic -n '__atomic_using_cmd workflow session; and not __fish_seen_subcommand_from list connect' -a connect -d 'Attach to a running session'
|
|
107
|
+
complete -c atomic -n '__atomic_using_cmd workflow session list' -s a -l agent -d 'Filter by agent' -r -a "$agents"
|
|
108
|
+
complete -c atomic -n '__atomic_using_cmd workflow session connect' -s a -l agent -d 'Filter by agent' -r -a "$agents"
|
|
109
|
+
|
|
110
|
+
# ── session (top-level) ────────────────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
complete -c atomic -n '__atomic_using_cmd session; and not __fish_seen_subcommand_from list connect' -a list -d 'List running sessions'
|
|
113
|
+
complete -c atomic -n '__atomic_using_cmd session; and not __fish_seen_subcommand_from list connect' -a connect -d 'Attach to a running session'
|
|
114
|
+
complete -c atomic -n '__atomic_using_cmd session list' -s a -l agent -d 'Filter by agent' -r -a "$agents"
|
|
115
|
+
complete -c atomic -n '__atomic_using_cmd session connect' -s a -l agent -d 'Filter by agent' -r -a "$agents"
|
|
116
|
+
|
|
117
|
+
# ── config ──────────────────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
complete -c atomic -n '__atomic_using_cmd config; and not __fish_seen_subcommand_from set' -a set -d 'Set a configuration value'
|
|
120
|
+
complete -c atomic -n '__atomic_using_cmd config set; and not __fish_seen_subcommand_from telemetry' -a telemetry -d 'Telemetry setting'
|
|
121
|
+
complete -c atomic -n '__atomic_using_cmd config set telemetry' -a 'true false'
|
|
122
|
+
|
|
123
|
+
# ── completions ─────────────────────────────────────────────────────────────
|
|
124
|
+
|
|
125
|
+
complete -c atomic -n '__atomic_using_cmd completions' -a 'bash zsh fish powershell' -d 'Shell type'
|
|
126
|
+
`;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { bashCompletionScript } from "./bash.ts";
|
|
2
|
+
export { zshCompletionScript } from "./zsh.ts";
|
|
3
|
+
export { fishCompletionScript } from "./fish.ts";
|
|
4
|
+
export { powershellCompletionScript } from "./powershell.ts";
|
|
5
|
+
|
|
6
|
+
export const SUPPORTED_SHELLS = ["bash", "zsh", "fish", "powershell"] as const;
|
|
7
|
+
export type Shell = (typeof SUPPORTED_SHELLS)[number];
|