@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,120 @@
1
+ /** @jsxImportSource @opentui/react */
2
+ /**
3
+ * WorkflowPickerPanel — interactive TUI for `atomic workflow -a <agent>`.
4
+ *
5
+ * Telescope-style fuzzy picker with a two-phase flow:
6
+ *
7
+ * 1. PICK — filter workflows scoped to the current agent, navigate with
8
+ * ↑/↓ or ⌃j/⌃k, press ↵ to lock in a selection.
9
+ * 2. PROMPT — fill the workflow's declared input schema (one field per
10
+ * declared `WorkflowInput`). Free-form workflows fall back to
11
+ * a single `prompt` text field.
12
+ *
13
+ * Pressing ⌃s in the prompt phase validates required fields and opens a
14
+ * CONFIRM modal that shows the fully-composed shell command before
15
+ * submission. y/↵ confirms, n/esc cancels back to the form.
16
+ *
17
+ * Lifecycle:
18
+ *
19
+ * const panel = await WorkflowPickerPanel.create({ agent, workflows });
20
+ * const result = await panel.waitForSelection();
21
+ * panel.destroy();
22
+ * if (result) await executeWorkflow({ ... });
23
+ *
24
+ * `waitForSelection()` resolves with `null` if the user exits without
25
+ * committing (esc in PICK phase, or ⌃c anywhere) and with a
26
+ * `{ workflow, inputs }` record if they confirm the run.
27
+ */
28
+ import { type CliRenderer } from "@opentui/core";
29
+ import { type TerminalTheme } from "../runtime/theme.js";
30
+ import type { AgentType, WorkflowInput } from "../types.js";
31
+ import type { WorkflowWithMetadata } from "../runtime/discovery.js";
32
+ export interface PickerTheme {
33
+ background: string;
34
+ backgroundPanel: string;
35
+ backgroundElement: string;
36
+ surface: string;
37
+ text: string;
38
+ textMuted: string;
39
+ textDim: string;
40
+ primary: string;
41
+ success: string;
42
+ error: string;
43
+ warning: string;
44
+ info: string;
45
+ mauve: string;
46
+ border: string;
47
+ borderActive: string;
48
+ }
49
+ export declare function buildPickerTheme(base: TerminalTheme): PickerTheme;
50
+ type Source = "local" | "global" | "builtin";
51
+ /** The payload the picker resolves with on successful submission. */
52
+ export interface WorkflowPickerResult {
53
+ /** The workflow the user committed to running. */
54
+ workflow: WorkflowWithMetadata;
55
+ /** Populated form values, one per declared input (or { prompt } for free-form). */
56
+ inputs: Record<string, string>;
57
+ }
58
+ /**
59
+ * Subsequence fuzzy match — Telescope-style. Returns a score (lower =
60
+ * better) or null for no match. Adjacent matches are rewarded; jumps over
61
+ * non-matching characters are penalized proportionally to the gap.
62
+ */
63
+ export declare function fuzzyMatch(query: string, target: string): number | null;
64
+ interface ListEntry {
65
+ workflow: WorkflowWithMetadata;
66
+ section: Source;
67
+ }
68
+ interface ListRow {
69
+ kind: "section" | "entry";
70
+ source?: Source;
71
+ entry?: ListEntry;
72
+ }
73
+ export declare function buildEntries(query: string, workflows: WorkflowWithMetadata[]): ListEntry[];
74
+ export declare function buildRows(entries: ListEntry[], query: string): ListRow[];
75
+ export declare function isFieldValid(field: WorkflowInput, value: string): boolean;
76
+ interface PickerAppProps {
77
+ theme: PickerTheme;
78
+ agent: AgentType;
79
+ workflows: WorkflowWithMetadata[];
80
+ onSubmit: (result: WorkflowPickerResult) => void;
81
+ onCancel: () => void;
82
+ }
83
+ export declare function WorkflowPicker({ theme, agent, workflows, onSubmit, onCancel, }: PickerAppProps): import("react").ReactNode;
84
+ export interface WorkflowPickerPanelOptions {
85
+ agent: AgentType;
86
+ /** Pre-loaded workflows to show. Must already be filtered to `agent`. */
87
+ workflows: WorkflowWithMetadata[];
88
+ }
89
+ /**
90
+ * Imperative shell around the React picker tree — mirrors the
91
+ * {@link OrchestratorPanel} lifecycle so both panels can be used
92
+ * interchangeably by the CLI command layer.
93
+ */
94
+ export declare class WorkflowPickerPanel {
95
+ private renderer;
96
+ private root;
97
+ private destroyed;
98
+ private resolveSelection;
99
+ private selectionPromise;
100
+ private constructor();
101
+ /**
102
+ * Create a new WorkflowPickerPanel. Initialises a CLI renderer and
103
+ * mounts the interactive tree. The caller should `await
104
+ * waitForSelection()` and then call `destroy()` regardless of outcome.
105
+ */
106
+ static create(options: WorkflowPickerPanelOptions): Promise<WorkflowPickerPanel>;
107
+ /** Create with an externally-provided renderer (e.g. a test renderer). */
108
+ static createWithRenderer(renderer: CliRenderer, options: WorkflowPickerPanelOptions): WorkflowPickerPanel;
109
+ /**
110
+ * Resolve with the user's selection once they confirm, or `null` if
111
+ * they exit the picker without committing. Idempotent — subsequent
112
+ * calls return the same promise.
113
+ */
114
+ waitForSelection(): Promise<WorkflowPickerResult | null>;
115
+ /** Tear down the CLI renderer. Idempotent. */
116
+ destroy(): void;
117
+ private handleSubmit;
118
+ private handleCancel;
119
+ }
120
+ export {};
@@ -57,7 +57,7 @@ export declare class WorkflowBuilder<A extends AgentType = AgentType> {
57
57
  * {},
58
58
  * async (s) => {
59
59
  * // s.client: CopilotClient, s.session: CopilotSession
60
- * await s.session.sendAndWait({ prompt: s.userPrompt });
60
+ * await s.session.sendAndWait({ prompt: s.inputs.prompt ?? "" });
61
61
  * s.save(await s.session.getMessages());
62
62
  * },
63
63
  * );
