pi-agent-flow 1.4.4 → 1.5.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 CHANGED
@@ -81,8 +81,8 @@ The result is faster, cheaper, and cleaner delegation: the main agent remains un
81
81
  | Flow | Purpose | Tools | Tier |
82
82
  |------|---------|-------|------|
83
83
  | `[scout]` | Discover files, trace code paths, map architecture | `batch`, `bash`, `find`, `grep`, `ls` | `lite` |
84
- | `[debug]` | Investigate logs, errors, stack traces, root causes | `batch`, `bash`, `find`, `grep`, `ls` | `lite` |
85
- | `[build]` | Implement features, fix bugs, write tests, ship | `batch`, `bash`, `find`, `grep`, `ls` | `flash` |
84
+ | `[debug]` | Investigate logs, errors, stack traces, root causes, and update relevant troubleshooting docs | `batch`, `bash`, `find`, `grep`, `ls` | `lite` |
85
+ | `[build]` | Implement features, fix bugs, write tests, update relevant docs, ship | `batch`, `bash`, `find`, `grep`, `ls` | `flash` |
86
86
  | `[craft]` | Plan structure, break down requirements, design solutions | `batch`, `bash`, `find`, `grep`, `ls` | `full` |
87
87
  | `[audit]` | Audit security, quality, correctness; fix issues autonomously | `batch`, `bash`, `find`, `grep`, `ls` | `flash` |
88
88
  | `[ideas]` | Generate ideas and explore possibilities with inherited context | `batch`, `bash` | `full` |
@@ -91,6 +91,8 @@ The result is faster, cheaper, and cleaner delegation: the main agent remains un
91
91
 
92
92
  > **Clean slate:** Set `inheritContext: false` in a custom flow's front-matter so it receives only the intent, ideal for unbiased creative work.
93
93
 
94
+ > **Docs hygiene:** Bundled `build` and `debug` flows are instructed to update relevant documentation after their work when the findings or implementation change developer or operational knowledge. If no docs apply, they should state why in the final report.
95
+
94
96
  ### Session modes
95
97
 
96
98
  Each flow call may set `sessionMode` to choose the child-agent time budget:
package/agents/build.md CHANGED
@@ -17,8 +17,9 @@ During this build flow — your mission is to implement and verify changes. Be a
17
17
  4. Execute — implement changes following core principles.
18
18
  5. Refactor — clean up only if the change is working.
19
19
  6. Verify — run tests and relevant checks before considering the work done.
20
- 7. Shipcommit, push, monitor CI/CD, and fix failures until green when shipping is in scope.
21
- 8. Finalizeconfirm implementation, tests, and CI/CD status.
20
+ 7. Documentupdate relevant docs after the implementation and verification are settled; if no docs apply, state why.
21
+ 8. Shipcommit, push, monitor CI/CD, and fix failures until green when shipping is in scope.
22
+ 9. Finalize — confirm implementation, docs, tests, and CI/CD status.
22
23
 
23
24
  ## Rules
24
25
 
@@ -28,6 +29,7 @@ During this build flow — your mission is to implement and verify changes. Be a
28
29
  - If already on a feature/fix branch, continue on it.
29
30
  - Commit with a clear conventional message such as `feat:`, `fix:`, or `refactor:` when committing is in scope.
30
31
  - Push only after local verification passes when shipping is in scope.
32
+ - Update relevant documentation after finishing the work; if no docs changed, explain why in the final report.
31
33
  - If CI/CD fails, diagnose, fix, commit, push, and repeat until green.
32
34
  - If an unexpected error or trace is needed, recommend [debug] rather than guessing.
33
35
 
package/agents/debug.md CHANGED
@@ -16,6 +16,8 @@ During this debug flow — your mission is to investigate root cause. Be forensi
16
16
  3. Check changes — inspect recent diffs, configuration, dependencies, and environment differences.
17
17
  4. Identify root cause — state exactly what is broken and why.
18
18
  5. Recommend fix — propose the smallest safe correction only after evidence confirms the cause.
19
+ 6. Document — update relevant docs, runbooks, or troubleshooting notes after finishing the investigation; if no docs apply, state why.
20
+ 7. Finalize — confirm root cause, evidence, documentation updates, and recommended next steps.
19
21
 
20
22
  ## Rules
21
23
 
@@ -23,7 +25,8 @@ During this debug flow — your mission is to investigate root cause. Be forensi
23
25
  - Read logs and symptoms before reading broad code areas.
24
26
  - Use `batch` with `o: "read"`, `s: <offset>`, and `l: <limit>` for targeted file reading instead of bash `sed`/`head`/`tail`.
25
27
  - Do not suggest fixes until root cause is confirmed.
