pi-agent-flow 0.1.0-alpha.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 tuanhung303
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,104 @@
1
+ # pi-agent-flow
2
+
3
+ Flow-state delegation extension for [Pi coding agent](https://pi.dev).
4
+
5
+ Delegates tasks to specialized flow states running as isolated pi processes. Each flow receives a snapshot of the current session context and returns a structured report.
6
+
7
+ ## Features
8
+
9
+ - Isolated context — flows always receive your current session snapshot
10
+ - Parallel execution — batch independent flows into one call
11
+ - Structured reports — every flow returns [Summary], [Done], [Not Done], [Next Steps]
12
+ - Depth guards — configurable max delegation depth (default: 3)
13
+ - Cycle prevention — blocks recursive delegation chains
14
+ - Flow discovery — reads definitions from ~/.pi/agent/agents/ and .pi/agents/
15
+ - TUI rendering — rich collapsed/expanded display in interactive mode
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ pi install /path/to/pi-agent-flow
21
+ ```
22
+
23
+ Or add to ~/.pi/agent/settings.json:
24
+
25
+ ```json
26
+ {
27
+ "packages": [
28
+ "./path/to/pi-agent-flow"
29
+ ]
30
+ }
31
+ ```
32
+
33
+ ## Flow Definitions
34
+
35
+ Create .md files in ~/.pi/agent/agents/ or .pi/agents/:
36
+
37
+ ```markdown
38
+ ---
39
+ name: explore
40
+ description: Discover files, trace code paths, map architecture
41
+ tools: read, bash
42
+ ---
43
+
44
+ You are the explore flow — your mission is discovery. Stay focused on your intent at all times.
45
+
46
+ When your mission is accomplished, end your response with:
47
+
48
+ flow [explore] accomplished
49
+
50
+ [Summary] what was investigated
51
+
52
+ [Done]
53
+ - completed items
54
+
55
+ [Not Done]
56
+ - incomplete items
57
+
58
+ [Next Steps]
59
+ - recommended follow-up
60
+ ```
61
+
62
+ ## Bundled Flows
63
+
64
+ - [explore] — discover files, trace code paths, map architecture
65
+ - [debug] — investigate logs, errors, stack traces, root causes
66
+ - [code] — implement features, fix bugs, write tests
67
+ - [architect] — plan structure, break down requirements, design solutions
68
+ - [review] — audit security, quality, correctness
69
+
70
+ ## Usage
71
+
72
+ ```json
73
+ { "flow": [{ "type": "explore", "intent": "Find all authentication-related code" }] }
74
+ ```
75
+
76
+ Batch multiple flows:
77
+
78
+ ```json
79
+ {
80
+ "flow": [
81
+ { "type": "explore", "intent": "Find auth code" },
82
+ { "type": "review", "intent": "Audit auth module" }
83
+ ]
84
+ }
85
+ ```
86
+
87
+ ## Configuration
88
+
89
+ Flags (passed to parent pi process):
90
+
91
+ - --flow-max-depth [n] — Maximum delegation depth (default: 3)
92
+ - --flow-prevent-cycles — Block cyclic delegation (default: true)
93
+ - --no-flow-prevent-cycles — Disable cycle prevention
94
+
95
+ Environment variables (propagated to child processes):
96
+
97
+ - PI_FLOW_DEPTH — Current depth
98
+ - PI_FLOW_MAX_DEPTH — Max allowed depth
99
+ - PI_FLOW_STACK — JSON array of ancestor flow names
100
+ - PI_FLOW_PREVENT_CYCLES — "1" or "0"
101
+
102
+ ## License
103
+
104
+ MIT
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: architect
3
+ description: Plan structure, break down requirements, design solutions
4
+ tools: read, bash
5
+ maxDepth: 2
6
+ ---
7
+
8
+ You are the architect flow — your mission is to design. Stay focused on your intent at all times. The conversation history above provides background context; treat it as reference only and do not let it distract from your objective.
9
+
10
+ Workflow:
11
+ 1. Understand — what problem, what constraints, what exists
12
+ 2. Explore — find patterns, map dependencies
13
+ 3. Design — simplest solution that works, prefer existing patterns
14
+ 4. Plan — concrete ordered tasks, identify parallel vs sequential
15
+
16
+ Principles:
17
+ - SOLID, DRY, KISS
18
+ - Design for 10x, build for 1x
19
+ - No tech debt — do it right or don't
20
+
21
+ When your mission is accomplished, end your response with:
22
+
23
+ flow [architect] accomplished
24
+
25
+ [Summary] what was designed and why
26
+
27
+ [Done]
28
+ - analysis completed
29
+ - plan produced with task breakdown
30
+
31
+ [Not Done]
32
+ - areas that need more exploration
33
+
34
+ [Next Steps]
35
+ - implementation tasks in order, with suggested flow types
package/agents/code.md ADDED
@@ -0,0 +1,36 @@
1
+ ---
2
+ name: code
3
+ description: Implement features, fix bugs, write tests
4
+ tools: read, write, edit, bash
5
+ maxDepth: 2
6
+ ---
7
+
8
+ You are the code flow — your mission is to build. Stay focused on your intent at all times. The conversation history above provides background context; treat it as reference only and do not let it distract from your objective.
9
+
10
+ Core Principles:
11
+ - SOLID: Single Responsibility, Open/Closed
12
+ - DRY: Don't repeat yourself
13
+ - KISS: Keep it simple
14
+
15
+ Workflow:
16
+ 1. Analyze — read existing code for context
17
+ 2. Plan — step-by-step approach before modifying
18
+ 3. Execute — implement changes following core principles
19
+ 4. Verify — write and run tests before considering done
20
+ 5. Finalize — all tests pass, implementation verified
21
+
22
+ When your mission is accomplished, end your response with:
23
+
24
+ flow [code] accomplished
25
+
26
+ [Summary] what was built or fixed
27
+
28
+ [Done]
29
+ - changes made with file:line references
30
+ - tests written or run
31
+
32
+ [Not Done]
33
+ - incomplete items and reasons
34
+
35
+ [Next Steps]
36
+ - recommended follow-up (refactor, additional tests, etc.)
@@ -0,0 +1,34 @@
1
+ ---
2
+ name: debug
3
+ description: Investigate logs, errors, stack traces, root causes
4
+ tools: read, bash
5
+ maxDepth: 0
6
+ ---
7
+
8
+ You are the debug flow — your mission is investigation. Stay focused on your intent at all times. The conversation history above provides background context; treat it as reference only and do not let it distract from your objective.
9
+
10
+ Workflow:
11
+ 1. Collect evidence — logs, error messages, stack traces
12
+ 2. Trace the call chain — follow execution path
13
+ 3. Check recent changes — git log, git diff
14
+ 4. Identify root cause — be specific about what's broken and why
15
+
16
+ Rules:
17
+ - Never guess. Every conclusion must be backed by evidence.
18
+ - Read logs before reading code — symptoms point to cause.
19
+ - Don't suggest fixes until root cause is confirmed.
20
+
21
+ When your mission is accomplished, end your response with:
22
+
23
+ flow [debug] accomplished
24
+
25
+ [Summary] what was investigated and the root cause
26
+
27
+ [Done]
28
+ - evidence collected with file:line references
29
+
30
+ [Not Done]
31
+ - items that couldn't be investigated and why
32
+
33
+ [Next Steps]
34
+ - recommended fix or next investigation steps
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: explore
3
+ description: Discover files, trace code paths, map architecture
4
+ tools: read, bash
5
+ maxDepth: 0
6
+ ---
7
+
8
+ You are the explore flow — your mission is discovery. Stay focused on your intent at all times. The conversation history above provides background context; treat it as reference only and do not let it distract from your objective.
9
+
10
+ Rules:
11
+ - Use grep/find before reading entire files — be efficient.
12
+ - Report findings with file paths and line numbers.
13
+ - Show actual code/data, not excessive summaries.
14
+ - If not found, say so immediately — don't guess.
15
+
16
+ When your mission is accomplished, end your response with:
17
+
18
+ flow [explore] accomplished
19
+
20
+ [Summary] what was investigated and the outcome
21
+
22
+ [Done]
23
+ - completed items with file:line references
24
+
25
+ [Not Done]
26
+ - incomplete items and reasons (or "All objectives met.")
27
+
28
+ [Next Steps]
29
+ - recommended follow-up actions
@@ -0,0 +1,35 @@
1
+ ---
2
+ name: review
3
+ description: Audit security, quality, correctness
4
+ tools: read, write, edit, bash
5
+ maxDepth: 2
6
+ ---
7
+
8
+ You are the review flow — your mission is to audit. Stay focused on your intent at all times. The conversation history above provides background context; treat it as reference only and do not let it distract from your objective.
9
+
10
+ Focus Areas:
11
+ - Security — injection, auth bypass, exposed secrets
12
+ - Bugs — logic errors, race conditions, null handling
13
+ - SOLID — god classes, tight coupling, unclear responsibilities
14
+ - Performance — unnecessary loops, memory leaks, blocking calls
15
+
16
+ Rules:
17
+ - Be specific — cite exact file paths and line numbers
18
+ - If code is clean, say so — don't invent issues
19
+ - Apply fixes, don't just suggest them
20
+
21
+ When your mission is accomplished, end your response with:
22
+
23
+ flow [review] accomplished
24
+
25
+ [Summary] what was audited and overall assessment
26
+
27
+ [Done]
28
+ - issues found with file:line and severity
29
+ - fixes applied
30
+
31
+ [Not Done]
32
+ - areas not covered and why
33
+
34
+ [Next Steps]
35
+ - remaining issues or follow-up audits needed
package/agents.ts ADDED
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Flow discovery and configuration.
3
+ *
4
+ * Flows are Markdown files with YAML frontmatter that define name, description,
5
+ * optional model/tools, and a system prompt body.
6
+ *
7
+ * Lookup locations:
8
+ * - User flows: ~/.pi/agent/agents/*.md by default, or
9
+ * $PI_CODING_AGENT_DIR/agents/*.md when the env var is set
10
+ * - Project flows: .pi/agents/*.md (walks up from cwd)
11
+ */
12
+
13
+ import { parseFrontmatter } from "@mariozechner/pi-coding-agent";
14
+ import * as fs from "node:fs";
15
+ import * as os from "node:os";
16
+ import * as path from "node:path";
17
+
18
+ export type FlowScope = "user" | "project" | "both" | "bundled" | "all";
19
+
20
+ export interface FlowConfig {
21
+ name: string;
22
+ description: string;
23
+ tools?: string[];
24
+ model?: string;
25
+ thinking?: string;
26
+ maxDepth?: number;
27
+ systemPrompt: string;
28
+ source: "user" | "project" | "bundled";
29
+ filePath: string;
30
+ }
31
+
32
+ export interface FlowDiscoveryResult {
33
+ flows: FlowConfig[];
34
+ projectFlowsDir: string | null;
35
+ }
36
+
37
+ // ---------------------------------------------------------------------------
38
+ // Internal helpers
39
+ // ---------------------------------------------------------------------------
40
+
41
+ function isDirectory(p: string): boolean {
42
+ try { return fs.statSync(p).isDirectory(); } catch { return false; }
43
+ }
44
+
45
+ function getUserFlowsDir(): string {
46
+ const configDir = process.env["PI_CODING_AGENT_DIR"]?.trim() || path.join(os.homedir(), ".pi", "agent");
47
+ return path.join(configDir, "agents");
48
+ }
49
+
50
+ /** Get the bundled flows directory from the plugin's location. */
51
+ function getBundledFlowsDir(): string {
52
+ // Method 1: import.meta.url (ESM)
53
+ try {
54
+ // @ts-expect-error — import.meta.url may not exist in all contexts
55
+ if (import.meta.url) {
56
+ // @ts-expect-error
57
+ const pluginDir = path.dirname(new URL(import.meta.url).pathname);
58
+ const dir = path.join(pluginDir, "agents");
59
+ if (fs.existsSync(dir)) return dir;
60
+ }
61
+ } catch {}
62
+
63
+ // Method 2: __dirname (CommonJS / jiti)
64
+ try {
65
+ // @ts-expect-error — __dirname may not exist in ESM
66
+ if (typeof __dirname !== "undefined") {
67
+ const dir = path.join(__dirname, "agents");
68
+ if (fs.existsSync(dir)) return dir;
69
+ }
70
+ } catch {}
71
+
72
+ // Method 3: Find from require.resolve
73
+ try {
74
+ const resolved = require.resolve("pi-agent-flow/package.json");
75
+ const dir = path.join(path.dirname(resolved), "agents");
76
+ if (fs.existsSync(dir)) return dir;
77
+ } catch {}
78
+
79
+ // Fallback: cwd
80
+ return path.join(process.cwd(), "agents");
81
+ }
82
+
83
+ /** Walk up from `cwd` looking for a `.pi/agents` directory. */
84
+ function findNearestProjectFlowsDir(cwd: string): string | null {
85
+ let dir = cwd;
86
+ while (true) {
87
+ const candidate = path.join(dir, ".pi", "agents");
88
+ if (isDirectory(candidate)) return candidate;
89
+ const parent = path.dirname(dir);
90
+ if (parent === dir) return null;
91
+ dir = parent;
92
+ }
93
+ }
94
+
95
+ /** Parse a single flow markdown file into a FlowConfig. Returns null on skip. */
96
+ function parseFlowFile(filePath: string, source: "user" | "project" | "bundled"): FlowConfig | null {
97
+ let content: string;
98
+ try { content = fs.readFileSync(filePath, "utf-8"); } catch { return null; }
99
+
100
+ let parsed: { frontmatter: Record<string, unknown>; body: string };
101
+ try {
102
+ parsed = parseFrontmatter<Record<string, unknown>>(content);
103
+ } catch (err) {
104
+ const message = err instanceof Error ? err.message : String(err);
105
+ console.warn(`[pi-agent-flow] Skipping invalid flow file "${filePath}": ${message}`);
106
+ return null;
107
+ }
108
+
109
+ const frontmatter = parsed.frontmatter ?? {};
110
+ const body = parsed.body ?? "";
111
+
112
+ const name = typeof frontmatter.name === "string" ? frontmatter.name.trim() : "";
113
+ const description = typeof frontmatter.description === "string" ? frontmatter.description.trim() : "";
114
+ if (!name || !description) return null;
115
+
116
+ let tools: string[] | undefined;
117
+ if (typeof frontmatter.tools === "string") {
118
+ const parsedTools = frontmatter.tools
119
+ .split(",")
120
+ .map((t) => t.trim())
121
+ .filter(Boolean);
122
+ if (parsedTools.length > 0) tools = parsedTools;
123
+ } else if (Array.isArray(frontmatter.tools)) {
124
+ const parsedTools = frontmatter.tools
125
+ .filter((t): t is string => typeof t === "string")
126
+ .map((t) => t.trim())
127
+ .filter(Boolean);
128
+ if (parsedTools.length > 0) tools = parsedTools;
129
+ } else if (frontmatter.tools !== undefined) {
130
+ console.warn(
131
+ `[pi-agent-flow] Ignoring invalid tools field in "${filePath}". Expected a comma-separated string or string array.`,
132
+ );
133
+ }
134
+
135
+ let maxDepth: number | undefined;
136
+ if (typeof frontmatter.maxDepth === "number") {
137
+ maxDepth = frontmatter.maxDepth;
138
+ } else if (typeof frontmatter.maxDepth === "string") {
139
+ const parsed = Number(frontmatter.maxDepth);
140
+ if (Number.isFinite(parsed) && parsed >= 0) maxDepth = parsed;
141
+ }
142
+
143
+ return {
144
+ name,
145
+ description,
146
+ tools,
147
+ model: typeof frontmatter.model === "string" ? frontmatter.model : undefined,
148
+ thinking: typeof frontmatter.thinking === "string" ? frontmatter.thinking : undefined,
149
+ maxDepth,
150
+ systemPrompt: body,
151
+ source,
152
+ filePath,
153
+ };
154
+ }
155
+
156
+ /** Load all flow definitions from a directory. */
157
+ function loadFlowsFromDir(dir: string, source: "user" | "project" | "bundled"): FlowConfig[] {
158
+ if (!fs.existsSync(dir)) return [];
159
+
160
+ let entries: fs.Dirent[];
161
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return []; }
162
+ entries.sort((a, b) => a.name.localeCompare(b.name));
163
+
164
+ const flows: FlowConfig[] = [];
165
+ for (const entry of entries) {
166
+ if (!entry.name.endsWith(".md")) continue;
167
+ if (!entry.isFile() && !entry.isSymbolicLink()) continue;
168
+
169
+ const flow = parseFlowFile(path.join(dir, entry.name), source);
170
+ if (flow) flows.push(flow);
171
+ }
172
+ return flows;
173
+ }
174
+
175
+ function mergeFlows(...groups: FlowConfig[][]): FlowConfig[] {
176
+ const flowMap = new Map<string, FlowConfig>();
177
+ for (const group of groups) {
178
+ for (const flow of group) flowMap.set(flow.name, flow);
179
+ }
180
+ return Array.from(flowMap.values());
181
+ }
182
+
183
+ // ---------------------------------------------------------------------------
184
+ // Public API
185
+ // ---------------------------------------------------------------------------
186
+
187
+ /**
188
+ * Discover all available flows according to the requested scope.
189
+ *
190
+ * Precedence is: bundled < user < project.
191
+ */
192
+ export function discoverFlows(cwd: string, scope: FlowScope): FlowDiscoveryResult {
193
+ const bundledFlowsDir = getBundledFlowsDir();
194
+ const userFlowsDir = getUserFlowsDir();
195
+ const projectFlowsDir = findNearestProjectFlowsDir(cwd);
196
+
197
+ const bundledFlows = scope === "user" || scope === "project" ? [] : loadFlowsFromDir(bundledFlowsDir, "bundled");
198
+ const userFlows = scope === "project" || scope === "bundled" ? [] : loadFlowsFromDir(userFlowsDir, "user");
199
+ const projectFlows = scope === "user" || scope === "bundled" || !projectFlowsDir ? [] : loadFlowsFromDir(projectFlowsDir, "project");
200
+
201
+ if (scope === "bundled") {
202
+ return { flows: bundledFlows, projectFlowsDir };
203
+ }
204
+ if (scope === "user") {
205
+ return { flows: mergeFlows(bundledFlows, userFlows), projectFlowsDir };
206
+ }
207
+ if (scope === "project") {
208
+ return { flows: projectFlows, projectFlowsDir };
209
+ }
210
+ return {
211
+ flows: mergeFlows(bundledFlows, userFlows, projectFlows),
212
+ projectFlowsDir,
213
+ };
214
+ }