@marcoscale98/piewf-cli 5.14.1-fork.1 → 5.17.0-fork.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/README.md +43 -0
- package/dist/src/cli.js +2 -1
- package/dist/src/doctor.js +3 -1
- package/dist/src/pi-role.d.ts +5 -0
- package/dist/src/pi-role.js +102 -0
- package/package.json +7 -5
package/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# @marcoscale98/piewf-cli
|
|
2
|
+
|
|
3
|
+
Terminal commands for [pi-extensible-workflows](https://github.com/vekexasia/pi-extensible-workflows): `pi-role` starts Pi as a role, `piewf` operates workflows.
|
|
4
|
+
|
|
5
|
+
Requires Node.js 22.19 or newer and the `pi` command on `PATH`. This is trusted host code with the same filesystem and process access as Pi.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install --global @marcoscale98/piewf-cli@fork
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## pi-role
|
|
12
|
+
|
|
13
|
+
Start a regular Pi session with a role's model, tools, skills, extensions, and system prompt. Everything after the role name goes to Pi unchanged.
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
pi-role # list the available roles
|
|
17
|
+
pi-role reviewer # interactive Pi as the reviewer role
|
|
18
|
+
pi-role scout -p "Where is the retry logic?" # one-shot prompt
|
|
19
|
+
pi-role developer --continue # resume the last session as the developer role
|
|
20
|
+
pi-role reviewer --approve # trust the project so its roles and settings apply
|
|
21
|
+
npx -p @marcoscale98/piewf-cli@fork pi-role oracle # without installing
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Roles come from the bundled starter roles, `~/.pi/agent/pi-extensible-workflows/roles/*.md`, and, in trusted projects, `.pi/pi-extensible-workflows/roles/*.md`. The [roles guide](https://vekexasia.github.io/pi-extensible-workflows/roles.html#pi-role) lists what each role field becomes on the Pi command line and the launcher's limits; the [role file reference](https://vekexasia.github.io/pi-extensible-workflows/roles.html#files) covers writing your own.
|
|
25
|
+
|
|
26
|
+
## piewf
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
piewf doctor [--role <role>] [--prompt <text>] [--json]
|
|
30
|
+
piewf doctor cleanup [--older-than-days <days>] [--yes]
|
|
31
|
+
piewf inspect [session-id] [--json|--summary] [--failed]
|
|
32
|
+
piewf transcript <session-file>
|
|
33
|
+
piewf run <workflow-name> [workflow arguments]
|
|
34
|
+
piewf run --script <workflow.js> [--name <workflow-name>] [--input <json>]
|
|
35
|
+
piewf export <workflow-name> [--name <command>] [--output <path>] [--force]
|
|
36
|
+
piewf bundle <workflow-name> [--name <command>] [--output <directory>] [--force]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
See [CLI operations](https://vekexasia.github.io/pi-extensible-workflows/developers.html#operations).
|
|
40
|
+
|
|
41
|
+
## License
|
|
42
|
+
|
|
43
|
+
MIT
|
package/dist/src/cli.js
CHANGED
|
@@ -8,7 +8,8 @@ import { ProjectTrustStore, SessionManager, SettingsManager, createAgentSessionF
|
|
|
8
8
|
import { Value } from "typebox/value";
|
|
9
9
|
import { doctor, doctorExitCode, formatDoctorReport } from "./doctor.js";
|
|
10
10
|
import { doctorCleanup, doctorCleanupExitCode, formatDoctorCleanupReport } from "./doctor-cleanup.js";
|
|
11
|
-
import
|
|
11
|
+
import { loadAgentDefinitions } from "@marcoscale98/pi-extensible-workflows/roles";
|
|
12
|
+
import workflowExtension, { errorText, formatWorkflowProgress, isNodeError, jsonValue, object, registeredWorkflowFunctionSources, sameFilesystemPath, truncateWorkflowProgress, workflowCatalog, workflowSettingsPath } from "@marcoscale98/pi-extensible-workflows";
|
|
12
13
|
import { CLI_PACKAGE_NAME, portableEngineVersion, portablePiVersion, writePortableWorkflowBundle } from "./bundles.js";
|
|
13
14
|
import { runSessionInspector, transcriptFileLines } from "./session-inspector.js";
|
|
14
15
|
import { isPersistedRun, listPersistedSessionIds, listRunIds } from "@marcoscale98/pi-extensible-workflows/persistence";
|
package/dist/src/doctor.js
CHANGED
|
@@ -2,7 +2,8 @@ import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
|
2
2
|
import { basename, dirname, extname, join } from "node:path";
|
|
3
3
|
import { InMemoryCredentialStore, InMemoryModelsStore } from "@earendil-works/pi-ai";
|
|
4
4
|
import { ModelRuntime, createAgentSessionFromServices, createAgentSessionServices, getAgentDir, hasTrustRequiringProjectResources, SessionManager, SettingsManager, } from "@earendil-works/pi-coding-agent";
|
|
5
|
-
import { DEFAULT_SETTINGS, canonicalPath, createLocalPiSession, errorText, isNodeError, isObject, loadSettings, prepareAgentSetupForInspection, resolveAgentResourcePolicy, resolveWorkflowSettings, resolveModelReference, resourcePatternHasMagic, parseThinking,
|
|
5
|
+
import { DEFAULT_SETTINGS, canonicalPath, createLocalPiSession, errorText, isNodeError, isObject, loadSettings, prepareAgentSetupForInspection, resolveAgentResourcePolicy, resolveWorkflowSettings, resolveModelReference, resourcePatternHasMagic, parseThinking, registeredWorkflowFunctions, registeredWorkflowRoleDirectoryRegistrations, workflowProjectSettingsPath, workflowSettingsPath, } from "@marcoscale98/pi-extensible-workflows";
|
|
6
|
+
import { parseRoleMarkdown, workflowRoleDirectories } from "@marcoscale98/pi-extensible-workflows/roles";
|
|
6
7
|
import { loadingRegistry } from "@marcoscale98/pi-extensible-workflows";
|
|
7
8
|
import { selectResourcesByLayers, unmatchedResourcePatterns, mergeWorkflowExtensionSettings } from "@marcoscale98/pi-extensible-workflows";
|
|
8
9
|
const THINKING_HINT = "Use off, minimal, low, medium, high, xhigh, or max.";
|
|
@@ -397,6 +398,7 @@ export async function doctor(options = {}) {
|
|
|
397
398
|
].sort((left, right) => left.name.localeCompare(right.name) || left.kind.localeCompare(right.kind));
|
|
398
399
|
const roles = [];
|
|
399
400
|
const definitions = new Map();
|
|
401
|
+
// Keep this scan local because doctor reports every invalid and duplicate file; discoverRoles intentionally fails closed on the complete set.
|
|
400
402
|
const extensionScan = scanExtensionRoleFiles(registeredWorkflowRoleDirectoryRegistrations());
|
|
401
403
|
for (const { registration, error } of extensionScan.errors) {
|
|
402
404
|
const message = errorText(error);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { type ResolvedRole } from "@marcoscale98/pi-extensible-workflows/roles";
|
|
3
|
+
export declare function piArguments(role: ResolvedRole, skillPaths: ReadonlyMap<string, string>, rest: readonly string[]): string[];
|
|
4
|
+
export declare function resolvePiArguments(name: string, rest: readonly string[], cwd?: string, agentDir?: string): Promise<string[]>;
|
|
5
|
+
export declare function runPiRole(argv: readonly string[]): Promise<number>;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { constants } from "node:os";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { DefaultPackageManager, DefaultResourceLoader, ProjectTrustStore, SettingsManager, getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
7
|
+
import { discoverRoles, loadRole, resolveRole } from "@marcoscale98/pi-extensible-workflows/roles";
|
|
8
|
+
import { CONTEXT_FILE_SCOPES, WorkflowError, errorText, resourcePatternHasMagic, sameFilesystemPath } from "@marcoscale98/pi-extensible-workflows";
|
|
9
|
+
// Pi builtin tools stand in for the workflow session boundary; extension tool names selected by the role pass through.
|
|
10
|
+
const PI_BUILTIN_TOOLS = ["read", "bash", "edit", "write", "grep", "find", "ls"];
|
|
11
|
+
function starterRoleDirectories() {
|
|
12
|
+
const core = dirname(fileURLToPath(import.meta.resolve("@marcoscale98/pi-extensible-workflows")));
|
|
13
|
+
return [{ path: resolve(core, "../starter/roles"), extension: { version: "0.0.0", headline: "Starter roles" }, builtin: true }];
|
|
14
|
+
}
|
|
15
|
+
function projectTrust(cwd, agentDir, args) {
|
|
16
|
+
const end = args.indexOf("--");
|
|
17
|
+
const flag = args.slice(0, end < 0 ? args.length : end).filter((arg) => ["--approve", "-a", "--no-approve", "-na"].includes(arg)).at(-1); // last wins, as in pi
|
|
18
|
+
if (flag)
|
|
19
|
+
return flag === "--approve" || flag === "-a";
|
|
20
|
+
return new ProjectTrustStore(agentDir).get(cwd) ?? false; // no saved decision: project roles, settings, and resources stay out
|
|
21
|
+
}
|
|
22
|
+
export function piArguments(role, skillPaths, rest) {
|
|
23
|
+
const argv = [];
|
|
24
|
+
if (role.model)
|
|
25
|
+
argv.push("--model", `${role.model.provider}/${role.model.model}${role.model.thinking ? `:${role.model.thinking}` : ""}`);
|
|
26
|
+
if (role.selectorLayers.tools.some((layer) => layer !== undefined))
|
|
27
|
+
argv.push("--tools", (role.tools ?? []).join(","));
|
|
28
|
+
argv.push("--no-skills");
|
|
29
|
+
for (const name of role.selectedSkills ?? [])
|
|
30
|
+
argv.push("--skill", skillPaths.get(name) ?? name);
|
|
31
|
+
argv.push("--no-extensions");
|
|
32
|
+
for (const path of role.selectedExtensions ?? [])
|
|
33
|
+
argv.push("--extension", path);
|
|
34
|
+
if (role.systemPrompt.text)
|
|
35
|
+
argv.push(role.systemPrompt.mode === "override" ? "--system-prompt" : "--append-system-prompt", role.systemPrompt.text);
|
|
36
|
+
if (role.contextFiles !== undefined && new Set(role.contextFiles).size < CONTEXT_FILE_SCOPES.length) {
|
|
37
|
+
if (role.contextFiles.length > 0)
|
|
38
|
+
throw new Error(`Role ${role.name ?? ""} selects a subset of context file scopes (${role.contextFiles.join(", ")}); pi-role only supports all scopes or none`);
|
|
39
|
+
argv.push("--no-context-files");
|
|
40
|
+
}
|
|
41
|
+
return [...argv, ...rest];
|
|
42
|
+
}
|
|
43
|
+
export async function resolvePiArguments(name, rest, cwd = process.cwd(), agentDir = getAgentDir()) {
|
|
44
|
+
const projectTrusted = projectTrust(cwd, agentDir, rest);
|
|
45
|
+
const settingsManager = SettingsManager.create(cwd, agentDir, { projectTrusted: false });
|
|
46
|
+
settingsManager.setProjectTrusted(projectTrusted);
|
|
47
|
+
const discovered = await new DefaultPackageManager({ cwd, agentDir, settingsManager }).resolve();
|
|
48
|
+
const visible = ({ enabled, metadata }) => enabled && (projectTrusted || metadata.scope !== "project");
|
|
49
|
+
const extensions = [...new Set(discovered.extensions.filter(visible).map(({ path }) => path))];
|
|
50
|
+
const loader = new DefaultResourceLoader({ cwd, agentDir, settingsManager, noExtensions: true, noSkills: true, additionalSkillPaths: [...new Set(discovered.skills.filter(visible).map(({ path }) => path))] });
|
|
51
|
+
await loader.reload();
|
|
52
|
+
const skillPaths = new Map(loader.getSkills().skills.map(({ name: skill, filePath }) => [skill, filePath]));
|
|
53
|
+
const discovery = { cwd, agentDir, projectTrusted, extensionRoleDirectories: starterRoleDirectories() };
|
|
54
|
+
const resolveWith = (definition) => {
|
|
55
|
+
const requestedTools = resolveRole(name, { ...discovery, definition }).selectorLayers.tools.flatMap((layer) => layer ?? []).filter((selector) => !selector.startsWith("!") && !resourcePatternHasMagic(selector));
|
|
56
|
+
return resolveRole(name, { ...discovery, definition, resources: { extensions, skills: [...skillPaths.keys()], tools: [...new Set([...PI_BUILTIN_TOOLS, ...requestedTools])] } });
|
|
57
|
+
};
|
|
58
|
+
const definition = loadRole(name, discovery);
|
|
59
|
+
let role;
|
|
60
|
+
try {
|
|
61
|
+
role = resolveWith(definition);
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
// Starter roles use aliases the workflow extension resolves against the launching session's model; standalone, pi's default model plays that part.
|
|
65
|
+
if (!(error instanceof WorkflowError) || error.code !== "UNKNOWN_MODEL")
|
|
66
|
+
throw error;
|
|
67
|
+
process.stderr.write(`pi-role: ${errorText(error)}; starting pi with its default model\n`);
|
|
68
|
+
const withoutModel = { ...definition };
|
|
69
|
+
delete withoutModel.model;
|
|
70
|
+
role = resolveWith(withoutModel);
|
|
71
|
+
}
|
|
72
|
+
return piArguments(role, skillPaths, rest);
|
|
73
|
+
}
|
|
74
|
+
function usage(cwd, agentDir) {
|
|
75
|
+
const roles = discoverRoles({ cwd, agentDir, projectTrusted: projectTrust(cwd, agentDir, []), extensionRoleDirectories: starterRoleDirectories() });
|
|
76
|
+
const lines = Object.entries(roles).map(([name, { description }]) => ` ${name.padEnd(16)}${description ?? ""}`);
|
|
77
|
+
return `Usage: pi-role <role> [pi arguments...]\n\nRoles:\n${lines.join("\n")}\n\nProject roles need pi's saved trust decision or --approve.\n`;
|
|
78
|
+
}
|
|
79
|
+
export async function runPiRole(argv) {
|
|
80
|
+
const [name, ...rest] = argv;
|
|
81
|
+
if (!name || name === "--help" || name === "-h") {
|
|
82
|
+
process.stdout.write(usage(process.cwd(), getAgentDir()));
|
|
83
|
+
return name ? 0 : 1;
|
|
84
|
+
}
|
|
85
|
+
let args;
|
|
86
|
+
try {
|
|
87
|
+
args = await resolvePiArguments(name, rest);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
process.stderr.write(`pi-role: ${errorText(error)}\n`);
|
|
91
|
+
return 1;
|
|
92
|
+
}
|
|
93
|
+
return new Promise((done) => {
|
|
94
|
+
const child = spawn("pi", args, { stdio: "inherit" });
|
|
95
|
+
process.on("SIGINT", () => { }); // the child owns the terminal; it handles Ctrl+C and exits
|
|
96
|
+
child.on("error", (error) => { process.stderr.write(`pi-role: ${errorText(error)}\n`); done(1); });
|
|
97
|
+
child.on("exit", (code, signal) => { done(signal ? 128 + constants.signals[signal] : code ?? 1); });
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
if (process.argv[1] && sameFilesystemPath(fileURLToPath(import.meta.url), process.argv[1])) {
|
|
101
|
+
process.exitCode = await runPiRole(process.argv.slice(2));
|
|
102
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marcoscale98/piewf-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.17.0-fork.1",
|
|
4
4
|
"description": "CLI for pi-extensible-workflows",
|
|
5
5
|
"homepage": "https://vekexasia.github.io/pi-extensible-workflows/",
|
|
6
6
|
"repository": {
|
|
@@ -10,16 +10,18 @@
|
|
|
10
10
|
},
|
|
11
11
|
"type": "module",
|
|
12
12
|
"exports": {
|
|
13
|
-
".": "./dist/src/cli.js"
|
|
13
|
+
".": "./dist/src/cli.js",
|
|
14
|
+
"./pi-role": "./dist/src/pi-role.js"
|
|
14
15
|
},
|
|
15
16
|
"bin": {
|
|
16
|
-
"piewf": "./dist/src/cli.js"
|
|
17
|
+
"piewf": "./dist/src/cli.js",
|
|
18
|
+
"pi-role": "./dist/src/pi-role.js"
|
|
17
19
|
},
|
|
18
20
|
"files": [
|
|
19
21
|
"dist/src"
|
|
20
22
|
],
|
|
21
23
|
"scripts": {
|
|
22
|
-
"build": "npm --prefix ../.. run build --workspace=../core && rm -rf dist && tsc -p tsconfig.json && chmod +x dist/src/cli.js",
|
|
24
|
+
"build": "npm --prefix ../.. run build --workspace=../core && rm -rf dist && tsc -p tsconfig.json && chmod +x dist/src/cli.js dist/src/pi-role.js",
|
|
23
25
|
"inspect": "node dist/src/cli.js inspect",
|
|
24
26
|
"test": "npm run build && TEST_FILES='dist/test/*.test.js' npm run test:run",
|
|
25
27
|
"test:run": "files=${TEST_FILES:-dist/test/*.test.js}; printf '%s\\n' $files | xargs -n1 -P20 sh -c 'tmp=$(mktemp -d); TMPDIR=\"$tmp\" node --test --test-concurrency=1 --test-timeout=60000 --test-force-exit --test-reporter=dot \"$1\"; status=$?; rm -rf \"$tmp\"; exit $status' sh",
|
|
@@ -43,7 +45,7 @@
|
|
|
43
45
|
"@earendil-works/pi-coding-agent": "0.85.0",
|
|
44
46
|
"@earendil-works/pi-server": "0.85.0",
|
|
45
47
|
"@earendil-works/pi-tui": "0.85.0",
|
|
46
|
-
"@marcoscale98/pi-extensible-workflows": "5.
|
|
48
|
+
"@marcoscale98/pi-extensible-workflows": "5.17.0-fork.1",
|
|
47
49
|
"typebox": "1.3.7"
|
|
48
50
|
},
|
|
49
51
|
"publishConfig": {
|