@yycholla/pi-dynamic-workflows 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +325 -0
  3. package/assets/readme/hero.png +0 -0
  4. package/assets/readme/hero.svg +70 -0
  5. package/assets/readme/package-cover.png +0 -0
  6. package/assets/readme/package-cover.svg +66 -0
  7. package/assets/readme/workflow.png +0 -0
  8. package/assets/readme/workflow.svg +77 -0
  9. package/dist/accept-workflow-guidance.d.ts +14 -0
  10. package/dist/accept-workflow-guidance.js +53 -0
  11. package/dist/adversarial-review.d.ts +30 -0
  12. package/dist/adversarial-review.js +107 -0
  13. package/dist/agent-history.d.ts +20 -0
  14. package/dist/agent-history.js +122 -0
  15. package/dist/agent-registry.d.ts +83 -0
  16. package/dist/agent-registry.js +190 -0
  17. package/dist/agent.d.ts +364 -0
  18. package/dist/agent.js +714 -0
  19. package/dist/builtin-commands.d.ts +19 -0
  20. package/dist/builtin-commands.js +251 -0
  21. package/dist/builtin-workflows.d.ts +45 -0
  22. package/dist/builtin-workflows.js +121 -0
  23. package/dist/code-review.d.ts +26 -0
  24. package/dist/code-review.js +181 -0
  25. package/dist/config.d.ts +37 -0
  26. package/dist/config.js +44 -0
  27. package/dist/deep-research.d.ts +30 -0
  28. package/dist/deep-research.js +124 -0
  29. package/dist/display.d.ts +134 -0
  30. package/dist/display.js +248 -0
  31. package/dist/effort-command.d.ts +28 -0
  32. package/dist/effort-command.js +68 -0
  33. package/dist/enums.d.ts +69 -0
  34. package/dist/enums.js +78 -0
  35. package/dist/errors.d.ts +113 -0
  36. package/dist/errors.js +140 -0
  37. package/dist/extension-reload.d.ts +37 -0
  38. package/dist/extension-reload.js +78 -0
  39. package/dist/fs-persistence.d.ts +63 -0
  40. package/dist/fs-persistence.js +102 -0
  41. package/dist/index.d.ts +57 -0
  42. package/dist/index.js +35 -0
  43. package/dist/logger.d.ts +21 -0
  44. package/dist/logger.js +66 -0
  45. package/dist/model-routing.d.ts +30 -0
  46. package/dist/model-routing.js +50 -0
  47. package/dist/model-spec.d.ts +29 -0
  48. package/dist/model-spec.js +252 -0
  49. package/dist/model-tier-config.d.ts +133 -0
  50. package/dist/model-tier-config.js +249 -0
  51. package/dist/run-persistence.d.ts +180 -0
  52. package/dist/run-persistence.js +294 -0
  53. package/dist/saved-commands.d.ts +28 -0
  54. package/dist/saved-commands.js +100 -0
  55. package/dist/shared-store.d.ts +98 -0
  56. package/dist/shared-store.js +212 -0
  57. package/dist/structured-output.d.ts +19 -0
  58. package/dist/structured-output.js +30 -0
  59. package/dist/task-panel.d.ts +61 -0
  60. package/dist/task-panel.js +422 -0
  61. package/dist/usage-limit-scheduler.d.ts +145 -0
  62. package/dist/usage-limit-scheduler.js +368 -0
  63. package/dist/web-tools.d.ts +20 -0
  64. package/dist/web-tools.js +120 -0
  65. package/dist/workflow-authoring-coverage.d.ts +70 -0
  66. package/dist/workflow-authoring-coverage.js +421 -0
  67. package/dist/workflow-authoring-reference.d.ts +20 -0
  68. package/dist/workflow-authoring-reference.js +156 -0
  69. package/dist/workflow-capability-contract.d.ts +131 -0
  70. package/dist/workflow-capability-contract.js +604 -0
  71. package/dist/workflow-commands.d.ts +18 -0
  72. package/dist/workflow-commands.js +260 -0
  73. package/dist/workflow-comprehension.d.ts +133 -0
  74. package/dist/workflow-comprehension.js +1321 -0
  75. package/dist/workflow-context-measurement.d.ts +72 -0
  76. package/dist/workflow-context-measurement.js +213 -0
  77. package/dist/workflow-control-tool.d.ts +30 -0
  78. package/dist/workflow-control-tool.js +176 -0
  79. package/dist/workflow-delivery-choice.d.ts +20 -0
  80. package/dist/workflow-delivery-choice.js +48 -0
  81. package/dist/workflow-editor.d.ts +93 -0
  82. package/dist/workflow-editor.js +363 -0
  83. package/dist/workflow-manager.d.ts +492 -0
  84. package/dist/workflow-manager.js +1124 -0
  85. package/dist/workflow-paths.d.ts +22 -0
  86. package/dist/workflow-paths.js +46 -0
  87. package/dist/workflow-release-gate.d.ts +39 -0
  88. package/dist/workflow-release-gate.js +309 -0
  89. package/dist/workflow-saved.d.ts +38 -0
  90. package/dist/workflow-saved.js +126 -0
  91. package/dist/workflow-settings.d.ts +70 -0
  92. package/dist/workflow-settings.js +131 -0
  93. package/dist/workflow-tool.d.ts +71 -0
  94. package/dist/workflow-tool.js +367 -0
  95. package/dist/workflow-ui.d.ts +182 -0
  96. package/dist/workflow-ui.js +1587 -0
  97. package/dist/workflow.d.ts +333 -0
  98. package/dist/workflow.js +1151 -0
  99. package/dist/workflows-models-command.d.ts +31 -0
  100. package/dist/workflows-models-command.js +156 -0
  101. package/dist/worktree.d.ts +25 -0
  102. package/dist/worktree.js +61 -0
  103. package/extensions/workflow.ts +151 -0
  104. package/package.json +104 -0
  105. package/skills/workflow-authoring/SKILL.md +30 -0
  106. package/skills/workflow-authoring/examples/adversarial-verification.js +63 -0
  107. package/skills/workflow-authoring/examples/bounded-semantic-retry.js +56 -0
  108. package/skills/workflow-authoring/examples/classify-and-act.js +67 -0
  109. package/skills/workflow-authoring/examples/fan-out-and-synthesize.js +47 -0
  110. package/skills/workflow-authoring/examples/generate-and-filter.js +68 -0
  111. package/skills/workflow-authoring/examples/loop-until-done.js +68 -0
  112. package/skills/workflow-authoring/examples/phased-budgets.js +75 -0
  113. package/skills/workflow-authoring/examples/saved-nested-workflows.js +44 -0
  114. package/skills/workflow-authoring/examples/structured-output.js +37 -0
  115. package/skills/workflow-authoring/examples/tournament.js +81 -0
  116. package/skills/workflow-authoring/examples/validated-gate.js +63 -0
  117. package/skills/workflow-authoring/references/capabilities.md +41 -0
  118. package/skills/workflow-authoring/references/capability-details.md +357 -0
  119. package/skills/workflow-authoring/references/common-helpers.md +4 -0
  120. package/skills/workflow-authoring/references/debugging.md +27 -0
  121. package/skills/workflow-authoring/references/focused-recipes.md +11 -0
  122. package/skills/workflow-authoring/references/helpers.md +9 -0
  123. package/skills/workflow-authoring/references/lifecycle.md +33 -0
  124. package/skills/workflow-authoring/references/pattern-selection.md +15 -0
  125. package/skills/workflow-authoring/references/quality-helpers.md +8 -0
  126. package/skills/workflow-authoring/references/registry-ownership.md +15 -0
  127. package/skills/workflow-authoring/references/retry-helper.md +5 -0
  128. package/skills/workflow-authoring/references/review.md +43 -0
  129. package/skills/workflow-authoring/references/runtime.md +27 -0
  130. package/skills/workflow-authoring/references/specialized-helpers.md +19 -0
  131. package/skills/workflow-authoring/references/versions.md +13 -0
  132. package/skills/workflow-patterns/SKILL.md +51 -0
  133. package/src/accept-workflow-guidance.ts +71 -0
  134. package/src/adversarial-review.ts +120 -0
  135. package/src/agent-history.ts +157 -0
  136. package/src/agent-registry.ts +221 -0
  137. package/src/agent.ts +929 -0
  138. package/src/builtin-commands.ts +286 -0
  139. package/src/builtin-workflows.ts +155 -0
  140. package/src/code-review.ts +183 -0
  141. package/src/config.ts +55 -0
  142. package/src/deep-research.ts +135 -0
  143. package/src/display.ts +367 -0
  144. package/src/effort-command.ts +87 -0
  145. package/src/enums.ts +77 -0
  146. package/src/errors.ts +199 -0
  147. package/src/extension-reload.ts +100 -0
  148. package/src/fs-persistence.ts +124 -0
  149. package/src/index.ts +176 -0
  150. package/src/logger.ts +88 -0
  151. package/src/model-routing.ts +73 -0
  152. package/src/model-spec.ts +309 -0
  153. package/src/model-tier-config.ts +296 -0
  154. package/src/run-persistence.ts +484 -0
  155. package/src/saved-commands.ts +115 -0
  156. package/src/shared-store.ts +228 -0
  157. package/src/structured-output.ts +47 -0
  158. package/src/task-panel.ts +490 -0
  159. package/src/usage-limit-scheduler.ts +432 -0
  160. package/src/web-tools.ts +124 -0
  161. package/src/workflow-authoring-coverage.ts +486 -0
  162. package/src/workflow-authoring-reference.ts +186 -0
  163. package/src/workflow-capability-contract.ts +806 -0
  164. package/src/workflow-commands.ts +287 -0
  165. package/src/workflow-comprehension.ts +1673 -0
  166. package/src/workflow-context-measurement.ts +262 -0
  167. package/src/workflow-control-tool.ts +238 -0
  168. package/src/workflow-delivery-choice.ts +69 -0
  169. package/src/workflow-editor.ts +444 -0
  170. package/src/workflow-manager.ts +1405 -0
  171. package/src/workflow-paths.ts +63 -0
  172. package/src/workflow-release-gate.ts +529 -0
  173. package/src/workflow-saved.ts +180 -0
  174. package/src/workflow-settings.ts +194 -0
  175. package/src/workflow-tool.ts +464 -0
  176. package/src/workflow-ui.ts +1789 -0
  177. package/src/workflow.ts +1615 -0
  178. package/src/workflows-models-command.ts +211 -0
  179. package/src/worktree.ts +76 -0
