@bastani/atomic 0.5.0-3 → 0.5.0-5

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 (42) hide show
  1. package/.atomic/workflows/hello/claude/index.ts +22 -25
  2. package/.atomic/workflows/hello/copilot/index.ts +41 -31
  3. package/.atomic/workflows/hello/opencode/index.ts +40 -40
  4. package/.atomic/workflows/hello-parallel/claude/index.ts +54 -54
  5. package/.atomic/workflows/hello-parallel/copilot/index.ts +89 -70
  6. package/.atomic/workflows/hello-parallel/opencode/index.ts +77 -77
  7. package/.atomic/workflows/ralph/claude/index.ts +128 -93
  8. package/.atomic/workflows/ralph/copilot/index.ts +212 -112
  9. package/.atomic/workflows/ralph/helpers/prompts.ts +45 -2
  10. package/.atomic/workflows/ralph/opencode/index.ts +174 -111
  11. package/README.md +138 -59
  12. package/package.json +1 -1
  13. package/src/cli.ts +0 -2
  14. package/src/commands/cli/chat/index.ts +28 -8
  15. package/src/commands/cli/init/index.ts +7 -10
  16. package/src/commands/cli/init/scm.ts +27 -10
  17. package/src/sdk/components/connectors.test.ts +45 -0
  18. package/src/sdk/components/layout.test.ts +321 -0
  19. package/src/sdk/components/layout.ts +51 -15
  20. package/src/sdk/components/orchestrator-panel-contexts.ts +13 -4
  21. package/src/sdk/components/orchestrator-panel-store.test.ts +156 -0
  22. package/src/sdk/components/orchestrator-panel-store.ts +24 -0
  23. package/src/sdk/components/orchestrator-panel.tsx +21 -0
  24. package/src/sdk/components/session-graph-panel.tsx +8 -15
  25. package/src/sdk/components/statusline.tsx +4 -6
  26. package/src/sdk/define-workflow.test.ts +71 -0
  27. package/src/sdk/define-workflow.ts +42 -39
  28. package/src/sdk/errors.ts +1 -1
  29. package/src/sdk/index.ts +4 -1
  30. package/src/sdk/providers/claude.ts +1 -1
  31. package/src/sdk/providers/copilot.ts +5 -3
  32. package/src/sdk/providers/opencode.ts +5 -3
  33. package/src/sdk/runtime/executor.ts +512 -301
  34. package/src/sdk/runtime/loader.ts +2 -2
  35. package/src/sdk/runtime/tmux.ts +31 -2
  36. package/src/sdk/types.ts +93 -20
  37. package/src/sdk/workflows.ts +7 -4
  38. package/src/services/config/definitions.ts +39 -2
  39. package/src/services/config/settings.ts +0 -6
  40. package/src/services/system/skills.ts +3 -7
  41. package/.atomic/workflows/package-lock.json +0 -31
  42. package/.atomic/workflows/package.json +0 -8
@@ -14,31 +14,28 @@ export default defineWorkflow({
14
14
  name: "hello",
15
15
  description: "Two-session Claude demo: describe → summarize",
16
16
  })
