@piewf/cli 5.17.1 → 5.17.2
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 +1 -1
- package/dist/src/cli.js +2 -2
- package/dist/src/doctor.js +51 -24
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ Roles come from the bundled starter roles, `~/.pi/agent/pi-extensible-workflows/
|
|
|
26
26
|
## piewf
|
|
27
27
|
|
|
28
28
|
```sh
|
|
29
|
-
piewf doctor [--role <role>] [--prompt <text>] [--json]
|
|
29
|
+
piewf doctor [role|role-file] [--role <role>] [--prompt <text>] [--json]
|
|
30
30
|
piewf doctor cleanup [--older-than-days <days>] [--yes]
|
|
31
31
|
piewf inspect [session-id] [--json|--summary] [--failed]
|
|
32
32
|
piewf transcript <session-file>
|
package/dist/src/cli.js
CHANGED
|
@@ -803,7 +803,7 @@ export async function runCli(args, options = {}, write = (text) => { process.std
|
|
|
803
803
|
const stderr = options.stderr ?? ((text) => { process.stderr.write(text); });
|
|
804
804
|
if (args[0] === "doctor" && args[1] !== "cleanup") {
|
|
805
805
|
if (args.slice(1).some((arg) => arg === "--help" || arg === "-h")) {
|
|
806
|
-
write("Usage: piewf doctor [role] [--role <role>] [--prompt <text>] [--json]\n");
|
|
806
|
+
write("Usage: piewf doctor [role|role-file] [--role <role>] [--prompt <text>] [--json]\n");
|
|
807
807
|
return 0;
|
|
808
808
|
}
|
|
809
809
|
try {
|
|
@@ -902,7 +902,7 @@ export async function runCli(args, options = {}, write = (text) => { process.std
|
|
|
902
902
|
return 1;
|
|
903
903
|
}
|
|
904
904
|
}
|
|
905
|
-
write("Usage: piewf doctor [role] [--role <role>] [--prompt <text>] [--json] | inspect [session-id] [--json|--summary] [--failed] | transcript <session-file> | share <run-id> | bundle <workflow-name> [--name <command>] [--output <path>] [--force] | run <workflow-name> [workflow arguments] | run --script <path> [--name <workflow-name>] [--input <json>] | export <workflow-name> [--name <command>] [--output <path>] [--force] [--bundle]\n");
|
|
905
|
+
write("Usage: piewf doctor [role|role-file] [--role <role>] [--prompt <text>] [--json] | inspect [session-id] [--json|--summary] [--failed] | transcript <session-file> | share <run-id> | bundle <workflow-name> [--name <command>] [--output <path>] [--force] | run <workflow-name> [workflow arguments] | run --script <path> [--name <workflow-name>] [--input <json>] | export <workflow-name> [--name <command>] [--output <path>] [--force] [--bundle]\n");
|
|
906
906
|
return 1;
|
|
907
907
|
}
|
|
908
908
|
if (process.argv[1] && sameFilesystemPath(fileURLToPath(import.meta.url), process.argv[1])) {
|
package/dist/src/doctor.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
|
-
import { basename, dirname, extname, join } from "node:path";
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, extname, join, resolve } 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
5
|
import { DEFAULT_SETTINGS, canonicalPath, createLocalPiSession, errorText, isNodeError, isObject, loadSettings, prepareAgentSetupForInspection, resolveAgentResourcePolicy, resolveWorkflowSettings, resolveModelReference, resourcePatternHasMagic, parseThinking, registeredWorkflowFunctions, registeredWorkflowRoleDirectoryRegistrations, workflowProjectSettingsPath, workflowSettingsPath, } from "pi-extensible-workflows";
|
|
@@ -207,10 +207,9 @@ function validateModel(value, known, available, source, diagnostics, aliases, dy
|
|
|
207
207
|
diagnostics.push(diagnostic("error", "MODEL_INVALID", message, source, message.includes("thinking") ? THINKING_HINT : "Use provider/model:thinking."));
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
|
-
function
|
|
211
|
-
let definition;
|
|
210
|
+
function parseRole(path, diagnostics, source) {
|
|
212
211
|
try {
|
|
213
|
-
|
|
212
|
+
return parseRoleMarkdown(readFileSync(path, "utf8"), true, path);
|
|
214
213
|
}
|
|
215
214
|
catch (error) {
|
|
216
215
|
if (usesLegacyRoleSelectors(path)) {
|
|
@@ -221,6 +220,8 @@ function inspectRole(path, activeTools, knownModels, availableModels, diagnostic
|
|
|
221
220
|
diagnostics.push(diagnostic("error", "ROLE_FRONTMATTER", source ? `${roleProvenance(source)} contains invalid role at "${path}": ${message}` : message, path, "Fix the role YAML frontmatter."));
|
|
222
221
|
return undefined;
|
|
223
222
|
}
|
|
223
|
+
}
|
|
224
|
+
function inspectRoleUsage(path, definition, activeTools, knownModels, availableModels, diagnostics, aliases, dynamicAliases, settingsPath) {
|
|
224
225
|
const toolSelectorDiagnostic = positiveOnlyToolSelectorDiagnostic(path, definition.tools);
|
|
225
226
|
if (toolSelectorDiagnostic)
|
|
226
227
|
diagnostics.push(toolSelectorDiagnostic);
|
|
@@ -236,8 +237,13 @@ function inspectRole(path, activeTools, knownModels, availableModels, diagnostic
|
|
|
236
237
|
for (const selector of definition.tools ?? []) {
|
|
237
238
|
const tool = selector.startsWith("!") ? selector.slice(1) : selector;
|
|
238
239
|
if (!selector.startsWith("!") && !resourcePatternHasMagic(selector) && !activeTools.has(tool))
|
|
239
|
-
diagnostics.push(diagnostic("
|
|
240
|
+
diagnostics.push(diagnostic("warning", "ROLE_TOOL_INACTIVE", `Tool is not in Pi's headless active tool list: ${tool}`, path, "Doctor cannot see tools that extensions add when a session starts; otherwise use a tool listed under Pi active tools or enable its Pi extension."));
|
|
240
241
|
}
|
|
242
|
+
}
|
|
243
|
+
function inspectRole(path, activeTools, knownModels, availableModels, diagnostics, aliases, dynamicAliases, settingsPath, source) {
|
|
244
|
+
const definition = parseRole(path, diagnostics, source);
|
|
245
|
+
if (definition)
|
|
246
|
+
inspectRoleUsage(path, definition, activeTools, knownModels, availableModels, diagnostics, aliases, dynamicAliases, settingsPath);
|
|
241
247
|
return definition;
|
|
242
248
|
}
|
|
243
249
|
function matchResourcePolicy(policy, pi) {
|
|
@@ -435,15 +441,13 @@ export async function doctor(options = {}) {
|
|
|
435
441
|
const starterPath = starterOverrides.get(file.path);
|
|
436
442
|
const overriddenBy = starterOverriddenBy.get(file.path);
|
|
437
443
|
roles.push({ name: file.name, path: file.path, scope: "extension", active: overriddenBy === undefined, extension: file.extension, ...(starterPath ? { overrides: starterPath } : {}), ...(overriddenBy ? { overriddenBy } : {}) });
|
|
438
|
-
const definition =
|
|
439
|
-
if (definition)
|
|
440
|
-
validateDoctorExtensionSettings(registry, mergeWorkflowExtensionSettings(settings.extensionSettings, definition.extensionSettings), "role", cwd, pi.trust.trusted, file.path, diagnostics, file.name);
|
|
444
|
+
const definition = parseRole(file.path, diagnostics, { directory: file.directory, extension: file.extension });
|
|
441
445
|
if (duplicateExtensionNames.has(file.name))
|
|
442
446
|
continue;
|
|
443
447
|
if (extensionPaths.get(file.name) !== file.path)
|
|
444
448
|
continue;
|
|
445
449
|
if (definition)
|
|
446
|
-
definitions.set(file.name, definition);
|
|
450
|
+
definitions.set(file.name, { path: file.path, definition });
|
|
447
451
|
}
|
|
448
452
|
const globalPaths = new Map();
|
|
449
453
|
const globalRoleDirs = workflowRoleDirectories(agentDir);
|
|
@@ -459,11 +463,9 @@ export async function doctor(options = {}) {
|
|
|
459
463
|
extension.overriddenBy = path;
|
|
460
464
|
}
|
|
461
465
|
}
|
|
462
|
-
const definition =
|
|
463
|
-
if (definition)
|
|
464
|
-
validateDoctorExtensionSettings(registry, mergeWorkflowExtensionSettings(settings.extensionSettings, definition.extensionSettings), "role", cwd, pi.trust.trusted, path, diagnostics, name);
|
|
466
|
+
const definition = parseRole(path, diagnostics);
|
|
465
467
|
if (definition)
|
|
466
|
-
definitions.set(name, definition);
|
|
468
|
+
definitions.set(name, { path, definition });
|
|
467
469
|
else
|
|
468
470
|
definitions.delete(name);
|
|
469
471
|
}
|
|
@@ -490,24 +492,49 @@ export async function doctor(options = {}) {
|
|
|
490
492
|
extension.overriddenBy = path;
|
|
491
493
|
}
|
|
492
494
|
}
|
|
493
|
-
const definition =
|
|
495
|
+
const definition = parseRole(path, diagnostics);
|
|
494
496
|
if (definition)
|
|
495
|
-
|
|
496
|
-
if (definition)
|
|
497
|
-
definitions.set(name, definition);
|
|
497
|
+
definitions.set(name, { path, definition });
|
|
498
498
|
else
|
|
499
499
|
definitions.delete(name);
|
|
500
500
|
}
|
|
501
|
+
for (const [name, { path, definition }] of definitions) {
|
|
502
|
+
inspectRoleUsage(path, definition, activeTools, knownModels, availableModels, diagnostics, aliases, dynamicAliases, settingsPath);
|
|
503
|
+
validateDoctorExtensionSettings(registry, mergeWorkflowExtensionSettings(settings.extensionSettings, definition.extensionSettings), "role", cwd, pi.trust.trusted, path, diagnostics, name);
|
|
504
|
+
}
|
|
501
505
|
const rolePaths = new Set(roles.map(({ path }) => path));
|
|
502
506
|
if (diagnostics.some(({ code, source }) => source !== undefined && rolePaths.has(source) && (code === "ROLE_FRONTMATTER" || code === "AGENT_RESOURCE_SELECTOR_MIGRATION")))
|
|
503
507
|
diagnostics.push(diagnostic("error", "ROLE_LOAD_BLOCKED", "Workflow role loading is blocked because the runtime rejects the complete role set when any active role file is invalid.", undefined, "Fix the reported role file before launching workflows."));
|
|
504
508
|
let roleInspection;
|
|
505
509
|
if (options.role !== undefined) {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
if (
|
|
509
|
-
|
|
510
|
+
let target;
|
|
511
|
+
//NOTE: installed role names drop ".md", so a target ending in ".md" is a role file path.
|
|
512
|
+
if (options.role.endsWith(".md")) {
|
|
513
|
+
const path = resolve(cwd, options.role);
|
|
514
|
+
if (!existsSync(path) || !statSync(path).isFile())
|
|
515
|
+
diagnostics.push(diagnostic("error", "ROLE_FILE_NOT_FOUND", `Role file not found: ${path}`, path));
|
|
516
|
+
else {
|
|
517
|
+
const name = basename(path, ".md");
|
|
518
|
+
const found = [];
|
|
519
|
+
const definition = inspectRole(path, activeTools, knownModels, availableModels, found, aliases, dynamicAliases, settingsPath);
|
|
520
|
+
if (definition)
|
|
521
|
+
validateDoctorExtensionSettings(registry, mergeWorkflowExtensionSettings(settings.extensionSettings, definition.extensionSettings), "role", cwd, pi.trust.trusted, path, found, name);
|
|
522
|
+
// Discovery already reported an installed file; add only what is new.
|
|
523
|
+
const known = new Set(diagnostics.map((item) => JSON.stringify(item)));
|
|
524
|
+
diagnostics.push(...found.filter((item) => !known.has(JSON.stringify(item))));
|
|
525
|
+
if (definition)
|
|
526
|
+
target = { name, path, definition };
|
|
527
|
+
}
|
|
528
|
+
}
|
|
510
529
|
else {
|
|
530
|
+
const effective = definitions.get(options.role);
|
|
531
|
+
if (!effective)
|
|
532
|
+
diagnostics.push(diagnostic("error", "ROLE_NOT_FOUND", `Active role not found: ${options.role}`, options.role));
|
|
533
|
+
else
|
|
534
|
+
target = { name: options.role, ...effective };
|
|
535
|
+
}
|
|
536
|
+
if (target) {
|
|
537
|
+
const { name, path, definition } = target;
|
|
511
538
|
const rootReference = pi.model ? `${pi.model.provider}/${pi.model.model}` : pi.availableModels[0] ?? pi.knownModels[0];
|
|
512
539
|
if (!rootReference)
|
|
513
540
|
diagnostics.push(diagnostic("error", "ROLE_INSPECTION_MODEL", "Cannot inspect a role because Pi has no registered model"));
|
|
@@ -525,12 +552,12 @@ export async function doctor(options = {}) {
|
|
|
525
552
|
const dynamic = await registry.resolveModelAliases({ cwd, projectTrusted: pi.trust.trusted, rootModel, knownModels, availableModels, signal: new AbortController().signal });
|
|
526
553
|
roleAliases = { ...aliases, ...dynamic };
|
|
527
554
|
}
|
|
528
|
-
roleInspection = await inspectRoleSession(cwd, agentDir,
|
|
555
|
+
roleInspection = await inspectRoleSession(cwd, agentDir, name, definition, path, resourcePolicy, rootModel, [...activeTools], roleAliases, knownModels, availableModels, settingsPath, settings.extensionSettings, options.prompt ?? "", registry.agentSetupHooks(), diagnostics);
|
|
529
556
|
if (roleInspection)
|
|
530
557
|
diagnostics.push(...roleInspection.setup.diagnostics);
|
|
531
558
|
}
|
|
532
559
|
catch (error) {
|
|
533
|
-
diagnostics.push(diagnostic("error", "ROLE_INSPECTION_MODEL", errorText(error),
|
|
560
|
+
diagnostics.push(diagnostic("error", "ROLE_INSPECTION_MODEL", errorText(error), path));
|
|
534
561
|
}
|
|
535
562
|
}
|
|
536
563
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@piewf/cli",
|
|
3
|
-
"version": "5.17.
|
|
3
|
+
"version": "5.17.2",
|
|
4
4
|
"description": "CLI for pi-extensible-workflows",
|
|
5
5
|
"homepage": "https://vekexasia.github.io/pi-extensible-workflows/",
|
|
6
6
|
"repository": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"@earendil-works/pi-coding-agent": "0.85.0",
|
|
46
46
|
"@earendil-works/pi-server": "0.85.0",
|
|
47
47
|
"@earendil-works/pi-tui": "0.85.0",
|
|
48
|
-
"pi-extensible-workflows": "5.17.
|
|
48
|
+
"pi-extensible-workflows": "5.17.2",
|
|
49
49
|
"typebox": "1.3.7"
|
|
50
50
|
},
|
|
51
51
|
"publishConfig": {
|