@@ -0,0 +1,287 @@
1
+ /**
2
+ * `/workflows` slash command: list, inspect, and control background workflow runs.
3
+ * Shares the extension's single WorkflowManager so background runs are reachable.
4
+ */
5
+
6
+ import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
7
+ import {
8
+ fmtFull,
9
+ fmtTokenSegment,
10
+ recomputeWorkflowSnapshot,
11
+ renderWorkflowText,
12
+ tokenFigures,
13
+ type WorkflowSnapshot,
14
+ } from "./display.js";
15
+ import { type EffortState, effortDirective } from "./effort-command.js";
16
+ import type { PersistedRunState } from "./run-persistence.js";
17
+ import { registerSavedWorkflow } from "./saved-commands.js";
18
+ import { buildForcedWorkflowPrompt, WORKFLOW_TOOL_NAME } from "./workflow-editor.js";
19
+ import type { WorkflowManager } from "./workflow-manager.js";
20
+ import type { WorkflowStorage } from "./workflow-saved.js";
21
+ import { openWorkflowNavigator } from "./workflow-ui.js";
22
+
23
+ const STATUS_ICON: Record<string, string> = {
24
+ pending: "·",
25
+ running: "◆",
26
+ paused: "⏸",
27
+ completed: "✓",
28
+ failed: "✗",
29
+ aborted: "⊘",
30
+ };
31
+
32
+ const USAGE =
33
+ "Usage: /workflows [list] | run <prompt> | status <id> | watch <id> | stop <id> | pause <id> | resume <id> | rm <id> | save <name> [runId]";
34
+
35
+ const RUN_USAGE = "Usage: /workflows run <prompt> — force a dynamic workflow from the prompt";
36
+
37
+ function summarizeRun(run: PersistedRunState): string {
38
+ const icon = STATUS_ICON[run.status] ?? "?";
39
+ const done = run.agents.filter((a) => a.status === "done").length;
40
+ const total = run.agents.length;
41
+ const segment = fmtTokenSegment(tokenFigures(run.tokenUsage), fmtFull);
42
+ const tokens = segment ? ` · ${segment}` : "";
43
+ return `${icon} ${run.runId} ${run.workflowName} [${run.status}] ${done}/${total} agents${tokens}`;
44
+ }
45
+
46
+ function oneLineProgress(snapshot: WorkflowSnapshot): string {
47
+ const total = snapshot.agents.length;
48
+ const done = snapshot.agents.filter((a) => a.status === "done").length;
49
+ const running = snapshot.agents.filter((a) => a.status === "running").length;
50
+ const errs = snapshot.agents.filter((a) => a.status === "error").length;
51
+ const phase = snapshot.currentPhase ? ` · ${snapshot.currentPhase}` : "";
52
+ return `◆ ${snapshot.name}: ${done}/${total} done${running ? `, ${running} running` : ""}${
53
+ errs ? `, ${errs} err` : ""
54
+ }${phase}`;
55
+ }
56
+
57
+ /**
58
+ * Subscribe to a running run's events and stream live progress to the status bar,
59
+ * printing the final snapshot when it finishes. Non-blocking: returns true if the
60
+ * run was active and is now being watched, false otherwise. Listeners clean up on
61
+ * completion so nothing leaks.
62
+ */
63
+ function watchRun(manager: WorkflowManager, pi: ExtensionAPI, ctx: ExtensionCommandContext, id: string): boolean {
64
+ const active = manager.getRun(id);
65
+ if (active?.status !== "running") return false;
66
+
67
+ const key = `wf:${id}`;
68
+ const update = () => {
69
+ const run = manager.getRun(id);
70
+ if (run) ctx.ui.setStatus(key, oneLineProgress(run.snapshot));
71
+ };
72
+ const onEvent = (e: { runId?: string }) => {
73
+ if (!e || e.runId === id) update();
74
+ };
75
+ let settled = false;
76
+ const progressEvents = ["agentStart", "agentEnd", "phase", "log"];
77
+ const finalEvents = ["complete", "error", "stopped", "paused"];
78
+ const finish = (e: { runId?: string }) => {
79
+ if (e && e.runId !== id) return;
80
+ if (settled) return;
81
+ settled = true;
82
+ for (const ev of progressEvents) manager.off(ev, onEvent);
83
+ for (const ev of finalEvents) manager.off(ev, finish);
84
+ ctx.ui.setStatus(key, undefined);
85
+ const run = manager.getRun(id);
86
+ if (run) {
87
+ void pi.sendMessage({
88
+ customType: "workflows",
89
+ content: renderWorkflowText(recomputeWorkflowSnapshot(run.snapshot), true),
90
+ display: true,
91
+ });
92
+ }
93
+ };
94
+ for (const ev of progressEvents) manager.on(ev, onEvent);
95
+ for (const ev of finalEvents) manager.on(ev, finish);
96
+ update();
97
+ return true;
98
+ }
99
+
100
+ function renderPersistedStatus(run: PersistedRunState): string {
101
+ const lines = [`${STATUS_ICON[run.status] ?? "?"} ${run.workflowName} (${run.runId}) — ${run.status}`];
102
+ if (run.currentPhase) lines.push(` phase: ${run.currentPhase}`);
103
+ for (const agent of run.agents) {
104
+ const icon =
105
+ agent.status === "done" ? "✓" : agent.status === "error" ? "✗" : agent.status === "running" ? "◆" : "·";
106
+ lines.push(` ${icon} ${agent.label}`);
107
+ }
108
+ const tokenSegment = fmtTokenSegment(tokenFigures(run.tokenUsage), fmtFull);
109
+ if (tokenSegment) lines.push(` tokens: ${tokenSegment}`);
110
+ if (run.durationMs) lines.push(` duration: ${(run.durationMs / 1000).toFixed(1)}s`);
111
+ return lines.join("\n");
112
+ }
113
+
114
+ export interface WorkflowCommandOptions {
115
+ /** Saved-workflow storage, enabling `/workflows save`. */
116
+ storage?: WorkflowStorage;
117
+ /** Working directory for saved workflows registered via `save`. */
118
+ cwd?: string;
119
+ /** Standing effort mode; when high/ultra, `/workflows run` carries its directive too. */
120
+ effort?: EffortState;
121
+ }
122
+
123
+ /** Register the `/workflows` command against the shared manager. Idempotent. */
124
+ export function registerWorkflowCommands(
125
+ pi: ExtensionAPI,
126
+ manager: WorkflowManager,
127
+ opts: WorkflowCommandOptions = {},
128
+ ): void {
129
+ try {
130
+ const taken = (pi.getCommands?.() ?? []).some((c: { name: string }) => c.name === "workflows");
131
+ if (taken) return;
132
+ } catch {
133
+ // getCommands may be unavailable in some hosts; fall through and try to register.
134
+ }
135
+
136
+ pi.registerCommand("workflows", {
137
+ description:
138
+ "Manage workflow runs — no args (opens navigator) | run <prompt> | status/stop/pause/resume <id> | rm <id> | save <name> [runId]",
139
+ async handler(args: string, ctx: ExtensionCommandContext) {
140
+ const parts = args.trim().split(/\s+/).filter(Boolean);
141
+ const sub = (parts[0] ?? "list").toLowerCase();
142
+ const id = parts[1];
143
+ const print = (text: string) => pi.sendMessage({ customType: "workflows", content: text, display: true });
144
+
145
+ switch (sub) {
146
+ case "run": {
147
+ const prompt = args
148
+ .trim()
149
+ .slice(parts[0]?.length ?? 0)
150
+ .trim();
151
+ if (!prompt) {
152
+ ctx.ui.notify(RUN_USAGE, "warning");
153
+ return;
154
+ }
155
+
156
+ // Best-effort: ensure the workflow tool is active (session_start usually has).
157
+ // Add-only so this does not interfere with the keyword hook's save/restore state.
158
+ try {
159
+ const active = pi.getActiveTools?.() ?? [];
160
+ if (!active.includes(WORKFLOW_TOOL_NAME)) pi.setActiveTools?.([...active, WORKFLOW_TOOL_NAME]);
161
+ } catch {
162
+ // ignore — the forced directive is the real forcing primitive
163
+ }
164
+
165
+ const effort = opts.effort;
166
+ const extra = effort && effort.level !== "off" ? effortDirective(effort.level) : undefined;
167
+ // `/workflows run` is an explicit, maximal-intent command — use the
168
+ // forcing directive (no "if it's a question just answer" escape),
169
+ // distinct from the heuristic keyword/effort arming.
170
+ const armed = buildForcedWorkflowPrompt(prompt, extra);
171
+ ctx.ui.notify(`Running workflow: ${prompt.slice(0, 60)}${prompt.length > 60 ? "…" : ""}`, "info");
172
+ try {
173
+ await pi.sendMessage(
174
+ { customType: "workflow-run", content: armed, display: true },
175
+ { triggerTurn: true, deliverAs: "followUp" },
176
+ );
177
+ } catch {
178
+ ctx.ui.notify("Could not start the workflow turn.", "error");
179
+ }
180
+ return;
181
+ }
182
+ case "ui":
183
+ case "list": {
184
+ // Interactive navigator when a UI is available; plain text otherwise
185
+ // (print/RPC mode) or when the user explicitly asks for `list`.
186
+ if (sub !== "list" && ctx.hasUI) {
187
+ await openWorkflowNavigator(pi, manager, ctx.ui, { storage: opts.storage, cwd: opts.cwd });
188
+ return;
189
+ }
190
+ if (parts.length === 0 && ctx.hasUI) {
191
+ await openWorkflowNavigator(pi, manager, ctx.ui, { storage: opts.storage, cwd: opts.cwd });
192
+ return;
193
+ }
194
+ const runs = manager.listRuns();
195
+ if (!runs.length) {
196
+ await print("No workflow runs yet. Start one with a background workflow (background: true).");
197
+ return;
198
+ }
199
+ await print(["Workflow runs:", ...runs.map(summarizeRun), "", USAGE].join("\n"));
200
+ return;
201
+ }
202
+ case "watch":
203
+ case "status": {
204
+ if (!id) {
205
+ ctx.ui.notify(USAGE, "warning");
206
+ return;
207
+ }
208
+ // A running run streams live progress to the status bar and prints the
209
+ // final snapshot when it finishes — no need to re-run the command.
210
+ if (watchRun(manager, pi, ctx, id)) {
211
+ ctx.ui.notify(`Watching ${id} — live progress in the status bar; result prints when it finishes.`, "info");
212
+ return;
213
+ }
214
+ const live = manager.getSnapshot(id);
215
+ if (live) {
216
+ await print(renderWorkflowText(recomputeWorkflowSnapshot(live), false));
217
+ return;
218
+ }
219
+ const run = manager.listRuns().find((r) => r.runId === id);
220
+ if (!run) {
221
+ ctx.ui.notify(`No workflow run "${id}"`, "error");
222
+ return;
223
+ }
224
+ await print(renderPersistedStatus(run));
225
+ return;
226
+ }
227
+ case "stop": {
228
+ if (!id) return ctx.ui.notify(USAGE, "warning");
229
+ ctx.ui.notify(
230
+ manager.stop(id) ? `Stopped ${id}` : `Cannot stop ${id} (not running)`,
231
+ manager.getRun(id) ? "info" : "warning",
232
+ );
233
+ return;
234
+ }
235
+ case "pause": {
236
+ if (!id) return ctx.ui.notify(USAGE, "warning");
237
+ ctx.ui.notify(manager.pause(id) ? `Paused ${id}` : `Cannot pause ${id} (not running)`, "info");
238
+ return;
239
+ }
240
+ case "resume": {
241
+ if (!id) return ctx.ui.notify(USAGE, "warning");
242
+ const ok = await manager.resume(id);
243
+ ctx.ui.notify(ok ? `Resumed ${id}` : `Resume not available for ${id} yet`, ok ? "info" : "warning");
244
+ return;
245
+ }
246
+ case "rm": {
247
+ if (!id) return ctx.ui.notify(USAGE, "warning");
248
+ ctx.ui.notify(manager.deleteRun(id) ? `Removed ${id}` : `No run ${id}`, "info");
249
+ return;
250
+ }
251
+ case "save": {
252
+ const name = id;
253
+ if (!name) return ctx.ui.notify("Usage: /workflows save <name> [runId]", "warning");
254
+ if (!opts.storage) return ctx.ui.notify("Saving is not available (no storage configured)", "error");
255
+ const storage = opts.storage;
256
+ const runs = manager.listRuns();
257
+ const runIdArg = parts[2];
258
+ // Pick the named run, else the most recent run that still has its script.
259
+ const run = runIdArg ? runs.find((r) => r.runId === runIdArg) : runs.find((r) => r.script);
260
+ if (!run?.script) {
261
+ ctx.ui.notify(runIdArg ? `No run ${runIdArg} with a script` : "No saved run to save", "error");
262
+ return;
263
+ }
264
+ let saved: ReturnType<WorkflowStorage["save"]>;
265
+ try {
266
+ saved = storage.save({
267
+ name,
268
+ description: run.workflowName,
269
+ script: run.script,
270
+ location: "project",
271
+ });
272
+ } catch (error) {
273
+ ctx.ui.notify(error instanceof Error ? error.message : String(error), "error");
274
+ return;
275
+ }
276
+ registerSavedWorkflow(pi, opts.cwd ?? process.cwd(), saved, undefined, () =>
277
+ storage.list().some((w) => w.name === saved.name),
278
+ );
279
+ ctx.ui.notify(`Saved /${name} (from ${run.runId})`, "info");
280
+ return;
281
+ }
282
+ default:
283
+ ctx.ui.notify(`Unknown subcommand "${sub}". ${USAGE}`, "warning");
284
+ }
285
+ },
286
+ });
287
+ }