26
- - Do not implement changes from this flow unless explicitly requested.
28
+ - Documentation-only updates are required after finishing the work when relevant and safe; if no docs changed, explain why in the final report.
29
+ - Do not implement product-code changes from this flow unless explicitly requested.
27
30
 
28
31
  ## Note
29
32
  Treat this as a clean-slate system rewrite, unless explicitly mentioned in the requirements. Perform a comprehensive migration with zero requirements for backwards compatibility. You must ensure that all residual code, variable names, test suites, and documentation are fully refactored and perfectly aligned with the new architecture.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-agent-flow",
3
- "version": "1.4.4",
3
+ "version": "1.5.0",
4
4
  "description": "Flow-state delegation extension for Pi coding agent.",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
package/src/agents.ts CHANGED
@@ -56,6 +56,18 @@ export function getFlowTier(flowName: string): FlowTier {
56
56
  }
57
57
  }
58
58
 
59
+ /** Return the bundled flow names associated with a given tier. */
60
+ export function getTierFlowNames(tier: FlowTier): string[] {
61
+ switch (tier) {
62
+ case "lite":
63
+ return ["scout", "debug"];
64
+ case "flash":
65
+ return ["build", "audit"];
66
+ case "full":
67
+ return ["ideas", "craft"];
68
+ }
69
+ }
70
+
59
71
  // ---------------------------------------------------------------------------
60
72
  // Internal helpers
61
73
  // ---------------------------------------------------------------------------
package/src/config.ts CHANGED
@@ -9,8 +9,8 @@ import * as fs from "node:fs";
9
9
  import * as os from "node:os";
10
10
  import * as path from "node:path";
11
11
  import { parseAgentSessionMode, type AgentSessionMode } from "./session-mode.js";
12
+ import { type FlowTier } from "./agents.js";
12
13
 
13
- export type FlowTier = "lite" | "flash" | "full";
14
14
 
15
15
  export interface FlowModelTierConfig {
16
16
  primary?: string;
@@ -369,3 +369,23 @@ export function resolveFlowModelCandidates(opts: {
369
369
 
370
370
  return { primary: candidates[0], candidates };
371
371
  }
372
+
373
+ export function formatFlowModelStrategy(modeName: string, strategy: FlowModelStrategy): string {
374
+ const tiers: FlowTier[] = ["lite", "flash", "full"];
375
+ const parts: string[] = [];
376
+ for (const tier of tiers) {
377
+ const config = strategy[tier];
378
+ const hasPrimary = Boolean(config?.primary);
379
+ const hasFailover = config?.failover && config.failover.length > 0;
380
+ let value: string;
381
+ if (hasPrimary) {
382
+ value = config!.primary!;
383
+ } else if (hasFailover) {
384
+ value = `failover: ${config!.failover!.join(", ")}`;
385
+ } else {
386
+ value = "(default)";
387
+ }
388
+ parts.push(`${tier}: ${value}`);
389
+ }
390
+ return `mode: ${modeName} | ${parts.join(" · ")}`;
391
+ }
package/src/index.ts CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  resolveFlowModelCandidates,
18
18
  selectFlowModelStrategy,
19
19
  writeGlobalFlowMode,
20
+ formatFlowModelStrategy,
20
21
  type LoadedFlowModelConfigs,
21
22
  } from "./config.js";
22
23
  import { getInheritedCliArgs } from "./cli-args.js";
@@ -727,8 +728,10 @@ export default function (pi: ExtensionAPI) {
727
728
  );
728
729
  } else {
729
730
  try {
730
- const writeResult = writeGlobalFlowMode(requestedFlowMode);
731
- console.warn(`[pi-agent-flow] Flow mode switched to "${requestedFlowMode}" in ${writeResult.path}.`);
731
+ writeGlobalFlowMode(requestedFlowMode);
732
+ const strategy = loadedFlowModelConfigs.configs[requestedFlowMode] ?? {};
733
+ const strategyDescription = formatFlowModelStrategy(requestedFlowMode, strategy);
734
+ console.warn(strategyDescription);
732
735
  } catch (error) {
733
736
  const message = error instanceof Error ? error.message : String(error);
734
737
  console.warn(`[pi-agent-flow] ${message}`);
@@ -1091,7 +1094,12 @@ flow [type] accomplished
1091
1094
  pi.emit("pi-agent-flow:ready", pluginApi);
1092
1095
  }
1093
1096
 
1094
- // Register cleanup on process exit (once)
1097
+ // Register cleanup on process exit (once).
1098
+ // We intentionally do NOT hook SIGINT/SIGTERM here. A plugin must not
1099
+ // override the host process signal handling; doing so can cut off the
1100
+ // host's terminal-cleanup logic and leave escape sequences or status
1101
+ // lines visible on Ctrl+C. The 'exit' event fires for normal exits,
1102
+ // including when the host handles the signal and calls process.exit().
1095
1103
  if (!(globalThis as any).__pi_agent_flow_shutdown_registered) {
1096
1104
  (globalThis as any).__pi_agent_flow_shutdown_registered = true;
1097
1105
  const emitShutdown = () => {
@@ -1100,8 +1108,6 @@ flow [type] accomplished
1100
1108
  }
1101
1109
  };
1102
1110
  process.on("exit", emitShutdown);
1103
- process.on("SIGINT", () => { emitShutdown(); process.exit(130); });
1104
- process.on("SIGTERM", () => { emitShutdown(); process.exit(143); });
1105
1111
  }
