@bastani/atomic 0.5.34 → 0.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.
Files changed (94) hide show
  1. package/README.md +329 -50
  2. package/dist/commands/cli/session.d.ts +67 -0
  3. package/dist/commands/cli/session.d.ts.map +1 -0
  4. package/dist/commands/cli/workflow-status.d.ts +63 -0
  5. package/dist/commands/cli/workflow-status.d.ts.map +1 -0
  6. package/dist/sdk/commander.d.ts +74 -0
  7. package/dist/sdk/commander.d.ts.map +1 -0
  8. package/dist/sdk/components/workflow-picker-panel.d.ts +14 -17
  9. package/dist/sdk/components/workflow-picker-panel.d.ts.map +1 -1
  10. package/dist/sdk/define-workflow.d.ts +18 -9
  11. package/dist/sdk/define-workflow.d.ts.map +1 -1
  12. package/dist/sdk/index.d.ts +4 -3
  13. package/dist/sdk/index.d.ts.map +1 -1
  14. package/dist/sdk/management-commands.d.ts +42 -0
  15. package/dist/sdk/management-commands.d.ts.map +1 -0
  16. package/dist/sdk/registry.d.ts +27 -0
  17. package/dist/sdk/registry.d.ts.map +1 -0
  18. package/dist/sdk/runtime/attached-footer.d.ts +1 -1
  19. package/dist/sdk/runtime/executor-env.d.ts +20 -0
  20. package/dist/sdk/runtime/executor-env.d.ts.map +1 -0
  21. package/dist/sdk/runtime/executor.d.ts +61 -10
  22. package/dist/sdk/runtime/executor.d.ts.map +1 -1
  23. package/dist/sdk/types.d.ts +147 -4
  24. package/dist/sdk/types.d.ts.map +1 -1
  25. package/dist/sdk/worker-shared.d.ts +42 -0
  26. package/dist/sdk/worker-shared.d.ts.map +1 -0
  27. package/dist/sdk/workflow-cli.d.ts +103 -0
  28. package/dist/sdk/workflow-cli.d.ts.map +1 -0
  29. package/dist/sdk/workflows/builtin-registry.d.ts +113 -0
  30. package/dist/sdk/workflows/builtin-registry.d.ts.map +1 -0
  31. package/dist/sdk/workflows/index.d.ts +5 -5
  32. package/dist/sdk/workflows/index.d.ts.map +1 -1
  33. package/package.json +12 -8
  34. package/src/cli.ts +85 -144
  35. package/src/commands/cli/chat/index.ts +10 -0
  36. package/src/commands/cli/workflow-command.test.ts +279 -938
  37. package/src/commands/cli/workflow-inputs.test.ts +41 -11
  38. package/src/commands/cli/workflow-inputs.ts +47 -12
  39. package/src/commands/cli/workflow-list.test.ts +234 -0
  40. package/src/commands/cli/workflow-list.ts +0 -0
  41. package/src/commands/cli/workflow.ts +11 -798
  42. package/src/scripts/constants.ts +2 -1
  43. package/src/sdk/commander.ts +161 -0
  44. package/src/sdk/components/workflow-picker-panel.tsx +78 -258
  45. package/src/sdk/define-workflow.test.ts +104 -11
  46. package/src/sdk/define-workflow.ts +47 -11
  47. package/src/sdk/errors.test.ts +16 -0
  48. package/src/sdk/index.ts +8 -8
  49. package/src/sdk/management-commands.ts +151 -0
  50. package/src/sdk/registry.ts +132 -0
  51. package/src/sdk/runtime/attached-footer.ts +1 -1
  52. package/src/sdk/runtime/executor-env.ts +45 -0
  53. package/src/sdk/runtime/executor.test.ts +37 -0
  54. package/src/sdk/runtime/executor.ts +147 -68
  55. package/src/sdk/types.ts +169 -4
  56. package/src/sdk/worker-shared.test.ts +163 -0
  57. package/src/sdk/worker-shared.ts +155 -0
  58. package/src/sdk/workflow-cli.ts +409 -0
  59. package/src/sdk/workflows/builtin/deep-research-codebase/claude/index.ts +1 -1
  60. package/src/sdk/workflows/builtin/deep-research-codebase/copilot/index.ts +1 -1
  61. package/src/sdk/workflows/builtin/deep-research-codebase/opencode/index.ts +1 -1
  62. package/src/sdk/workflows/builtin/open-claude-design/claude/index.ts +1 -1
  63. package/src/sdk/workflows/builtin/open-claude-design/copilot/index.ts +1 -1
  64. package/src/sdk/workflows/builtin/open-claude-design/opencode/index.ts +1 -1
  65. package/src/sdk/workflows/builtin/ralph/claude/index.ts +1 -1
  66. package/src/sdk/workflows/builtin/ralph/copilot/index.ts +1 -1
  67. package/src/sdk/workflows/builtin/ralph/opencode/index.ts +1 -1
  68. package/src/sdk/workflows/builtin-registry.ts +23 -0
  69. package/src/sdk/workflows/index.ts +10 -20
  70. package/src/services/system/auth.test.ts +63 -1
  71. package/.agents/skills/workflow-creator/SKILL.md +0 -334
  72. package/.agents/skills/workflow-creator/references/agent-sessions.md +0 -888
  73. package/.agents/skills/workflow-creator/references/computation-and-validation.md +0 -201
  74. package/.agents/skills/workflow-creator/references/control-flow.md +0 -470
  75. package/.agents/skills/workflow-creator/references/discovery-and-verification.md +0 -232
  76. package/.agents/skills/workflow-creator/references/failure-modes.md +0 -903
  77. package/.agents/skills/workflow-creator/references/getting-started.md +0 -275
  78. package/.agents/skills/workflow-creator/references/running-workflows.md +0 -235
  79. package/.agents/skills/workflow-creator/references/session-config.md +0 -384
  80. package/.agents/skills/workflow-creator/references/state-and-data-flow.md +0 -357
  81. package/.agents/skills/workflow-creator/references/user-input.md +0 -234
  82. package/.agents/skills/workflow-creator/references/workflow-inputs.md +0 -272
  83. package/dist/sdk/runtime/discovery.d.ts +0 -132
  84. package/dist/sdk/runtime/discovery.d.ts.map +0 -1
  85. package/dist/sdk/runtime/executor-entry.d.ts +0 -11
  86. package/dist/sdk/runtime/executor-entry.d.ts.map +0 -1
  87. package/dist/sdk/runtime/loader.d.ts +0 -70
  88. package/dist/sdk/runtime/loader.d.ts.map +0 -1
  89. package/dist/version.d.ts +0 -2
  90. package/dist/version.d.ts.map +0 -1
  91. package/src/commands/cli/workflow.test.ts +0 -317
  92. package/src/sdk/runtime/discovery.ts +0 -368
  93. package/src/sdk/runtime/executor-entry.ts +0 -18
  94. package/src/sdk/runtime/loader.ts +0 -267
