@pi-unipi/workflow 2.4.0 → 2.4.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/commands.ts +13 -1
- package/index.ts +12 -1
- package/lifecycle.ts +43 -0
- package/package.json +3 -2
package/commands.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
9
9
|
import { readFileSync, readdirSync, existsSync, statSync } from "fs";
|
|
10
10
|
import { join, basename } from "path";
|
|
11
|
-
import { UNIPI_PREFIX, WORKFLOW_COMMANDS, getToolsForCommand, getSandboxLevel, type SandboxLevel } from "@pi-unipi/core";
|
|
11
|
+
import { UNIPI_PREFIX, WORKFLOW_COMMANDS, getToolsForCommand, getSandboxLevel, type SandboxLevel, type UnipiWorkflowEvent } from "@pi-unipi/core";
|
|
12
12
|
|
|
13
13
|
type CompletionItem = { value: string; label: string; description: string };
|
|
14
14
|
|
|
@@ -22,6 +22,8 @@ export interface WorkflowCommandOptions {
|
|
|
22
22
|
setActiveTools: (tools: string[], level: SandboxLevel) => void;
|
|
23
23
|
/** Save tools for later restore */
|
|
24
24
|
saveTools: (tools: string[]) => void;
|
|
25
|
+
/** Begin one workflow lifecycle; false means another workflow is active. */
|
|
26
|
+
startWorkflow: (event: UnipiWorkflowEvent) => boolean;
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
/** Command definition */
|
|
@@ -402,6 +404,16 @@ export function registerWorkflowCommands(
|
|
|
402
404
|
return null;
|
|
403
405
|
},
|
|
404
406
|
handler: async (args, ctx) => {
|
|
407
|
+
const workflowEvent: UnipiWorkflowEvent = {
|
|
408
|
+
command: cmd.name,
|
|
409
|
+
fullCommand: `/${fullCommand}`,
|
|
410
|
+
args: args?.trim() ?? "",
|
|
411
|
+
};
|
|
412
|
+
if (!options.startWorkflow(workflowEvent)) {
|
|
413
|
+
if (ctx.hasUI) ctx.ui.notify("Another UniPi workflow is still active", "warning");
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
|
|
405
417
|
// Apply sandbox — save current tools, set command's tools
|
|
406
418
|
const currentTools = options.getActiveTools();
|
|
407
419
|
options.saveTools(currentTools);
|
package/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
getBlockedToolsForLevel,
|
|
22
22
|
} from "@pi-unipi/core";
|
|
23
23
|
import { registerWorkflowCommands } from "./commands.js";
|
|
24
|
+
import { WorkflowLifecycle } from "./lifecycle.js";
|
|
24
25
|
|
|
25
26
|
/** Package version (read from package.json at load time) */
|
|
26
27
|
const VERSION = getPackageVersion(dirname(fileURLToPath(import.meta.url)));
|
|
@@ -41,6 +42,7 @@ let currentSandboxLevel: SandboxLevel | null = null;
|
|
|
41
42
|
let currentSandboxTools: string[] | null = null;
|
|
42
43
|
|
|
43
44
|
export default function (pi: ExtensionAPI) {
|
|
45
|
+
const workflowLifecycle = new WorkflowLifecycle();
|
|
44
46
|
|
|
45
47
|
// Register all workflow commands
|
|
46
48
|
registerWorkflowCommands(pi, {
|
|
@@ -55,6 +57,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
55
57
|
saveTools: (tools: string[]) => {
|
|
56
58
|
savedTools = tools;
|
|
57
59
|
},
|
|
60
|
+
startWorkflow: (event) => {
|
|
61
|
+
if (!workflowLifecycle.start(event)) return false;
|
|
62
|
+
emitEvent(pi, UNIPI_EVENTS.WORKFLOW_START, event);
|
|
63
|
+
return true;
|
|
64
|
+
},
|
|
58
65
|
});
|
|
59
66
|
|
|
60
67
|
// Block tool calls that violate sandbox
|
|
@@ -109,7 +116,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
109
116
|
});
|
|
110
117
|
|
|
111
118
|
// Restore tools when agent finishes
|
|
112
|
-
pi.on("agent_end", async (
|
|
119
|
+
pi.on("agent_end", async (event, _ctx) => {
|
|
113
120
|
if (sandboxActive && savedTools) {
|
|
114
121
|
pi.setActiveTools(savedTools);
|
|
115
122
|
savedTools = null;
|
|
@@ -117,6 +124,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
117
124
|
currentSandboxLevel = null;
|
|
118
125
|
currentSandboxTools = null;
|
|
119
126
|
}
|
|
127
|
+
|
|
128
|
+
const completedWorkflow = workflowLifecycle.complete(event.messages);
|
|
129
|
+
if (completedWorkflow) emitEvent(pi, UNIPI_EVENTS.WORKFLOW_END, completedWorkflow);
|
|
120
130
|
});
|
|
121
131
|
|
|
122
132
|
// Announce module presence on session start
|
|
@@ -163,5 +173,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
163
173
|
ralphDetected = false;
|
|
164
174
|
savedTools = null;
|
|
165
175
|
sandboxActive = false;
|
|
176
|
+
workflowLifecycle.reset();
|
|
166
177
|
});
|
|
167
178
|
}
|
package/lifecycle.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { UnipiWorkflowEvent } from "@pi-unipi/core";
|
|
2
|
+
|
|
3
|
+
interface WorkflowMessage {
|
|
4
|
+
role: string;
|
|
5
|
+
stopReason?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface CompletedWorkflowEvent extends UnipiWorkflowEvent {
|
|
9
|
+
success: boolean;
|
|
10
|
+
durationMs: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Single-active workflow lifecycle state shared by slash handlers and agent_end. */
|
|
14
|
+
export class WorkflowLifecycle {
|
|
15
|
+
private active: (UnipiWorkflowEvent & { startedAt: number }) | null = null;
|
|
16
|
+
|
|
17
|
+
constructor(private readonly now: () => number = Date.now) {}
|
|
18
|
+
|
|
19
|
+
start(event: UnipiWorkflowEvent): boolean {
|
|
20
|
+
if (this.active) return false;
|
|
21
|
+
this.active = { ...event, startedAt: this.now() };
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
complete(messages: WorkflowMessage[]): CompletedWorkflowEvent | undefined {
|
|
26
|
+
if (!this.active) return undefined;
|
|
27
|
+
const workflow = this.active;
|
|
28
|
+
this.active = null;
|
|
29
|
+
const finalAssistant = [...messages].reverse().find((message) => message.role === "assistant");
|
|
30
|
+
const success = finalAssistant?.stopReason !== "error" && finalAssistant?.stopReason !== "aborted";
|
|
31
|
+
return {
|
|
32
|
+
command: workflow.command,
|
|
33
|
+
fullCommand: workflow.fullCommand,
|
|
34
|
+
args: workflow.args,
|
|
35
|
+
success,
|
|
36
|
+
durationMs: this.now() - workflow.startedAt,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
reset(): void {
|
|
41
|
+
this.active = null;
|
|
42
|
+
}
|
|
43
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-unipi/workflow",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.1",
|
|
4
4
|
"description": "Structured development workflow commands for Pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
],
|
|
20
20
|
"files": [
|
|
21
21
|
"*.ts",
|
|
22
|
+
"!*.test.ts",
|
|
22
23
|
"skills/**/*",
|
|
23
24
|
"README.md"
|
|
24
25
|
],
|
|
@@ -26,7 +27,7 @@
|
|
|
26
27
|
"access": "public"
|
|
27
28
|
},
|
|
28
29
|
"dependencies": {
|
|
29
|
-
"@pi-unipi/core": "2.4.
|
|
30
|
+
"@pi-unipi/core": "2.4.1"
|
|
30
31
|
},
|
|
31
32
|
"peerDependencies": {
|
|
32
33
|
"@earendil-works/pi-ai": "^0.80.0",
|