@bastani/atomic 0.5.3-1 → 0.5.4-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 (48) hide show
  1. package/README.md +110 -11
  2. package/dist/{chunk-mn870nrv.js → chunk-xkxndz5g.js} +213 -154
  3. package/dist/sdk/components/workflow-picker-panel.d.ts +120 -0
  4. package/dist/sdk/define-workflow.d.ts +1 -1
  5. package/dist/sdk/index.js +1 -1
  6. package/dist/sdk/runtime/discovery.d.ts +57 -3
  7. package/dist/sdk/runtime/executor.d.ts +15 -2
  8. package/dist/sdk/runtime/tmux.d.ts +9 -0
  9. package/dist/sdk/types.d.ts +63 -4
  10. package/dist/sdk/workflows/builtin/deep-research-codebase/claude/index.d.ts +61 -0
  11. package/dist/sdk/workflows/builtin/deep-research-codebase/copilot/index.d.ts +48 -0
  12. package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/heuristic.d.ts +25 -0
  13. package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/prompts.d.ts +91 -0
  14. package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/scout.d.ts +56 -0
  15. package/dist/sdk/workflows/builtin/deep-research-codebase/opencode/index.d.ts +48 -0
  16. package/dist/sdk/workflows/builtin/ralph/claude/index.js +6 -5
  17. package/dist/sdk/workflows/builtin/ralph/copilot/index.js +6 -5
  18. package/dist/sdk/workflows/builtin/ralph/opencode/index.js +6 -5
  19. package/dist/sdk/workflows/index.d.ts +4 -4
  20. package/dist/sdk/workflows/index.js +7 -1
  21. package/package.json +1 -1
  22. package/src/cli.ts +25 -3
  23. package/src/commands/cli/chat/index.ts +5 -5
  24. package/src/commands/cli/init/index.ts +79 -77
  25. package/src/commands/cli/workflow-command.test.ts +757 -0
  26. package/src/commands/cli/workflow.test.ts +310 -0
  27. package/src/commands/cli/workflow.ts +445 -105
  28. package/src/sdk/components/workflow-picker-panel.tsx +1462 -0
  29. package/src/sdk/define-workflow.test.ts +101 -0
  30. package/src/sdk/define-workflow.ts +62 -2
  31. package/src/sdk/runtime/discovery.ts +111 -8
  32. package/src/sdk/runtime/executor.ts +89 -32
  33. package/src/sdk/runtime/tmux.conf +55 -0
  34. package/src/sdk/runtime/tmux.ts +34 -10
  35. package/src/sdk/types.ts +67 -4
  36. package/src/sdk/workflows/builtin/deep-research-codebase/claude/index.ts +294 -0
  37. package/src/sdk/workflows/builtin/deep-research-codebase/copilot/index.ts +276 -0
  38. package/src/sdk/workflows/builtin/deep-research-codebase/helpers/heuristic.ts +38 -0
  39. package/src/sdk/workflows/builtin/deep-research-codebase/helpers/prompts.ts +816 -0
  40. package/src/sdk/workflows/builtin/deep-research-codebase/helpers/scout.ts +334 -0
  41. package/src/sdk/workflows/builtin/deep-research-codebase/opencode/index.ts +284 -0
  42. package/src/sdk/workflows/builtin/ralph/claude/index.ts +8 -4
  43. package/src/sdk/workflows/builtin/ralph/copilot/index.ts +10 -4
  44. package/src/sdk/workflows/builtin/ralph/opencode/index.ts +8 -4
  45. package/src/sdk/workflows/index.ts +9 -1
  46. package/src/services/system/auto-sync.ts +1 -1
  47. package/src/services/system/install-ui.ts +109 -39
  48. package/src/theme/colors.ts +65 -1