package/src/cli.ts CHANGED
@@ -26,85 +26,17 @@ import { VERSION } from "./version.ts";
26
26
  import { COLORS } from "./theme/colors.ts";
27
27
  import { AGENT_CONFIG, type AgentKey } from "./services/config/index.ts";
28
28
  import { SUPPORTED_SHELLS, type Shell } from "./completions/index.ts";
29
-
30
- // ─── Session subcommand factory ─────────────────────────────────────────────
31
-
32
- /**
33
- * Build a `session` subcommand group with `list` and `connect` children.
34
- * Reused under `chat`, `workflow`, and at the top level.
35
- */
36
- /** Commander collect helper: accumulates repeated `-a` values into an array. */
37
- function collectAgent(value: string, previous: string[]): string[] {
38
- return [...previous, value];
39
- }
40
-
41
- function addSessionSubcommand(parent: Command, scope: "chat" | "workflow" | "all" = "all") {
42
- const sessionCmd = parent
43
- .command("session")
44
- .description("Manage running tmux sessions");
45
-
46
- sessionCmd
47
- .command("list")
48
- .description("List running sessions on the atomic tmux socket")
49
- .option(
50
- "-a, --agent <name>",
51
- `Filter by agent backend (${Object.keys(AGENT_CONFIG).join(", ")}); repeatable`,
52
- collectAgent,
53
- [] as string[],
54
- )
55
- .action(async (localOpts) => {
56
- const { sessionListCommand } = await import("./commands/cli/session.ts");
57
- const exitCode = await sessionListCommand(localOpts.agent, scope);
58
- process.exit(exitCode);
59
- });
60
-
61
- sessionCmd
62
- .command("connect")
63
- .description("Attach to a running session (interactive picker when no id given)")
64
- .argument("[session_id]", "Session name to connect to")
65
- .option(
66
- "-a, --agent <name>",
67
- `Filter picker by agent backend (${Object.keys(AGENT_CONFIG).join(", ")}); repeatable`,
68
- collectAgent,
69
- [] as string[],
70
- )
71
- .action(async (sessionId, localOpts) => {
72
- if (sessionId) {
73
- const { sessionConnectCommand } = await import("./commands/cli/session.ts");
74
- const exitCode = await sessionConnectCommand(sessionId);
75
- process.exit(exitCode);
76
- } else {
77
- const { sessionPickerCommand } = await import("./commands/cli/session.ts");
78
- const exitCode = await sessionPickerCommand(localOpts.agent, scope);
79
- process.exit(exitCode);
80
- }
81
- });
82
-
83
- sessionCmd
84
- .command("kill")
85
- .description("Kill a running session (omit id to kill all)")
86
- .argument("[session_id]", "Session name to kill (omit to kill all)")
87
- .option(
88
- "-a, --agent <name>",
89
- `Filter by agent backend (${Object.keys(AGENT_CONFIG).join(", ")}); repeatable`,
90
- collectAgent,
91
- [] as string[],
92
- )
93
- .option("-y, --yes", "Skip the confirmation prompt (for non-interactive callers like agents)")
94
- .action(async (sessionId, localOpts) => {
95
- const { sessionKillCommand } = await import("./commands/cli/session.ts");
96
- const exitCode = await sessionKillCommand(
97
- sessionId,
98
- localOpts.agent,
99
- scope,
100
- undefined,
101
- { yes: localOpts.yes === true },
102
- );
103
- process.exit(exitCode);
104
- });
105
-
106
- return sessionCmd;
107
- }
29
+ import { workflowCommand } from "./commands/cli/workflow.ts";
30
+ import { addSessionSubcommand } from "./sdk/management-commands.ts";
31
+
32
+ // Orchestrator re-entry is handled transparently by `runCli` in `main()`
33
+ // below no explicit guard needed. The `atomic workflow` command exposes
34
+ // the builtin workflow CLI, which `runCli` uses to resolve re-entry keys.
35
+ //
36
+ // Session subcommand builders live in `./sdk/management-commands.ts` so the
37
+ // exact same `session list | connect | kill` surface is shipped by every
38
+ // `createWorkflowCli()` user CLI — sessions live on the shared atomic tmux
39
+ // socket, so there's one implementation.
108
40
 