17
- .session({
18
- name: "describe",
19
- description: "Ask Claude to describe the project",
20
- run: async (ctx) => {
21
- await createClaudeSession({ paneId: ctx.paneId });
22
- await claudeQuery({
23
- paneId: ctx.paneId,
24
- prompt: ctx.userPrompt,
25
- });
26
- // Save transcript via Claude Agent SDK (reads from ~/.claude session files)
27
- ctx.save(ctx.sessionId);
28
- },
29
- })
30
- .session({
31
- name: "summarize",
32
- description: "Summarize the previous session's output",
33
- run: async (ctx) => {
34
- await createClaudeSession({ paneId: ctx.paneId });
35
- const research = await ctx.transcript("describe");
17
+ .run(async (ctx) => {
18
+ const describe = await ctx.session(
19
+ { name: "describe", description: "Ask Claude to describe the project" },
20
+ async (s) => {
21
+ await createClaudeSession({ paneId: s.paneId });
22
+ await claudeQuery({ paneId: s.paneId, prompt: s.userPrompt });
23
+ s.save(s.sessionId);
24
+ },
25
+ );
26
+
27
+ await ctx.session(
28
+ { name: "summarize", description: "Summarize the previous session's output" },
29
+ async (s) => {
30
+ await createClaudeSession({ paneId: s.paneId });
31
+ const research = await s.transcript(describe);
36
32
 
37
- await claudeQuery({
38
- paneId: ctx.paneId,
39
- prompt: `Read ${research.path} and summarize it in 2-3 bullet points.`,
40
- });
41
- ctx.save(ctx.sessionId);
42
- },
33
+ await claudeQuery({
34
+ paneId: s.paneId,
35
+ prompt: `Read ${research.path} and summarize it in 2-3 bullet points.`,
36
+ });
37
+ s.save(s.sessionId);
38
+ },
39
+ );
43
40
  })
44
41
  .compile();
@@ -10,49 +10,59 @@
10
10
  import { defineWorkflow } from "@bastani/atomic/workflows";
11
11
  import { CopilotClient, approveAll } from "@github/copilot-sdk";
12
12
 
13
+ /**
14
+ * `CopilotSession.sendAndWait` defaults to a 60s timeout and THROWS on
15
+ * expiry, which crashes the workflow mid-stage. Override with a generous
16
+ * 30-minute budget so legitimate long-running agent work completes.
17
+ */
18
+ const SEND_TIMEOUT_MS = 30 * 60 * 1000;
19
+
13
20
  export default defineWorkflow({
14
21
  name: "hello",
15
22
  description: "Two-session Copilot demo: describe → summarize",
16
23
  })
17
- .session({
18
- name: "describe",
19
- description: "Ask the agent to describe the project",
20
- run: async (ctx) => {
21
- const client = new CopilotClient({ cliUrl: ctx.serverUrl });
22
- await client.start();
24
+ .run(async (ctx) => {
25
+ const describe = await ctx.session(
26
+ { name: "describe", description: "Ask the agent to describe the project" },
27
+ async (s) => {
28
+ const client = new CopilotClient({ cliUrl: s.serverUrl });
29
+ await client.start();
23
30
 
24
- const session = await client.createSession({ onPermissionRequest: approveAll });
25
- await client.setForegroundSessionId(session.sessionId);
31
+ const session = await client.createSession({ onPermissionRequest: approveAll });
32
+ await client.setForegroundSessionId(session.sessionId);
26
33
 
27
- await session.sendAndWait({ prompt: ctx.userPrompt });
34
+ await session.sendAndWait({ prompt: s.userPrompt }, SEND_TIMEOUT_MS);
28
35
 
29
- // Save Copilot messages for the next session
30
- ctx.save(await session.getMessages() );
36
+ s.save(await session.getMessages());
31
37
 
32
- await session.disconnect();
33
- await client.stop();
34
- },
35
- })
36
- .session({
37
- name: "summarize",
38
- description: "Summarize the previous session's output",
39
- run: async (ctx) => {
40
- const research = await ctx.transcript("describe");
38
+ await session.disconnect();
39
+ await client.stop();
40
+ },
41
+ );
42
+
43
+ await ctx.session(
44
+ { name: "summarize", description: "Summarize the previous session's output" },
45
+ async (s) => {
46
+ const research = await s.transcript(describe);
41
47
 
42
- const client = new CopilotClient({ cliUrl: ctx.serverUrl });
43
- await client.start();
48
+ const client = new CopilotClient({ cliUrl: s.serverUrl });
49
+ await client.start();
44
50
 
45
- const session = await client.createSession({ onPermissionRequest: approveAll });
46
- await client.setForegroundSessionId(session.sessionId);
51
+ const session = await client.createSession({ onPermissionRequest: approveAll });
52
+ await client.setForegroundSessionId(session.sessionId);
47
53
 
48
- await session.sendAndWait({
49
- prompt: `Summarize the following in 2-3 bullet points:\n\n${research.content}`,
50
- });
54
+ await session.sendAndWait(
55
+ {
56
+ prompt: `Summarize the following in 2-3 bullet points:\n\n${research.content}`,
57
+ },
58
+ SEND_TIMEOUT_MS,
59
+ );
51
60
 
52
- ctx.save(await session.getMessages());
61
+ s.save(await session.getMessages());
53
62
 
54
- await session.disconnect();
55
- await client.stop();
56
- },
63
+ await session.disconnect();
64
+ await client.stop();
65
+ },
66
+ );
57
67
  })
58
68
  .compile();
@@ -14,45 +14,45 @@ export default defineWorkflow({
14
14
  name: "hello",
15
15
  description: "Two-session OpenCode demo: describe → summarize",
16
16
  })
17
- .session({
18
- name: "describe",
19
- description: "Ask the agent to describe the project",
20
- run: async (ctx) => {
21
- const client = createOpencodeClient({ baseUrl: ctx.serverUrl });
22
-
23
- const session = await client.session.create({ title: "describe" });
24
- await client.tui.selectSession({ sessionID: session.data!.id });
25
-
26
- const result = await client.session.prompt({
27
- sessionID: session.data!.id,
28
- parts: [{ type: "text", text: ctx.userPrompt }],
29
- });
30
-
31
- // Save OpenCode response parts for the next session
32
- ctx.save(result.data!);
33
- },
34
- })
35
- .session({
36
- name: "summarize",
37
- description: "Summarize the previous session's output",
38
- run: async (ctx) => {
39
- const research = await ctx.transcript("describe");
40
- const client = createOpencodeClient({ baseUrl: ctx.serverUrl });
41
-
42
- const session = await client.session.create({ title: "summarize" });
43
- await client.tui.selectSession({ sessionID: session.data!.id });
44
-
45
- const result = await client.session.prompt({
46
- sessionID: session.data!.id,
47
- parts: [
48
- {
49
- type: "text",
50
- text: `Summarize the following in 2-3 bullet points:\n\n${research.content}`,
51
- },
52
- ],
53
- });
54
-
55
- ctx.save(result.data!);
56
- },
17
+ .run(async (ctx) => {
18
+ const describe = await ctx.session(
19
+ { name: "describe", description: "Ask the agent to describe the project" },
20
+ async (s) => {
21
+ const client = createOpencodeClient({ baseUrl: s.serverUrl });
22
+
23
+ const session = await client.session.create({ title: "describe" });
24
+ await client.tui.selectSession({ sessionID: session.data!.id });
25
+
26
+ const result = await client.session.prompt({
27
+ sessionID: session.data!.id,
28
+ parts: [{ type: "text", text: s.userPrompt }],
29
+ });
30
+
31
+ s.save(result.data!);
32
+ },
33
+ );
34
+
35
+ await ctx.session(
36
+ { name: "summarize", description: "Summarize the previous session's output" },
37
+ async (s) => {
38
+ const research = await s.transcript(describe);
39
+ const client = createOpencodeClient({ baseUrl: s.serverUrl });
40
+
41
+ const session = await client.session.create({ title: "summarize" });
42
+ await client.tui.selectSession({ sessionID: session.data!.id });
43
+
44
+ const result = await client.session.prompt({
45
+ sessionID: session.data!.id,
46
+ parts: [
47
+ {
48
+ type: "text",
49
+ text: `Summarize the following in 2-3 bullet points:\n\n${research.content}`,
50
+ },
51
+ ],
52
+ });
53
+
54
+ s.save(result.data!);
55
+ },
56
+ );
57
57
  })
58
58
  .compile();
@@ -14,63 +14,63 @@ export default defineWorkflow({
14
14
  name: "hello-parallel",
15
15
  description: "Parallel Claude demo: describe → [summarize-a, summarize-b] → merge",
16
16
  })
17
- .session({
18
- name: "describe",
19
- description: "Ask Claude to describe the project",
20
- run: async (ctx) => {
21
- await createClaudeSession({ paneId: ctx.paneId });
22
- await claudeQuery({ paneId: ctx.paneId, prompt: ctx.userPrompt });
23
- ctx.save(ctx.sessionId);
24
- },
25
- })
26
- .session([
27
- {
28
- name: "summarize-a",
29
- description: "Summarize the description as bullet points",
30
- run: async (ctx) => {
31
- const research = await ctx.transcript("describe");
32
- await createClaudeSession({ paneId: ctx.paneId });
33
- await claudeQuery({
34
- paneId: ctx.paneId,
35
- prompt: `Read ${research.path} and summarize it in 2-3 bullet points.`,
36
- });
37
- ctx.save(ctx.sessionId);
17
+ .run(async (ctx) => {
18
+ const describe = await ctx.session(
19
+ { name: "describe", description: "Ask Claude to describe the project" },
20
+ async (s) => {
21
+ await createClaudeSession({ paneId: s.paneId });
22
+ await claudeQuery({ paneId: s.paneId, prompt: s.userPrompt });
23
+ s.save(s.sessionId);
38
24
  },
39
- },
40
- {
41
- name: "summarize-b",
42
- description: "Summarize the description as a one-liner",
43
- run: async (ctx) => {
44
- const research = await ctx.transcript("describe");
45
- await createClaudeSession({ paneId: ctx.paneId });
25
+ );
26
+
27
+ const [summarizeA, summarizeB] = await Promise.all([
28
+ ctx.session(
29
+ { name: "summarize-a", description: "Summarize the description as bullet points" },
30
+ async (s) => {
31
+ const research = await s.transcript(describe);
32
+ await createClaudeSession({ paneId: s.paneId });
33
+ await claudeQuery({
34
+ paneId: s.paneId,
35
+ prompt: `Read ${research.path} and summarize it in 2-3 bullet points.`,
36
+ });
37
+ s.save(s.sessionId);
38
+ },
39
+ ),
40
+ ctx.session(
41
+ { name: "summarize-b", description: "Summarize the description as a one-liner" },
42
+ async (s) => {
43
+ const research = await s.transcript(describe);
44
+ await createClaudeSession({ paneId: s.paneId });
45
+ await claudeQuery({
46
+ paneId: s.paneId,
47
+ prompt: `Read ${research.path} and summarize it in a single sentence.`,
48
+ });
49
+ s.save(s.sessionId);
50
+ },
51
+ ),
52
+ ]);
53
+
54
+ await ctx.session(
55
+ { name: "merge", description: "Merge both summaries into a final output" },
56
+ async (s) => {
57
+ const bullets = await s.transcript(summarizeA);
58
+ const oneliner = await s.transcript(summarizeB);
59
+ await createClaudeSession({ paneId: s.paneId });
46
60
  await claudeQuery({
47
- paneId: ctx.paneId,
48
- prompt: `Read ${research.path} and summarize it in a single sentence.`,
61
+ paneId: s.paneId,
62
+ prompt: [
63
+ "Combine the following two summaries into one concise paragraph:",
64
+ "",
65
+ "## Bullet points",
66
+ bullets.content,
67
+ "",
68
+ "## One-liner",
69
+ oneliner.content,
70
+ ].join("\n"),
49
71
  });
50
- ctx.save(ctx.sessionId);
72
+ s.save(s.sessionId);
51
73
  },
52
- },
53
- ])
54
- .session({
55
- name: "merge",
56
- description: "Merge both summaries into a final output",
57
- run: async (ctx) => {
58
- const bullets = await ctx.transcript("summarize-a");
59
- const oneliner = await ctx.transcript("summarize-b");
60
- await createClaudeSession({ paneId: ctx.paneId });
61
- await claudeQuery({
62
- paneId: ctx.paneId,
63
- prompt: [
64
- "Combine the following two summaries into one concise paragraph:",
65
- "",
66
- "## Bullet points",
67
- bullets.content,
68
- "",
69
- "## One-liner",
70
- oneliner.content,
71
- ].join("\n"),
72
- });
73
- ctx.save(ctx.sessionId);
74
- },
74
+ );
75
75
  })
76
76
  .compile();
@@ -11,95 +11,114 @@
11
11
  import { defineWorkflow } from "@bastani/atomic/workflows";
12
12
  import { CopilotClient, approveAll } from "@github/copilot-sdk";
13
13
 
14
+ /**
15
+ * `CopilotSession.sendAndWait` defaults to a 60s timeout and THROWS on
16
+ * expiry, which crashes the workflow mid-stage. Override with a generous
17
+ * 30-minute budget so legitimate long-running agent work completes.
18
+ */
19
+ const SEND_TIMEOUT_MS = 30 * 60 * 1000;
20
+
14
21
  export default defineWorkflow({
15
22
  name: "hello-parallel",
16
23
  description: "Parallel Copilot demo: describe → [summarize-a, summarize-b] → merge",
17
24
  })
18
- .session({
19
- name: "describe",
20
- description: "Ask the agent to describe the project",
21
- run: async (ctx) => {
22
- const client = new CopilotClient({ cliUrl: ctx.serverUrl });
23
- await client.start();
24
-
25
- const session = await client.createSession({ onPermissionRequest: approveAll });
26
- await client.setForegroundSessionId(session.sessionId);
27
- await session.sendAndWait({ prompt: ctx.userPrompt });
28
-
29
- ctx.save(await session.getMessages());
30
- await session.disconnect();
31
- await client.stop();
32
- },
33
- })
34
- .session([
35
- {
36
- name: "summarize-a",
37
- description: "Summarize the description as bullet points",
38
- run: async (ctx) => {
39
- const research = await ctx.transcript("describe");
40
-
41
- const client = new CopilotClient({ cliUrl: ctx.serverUrl });
25
+ .run(async (ctx) => {
26
+ // Sequential: describe
27
+ const describe = await ctx.session(
28
+ { name: "describe", description: "Ask the agent to describe the project" },
29
+ async (s) => {
30
+ const client = new CopilotClient({ cliUrl: s.serverUrl });
42
31
  await client.start();
43
32
 
44
33
  const session = await client.createSession({ onPermissionRequest: approveAll });
45
34
  await client.setForegroundSessionId(session.sessionId);
46
- await session.sendAndWait({
47
- prompt: `Summarize the following in 2-3 bullet points:\n\n${research.content}`,
48
- });
35
+ await session.sendAndWait({ prompt: s.userPrompt }, SEND_TIMEOUT_MS);
49
36
 
50
- ctx.save(await session.getMessages());
37
+ s.save(await session.getMessages());
51
38
  await session.disconnect();
52
39
  await client.stop();
53
40
  },
54
- },
55
- {
56
- name: "summarize-b",
57
- description: "Summarize the description as a one-liner",
58
- run: async (ctx) => {
59
- const research = await ctx.transcript("describe");
60
-
61
- const client = new CopilotClient({ cliUrl: ctx.serverUrl });
41
+ );
42
+
43
+ // Parallel: summarize-a + summarize-b
44
+ const [summarizeA, summarizeB] = await Promise.all([
45
+ ctx.session(
46
+ { name: "summarize-a", description: "Summarize the description as bullet points" },
47
+ async (s) => {
48
+ const research = await s.transcript(describe);
49
+
50
+ const client = new CopilotClient({ cliUrl: s.serverUrl });
51
+ await client.start();
52
+
53
+ const session = await client.createSession({ onPermissionRequest: approveAll });
54
+ await client.setForegroundSessionId(session.sessionId);
55
+ await session.sendAndWait(
56
+ {
57
+ prompt: `Summarize the following in 2-3 bullet points:\n\n${research.content}`,
58
+ },
59
+ SEND_TIMEOUT_MS,
60
+ );
61
+
62
+ s.save(await session.getMessages());
63
+ await session.disconnect();
64
+ await client.stop();
65
+ },
66
+ ),
67
+ ctx.session(
68
+ { name: "summarize-b", description: "Summarize the description as a one-liner" },
69
+ async (s) => {
70
+ const research = await s.transcript(describe);
71
+
72
+ const client = new CopilotClient({ cliUrl: s.serverUrl });
73
+ await client.start();
74
+
75
+ const session = await client.createSession({ onPermissionRequest: approveAll });
76
+ await client.setForegroundSessionId(session.sessionId);
77
+ await session.sendAndWait(
78
+ {
79
+ prompt: `Summarize the following in a single sentence:\n\n${research.content}`,
80
+ },
81
+ SEND_TIMEOUT_MS,
82
+ );
83
+
84
+ s.save(await session.getMessages());
85
+ await session.disconnect();
86
+ await client.stop();
87
+ },
88
+ ),
89
+ ]);
90
+
91
+ // Sequential: merge
92
+ await ctx.session(
93
+ { name: "merge", description: "Merge both summaries into a final output" },
94
+ async (s) => {
95
+ const bullets = await s.transcript(summarizeA);
96
+ const oneliner = await s.transcript(summarizeB);
97
+
98
+ const client = new CopilotClient({ cliUrl: s.serverUrl });
62
99
  await client.start();
63
100
 
64
101
  const session = await client.createSession({ onPermissionRequest: approveAll });
65
102
  await client.setForegroundSessionId(session.sessionId);
66
- await session.sendAndWait({
67
- prompt: `Summarize the following in a single sentence:\n\n${research.content}`,
68
- });
103
+ await session.sendAndWait(
104
+ {
105
+ prompt: [
106
+ "Combine the following two summaries into one concise paragraph:",
107
+ "",
108
+ "## Bullet points",
109
+ bullets.content,
110
+ "",
111
+ "## One-liner",
112
+ oneliner.content,
113
+ ].join("\n"),
114
+ },
115
+ SEND_TIMEOUT_MS,
116
+ );
69
117
 
70
- ctx.save(await session.getMessages());
118
+ s.save(await session.getMessages());
71
119
  await session.disconnect();
72
120
  await client.stop();
73
121
  },
74
- },
75
- ])
76
- .session({
77
- name: "merge",
78
- description: "Merge both summaries into a final output",
79
- run: async (ctx) => {
80
- const bullets = await ctx.transcript("summarize-a");
81
- const oneliner = await ctx.transcript("summarize-b");
82
-
83
- const client = new CopilotClient({ cliUrl: ctx.serverUrl });
84
- await client.start();
85
-
86
- const session = await client.createSession({ onPermissionRequest: approveAll });
87
- await client.setForegroundSessionId(session.sessionId);
88
- await session.sendAndWait({
89
- prompt: [
90
- "Combine the following two summaries into one concise paragraph:",
91
- "",
92
- "## Bullet points",
93
- bullets.content,
94
- "",
95
- "## One-liner",
96
- oneliner.content,
97
- ].join("\n"),
98
- });
99
-
100
- ctx.save(await session.getMessages());
101
- await session.disconnect();
102
- await client.stop();
103
- },
122
+ );
104
123
  })
105
124
  .compile();