@@ -0,0 +1,276 @@
1
+ /**
2
+ * deep-research-codebase / copilot
3
+ *
4
+ * Copilot replica of the Claude deep-research-codebase workflow. The Claude
5
+ * version dispatches specialist sub-agents (codebase-locator, codebase-
6
+ * analyzer, etc.) inside a single explorer session via `@"name (agent)"`
7
+ * syntax — a Claude-specific feature. Copilot sessions are bound to a single
8
+ * agent for their entire lifetime, so we keep the SAME graph topology
9
+ * (scout ∥ history → explorer-1..N → aggregator) but drive each explorer
10
+ * through the locate → analyze → patterns → synthesize sequence inline using
11
+ * the default agent's built-in file tools.
12
+ *
13
+ * Topology (identical to Claude version):
14
+ *
15
+ * ┌─→ codebase-scout
16
+ * parent ─┤
17
+ * └─→ research-history
18
+ * │
19
+ * ▼
20
+ * ┌──────────────────────────────────────────────────┐
21
+ * │ explorer-1 explorer-2 ... explorer-N │ (Promise.all)
22
+ * └──────────────────────────────────────────────────┘
23
+ * │
24
+ * ▼
25
+ * aggregator
26
+ *
27
+ * Copilot-specific concerns baked in:
28
+ *
29
+ * • F10 — every `sendAndWait` passes an explicit 30-minute timeout. The SDK
30
+ * default is 60 seconds; a timeout THROWS and aborts the entire stage.
31
+ * Explorers can easily exceed 60s on large partitions.
32
+ *
33
+ * • F5 — every `ctx.stage()` call is a FRESH session with no memory of prior
34
+ * stages. We forward the scout overview, history overview, and partition
35
+ * assignment explicitly into each explorer's first prompt. The aggregator
36
+ * gets the same plus the explorer scratch file paths.
37
+ *
38
+ * • F9 — `s.save()` receives `SessionEvent[]` via `s.session.getMessages()`
39
+ * (Copilot's correct shape). Passing anything else breaks downstream
40
+ * `transcript()` reads.
41
+ *
42
+ * • F6 — every prompt explicitly requires trailing prose AFTER any tool
43
+ * call, so `transcript()` is never empty. A Copilot turn whose final
44
+ * message is a tool call produces an empty assistant.message terminator
45
+ * (F1); trailing prose is our insurance.
46
+ */
47
+
48
+ import { defineWorkflow } from "../../../index.ts";
49
+ import { mkdir } from "node:fs/promises";
50
+ import path from "node:path";
51
+
52
+ import {
53
+ getCodebaseRoot,
54
+ partitionUnits,
55
+ scoutCodebase,
56
+ } from "../helpers/scout.ts";
57
+ import {
58
+ calculateExplorerCount,
59
+ explainHeuristic,
60
+ } from "../helpers/heuristic.ts";
61
+ import {
62
+ buildAggregatorPrompt,
63
+ buildExplorerPromptGeneric,
64
+ buildHistoryPromptGeneric,
65
+ buildScoutPrompt,
66
+ slugifyPrompt,
67
+ } from "../helpers/prompts.ts";
68
+
69
+ // ── Timeouts ────────────────────────────────────────────────────────────────
70
+ // Every sendAndWait call passes one of these explicitly — never relying on
71
+ // the 60-second default (F10). Pick generously; a hung session still surfaces
72
+ // as a clear error rather than silently breaking downstream stages.
73
+ const SCOUT_TIMEOUT_MS = 15 * 60 * 1000; // 15 min — short orientation call
74
+ const HISTORY_TIMEOUT_MS = 20 * 60 * 1000; // 20 min — reads research/ docs
75
+ const EXPLORER_TIMEOUT_MS = 45 * 60 * 1000; // 45 min — multi-step locate/analyze
76
+ const AGGREGATOR_TIMEOUT_MS = 45 * 60 * 1000; // 45 min — reads N explorer reports
77
+
78
+ export default defineWorkflow<"copilot">({
79
+ name: "deep-research-codebase",
80
+ description:
81
+ "Deterministic deep codebase research: scout → LOC-driven parallel explorers → aggregator",
82
+ })
83
+ .run(async (ctx) => {
84
+ // Free-form workflows receive their positional prompt under
85
+ // `inputs.prompt`; destructure once so every stage below can close
86
+ // over a bare `prompt` string without re-reaching into ctx.inputs.
87
+ const prompt = ctx.inputs.prompt ?? "";
88
+ const root = getCodebaseRoot();
89
+ const startedAt = new Date();
90
+ const isoDate = startedAt.toISOString().slice(0, 10);
91
+ const slug = slugifyPrompt(prompt);
92
+
93
+ // ── Stages 1a + 1b: codebase-scout ∥ research-history ──────────────────
94
+ const [scout, history] = await Promise.all([
95
+ ctx.stage(
96
+ {
97
+ name: "codebase-scout",
98
+ description: "Map codebase, count LOC, partition for parallel explorers",
99
+ },
100
+ {},
101
+ {},
102
+ async (s) => {
103
+ // 1. Deterministic scouting (pure TypeScript — no LLM).
104
+ const data = scoutCodebase(root);
105
+ if (data.units.length === 0) {
106
+ throw new Error(
107
+ `deep-research-codebase: scout found no source files under ${root}. ` +
108
+ `Run from inside a code repository or check the CODE_EXTENSIONS list.`,
109
+ );
110
+ }
111
+
112
+ // 2. Heuristic decides explorer count (capped by available units).
113
+ const targetCount = calculateExplorerCount(data.totalLoc);
114
+ const partitions = partitionUnits(data.units, targetCount);
115
+ const actualCount = partitions.length;
116
+
117
+ // 3. Scratch directory for explorer outputs (timestamped to avoid
118
+ // collisions across runs).
119
+ const scratchDir = path.join(
120
+ root,
121
+ "research",
122
+ "docs",
123
+ `.deep-research-${startedAt.getTime()}`,
124
+ );
125
+ await mkdir(scratchDir, { recursive: true });
126
+
127
+ // 4. Short LLM call: architectural orientation for downstream
128
+ // explorers. The prompt forbids the agent from answering the
129
+ // research question — its only job here is to orient.
130
+ await s.session.sendAndWait(
131
+ {
132
+ prompt: buildScoutPrompt({
133
+ question: prompt,
134
+ tree: data.tree,
135
+ totalLoc: data.totalLoc,
136
+ totalFiles: data.totalFiles,
137
+ explorerCount: actualCount,
138
+ partitionPreview: partitions,
139
+ }),
140
+ },
141
+ SCOUT_TIMEOUT_MS,
142
+ );
143
+ // F9: Copilot takes SessionEvent[], not a session ID.
144
+ s.save(await s.session.getMessages());
145
+
146
+ return {
147
+ root,
148
+ totalLoc: data.totalLoc,
149
+ totalFiles: data.totalFiles,
150
+ tree: data.tree,
151
+ partitions,
152
+ explorerCount: actualCount,
153
+ scratchDir,
154
+ heuristicNote: explainHeuristic(data.totalLoc, actualCount),
155
+ };
156
+ },
157
+ ),
158
+ ctx.stage(
159
+ {
160
+ name: "research-history",
161
+ description: "Surface prior research from research/ directory",
162
+ },
163
+ {},
164
+ {},
165
+ async (s) => {
166
+ // The generic history prompt drives a single default-agent session
167
+ // through locate → analyze → synthesize inline, instead of Claude's
168
+ // sub-agent dispatch.
169
+ await s.session.sendAndWait(
170
+ { prompt: buildHistoryPromptGeneric({ question: prompt, root }) },
171
+ HISTORY_TIMEOUT_MS,
172
+ );
173
+ s.save(await s.session.getMessages());
174
+ },
175
+ ),
176
+ ]);
177
+
178
+ const {
179
+ partitions,
180
+ explorerCount,
181
+ scratchDir,
182
+ totalLoc,
183
+ totalFiles,
184
+ } = scout.result;
185
+
186
+ // Pull both scout transcripts ONCE at the workflow level so every
187
+ // explorer + the aggregator can embed them in their prompts (F5). Both
188
+ // stages have completed here (we're past Promise.all), so these reads
189
+ // are safe (F13).
190
+ const scoutOverview = (await ctx.transcript(scout)).content;
191
+ const historyOverview = (await ctx.transcript(history)).content;
192
+
193
+ // ── Stage 2: parallel explorers ────────────────────────────────────────
194
+ // Each explorer is a separate Copilot session, running concurrently via
195
+ // Promise.all. Because the session is fresh (F5), every piece of context
196
+ // it needs — question, architectural orientation, historical context,
197
+ // partition assignment, scratch path — is injected into the first prompt
198
+ // via buildExplorerPromptGeneric.
199
+ const explorerHandles = await Promise.all(
200
+ partitions.map((partition, idx) => {
201
+ const i = idx + 1;
202
+ const scratchPath = path.join(scratchDir, `explorer-${i}.md`);
203
+ return ctx.stage(
204
+ {
205
+ name: `explorer-${i}`,
206
+ description: `Explore ${partition
207
+ .map((u) => u.path)
208
+ .join(", ")} (${partition.reduce((s, u) => s + u.fileCount, 0)} files)`,
209
+ },
210
+ {},
211
+ {},
212
+ async (s) => {
213
+ await s.session.sendAndWait(
214
+ {
215
+ prompt: buildExplorerPromptGeneric({
216
+ question: prompt,
217
+ index: i,
218
+ total: explorerCount,
219
+ partition,
220
+ scoutOverview,
221
+ historyOverview,
222
+ scratchPath,
223
+ root,
224
+ }),
225
+ },
226
+ EXPLORER_TIMEOUT_MS,
227
+ );
228
+ s.save(await s.session.getMessages());
229
+
230
+ // Returning structured metadata lets the aggregator stage reach
231
+ // each explorer's scratch path without re-parsing transcripts.
232
+ return { index: i, scratchPath, partition };
233
+ },
234
+ );
235
+ }),
236
+ );
237
+
238
+ // ── Stage 3: aggregator ────────────────────────────────────────────────
239
+ // Reads explorer findings via FILE PATHS (filesystem-context skill) to
240
+ // keep the aggregator's own context lean — we deliberately do NOT inline
241
+ // N transcripts into the prompt. Token cost stays roughly constant in N.
242
+ const finalPath = path.join(
243
+ root,
244
+ "research",
245
+ "docs",
246
+ `${isoDate}-${slug}.md`,
247
+ );
248
+
249
+ await ctx.stage(
250
+ {
251
+ name: "aggregator",
252
+ description: "Synthesize explorer findings + history into final research doc",
253
+ },
254
+ {},
255
+ {},
256
+ async (s) => {
257
+ await s.session.sendAndWait(
258
+ {
259
+ prompt: buildAggregatorPrompt({
260
+ question: prompt,
261
+ totalLoc,
262
+ totalFiles,
263
+ explorerCount,
264
+ explorerFiles: explorerHandles.map((h) => h.result),
265
+ finalPath,
266
+ scoutOverview,
267
+ historyOverview,
268
+ }),
269
+ },
270
+ AGGREGATOR_TIMEOUT_MS,
271
+ );
272
+ s.save(await s.session.getMessages());
273
+ },
274
+ );
275
+ })
276
+ .compile();
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Determine how many parallel explorer sub-agents to spawn for the
3
+ * deep-research-codebase workflow, based on lines of code in the codebase.
4
+ *
5
+ * The heuristic balances coverage against coordination overhead:
6
+ * - Too few explorers leave parts of the codebase under-investigated.
7
+ * - Too many explorers flood the aggregator with redundant findings,
8
+ * burn tokens on coordination, and exhaust tmux/process budgets.
9
+ *
10
+ * Tier choices were anchored to the rough sizes of common project shapes:
11
+ *
12
+ * < 5,000 LOC → 2 explorers scripts, single-purpose tools
13
+ * < 25,000 LOC → 3 explorers small libraries, CLI utilities
14
+ * < 100,000 LOC → 5 explorers medium applications
15
+ * < 500,000 LOC → 7 explorers large applications, small monorepos
16
+ * <2,000,000 LOC → 9 explorers large monorepos
17
+ * ≥2,000,000 LOC → 12 explorers massive monorepos (hard cap)
18
+ *
19
+ * The hard cap of 12 prevents runaway parallelism: each explorer is a
20
+ * Claude tmux pane plus an LLM session, so the cost grows linearly in
21
+ * tokens, processes, and walltime as well as in aggregator context.
22
+ */
23
+ export function calculateExplorerCount(loc: number): number {
24
+ if (!Number.isFinite(loc) || loc <= 0) return 2;
25
+ if (loc < 5_000) return 2;
26
+ if (loc < 25_000) return 3;
27
+ if (loc < 100_000) return 5;
28
+ if (loc < 500_000) return 7;
29
+ if (loc < 2_000_000) return 9;
30
+ return 12;
31
+ }
32
+
33
+ /** Human-readable rationale for the heuristic decision — surfaced in logs/prompts. */
34
+ export function explainHeuristic(loc: number, count: number): string {
35
+ return `Codebase: ${loc.toLocaleString()} LOC → spawning ${count} parallel explorer${
36
+ count === 1 ? "" : "s"
37
+ }.`;
38
+ }