109
41
  // ─── Program ────────────────────────────────────────────────────────────────
110
42
 
@@ -200,24 +132,19 @@ Examples:
200
132
 
201
133
  // ── Workflow command ─────────────────────────────────────────────────────
202
134
  //
203
- // Three shapes:
204
- // 1. `atomic workflow -a <agent>` — interactive picker
205
- // 2. `atomic workflow -n <name> -a <agent> ...` — named run
206
- // 3. `atomic workflow list [-a <agent>]` — list workflows
135
+ // The base Command (with -n, -a, -d flags and workflow dispatch) is
136
+ // produced by the SDK dispatcher. Subcommands (list, inputs, status,
137
+ // session) are attached here so they live under `atomic workflow *`.
207
138
  //
208
- // `allowUnknownOption` + `allowExcessArguments` let unknown flags and
209
- // positional tokens land in `cmd.args`, forwarded as `passthroughArgs`
210
- // so the command layer can parse them against the workflow's schema.
211
- const workflowCmd = program
212
- .command("workflow")
139
+ // `enablePositionalOptions()` on the dispatcher is what makes
140
+ // `atomic workflow list -a claude` route `-a` to the `list`
141
+ // subcommand instead of the parent dispatcher (which *also* declares
142
+ // `-a/--agent` for the dispatch path). Without it, Commander would
143
+ // greedily bind the flag to the parent and the subcommand would
144
+ // never see it.
145
+ workflowCommand
213
146
  .description("Run a multi-session agent workflow")
214
- .option("-n, --name <name>", "Workflow name (matches directory under .atomic/workflows/<name>/)")
215
- .option("-a, --agent <name>", `Agent to use (${agentChoices})`)
216
- .option("-d, --detach", "Start the workflow in the background without attaching (auto-enabled when launched from inside an atomic chat/workflow session to avoid hijacking it). Attach later with 'atomic workflow session connect <id>'.")
217
- .allowUnknownOption()
218
- .allowExcessArguments(true)
219
147
  .enablePositionalOptions()