package/dist/sdk/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  discoverWorkflows,
6
6
  executeWorkflow,
7
7
  findWorkflow
8
- } from "../chunk-mn870nrv.js";
8
+ } from "../chunk-xkxndz5g.js";
9
9
 
10
10
  // src/sdk/errors.ts
11
11
  class MissingDependencyError extends Error {
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * Project-local workflows take precedence over global ones with the same name.
9
9
  */
10
- import type { AgentType } from "../types.js";
10
+ import type { AgentType, WorkflowInput } from "../types.js";
11
11
  export interface DiscoveredWorkflow {
12
12
  name: string;
13
13
  agent: AgentType;
@@ -22,10 +22,64 @@ export declare const AGENTS: AgentType[];
22
22
  export declare const WORKFLOWS_GITIGNORE: string;
23
23
  /**
24
24
  * Discover all available workflows from built-in, global, and local sources.
25
- * Optionally filter by agent. Precedence: local > global > builtin.
25
+ * Optionally filter by agent.
26
+ *
27
+ * **Merge precedence:** `builtin > local > global`.
28
+ *
29
+ * Builtin names are **strictly reserved** — a user-defined local or
30
+ * global workflow whose name matches any built-in workflow is dropped
31
+ * entirely from discovery. It will not be registered, returned from
32
+ * `findWorkflow`, appear in the interactive picker, or show up in
33
+ * `atomic workflow -l`. This protects SDK-shipped workflows (e.g.
34
+ * `ralph`) from being silently overridden or even visibly "competing
35
+ * with" a user's own definition, which would otherwise be confusing
36
+ * when someone tries to run the canonical version.
37
+ *
38
+ * Reservation is by **name only**, across all agents: if a builtin
39
+ * defines `ralph` for any agent, a local `ralph` for any other agent is
40
+ * also dropped. Local still overrides global for every non-builtin
41
+ * name, so project-scoped customisation of user-scoped workflows
42
+ * continues to work.
43
+ *
44
+ * By default, the result is **merged by precedence** — if a workflow is
45
+ * defined in both local and global sources, only the higher-precedence
46
+ * entry is returned. This is the right shape for `findWorkflow`, which
47
+ * needs the single resolved entry per (name, agent) pair.
48
+ *
49
+ * Pass `{ merge: false }` to get the **unmerged** result — local and
50
+ * global contribute their entries independently, so `--list` can show
51
+ * both a local and a global copy of the same workflow when they coexist
52
+ * on disk. (Builtin reservation still applies in both modes.)
26
53
  */
27
- export declare function discoverWorkflows(projectRoot?: string, agentFilter?: AgentType): Promise<DiscoveredWorkflow[]>;
54
+ export declare function discoverWorkflows(projectRoot?: string, agentFilter?: AgentType, options?: {
55
+ merge?: boolean;
56
+ }): Promise<DiscoveredWorkflow[]>;
28
57
  /**
29
58
  * Find a specific workflow by name and agent.
30
59
  */
31
60
  export declare function findWorkflow(name: string, agent: AgentType, projectRoot?: string): Promise<DiscoveredWorkflow | null>;
61
+ /**
62
+ * A discovered workflow enriched with the metadata the picker needs to
63
+ * render it: the human description and the declared input schema.
64
+ *
65
+ * Populated by {@link loadWorkflowsMetadata}, which runs each discovered
66
+ * workflow through {@link WorkflowLoader.loadWorkflow} and extracts just
67
+ * the display-relevant fields — the full compiled definition is
68
+ * discarded after extraction so re-imports during execution are cheap.
69
+ */
70
+ export interface WorkflowWithMetadata extends DiscoveredWorkflow {
71
+ /** Workflow description, empty string when none was declared. */
72
+ description: string;
73
+ /** Declared input schema, empty array for free-form workflows. */
74
+ inputs: readonly WorkflowInput[];
75
+ }
76
+ /**
77
+ * Load metadata (description + inputs) for a batch of discovered workflows.
78
+ *
79
+ * Workflows that fail to import are **skipped silently** so one broken
80
+ * entry can never prevent the picker from rendering. Callers that need
81
+ * to surface load errors (e.g. `atomic workflow -n broken`) should use
82
+ * {@link WorkflowLoader.loadWorkflow} directly — that path produces
83
+ * structured error reports.
84
+ */
85
+ export declare function loadWorkflowsMetadata(discovered: DiscoveredWorkflow[]): Promise<WorkflowWithMetadata[]>;
@@ -19,8 +19,13 @@ export interface WorkflowRunOptions {
19
19
  definition: WorkflowDefinition;
20
20
  /** Agent type */
21
21
  agent: AgentType;
22
- /** The user's prompt */
23
- prompt: string;
22
+ /**
23
+ * Structured inputs for this run. Free-form workflows model their
24
+ * single positional prompt as `{ prompt: "..." }` so workflow
25
+ * authors can read `ctx.inputs.prompt` uniformly regardless of
26
+ * whether the workflow declares a schema. Empty record is valid.
27
+ */
28
+ inputs?: Record<string, string>;
24
29
  /** Absolute path to the workflow's index.ts file (from discovery) */
25
30
  workflowFile: string;
26
31
  /** Project root (defaults to cwd) */
@@ -41,6 +46,14 @@ export declare function escBash(s: string): string;
41
46
  * variable expansion. Null bytes are stripped for safety.
42
47
  */
43
48
  export declare function escPwsh(s: string): string;
49
+ /**
50
+ * Decode the ATOMIC_WF_INPUTS env var (base64-encoded JSON) into a
51
+ * `Record<string, string>`. Returns an empty record when the variable
52
+ * is missing, malformed, or does not decode to a string-map object —
53
+ * structured inputs are optional, so a corrupt payload must never
54
+ * prevent free-form workflows from running.
55
+ */
56
+ export declare function parseInputsEnv(raw: string | undefined): Record<string, string>;
44
57
  /**
45
58
  * Called by `atomic workflow -n <name> -a <agent> <prompt>`.
46
59
  *
@@ -5,6 +5,9 @@
5
5
  * creating sessions, splitting panes, spawning commands, capturing output,
6
6
  * sending keystrokes, and pane state detection.
7
7
  */
8
+ import type { Subprocess } from "bun";
9
+ /** Dedicated tmux socket name — isolates Atomic sessions from the user's default server. */
10
+ export declare const SOCKET_NAME = "atomic";
8
11
  /**
9
12
  * Resolve the terminal multiplexer binary for the current platform.
10
13
  *
@@ -120,6 +123,12 @@ export declare function sessionExists(sessionName: string): boolean;
120
123
  * Attach to an existing tmux session (takes over the current terminal).
121
124
  */
122
125
  export declare function attachSession(sessionName: string): void;
126
+ /**
127
+ * Spawn an interactive attach-session process.
128
+ * Encapsulates binary resolution, config injection, and socket isolation.
129
+ * Used by all async attach call sites (executor, chat).
130
+ */
131
+ export declare function spawnMuxAttach(sessionName: string): Subprocess;
123
132
  /**
124
133
  * Switch the current tmux client to a different session.
125
134
  * Use this instead of `attachSession` when already inside tmux to avoid
@@ -62,6 +62,40 @@ export type ProviderClient<A extends AgentType> = ClientMap[A];
62
62
  /** The `s.session` type in a stage callback, resolved by agent type. */
63
63
  export type ProviderSession<A extends AgentType> = SessionMap[A];
64
64
  export type { CopilotClient, CopilotClientOptions, CopilotSession, CopilotSessionConfig, OpencodeClient, OpencodeSession, ClaudeClientWrapper, ClaudeSessionWrapper, ClaudeQueryDefaults, };
65
+ /**
66
+ * Supported field types for a workflow's declared inputs.
67
+ *
68
+ * - `"string"` — single-line free-form input (short values, identifiers, paths)
69
+ * - `"text"` — multi-line free-form input (long prose, prompts, specs)
70
+ * - `"enum"` — one of a fixed list of allowed `values`
71
+ */
72
+ export type WorkflowInputType = "string" | "text" | "enum";
73
+ /**
74
+ * A declared input for a workflow. When a workflow provides an `inputs`
75
+ * array, the CLI materialises one `--<name>` flag per input (and the
76
+ * interactive picker renders one field per input) so users can pass
77
+ * structured values rather than a single free-form prompt.
78
+ *
79
+ * Leaving `inputs` unset (or empty) signals that the workflow consumes a
80
+ * single free-form prompt instead — the legacy
81
+ * `atomic workflow -n <name> -a <agent> "prompt"` form.
82
+ */
83
+ export interface WorkflowInput {
84
+ /** Field name — also the CLI flag (`--<name>`) and form field identifier. */
85
+ name: string;
86
+ /** Input kind — see {@link WorkflowInputType}. */
87
+ type: WorkflowInputType;
88
+ /** Whether the field must be non-empty before the workflow can run. */
89
+ required?: boolean;
90
+ /** Short human description shown as the field caption. */
91
+ description?: string;
92
+ /** Placeholder text shown when the field is empty. */
93
+ placeholder?: string;
94
+ /** Default value pre-filled into the field. Enums use this to pick their initial value. */
95
+ default?: string;
96
+ /** Allowed values — required when `type` is `"enum"`. */
97
+ values?: string[];
98
+ }
65
99
  /**
66
100
  * A transcript from a completed session.
67
101
  * Provides both the file path and rendered text content.
@@ -134,8 +168,16 @@ export interface SessionContext<A extends AgentType = AgentType> {
134
168
  client: ProviderClient<A>;
135
169
  /** Provider-specific session (auto-created by runtime) */
136
170
  session: ProviderSession<A>;
137
- /** The original user prompt from the CLI invocation */
138
- userPrompt: string;
171
+ /**
172
+ * Structured inputs for this workflow run. Populated from CLI flags
173
+ * (`--<name>=<value>`) or the interactive picker.
174
+ *
175
+ * Free-form workflows (no declared `inputs` schema) receive their
176
+ * single positional prompt under the `prompt` key — so
177
+ * `s.inputs.prompt` is the canonical way to read the user's prompt
178
+ * regardless of whether the workflow is structured or free-form.
179
+ */
180
+ inputs: Record<string, string>;
139
181
  /** Which agent is running */
140
182
  agent: A;
141
183
  /**
@@ -171,8 +213,16 @@ export interface SessionContext<A extends AgentType = AgentType> {
171
213
  * Does not have session-specific fields (paneId, save, etc.).
172
214
  */
173
215
  export interface WorkflowContext<A extends AgentType = AgentType> {
174
- /** The original user prompt from the CLI invocation */
175
- userPrompt: string;
216
+ /**
217
+ * Structured inputs for this workflow run. Populated from CLI flags
218
+ * (`--<name>=<value>`) or the interactive picker.
219
+ *
220
+ * Free-form workflows (no declared `inputs` schema) receive their
221
+ * single positional prompt under the `prompt` key — so
222
+ * `ctx.inputs.prompt` is the canonical way to read the user's prompt
223
+ * regardless of whether the workflow is structured or free-form.
224
+ */
225
+ inputs: Record<string, string>;
176
226
  /** Which agent is running */
177
227
  agent: A;
178
228
  /**
@@ -201,6 +251,13 @@ export interface WorkflowOptions {
201
251
  name: string;
202
252
  /** Human-readable description */
203
253
  description?: string;
254
+ /**
255
+ * Optional declared inputs. When provided, the CLI materialises one
256
+ * `--<name>` flag per entry and the interactive picker renders one form
257
+ * field per entry. Leave unset to keep the workflow free-form (a single
258
+ * positional prompt argument).
259
+ */
260
+ inputs?: WorkflowInput[];
204
261
  }
205
262
  /**
206
263
  * A compiled workflow definition — the sealed output of defineWorkflow().compile().
@@ -209,6 +266,8 @@ export interface WorkflowDefinition<A extends AgentType = AgentType> {
209
266
  readonly __brand: "WorkflowDefinition";
210
267
  readonly name: string;
211
268
  readonly description: string;
269
+ /** Declared input schema — empty array for free-form workflows. */
270
+ readonly inputs: readonly WorkflowInput[];
212
271
  /** The workflow's entry point. Called by the executor with a WorkflowContext. */
213
272
  readonly run: (ctx: WorkflowContext<A>) => Promise<void>;
214
273
  }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * deep-research-codebase / claude
3
+ *
4
+ * A deterministically-orchestrated, distributed version of the
5
+ * `research-codebase` skill. The research-codebase skill spawns
6
+ * codebase-locator / codebase-analyzer / codebase-pattern-finder /
7
+ * codebase-research-locator / codebase-research-analyzer /
8
+ * codebase-online-researcher sub-agents on the fly via LLM judgment;
9
+ * this workflow spawns the same agents on a deterministic schedule
10
+ * driven by the codebase's lines of code.
11
+ *
12
+ * Topology:
13
+ *
14
+ * ┌─→ codebase-scout
15
+ * parent ─┤
16
+ * └─→ research-history
17
+ * │
18
+ * ▼
19
+ * ┌──────────────────────────────────────────────────┐
20
+ * │ explorer-1 explorer-2 ... explorer-N │ (Promise.all)
21
+ * └──────────────────────────────────────────────────┘
22
+ * │
23
+ * ▼
24
+ * aggregator
25
+ *
26
+ * Stage 1a — codebase-scout
27
+ * Pure-TypeScript: lists files (git ls-files), counts LOC (batched wc -l),
28
+ * renders a depth-bounded ASCII tree, and bin-packs directories into N
29
+ * partitions where N is determined by the LOC heuristic. Then makes one
30
+ * short LLM call to produce an architectural orientation that primes the
31
+ * downstream explorers. Returns structured data via `handle.result` and
32
+ * the agent's prose via `ctx.transcript(handle)`.
33
+ *
34
+ * Stage 1b — research-history (parallel sibling of scout)
35
+ * Dispatches the codebase-research-locator and codebase-research-analyzer
36
+ * sub-agents over the project's existing research/ directory to surface
37
+ * prior decisions, completed investigations, and unresolved questions.
38
+ * Output is consumed via session transcript (≤400 words) and feeds into
39
+ * the aggregator as supplementary context.
40
+ *
41
+ * Stage 2 — explorer-1..N (parallel; depends on scout + history)
42
+ * Each explorer is a coordinator that dispatches specialized sub-agents
43
+ * over its assigned partition (single LOC-balanced slice of the codebase):
44
+ * - codebase-locator → finds relevant files in the partition
45
+ * - codebase-analyzer → documents how the most relevant files work
46
+ * - codebase-pattern-finder → finds existing pattern examples
47
+ * - codebase-online-researcher → (conditional) external library docs
48
+ * The explorer never reads files directly — it orchestrates specialists
49
+ * and writes a synthesized findings document to a known scratch path.
50
+ *
51
+ * Stage 3 — aggregator
52
+ * Reads each explorer's scratch file by path (file-based handoff to keep
53
+ * the aggregator's own context lean — we deliberately do NOT inline N
54
+ * transcripts into the prompt). Folds in the research-history overview
55
+ * as supplementary context. Synthesizes a single research document at
56
+ * research/docs/YYYY-MM-DD-<slug>.md.
57
+ *
58
+ * Context-engineering decisions are documented at each stage below.
59
+ */
60
+ declare const _default: import("../../../index.ts").WorkflowDefinition<"claude">;
61
+ export default _default;
@@ -0,0 +1,48 @@
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
+ declare const _default: import("../../../index.ts").WorkflowDefinition<"copilot">;
48
+ export default _default;
@@ -0,0 +1,25 @@
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 declare function calculateExplorerCount(loc: number): number;
24
+ /** Human-readable rationale for the heuristic decision — surfaced in logs/prompts. */
25
+ export declare function explainHeuristic(loc: number, count: number): string;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Prompt builders for the deep-research-codebase workflow.
3
+ *
4
+ * Context-engineering principles applied throughout:
5
+ * - Position-aware placement: the research question is repeated at the
6
+ * TOP and BOTTOM of every prompt (recall is 85-95% at the edges and
7
+ * drops to 76-82% in the middle — see context-fundamentals).
8
+ * - Informativity over exhaustiveness: each explorer prompt contains
9
+ * only that explorer's partition, never the full file list.
10
+ * - Explicit trailing commentary (F6): every prompt asks the agent to
11
+ * produce a short text summary AFTER any tool/file output, so the
12
+ * transcript is not empty when downstream stages read it.
13
+ * - File-based handoff (filesystem-context skill): explorer findings
14
+ * are written to disk and the aggregator reads them by path, instead
15
+ * of inlining N transcripts into the aggregator's prompt.
16
+ * - Documentarian role: every prompt explicitly forbids critique or
17
+ * improvement suggestions; we are recording what exists.
18
+ */
19
+ import type { PartitionUnit } from "./scout.js";
20
+ /** Slugify the user's prompt for use in the final research filename. */
21
+ export declare function slugifyPrompt(prompt: string): string;
22
+ export declare function buildScoutPrompt(opts: {
23
+ question: string;
24
+ tree: string;
25
+ totalLoc: number;
26
+ totalFiles: number;
27
+ explorerCount: number;
28
+ partitionPreview: PartitionUnit[][];
29
+ }): string;
30
+ export declare function buildExplorerPrompt(opts: {
31
+ question: string;
32
+ index: number;
33
+ total: number;
34
+ partition: PartitionUnit[];
35
+ scoutOverview: string;
36
+ scratchPath: string;
37
+ root: string;
38
+ }): string;
39
+ /**
40
+ * The research-history scout dispatches specialized sub-agents to surface
41
+ * historical context from the project's `research/` directory:
42
+ * - codebase-research-locator → finds prior research docs about the topic
43
+ * - codebase-research-analyzer → extracts key insights from the most
44
+ * relevant docs
45
+ *
46
+ * Output is consumed via session transcript (not file write) — kept short
47
+ * (≤400 words) so the aggregator can embed it cheaply.
48
+ */
49
+ export declare function buildHistoryPrompt(opts: {
50
+ question: string;
51
+ root: string;
52
+ }): string;
53
+ /**
54
+ * Generic explorer prompt (Copilot / OpenCode). Drives a single default-agent
55
+ * session through the locate → analyze → patterns → synthesize sequence using
56
+ * built-in tools directly, instead of Claude's sub-agent dispatch.
57
+ */
58
+ export declare function buildExplorerPromptGeneric(opts: {
59
+ question: string;
60
+ index: number;
61
+ total: number;
62
+ partition: PartitionUnit[];
63
+ scoutOverview: string;
64
+ historyOverview: string;
65
+ scratchPath: string;
66
+ root: string;
67
+ }): string;
68
+ /**
69
+ * Generic research-history prompt (Copilot / OpenCode). A single default-agent
70
+ * session searches the project's research/ directory using its own file tools,
71
+ * instead of dispatching Claude's codebase-research-locator / analyzer
72
+ * sub-agents.
73
+ */
74
+ export declare function buildHistoryPromptGeneric(opts: {
75
+ question: string;
76
+ root: string;
77
+ }): string;
78
+ export declare function buildAggregatorPrompt(opts: {
79
+ question: string;
80
+ totalLoc: number;
81
+ totalFiles: number;
82
+ explorerCount: number;
83
+ explorerFiles: {
84
+ index: number;
85
+ scratchPath: string;
86
+ partition: PartitionUnit[];
87
+ }[];
88
+ finalPath: string;
89
+ scoutOverview: string;
90
+ historyOverview: string;
91
+ }): string;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Codebase scout: deterministic helpers for the deep-research-codebase workflow.
3
+ *
4
+ * Responsibilities:
5
+ * 1. Discover the codebase root (git toplevel, falling back to cwd).
6
+ * 2. List all source files, respecting .gitignore when in a git repo.
7
+ * 3. Count lines of code per file using batched `wc -l`.
8
+ * 4. Render a compact directory tree (depth-bounded) for prompt context.
9
+ * 5. Build "partition units" by aggregating LOC at depth-1, then drilling
10
+ * down on any unit that is too large to live in a single explorer.
11
+ * 6. Bin-pack partition units into N balanced groups (largest-first).
12
+ *
13
+ * Everything here is pure TypeScript + child_process — no LLM calls.
14
+ */
15
+ /** Per-file LOC + path. */
16
+ export type FileStats = {
17
+ path: string;
18
+ loc: number;
19
+ };
20
+ /**
21
+ * A "partition unit" is the atomic chunk of work that gets bin-packed into
22
+ * an explorer. It is always one directory (possibly drilled down to depth 2)
23
+ * with all of the code files that live anywhere underneath it.
24
+ */
25
+ export type PartitionUnit = {
26
+ /** Repo-relative path, e.g. "src/cli" or "packages/foo/src". */
27
+ path: string;
28
+ loc: number;
29
+ fileCount: number;
30
+ /** Repo-relative file paths inside this unit (full recursive listing). */
31
+ files: string[];
32
+ };
33
+ export type CodebaseScout = {
34
+ /** Absolute path to the repository root. */
35
+ root: string;
36
+ totalLoc: number;
37
+ totalFiles: number;
38
+ /** Compact rendered directory tree (depth-bounded) for prompt context. */
39
+ tree: string;
40
+ /** Partition units, sorted by LOC descending. */
41
+ units: PartitionUnit[];
42
+ };
43
+ /** Resolve the project root. Prefers `git rev-parse --show-toplevel`. */
44
+ export declare function getCodebaseRoot(): string;
45
+ /**
46
+ * Run the full scout: list files, count LOC, render tree, build partition units.
47
+ */
48
+ export declare function scoutCodebase(root: string): CodebaseScout;
49
+ /**
50
+ * Bin-pack partition units into `count` balanced groups. Greedy
51
+ * largest-first: assign each unit to the currently-lightest bin.
52
+ *
53
+ * If there are fewer units than requested bins, the result has exactly
54
+ * `units.length` non-empty bins (we never return empty bins).
55
+ */
56
+ export declare function partitionUnits(units: PartitionUnit[], count: number): PartitionUnit[][];