@quintinshaw/pi-dynamic-workflows 1.0.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 +159 -0
- package/dist/adversarial-review.d.ts +20 -0
- package/dist/adversarial-review.js +87 -0
- package/dist/agent.d.ts +29 -0
- package/dist/agent.js +90 -0
- package/dist/auto-workflow.d.ts +26 -0
- package/dist/auto-workflow.js +121 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.js +17 -0
- package/dist/deep-research.d.ts +22 -0
- package/dist/deep-research.js +110 -0
- package/dist/display.d.ts +62 -0
- package/dist/display.js +163 -0
- package/dist/errors.d.ts +41 -0
- package/dist/errors.js +63 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +15 -0
- package/dist/logger.d.ts +21 -0
- package/dist/logger.js +67 -0
- package/dist/model-routing.d.ts +33 -0
- package/dist/model-routing.js +57 -0
- package/dist/run-persistence.d.ts +53 -0
- package/dist/run-persistence.js +78 -0
- package/dist/structured-output.d.ts +19 -0
- package/dist/structured-output.js +30 -0
- package/dist/workflow-manager.d.ts +74 -0
- package/dist/workflow-manager.js +241 -0
- package/dist/workflow-saved.d.ts +35 -0
- package/dist/workflow-saved.js +91 -0
- package/dist/workflow-tool.d.ts +22 -0
- package/dist/workflow-tool.js +216 -0
- package/dist/workflow.d.ts +75 -0
- package/dist/workflow.js +364 -0
- package/extensions/workflow.ts +14 -0
- package/package.json +70 -0
- package/src/adversarial-review.ts +107 -0
- package/src/agent.ts +135 -0
- package/src/auto-workflow.ts +146 -0
- package/src/config.ts +24 -0
- package/src/deep-research.ts +128 -0
- package/src/display.ts +236 -0
- package/src/errors.ts +85 -0
- package/src/index.ts +55 -0
- package/src/logger.ts +89 -0
- package/src/model-routing.ts +80 -0
- package/src/run-persistence.ts +132 -0
- package/src/structured-output.ts +47 -0
- package/src/workflow-manager.ts +294 -0
- package/src/workflow-saved.ts +131 -0
- package/src/workflow-tool.ts +254 -0
- package/src/workflow.ts +492 -0
package/README.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# pi-dynamic-workflows
|
|
2
|
+
|
|
3
|
+
> Claude-Code-style dynamic workflows for [Pi](https://github.com/earendil-works/pi).
|
|
4
|
+
|
|
5
|
+
A Pi extension that adds a `workflow` tool. Instead of one assistant doing everything sequentially, the model writes a small JavaScript script that fans out the work across many isolated subagents, then synthesizes the results.
|
|
6
|
+
|
|
7
|
+
Great for codebase audits, multi-perspective review, large refactors, and fan-out research. Inspired by Anthropic's [dynamic workflows in Claude Code](https://claude.com/blog/introducing-dynamic-workflows-in-claude-code).
|
|
8
|
+
|
|
9
|
+
Fork of [Michaelliv/pi-dynamic-workflows](https://github.com/Michaelliv/pi-dynamic-workflows), updated for `@earendil-works/*` packages with a subagent settings-inheritance fix.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pi install @quintinshaw/pi-dynamic-workflows
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Then `/reload` in Pi. The extension registers a `workflow` tool and activates it on session start.
|
|
18
|
+
|
|
19
|
+
<details>
|
|
20
|
+
<summary>From source (for development)</summary>
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
git clone git@github.com:QuintinShaw/pi-dynamic-workflows.git
|
|
24
|
+
pi install /path/to/pi-dynamic-workflows
|
|
25
|
+
```
|
|
26
|
+
</details>
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
Ask Pi for a workflow in plain language:
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
Run a workflow to inspect this repository and summarize the main modules.
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The model writes a workflow script and calls the `workflow` tool. Live progress streams inline:
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
◆ Workflow: inspect_project (3/3 done · 12,480 tokens)
|
|
40
|
+
✓ Scan 1/1
|
|
41
|
+
#1 ✓ repo inventory
|
|
42
|
+
✓ Analyze 2/2
|
|
43
|
+
#2 ✓ source modules
|
|
44
|
+
#3 ✓ final summary
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Press `Esc` to cancel a running run; active subagents are aborted and surfaced as skipped.
|
|
48
|
+
|
|
49
|
+
## Workflow script shape
|
|
50
|
+
|
|
51
|
+
A workflow is plain JavaScript. The first statement must export literal metadata:
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
export const meta = {
|
|
55
|
+
name: 'inspect_project',
|
|
56
|
+
description: 'Inspect a repository and summarize the main modules',
|
|
57
|
+
phases: [{ title: 'Scan' }, { title: 'Analyze' }],
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
phase('Scan')
|
|
61
|
+
const inventory = await agent('Inspect the repository structure.', { label: 'repo inventory' })
|
|
62
|
+
|
|
63
|
+
phase('Analyze')
|
|
64
|
+
const summary = await agent('Summarize the main modules:\n' + inventory, { label: 'module summary' })
|
|
65
|
+
|
|
66
|
+
return { inventory, summary }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Globals
|
|
70
|
+
|
|
71
|
+
| Global | Description |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| `agent(prompt, opts)` | Spawn an isolated subagent. Returns its final text, or a validated object with `opts.schema`. |
|
|
74
|
+
| `parallel(thunks)` | Run an array of `() => agent(...)` thunks concurrently. Results returned in input order. |
|
|
75
|
+
| `pipeline(items, ...stages)` | Fan items out through sequential stages. Each stage receives `(prev, original, index)`. |
|
|
76
|
+
| `phase(title)` | Mark the current phase for the live progress view. |
|
|
77
|
+
| `log(message)` | Append a workflow-level log line. |
|
|
78
|
+
| `args` | Optional JSON value passed via the tool's `args` parameter. |
|
|
79
|
+
| `budget` | `{ total, spent(), remaining() }` token-budget tracker. |
|
|
80
|
+
| `cwd`, `process.cwd()` | Working directory for subagents. |
|
|
81
|
+
|
|
82
|
+
### Agent options
|
|
83
|
+
|
|
84
|
+
| Option | Type | Description |
|
|
85
|
+
| --- | --- | --- |
|
|
86
|
+
| `label` | string | Human-readable label for progress display |
|
|
87
|
+
| `phase` | string | Override the current phase for this agent |
|
|
88
|
+
| `schema` | object | JSON Schema for structured output |
|
|
89
|
+
| `timeoutMs` | number | Override the default 5-minute agent timeout |
|
|
90
|
+
|
|
91
|
+
### Structured output
|
|
92
|
+
|
|
93
|
+
Pass a JSON Schema via `opts.schema` and the subagent returns a validated object:
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
const finding = await agent('Find security-sensitive files.', {
|
|
97
|
+
label: 'security scan',
|
|
98
|
+
schema: {
|
|
99
|
+
type: 'object',
|
|
100
|
+
properties: {
|
|
101
|
+
paths: { type: 'array', items: { type: 'string' } },
|
|
102
|
+
reason: { type: 'string' },
|
|
103
|
+
},
|
|
104
|
+
required: ['paths', 'reason'],
|
|
105
|
+
},
|
|
106
|
+
})
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Backed by a Pi `structured_output` tool with `terminate: true`, so the subagent ends on that call.
|
|
110
|
+
|
|
111
|
+
### Determinism rules
|
|
112
|
+
|
|
113
|
+
Scripts run inside a Node `vm` sandbox. Intentionally unavailable: `Date.now()`, `new Date()`, `Math.random()`, `require`/`import`/`fs`/network, and (inside `meta`) spreads, computed keys, template interpolation, and function calls. This keeps `meta` parseable and runs reproducible.
|
|
114
|
+
|
|
115
|
+
## What works today
|
|
116
|
+
|
|
117
|
+
- **Core runtime** — `agent` / `parallel` / `pipeline` / `phase` / `log` / `budget` in a sandboxed script
|
|
118
|
+
- **Structured output** — JSON-Schema-validated subagent results
|
|
119
|
+
- **Safety limits** — 1000-agent cap (`maxAgents`), per-agent timeout (`agentTimeoutMs`), recoverable-vs-fatal error classification
|
|
120
|
+
- **Live progress + token display**, `Esc` to abort
|
|
121
|
+
- **Log persistence** to `.pi/workflows/runs/`
|
|
122
|
+
|
|
123
|
+
## Roadmap
|
|
124
|
+
|
|
125
|
+
Tracked toward closer parity with Claude Code dynamic workflows:
|
|
126
|
+
|
|
127
|
+
- **Real per-agent / per-phase model routing** (`opts.model`, `meta.phases[].model`)
|
|
128
|
+
- **Real token accounting** via the SDK's session stats (today's display uses an estimate)
|
|
129
|
+
- **Command surface** — `/workflows` (list / status / stop) and reachable background runs
|
|
130
|
+
- **Resume** — journaled results, replay the unchanged prefix, run the rest live
|
|
131
|
+
- **Worktree isolation** for parallel edits, and **bundled `/deep-research`**
|
|
132
|
+
- **Saved workflows** as `/<name>` slash commands
|
|
133
|
+
|
|
134
|
+
## How it works
|
|
135
|
+
|
|
136
|
+
```text
|
|
137
|
+
user prompt
|
|
138
|
+
→ Pi model writes a workflow script
|
|
139
|
+
→ workflow tool parses + runs it in a vm sandbox
|
|
140
|
+
→ script calls agent() / parallel() / pipeline()
|
|
141
|
+
→ each agent() spawns a fresh in-memory Pi subagent session
|
|
142
|
+
→ snapshots stream back as compact progress
|
|
143
|
+
→ final structured result returns to the parent assistant
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Subagents run in fresh in-memory Pi sessions with the standard coding tools (read, bash, edit, write, grep, find, ls), so they work exactly like a normal Pi turn.
|
|
147
|
+
|
|
148
|
+
## Development
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
npm install
|
|
152
|
+
npm test # biome check + tsc + unit tests
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Parser unit tests live in `tests/workflow-parser.test.ts`.
|
|
156
|
+
|
|
157
|
+
## License
|
|
158
|
+
|
|
159
|
+
MIT
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adversarial review mode for workflows.
|
|
3
|
+
* Agents cross-check each other's findings for higher quality results.
|
|
4
|
+
*/
|
|
5
|
+
export interface AdversarialReviewConfig {
|
|
6
|
+
/** Number of independent reviewers per finding. */
|
|
7
|
+
reviewerCount: number;
|
|
8
|
+
/** Whether to filter out findings that don't survive cross-checking. */
|
|
9
|
+
filterContested: boolean;
|
|
10
|
+
/** Minimum agreement threshold (0-1). */
|
|
11
|
+
agreementThreshold: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Generate an adversarial review workflow script.
|
|
15
|
+
*/
|
|
16
|
+
export declare function generateAdversarialReviewWorkflow(taskDescription: string, config?: Partial<AdversarialReviewConfig>): string;
|
|
17
|
+
/**
|
|
18
|
+
* Generate a multi-perspective analysis workflow.
|
|
19
|
+
*/
|
|
20
|
+
export declare function generateMultiPerspectiveWorkflow(topic: string, perspectives: string[]): string;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adversarial review mode for workflows.
|
|
3
|
+
* Agents cross-check each other's findings for higher quality results.
|
|
4
|
+
*/
|
|
5
|
+
const DEFAULT_CONFIG = {
|
|
6
|
+
reviewerCount: 2,
|
|
7
|
+
filterContested: true,
|
|
8
|
+
agreementThreshold: 0.5,
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Generate an adversarial review workflow script.
|
|
12
|
+
*/
|
|
13
|
+
export function generateAdversarialReviewWorkflow(taskDescription, config = {}) {
|
|
14
|
+
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
15
|
+
return `export const meta = {
|
|
16
|
+
name: 'adversarial_review',
|
|
17
|
+
description: 'Adversarial review with ${cfg.reviewerCount} independent reviewers',
|
|
18
|
+
phases: [
|
|
19
|
+
{ title: 'Initial Investigation' },
|
|
20
|
+
{ title: 'Independent Review' },
|
|
21
|
+
{ title: 'Cross-Check' },
|
|
22
|
+
{ title: 'Consensus' },
|
|
23
|
+
],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
phase('Initial Investigation');
|
|
27
|
+
const findings = await agent(
|
|
28
|
+
'Investigate and document findings for: ${taskDescription.replace(/'/g, "\\'").slice(0, 80)}',
|
|
29
|
+
{ label: 'investigator' }
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
phase('Independent Review');
|
|
33
|
+
const reviews = await parallel(Array.from({ length: ${cfg.reviewerCount} }, (_, i) => () =>
|
|
34
|
+
agent(
|
|
35
|
+
'Independently review these findings. Agree or disagree with each point, and explain why:\\n\\n' + findings,
|
|
36
|
+
{ label: 'reviewer-' + (i + 1) }
|
|
37
|
+
)
|
|
38
|
+
));
|
|
39
|
+
|
|
40
|
+
phase('Cross-Check');
|
|
41
|
+
const crossCheck = await agent(
|
|
42
|
+
'Compare these independent reviews and identify points of agreement and disagreement:\\n' +
|
|
43
|
+
'Reviews: ' + JSON.stringify(reviews) + '\\n' +
|
|
44
|
+
'Original findings: ' + findings,
|
|
45
|
+
{ label: 'cross-checker' }
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
phase('Consensus');
|
|
49
|
+
const consensus = await agent(
|
|
50
|
+
'Based on the cross-check, produce a final verified report. Only include findings that survived independent review:\\n' + crossCheck,
|
|
51
|
+
{ label: 'consensus-builder' }
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
return { findings, reviews, crossCheck, consensus };`;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Generate a multi-perspective analysis workflow.
|
|
58
|
+
*/
|
|
59
|
+
export function generateMultiPerspectiveWorkflow(topic, perspectives) {
|
|
60
|
+
const perspectiveAgents = perspectives
|
|
61
|
+
.map((p, _i) => ` () => agent('Analyze from ${p} perspective: ' + topic, { label: '${p.toLowerCase().replace(/\\s+/g, "-")}' }),`)
|
|
62
|
+
.join("\n");
|
|
63
|
+
return `export const meta = {
|
|
64
|
+
name: 'multi_perspective_analysis',
|
|
65
|
+
description: 'Analyze from ${perspectives.length} different perspectives',
|
|
66
|
+
phases: [
|
|
67
|
+
{ title: 'Perspective Analysis' },
|
|
68
|
+
{ title: 'Synthesis' },
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
phase('Perspective Analysis');
|
|
73
|
+
const topic = '${topic.replace(/'/g, "\\'")}';
|
|
74
|
+
const analyses = await parallel([
|
|
75
|
+
${perspectiveAgents}
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
phase('Synthesis');
|
|
79
|
+
const synthesis = await agent(
|
|
80
|
+
'Synthesize these different perspectives into a balanced analysis:\\n' +
|
|
81
|
+
'Analyses: ' + JSON.stringify(analyses) + '\\n' +
|
|
82
|
+
'Topic: ' + topic,
|
|
83
|
+
{ label: 'synthesizer' }
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
return { analyses, synthesis };`;
|
|
87
|
+
}
|
package/dist/agent.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type CreateAgentSessionOptions, type ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { Static, TSchema } from "typebox";
|
|
3
|
+
export interface WorkflowAgentOptions {
|
|
4
|
+
cwd?: string;
|
|
5
|
+
/** Extra tools available to the subagent in addition to the structured output tool. */
|
|
6
|
+
tools?: ToolDefinition[];
|
|
7
|
+
/** Override any createAgentSession option (model, authStorage, resourceLoader, etc.). */
|
|
8
|
+
session?: Partial<CreateAgentSessionOptions>;
|
|
9
|
+
/** Extra system guidance prepended to every subagent task. */
|
|
10
|
+
instructions?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface AgentRunOptions<TSchemaDef extends TSchema | undefined = undefined> {
|
|
13
|
+
label?: string;
|
|
14
|
+
schema?: TSchemaDef;
|
|
15
|
+
tools?: ToolDefinition[];
|
|
16
|
+
instructions?: string;
|
|
17
|
+
signal?: AbortSignal;
|
|
18
|
+
}
|
|
19
|
+
export type AgentRunResult<TSchemaDef extends TSchema | undefined> = TSchemaDef extends TSchema ? Static<TSchemaDef> : string;
|
|
20
|
+
export declare class WorkflowAgent {
|
|
21
|
+
private readonly cwd;
|
|
22
|
+
private readonly baseTools;
|
|
23
|
+
private readonly sessionOptions;
|
|
24
|
+
private readonly instructions?;
|
|
25
|
+
constructor(options?: WorkflowAgentOptions);
|
|
26
|
+
run<TSchemaDef extends TSchema | undefined = undefined>(prompt: string, options?: AgentRunOptions<TSchemaDef>): Promise<AgentRunResult<TSchemaDef>>;
|
|
27
|
+
private buildPrompt;
|
|
28
|
+
private lastAssistantText;
|
|
29
|
+
}
|
package/dist/agent.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createAgentSession, createCodingTools, getAgentDir, SessionManager, SettingsManager, } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { createStructuredOutputTool } from "./structured-output.js";
|
|
3
|
+
export class WorkflowAgent {
|
|
4
|
+
cwd;
|
|
5
|
+
baseTools;
|
|
6
|
+
sessionOptions;
|
|
7
|
+
instructions;
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
this.cwd = options.cwd ?? process.cwd();
|
|
10
|
+
this.baseTools = options.tools ?? createCodingTools(this.cwd);
|
|
11
|
+
this.sessionOptions = options.session ?? {};
|
|
12
|
+
this.instructions = options.instructions;
|
|
13
|
+
}
|
|
14
|
+
async run(prompt, options = {}) {
|
|
15
|
+
const capture = { called: false, value: undefined };
|
|
16
|
+
const customTools = [...this.baseTools, ...(options.tools ?? [])];
|
|
17
|
+
if (options.schema) {
|
|
18
|
+
customTools.push(createStructuredOutputTool({ schema: options.schema, capture }));
|
|
19
|
+
}
|
|
20
|
+
const agentDir = getAgentDir();
|
|
21
|
+
const { session } = await createAgentSession({
|
|
22
|
+
cwd: this.cwd,
|
|
23
|
+
agentDir,
|
|
24
|
+
sessionManager: SessionManager.inMemory(),
|
|
25
|
+
// Use real SettingsManager to inherit user's default provider/model settings.
|
|
26
|
+
// SettingsManager.inMemory() doesn't load ~/.pi/settings.json, so subagents
|
|
27
|
+
// would fall back to the first available model (e.g. openai-codex) which may
|
|
28
|
+
// not have valid auth, causing silent empty responses.
|
|
29
|
+
settingsManager: SettingsManager.create(this.cwd, agentDir),
|
|
30
|
+
customTools,
|
|
31
|
+
...this.sessionOptions,
|
|
32
|
+
});
|
|
33
|
+
let removeAbortListener;
|
|
34
|
+
try {
|
|
35
|
+
if (options.signal?.aborted)
|
|
36
|
+
throw new Error("Subagent was aborted");
|
|
37
|
+
if (options.signal) {
|
|
38
|
+
const onAbort = () => void session.abort();
|
|
39
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
40
|
+
removeAbortListener = () => options.signal?.removeEventListener("abort", onAbort);
|
|
41
|
+
}
|
|
42
|
+
await session.prompt(this.buildPrompt(prompt, options, Boolean(options.schema)));
|
|
43
|
+
if (options.signal?.aborted)
|
|
44
|
+
throw new Error("Subagent was aborted");
|
|
45
|
+
if (options.schema) {
|
|
46
|
+
if (!capture.called) {
|
|
47
|
+
throw new Error("Subagent finished without calling structured_output");
|
|
48
|
+
}
|
|
49
|
+
return capture.value;
|
|
50
|
+
}
|
|
51
|
+
return this.lastAssistantText(session.messages);
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
removeAbortListener?.();
|
|
55
|
+
session.dispose();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
buildPrompt(prompt, options, structured) {
|
|
59
|
+
const parts = [
|
|
60
|
+
this.instructions,
|
|
61
|
+
options.instructions,
|
|
62
|
+
options.label ? `Task label: ${options.label}` : undefined,
|
|
63
|
+
prompt,
|
|
64
|
+
].filter(Boolean);
|
|
65
|
+
if (structured) {
|
|
66
|
+
parts.push([
|
|
67
|
+
"Final output contract:",
|
|
68
|
+
"- Your final action MUST be a structured_output tool call.",
|
|
69
|
+
"- The structured_output arguments are the return value of this subagent.",
|
|
70
|
+
"- Do not emit a prose final answer instead of structured_output.",
|
|
71
|
+
"- If you need to inspect files or run commands first, do so, then call structured_output exactly once.",
|
|
72
|
+
].join("\n"));
|
|
73
|
+
}
|
|
74
|
+
return parts.join("\n\n");
|
|
75
|
+
}
|
|
76
|
+
lastAssistantText(messages) {
|
|
77
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
78
|
+
const message = messages[i];
|
|
79
|
+
if (message?.role !== "assistant" || !Array.isArray(message.content))
|
|
80
|
+
continue;
|
|
81
|
+
const text = message.content
|
|
82
|
+
.filter((part) => part.type === "text")
|
|
83
|
+
.map((part) => part.text)
|
|
84
|
+
.join("");
|
|
85
|
+
if (text.trim())
|
|
86
|
+
return text;
|
|
87
|
+
}
|
|
88
|
+
return "";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-workflow mode (ultracode equivalent).
|
|
3
|
+
* Automatically decides when to use workflows based on task complexity.
|
|
4
|
+
*/
|
|
5
|
+
export interface AutoWorkflowConfig {
|
|
6
|
+
/** Enable auto-workflow mode. */
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
/** Minimum number of subtasks to trigger a workflow. */
|
|
9
|
+
minSubtasks: number;
|
|
10
|
+
/** Keywords that suggest workflow usage. */
|
|
11
|
+
triggerKeywords: string[];
|
|
12
|
+
/** Maximum complexity score before auto-triggering. */
|
|
13
|
+
complexityThreshold: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Analyze a task description and determine if it should use a workflow.
|
|
17
|
+
*/
|
|
18
|
+
export declare function shouldUseWorkflow(taskDescription: string, config?: Partial<AutoWorkflowConfig>): {
|
|
19
|
+
useWorkflow: boolean;
|
|
20
|
+
confidence: number;
|
|
21
|
+
reason: string;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Generate a workflow script suggestion from a task description.
|
|
25
|
+
*/
|
|
26
|
+
export declare function suggestWorkflowScript(taskDescription: string): string;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-workflow mode (ultracode equivalent).
|
|
3
|
+
* Automatically decides when to use workflows based on task complexity.
|
|
4
|
+
*/
|
|
5
|
+
const DEFAULT_CONFIG = {
|
|
6
|
+
enabled: false,
|
|
7
|
+
minSubtasks: 3,
|
|
8
|
+
triggerKeywords: [
|
|
9
|
+
"workflow",
|
|
10
|
+
"parallel",
|
|
11
|
+
"fan-out",
|
|
12
|
+
"audit",
|
|
13
|
+
"migrate",
|
|
14
|
+
"review",
|
|
15
|
+
"research",
|
|
16
|
+
"analyze all",
|
|
17
|
+
"check every",
|
|
18
|
+
"sweep",
|
|
19
|
+
"batch",
|
|
20
|
+
"bulk",
|
|
21
|
+
],
|
|
22
|
+
complexityThreshold: 7,
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Analyze a task description and determine if it should use a workflow.
|
|
26
|
+
*/
|
|
27
|
+
export function shouldUseWorkflow(taskDescription, config = { enabled: true }) {
|
|
28
|
+
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
29
|
+
if (!cfg.enabled) {
|
|
30
|
+
return { useWorkflow: false, confidence: 0, reason: "Auto-workflow disabled" };
|
|
31
|
+
}
|
|
32
|
+
const lower = taskDescription.toLowerCase();
|
|
33
|
+
// Check for explicit workflow keywords
|
|
34
|
+
const keywordMatches = cfg.triggerKeywords.filter((kw) => lower.includes(kw));
|
|
35
|
+
if (keywordMatches.length > 0) {
|
|
36
|
+
return {
|
|
37
|
+
useWorkflow: true,
|
|
38
|
+
confidence: Math.min(0.5 + keywordMatches.length * 0.15, 1),
|
|
39
|
+
reason: `Matched keywords: ${keywordMatches.join(", ")}`,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// Analyze complexity indicators
|
|
43
|
+
const complexityIndicators = [
|
|
44
|
+
{ pattern: /\b(all|every|each|entire|whole)\b/i, weight: 2 },
|
|
45
|
+
{ pattern: /\b(files?|directories|folders?|modules?|components?|endpoints?)\b/i, weight: 1.5 },
|
|
46
|
+
{ pattern: /\b(parallel|concurrent|simultaneously)\b/i, weight: 2 },
|
|
47
|
+
{ pattern: /\b(review|audit|check|verify|validate)\b/i, weight: 1 },
|
|
48
|
+
{ pattern: /\b(migrate|refactor|update|modify|change)\b/i, weight: 1.5 },
|
|
49
|
+
{ pattern: /\b(research|investigate|analyze|compare)\b/i, weight: 1 },
|
|
50
|
+
{ pattern: /\d+\s*(files?|items?|tasks?|components?)/i, weight: 2 },
|
|
51
|
+
{ pattern: /\b(across|throughout|cross-cutting)\b/i, weight: 1.5 },
|
|
52
|
+
];
|
|
53
|
+
let complexityScore = 0;
|
|
54
|
+
for (const indicator of complexityIndicators) {
|
|
55
|
+
if (indicator.pattern.test(taskDescription)) {
|
|
56
|
+
complexityScore += indicator.weight;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Estimate subtask count
|
|
60
|
+
const subtaskIndicators = [
|
|
61
|
+
/\bfirst\b/gi,
|
|
62
|
+
/\bthen\b/gi,
|
|
63
|
+
/\bfinally\b/gi,
|
|
64
|
+
/\bafter\b/gi,
|
|
65
|
+
/\bnext\b/gi,
|
|
66
|
+
/\balso\b/gi,
|
|
67
|
+
/\bstep \d/gi,
|
|
68
|
+
];
|
|
69
|
+
let estimatedSubtasks = 1;
|
|
70
|
+
for (const pattern of subtaskIndicators) {
|
|
71
|
+
const matches = taskDescription.match(pattern);
|
|
72
|
+
if (matches)
|
|
73
|
+
estimatedSubtasks += matches.length;
|
|
74
|
+
}
|
|
75
|
+
if (complexityScore >= cfg.complexityThreshold || estimatedSubtasks >= cfg.minSubtasks) {
|
|
76
|
+
return {
|
|
77
|
+
useWorkflow: true,
|
|
78
|
+
confidence: Math.min(complexityScore / 10, 1),
|
|
79
|
+
reason: `Complexity score: ${complexityScore}, estimated subtasks: ${estimatedSubtasks}`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
useWorkflow: false,
|
|
84
|
+
confidence: 0.3,
|
|
85
|
+
reason: `Below threshold (complexity: ${complexityScore}, subtasks: ${estimatedSubtasks})`,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Generate a workflow script suggestion from a task description.
|
|
90
|
+
*/
|
|
91
|
+
export function suggestWorkflowScript(taskDescription) {
|
|
92
|
+
return `export const meta = {
|
|
93
|
+
name: 'auto_generated',
|
|
94
|
+
description: '${taskDescription.replace(/'/g, "\\'").slice(0, 100)}',
|
|
95
|
+
phases: [
|
|
96
|
+
{ title: 'Analyze' },
|
|
97
|
+
{ title: 'Execute' },
|
|
98
|
+
{ title: 'Verify' },
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
phase('Analyze');
|
|
103
|
+
const analysis = await agent(
|
|
104
|
+
'Analyze this task and break it into subtasks: ${taskDescription.replace(/'/g, "\\'").slice(0, 80)}',
|
|
105
|
+
{ label: 'task analysis' }
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
phase('Execute');
|
|
109
|
+
const results = await parallel([
|
|
110
|
+
() => agent('Execute subtask 1 based on: ' + analysis, { label: 'subtask-1' }),
|
|
111
|
+
// Add more subtasks as needed
|
|
112
|
+
]);
|
|
113
|
+
|
|
114
|
+
phase('Verify');
|
|
115
|
+
const verification = await agent(
|
|
116
|
+
'Verify these results are correct: ' + JSON.stringify(results),
|
|
117
|
+
{ label: 'verification' }
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
return { analysis, results, verification };`;
|
|
121
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration constants for pi-dynamic-workflows.
|
|
3
|
+
*/
|
|
4
|
+
/** Maximum number of agents allowed per workflow run. */
|
|
5
|
+
export declare const MAX_AGENTS_PER_RUN = 1000;
|
|
6
|
+
/** Default timeout for a single agent in milliseconds (5 minutes). */
|
|
7
|
+
export declare const DEFAULT_AGENT_TIMEOUT_MS: number;
|
|
8
|
+
/** Maximum concurrent agents (matches Claude Code limit). */
|
|
9
|
+
export declare const MAX_CONCURRENCY = 16;
|
|
10
|
+
/** Default token budget if none specified. */
|
|
11
|
+
export declare const DEFAULT_TOKEN_BUDGET: null;
|
|
12
|
+
/** Directory for persisting workflow run state. */
|
|
13
|
+
export declare const WORKFLOW_RUNS_DIR = ".pi/workflows/runs";
|
|
14
|
+
/** Directory for saved workflow commands. */
|
|
15
|
+
export declare const WORKFLOW_SAVED_DIR = ".pi/workflows/saved";
|
|
16
|
+
/** User-level saved workflows directory. */
|
|
17
|
+
export declare const USER_WORKFLOW_SAVED_DIR = "~/.pi/workflows/saved";
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration constants for pi-dynamic-workflows.
|
|
3
|
+
*/
|
|
4
|
+
/** Maximum number of agents allowed per workflow run. */
|
|
5
|
+
export const MAX_AGENTS_PER_RUN = 1000;
|
|
6
|
+
/** Default timeout for a single agent in milliseconds (5 minutes). */
|
|
7
|
+
export const DEFAULT_AGENT_TIMEOUT_MS = 5 * 60 * 1000;
|
|
8
|
+
/** Maximum concurrent agents (matches Claude Code limit). */
|
|
9
|
+
export const MAX_CONCURRENCY = 16;
|
|
10
|
+
/** Default token budget if none specified. */
|
|
11
|
+
export const DEFAULT_TOKEN_BUDGET = null;
|
|
12
|
+
/** Directory for persisting workflow run state. */
|
|
13
|
+
export const WORKFLOW_RUNS_DIR = ".pi/workflows/runs";
|
|
14
|
+
/** Directory for saved workflow commands. */
|
|
15
|
+
export const WORKFLOW_SAVED_DIR = ".pi/workflows/saved";
|
|
16
|
+
/** User-level saved workflows directory. */
|
|
17
|
+
export const USER_WORKFLOW_SAVED_DIR = "~/.pi/workflows/saved";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep research workflow.
|
|
3
|
+
* Built-in workflow for comprehensive research across multiple sources.
|
|
4
|
+
*/
|
|
5
|
+
export interface DeepResearchConfig {
|
|
6
|
+
/** Number of search angles to explore. */
|
|
7
|
+
searchAngles: number;
|
|
8
|
+
/** Number of sources to fetch per angle. */
|
|
9
|
+
sourcesPerAngle: number;
|
|
10
|
+
/** Whether to cross-check claims across sources. */
|
|
11
|
+
crossCheck: boolean;
|
|
12
|
+
/** Maximum number of agents to use. */
|
|
13
|
+
maxAgents: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Generate a deep research workflow script.
|
|
17
|
+
*/
|
|
18
|
+
export declare function generateDeepResearchWorkflow(question: string, config?: Partial<DeepResearchConfig>): string;
|
|
19
|
+
/**
|
|
20
|
+
* Generate a codebase audit workflow.
|
|
21
|
+
*/
|
|
22
|
+
export declare function generateCodebaseAuditWorkflow(scope: string, checks: string[]): string;
|