220
- .passThroughOptions()
221
148
  .addHelpText(
222
149
  "after",
223
150
  `
@@ -235,27 +162,22 @@ Examples:
235
162
  $ atomic workflow session list List running sessions
236
163
  $ atomic workflow session connect <id> Attach to a session
237
164
  $ atomic workflow session kill [id] -y Kill a workflow session (or all), no prompt`,
238
- )
239
- .action(async (localOpts, cmd) => {
240
- const { workflowCommand } = await import("./commands/cli/workflow.ts");
241
- const exitCode = await workflowCommand({
242
- name: localOpts.name,
243
- agent: localOpts.agent,
244
- detach: localOpts.detach,
245
- passthroughArgs: cmd.args,
246
- });
247
- process.exit(exitCode);
248
- });
165
+ );
166
+
167
+ program.addCommand(workflowCommand);
249
168
 
250
169
  // Workflow list subcommand: atomic workflow list [-a <agent>]
251
- workflowCmd
170
+ // Prints the builtin registry. `-a` filters to one agent so users
171
+ // can narrow to workflows runnable with their configured provider.
172
+ workflowCommand
252
173
  .command("list")
253
- .description("List available workflows")
254
- .option("-a, --agent <name>", `Filter by agent (${agentChoices})`)
174
+ .description("List available workflows (optionally filter by agent)")
175
+ .option("-a, --agent <name>", `Filter by agent backend (${agentChoices})`)
255
176
  .action(async (localOpts) => {
256
- const { workflowCommand } = await import("./commands/cli/workflow.ts");
257
- const exitCode = await workflowCommand({
258
- list: true,
177
+ const { workflowListCommand } = await import(
178
+ "./commands/cli/workflow-list.ts"
179
+ );
180
+ const exitCode = await workflowListCommand({
259
181
  agent: localOpts.agent,
260
182
  });
261
183
  process.exit(exitCode);
@@ -264,7 +186,7 @@ Examples:
264
186
  // Workflow inputs subcommand: atomic workflow inputs <name> -a <agent>
265
187
  // Exposes the declared input schema so an orchestrating agent can build
266
188
  // a valid `atomic workflow -n ...` invocation without reading source.
267
- workflowCmd
189
+ workflowCommand
268
190
  .command("inputs")
269
191
  .description("Print a workflow's declared input schema (JSON by default)")
270
192
  .argument("<name>", "Workflow name")
@@ -285,7 +207,7 @@ Examples:
285
207
  // Workflow status subcommand: atomic workflow status [<id>]
286
208
  // Returns one of in_progress | error | completed | needs_review.
287
209
  // Defaults to JSON so agents can parse it without screen-scraping.
288
- workflowCmd
210
+ workflowCommand
289
211
  .command("status")
290
212
  .description(
291
213
  "Query workflow status (in_progress, error, completed, needs_review)",
@@ -304,7 +226,7 @@ Examples:
304
226
  });
305
227
 
306
228
  // Workflow session subcommands: atomic workflow session list / connect
307
- addSessionSubcommand(workflowCmd, "workflow");
229
+ addSessionSubcommand(workflowCommand, "workflow");
308
230
 
309
231
  // ── Top-level session command ───────────────────────────────────────────
310
232
  addSessionSubcommand(program);
@@ -410,39 +332,58 @@ Install completions for your shell:
410
332
  export const program = createProgram();
411
333
 
412
334
  /**
413
- * Main entry point for the CLI
335
+ * Main entry point for the CLI.
336
+ *
337
+ * `runCli` handles detached orchestrator re-entry transparently — when the
338
+ * process is spawned by a tmux orchestrator pane (ATOMIC_ORCHESTRATOR_MODE
339
+ * set), it resolves the workflow against the builtin CLI's registry
340
+ * and drives it via `runOrchestrator`, skipping bootstrap + argv parsing.
341
+ * Otherwise, it runs the provided CLI callback (bootstrap + parseAsync).
414
342
  */
415
343
  async function main(): Promise<void> {
416
344
  try {
417
- // Bootstrap `~/.atomic/settings.json` on every invocation if absent,
418
- // so users always have a file to edit with JSON Schema intellisense
419
- // wired up. Idempotent; swallows FS errors internally.
420
- const { ensureGlobalAtomicSettings } = await import("./services/config/settings.ts");
421
- await ensureGlobalAtomicSettings();
422
-
423
- // Sync tooling deps and global skills on first launch after install
424
- // or upgrade. Runs at most once per version bump (gated on a marker
425
- // file under ~/.atomic). Skipped for `--version` / `--help` so info
426
- // paths stay instant.
427
- const argv = process.argv.slice(2);
428
- const isInfoCommand =
429
- argv.includes("--version") ||
430
- argv.includes("-v") ||
431
- argv.includes("--help") ||
432
- argv.includes("-h") ||
433
- argv[0] === "completions" ||
434
- argv[0] === "_footer" ||
435
- argv[0] === "_claude-stop-hook" ||
436
- argv[0] === "_claude-ask-hook" ||
437
- argv[0] === "_claude-session-start-hook";
438
-
439
- if (!isInfoCommand) {
440
- const { autoSyncIfStale } = await import("./services/system/auto-sync.ts");
441
- await autoSyncIfStale();
442
- }
443
-
444
- // Parse and execute the command
445
- await program.parseAsync();
345
+ const { createWorkflowCli } = await import("./sdk/workflow-cli.ts");
346
+ const { runCli } = await import("./sdk/commander.ts");
347
+ const { createBuiltinRegistry } = await import(
348
+ "./sdk/workflows/builtin-registry.ts"
349
+ );
350
+
351
+ const builtinCli = createWorkflowCli(createBuiltinRegistry());
352
+
353
+ await runCli([builtinCli], async () => {
354
+ // Bootstrap `~/.atomic/settings.json` on every invocation if absent,
355
+ // so users always have a file to edit with JSON Schema intellisense
356
+ // wired up. Idempotent; swallows FS errors internally.
357
+ const { ensureGlobalAtomicSettings } = await import(
358
+ "./services/config/settings.ts"
359
+ );
360
+ await ensureGlobalAtomicSettings();
361
+
362
+ // Sync tooling deps and global skills on first launch after install
363
+ // or upgrade. Runs at most once per version bump (gated on a marker
364
+ // file under ~/.atomic). Skipped for `--version` / `--help` so info
365
+ // paths stay instant.
366
+ const argv = process.argv.slice(2);
367
+ const isInfoCommand =
368
+ argv.includes("--version") ||
369
+ argv.includes("-v") ||
370
+ argv.includes("--help") ||
371
+ argv.includes("-h") ||
372
+ argv[0] === "completions" ||
373
+ argv[0] === "_footer" ||
374
+ argv[0] === "_claude-stop-hook" ||
375
+ argv[0] === "_claude-ask-hook" ||
376
+ argv[0] === "_claude-session-start-hook";
377
+
378
+ if (!isInfoCommand) {
379
+ const { autoSyncIfStale } = await import(
380
+ "./services/system/auto-sync.ts"
381
+ );
382
+ await autoSyncIfStale();
383
+ }
384
+
385
+ await program.parseAsync();
386
+ });
446
387
  } catch (error) {
447
388
  const message = error instanceof Error ? error.message : String(error);
448
389
  console.error(`${COLORS.red}Error: ${message}${COLORS.reset}`);
@@ -145,10 +145,20 @@ function buildLauncherScript(
145
145
  const envLines = envEntries.map(
146
146
  ([key, value]) => `export ${key}="${escBash(value)}"`,
147
147
  );
148
+ // Pre-silence the tty before exec'ing the agent. copilot and opencode
149
+ // write terminal-capability probes (CSI ?Pn $p, OSC 4;0;?, CSI c) to
150
+ // stdout at startup; under tmux the DECRPM / OSC 4 / DA1 replies arrive
151
+ // on stdin a few ms later. If the agent hasn't enabled raw mode yet,
152
+ // the tty driver echoes those replies back to the screen as garbage
153
+ // (e.g. `^[[?1016;2$y^[[?2004;1$y^[]4;0;rgb:...`). Disabling echo and
154
+ // canonical mode here means the driver drops them silently; the agent's
155
+ // own termios setup then takes over once it's ready. Redirected stdin
156
+ // (no tty) harmlessly no-ops the stty call.
148
157
  const script = [
149
158
  "#!/bin/bash",
150
159
  `cd "${escBash(projectRoot)}"`,
151
160
  ...envLines,
161
+ "stty -echo -icanon 2>/dev/null || true",
152
162
  `exec "${escBash(cmd)}" ${quotedArgs}`,
153
163
  ].join("\n");
154
164
  return { script, ext: "sh" };