1106
1112
 
1107
1113
  }
@@ -47,27 +47,30 @@ export function formatCompactTokenPair(usage: Partial<UsageStats>): string {
47
47
  return `↑ ${formatFixedTokens(usage.input || 0)} · ↓ ${formatFixedTokens(usage.output || 0)}`;
48
48
  }
49
49
 
50
- export function formatCompactStats(usage: Partial<UsageStats>, model?: string, maxWidth?: number): string {
51
- const parts: string[] = [];
52
- parts.push(`↑ ${formatFixedTokens(usage.input || 0)}`);
53
- parts.push(`↓ ${formatFixedTokens(usage.output || 0)}`);
54
- parts.push(`tps: ${formatTps(usage.smoothedTps)}`);
55
- parts.push(`ctx: ${formatFixedTokens(usage.contextTokens || 0)}`);
50
+ export function formatCompactStats(
51
+ usage: Partial<UsageStats>,
52
+ model?: string,
53
+ maxWidth?: number,
54
+ options: { skipTokens?: boolean } = {},
55
+ ): string {
56
+ const tokenParts = [`↑ ${formatFixedTokens(usage.input || 0)}`, `↓ ${formatFixedTokens(usage.output || 0)}`];
57
+ const runtimeParts = [`tps: ${formatTps(usage.smoothedTps)}`, `ctx: ${formatFixedTokens(usage.contextTokens || 0)}`];
58
+ const parts = options.skipTokens ? runtimeParts : [...tokenParts, ...runtimeParts];
56
59
 
57
60
  const displayModel = model ? model.replace(/^[^/]+\//, "") : undefined;
58
61
  let result = parts.join(" · ") + (displayModel ? ` · ${displayModel}` : "");
59
62
  if (maxWidth && visibleLength(result) > maxWidth) {
60
- // Drop model first
63
+ // Drop model first.
61
64
  let narrow = parts.join(" · ");
62
65
  if (visibleLength(narrow) <= maxWidth) return narrow;
63
66
 
64
- // Drop context tokens
65
- const narrowParts = parts.slice(0, 3); // up to tps
66
- narrow = narrowParts.join(" · ");
67
+ // Drop context tokens next.
68
+ const withoutContext = parts.filter((part) => !part.startsWith("ctx:"));
69
+ narrow = withoutContext.join(" · ");
67
70
  if (visibleLength(narrow) <= maxWidth) return narrow;
68
71
 
69
- // Bare minimum (just input/output)
70
- narrow = `${parts[0]} · ${parts[1]}`;
72
+ // Bare minimum: token pair for normal stats, tps for token-free headers.
73
+ narrow = options.skipTokens ? runtimeParts[0] : tokenParts.join(" · ");
71
74
  if (visibleLength(narrow) <= maxWidth) return narrow;
72
75
 
73
76
  return truncateChars(result, maxWidth);
package/src/render.ts CHANGED
@@ -319,7 +319,7 @@ function renderFlowCollapsed(
319
319
  ): Container {
320
320
  const container = new Container();
321
321
  const maxWidth = process.stdout.columns ?? 80;
322
- const stats = formatCompactStats(r.usage, r.model, maxWidth);
322
+ const stats = formatCompactStats(r.usage, r.model, maxWidth, { skipTokens: true });
323
323
  const typeName = formatFlowTypeName(r.type);
324
324
  let header = `${theme.fg("accent", theme.bold(typeName))} ${theme.fg("dim", stats)}`;
325
325
  if (error && r.stopReason) header += ` ${theme.fg("error", `[${r.stopReason}]`)}`;
@@ -451,7 +451,7 @@ function renderActivityPanel(
451
451
  for (let i = 0; i < results.length; i++) {
452
452
  const r = results[i];
453
453
  const isLast = i === results.length - 1;
454
- const stats = formatCompactStats(r.usage, r.model, maxWidth);
454
+ const stats = formatCompactStats(r.usage, r.model, maxWidth, { skipTokens: true });
455
455
  const error = isFlowError(r);
456
456
  const typeName = formatFlowTypeName(r.type);
457
457