@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.
- package/README.md +110 -11
- package/dist/{chunk-mn870nrv.js → chunk-xkxndz5g.js} +213 -154
- package/dist/sdk/components/workflow-picker-panel.d.ts +120 -0
- package/dist/sdk/define-workflow.d.ts +1 -1
- package/dist/sdk/index.js +1 -1
- package/dist/sdk/runtime/discovery.d.ts +57 -3
- package/dist/sdk/runtime/executor.d.ts +15 -2
- package/dist/sdk/runtime/tmux.d.ts +9 -0
- package/dist/sdk/types.d.ts +63 -4
- package/dist/sdk/workflows/builtin/deep-research-codebase/claude/index.d.ts +61 -0
- package/dist/sdk/workflows/builtin/deep-research-codebase/copilot/index.d.ts +48 -0
- package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/heuristic.d.ts +25 -0
- package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/prompts.d.ts +91 -0
- package/dist/sdk/workflows/builtin/deep-research-codebase/helpers/scout.d.ts +56 -0
- package/dist/sdk/workflows/builtin/deep-research-codebase/opencode/index.d.ts +48 -0
- package/dist/sdk/workflows/builtin/ralph/claude/index.js +6 -5
- package/dist/sdk/workflows/builtin/ralph/copilot/index.js +6 -5
- package/dist/sdk/workflows/builtin/ralph/opencode/index.js +6 -5
- package/dist/sdk/workflows/index.d.ts +4 -4
- package/dist/sdk/workflows/index.js +7 -1
- package/package.json +1 -1
- package/src/cli.ts +25 -3
- package/src/commands/cli/chat/index.ts +5 -5
- package/src/commands/cli/init/index.ts +79 -77
- package/src/commands/cli/workflow-command.test.ts +757 -0
- package/src/commands/cli/workflow.test.ts +310 -0
- package/src/commands/cli/workflow.ts +445 -105
- package/src/sdk/components/workflow-picker-panel.tsx +1462 -0
- package/src/sdk/define-workflow.test.ts +101 -0
- package/src/sdk/define-workflow.ts +62 -2
- package/src/sdk/runtime/discovery.ts +111 -8
- package/src/sdk/runtime/executor.ts +89 -32
- package/src/sdk/runtime/tmux.conf +55 -0
- package/src/sdk/runtime/tmux.ts +34 -10
- package/src/sdk/types.ts +67 -4
- package/src/sdk/workflows/builtin/deep-research-codebase/claude/index.ts +294 -0
- package/src/sdk/workflows/builtin/deep-research-codebase/copilot/index.ts +276 -0
- package/src/sdk/workflows/builtin/deep-research-codebase/helpers/heuristic.ts +38 -0
- package/src/sdk/workflows/builtin/deep-research-codebase/helpers/prompts.ts +816 -0
- package/src/sdk/workflows/builtin/deep-research-codebase/helpers/scout.ts +334 -0
- package/src/sdk/workflows/builtin/deep-research-codebase/opencode/index.ts +284 -0
- package/src/sdk/workflows/builtin/ralph/claude/index.ts +8 -4
- package/src/sdk/workflows/builtin/ralph/copilot/index.ts +10 -4
- package/src/sdk/workflows/builtin/ralph/opencode/index.ts +8 -4
- package/src/sdk/workflows/index.ts +9 -1
- package/src/services/system/auto-sync.ts +1 -1
- package/src/services/system/install-ui.ts +109 -39
- 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.
|
|
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
|
@@ -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.
|
|
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
|
|
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
|
-
/**
|
|
23
|
-
|
|
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
|
package/dist/sdk/types.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
138
|
-
|
|
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
|
-
/**
|
|
175
|
-
|
|
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[][];
|