@piewf/cli 5.4.0 → 5.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/dist/src/bundles.js +4 -2
- package/dist/src/cli.d.ts +1 -0
- package/dist/src/cli.js +10 -5
- package/dist/src/doctor-cleanup.js +20 -11
- package/dist/src/doctor.d.ts +8 -2
- package/dist/src/doctor.js +105 -27
- package/dist/src/session-inspector.js +11 -7
- package/package.json +2 -2
package/dist/src/bundles.js
CHANGED
|
@@ -366,8 +366,10 @@ function roleMarkdown(role) {
|
|
|
366
366
|
metadata.push(`overrideSystemPrompt: ${String(role.overrideSystemPrompt)}`);
|
|
367
367
|
if (role.contextFiles !== undefined)
|
|
368
368
|
metadata.push(`contextFiles: ${JSON.stringify(role.contextFiles)}`);
|
|
369
|
-
if (role.
|
|
370
|
-
metadata.push(`
|
|
369
|
+
if (role.skills !== undefined)
|
|
370
|
+
metadata.push(`skills: ${JSON.stringify(role.skills)}`);
|
|
371
|
+
if (role.extensions !== undefined)
|
|
372
|
+
metadata.push(`extensions: ${JSON.stringify(role.extensions)}`);
|
|
371
373
|
metadata.push("---");
|
|
372
374
|
return `${metadata.join("\n")}\n${role.prompt ?? ""}\n`;
|
|
373
375
|
}
|
package/dist/src/cli.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export declare function parseWorkflowCliArgs(schema: JsonSchema, rawArgs: readon
|
|
|
18
18
|
export declare function parseDoctorArgs(rawArgs: readonly string[]): {
|
|
19
19
|
role?: string;
|
|
20
20
|
prompt?: string;
|
|
21
|
+
json?: boolean;
|
|
21
22
|
};
|
|
22
23
|
export declare function parseDoctorCleanupArgs(rawArgs: readonly string[]): Required<Pick<DoctorCleanupOptions, "olderThanDays" | "yes">>;
|
|
23
24
|
export declare function runCli(args: readonly string[], options?: CliOptions, write?: (text: string) => void): Promise<number>;
|
package/dist/src/cli.js
CHANGED
|
@@ -241,10 +241,15 @@ function parseInspectArgs(rawArgs) {
|
|
|
241
241
|
export function parseDoctorArgs(rawArgs) {
|
|
242
242
|
let role;
|
|
243
243
|
let prompt;
|
|
244
|
+
let json = false;
|
|
244
245
|
for (let index = 0; index < rawArgs.length; index += 1) {
|
|
245
246
|
const token = requiredArg(rawArgs, index);
|
|
246
247
|
const equals = token.indexOf("=");
|
|
247
248
|
const option = equals >= 0 ? token.slice(0, equals) : token;
|
|
249
|
+
if (option === "--json" && equals < 0) {
|
|
250
|
+
json = true;
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
248
253
|
if (option === "--role" || option === "--prompt") {
|
|
249
254
|
const value = equals >= 0 ? token.slice(equals + 1) : rawArgs[++index];
|
|
250
255
|
if (!value)
|
|
@@ -271,7 +276,7 @@ export function parseDoctorArgs(rawArgs) {
|
|
|
271
276
|
}
|
|
272
277
|
if (prompt !== undefined && role === undefined)
|
|
273
278
|
throw new Error("--prompt requires --role");
|
|
274
|
-
return { ...(role === undefined ? {} : { role }), ...(prompt === undefined ? {} : { prompt }) };
|
|
279
|
+
return { ...(role === undefined ? {} : { role }), ...(prompt === undefined ? {} : { prompt }), ...(json ? { json: true } : {}) };
|
|
275
280
|
}
|
|
276
281
|
export function parseDoctorCleanupArgs(rawArgs) {
|
|
277
282
|
let olderThanDays = 90;
|
|
@@ -737,13 +742,13 @@ export async function runCli(args, options = {}, write = (text) => { process.std
|
|
|
737
742
|
const stderr = options.stderr ?? ((text) => { process.stderr.write(text); });
|
|
738
743
|
if (args[0] === "doctor" && args[1] !== "cleanup") {
|
|
739
744
|
if (args.slice(1).some((arg) => arg === "--help" || arg === "-h")) {
|
|
740
|
-
write("Usage: piewf doctor [role] [--role <role>] [--prompt <text>]\n");
|
|
745
|
+
write("Usage: piewf doctor [role] [--role <role>] [--prompt <text>] [--json]\n");
|
|
741
746
|
return 0;
|
|
742
747
|
}
|
|
743
748
|
try {
|
|
744
|
-
const parsed = parseDoctorArgs(args.slice(1));
|
|
749
|
+
const { json, ...parsed } = parseDoctorArgs(args.slice(1));
|
|
745
750
|
const report = await doctor({ ...options, ...parsed });
|
|
746
|
-
write(formatDoctorReport(report));
|
|
751
|
+
write(json ? `${JSON.stringify(report)}\n` : formatDoctorReport(report));
|
|
747
752
|
return doctorExitCode(report);
|
|
748
753
|
}
|
|
749
754
|
catch (error) {
|
|
@@ -808,7 +813,7 @@ export async function runCli(args, options = {}, write = (text) => { process.std
|
|
|
808
813
|
return 1;
|
|
809
814
|
}
|
|
810
815
|
}
|
|
811
|
-
write("Usage: piewf doctor [role] [--role <role>] [--prompt <text>] | inspect [session-id] [--json|--summary] [--failed] | transcript <session-file> | bundle <workflow-name> [--name <command>] [--output <path>] [--force] | run <workflow-name> [workflow arguments] | export <workflow-name> [--name <command>] [--output <path>] [--force] [--bundle]\n");
|
|
816
|
+
write("Usage: piewf doctor [role] [--role <role>] [--prompt <text>] [--json] | inspect [session-id] [--json|--summary] [--failed] | transcript <session-file> | bundle <workflow-name> [--name <command>] [--output <path>] [--force] | run <workflow-name> [workflow arguments] | export <workflow-name> [--name <command>] [--output <path>] [--force] [--bundle]\n");
|
|
812
817
|
return 1;
|
|
813
818
|
}
|
|
814
819
|
if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
|
|
@@ -48,15 +48,18 @@ function model(value, label) { if (!object(value) || typeof value.provider !== "
|
|
|
48
48
|
function accounting(value, label) { if (!object(value))
|
|
49
49
|
throw new Error(`${label} is invalid`); for (const key of ["input", "output", "cacheRead", "cacheWrite", "cost"])
|
|
50
50
|
finiteNumber(value[key], `${label}.${key}`); }
|
|
51
|
-
function
|
|
51
|
+
function resourceSelectors(value, label) { if (value === undefined)
|
|
52
52
|
return; if (!object(value))
|
|
53
53
|
throw new Error(`${label} is invalid`); if (value.skills !== undefined)
|
|
54
54
|
stringList(value.skills, `${label}.skills`); if (value.extensions !== undefined)
|
|
55
|
-
stringList(value.extensions, `${label}.extensions`);
|
|
55
|
+
stringList(value.extensions, `${label}.extensions`); if (value.tools !== undefined)
|
|
56
|
+
stringList(value.tools, `${label}.tools`); }
|
|
56
57
|
function agentDefinition(value, label) { if (!object(value))
|
|
57
58
|
throw new Error(`${label} is invalid`); optionalString(value.prompt, `${label}.prompt`); optionalString(value.description, `${label}.description`); optionalString(value.model, `${label}.model`); if (value.thinking !== undefined && !isThinking(value.thinking))
|
|
58
59
|
throw new Error(`${label}.thinking is invalid`); if (value.tools !== undefined)
|
|
59
|
-
stringList(value.tools, `${label}.tools`);
|
|
60
|
+
stringList(value.tools, `${label}.tools`); if (value.skills !== undefined)
|
|
61
|
+
stringList(value.skills, `${label}.skills`); if (value.extensions !== undefined)
|
|
62
|
+
stringList(value.extensions, `${label}.extensions`); }
|
|
60
63
|
function optionalRole(value, label) {
|
|
61
64
|
if (value === undefined)
|
|
62
65
|
return;
|
|
@@ -78,11 +81,15 @@ function optionalRole(value, label) {
|
|
|
78
81
|
throw new Error(`${label}.overrideSystemPrompt is invalid`);
|
|
79
82
|
if (value.contextFiles !== undefined && value.contextFiles !== null)
|
|
80
83
|
stringList(value.contextFiles, `${label}.contextFiles`);
|
|
81
|
-
if (value.
|
|
82
|
-
|
|
84
|
+
if (value.skills !== undefined && value.skills !== null)
|
|
85
|
+
stringList(value.skills, `${label}.skills`);
|
|
86
|
+
if (value.extensions !== undefined && value.extensions !== null)
|
|
87
|
+
stringList(value.extensions, `${label}.extensions`);
|
|
83
88
|
}
|
|
84
89
|
function validateScheduledOptions(value, label) { if (!object(value) || typeof value.label !== "string" || !value.label || typeof value.cwd !== "string" || !value.cwd)
|
|
85
|
-
throw new Error(`${label} is invalid`); optionalString(value.requestedLabel, `${label}.requestedLabel`); optionalString(value.parentBreadcrumb, `${label}.parentBreadcrumb`); stringList(value.tools, `${label}.tools`);
|
|
90
|
+
throw new Error(`${label} is invalid`); optionalString(value.requestedLabel, `${label}.requestedLabel`); optionalString(value.parentBreadcrumb, `${label}.parentBreadcrumb`); stringList(value.tools, `${label}.tools`); if (value.skills !== undefined)
|
|
91
|
+
stringList(value.skills, `${label}.skills`); if (value.extensions !== undefined)
|
|
92
|
+
stringList(value.extensions, `${label}.extensions`); optionalString(value.worktreeOwner, `${label}.worktreeOwner`); optionalString(value.model, `${label}.model`); if (value.thinking !== undefined && !isThinking(value.thinking))
|
|
86
93
|
throw new Error(`${label}.thinking is invalid`); optionalRole(value.role, `${label}.role`); if (value.schema !== undefined)
|
|
87
94
|
validateSchema(value.schema, `${label}.schema`); if (value.retries !== undefined)
|
|
88
95
|
nonNegativeInteger(value.retries, `${label}.retries`); if (value.timeoutMs !== undefined && value.timeoutMs !== null)
|
|
@@ -104,7 +111,8 @@ function validateAgentSetup(value, label) {
|
|
|
104
111
|
stringList(value.tools, `${label}.tools`);
|
|
105
112
|
if (typeof value.cwd !== "string" || !value.cwd)
|
|
106
113
|
throw new Error(`${label}.cwd is invalid`);
|
|
107
|
-
|
|
114
|
+
if (value.resourceSelectors !== undefined)
|
|
115
|
+
resourceSelectors(value.resourceSelectors, `${label}.resourceSelectors`);
|
|
108
116
|
}
|
|
109
117
|
function validateAgent(value, label) {
|
|
110
118
|
if (!object(value) || typeof value.id !== "string" || !value.id || typeof value.name !== "string" || !value.name || typeof value.path !== "string" || !value.path || !isAgentState(value.state))
|
|
@@ -254,17 +262,18 @@ function validateSnapshot(snapshot) {
|
|
|
254
262
|
positiveInteger(snapshot.identityVersion, "Persisted snapshot identity version");
|
|
255
263
|
optionalString(snapshot.settingsPath, "Persisted snapshot settings path");
|
|
256
264
|
if (snapshot.settingsSources !== undefined) {
|
|
257
|
-
if (!object(snapshot.settingsSources) || typeof snapshot.settingsSources.concurrency !== "string" || typeof snapshot.settingsSources.modelAliases !== "string" || typeof snapshot.settingsSources.
|
|
265
|
+
if (!object(snapshot.settingsSources) || typeof snapshot.settingsSources.concurrency !== "string" || typeof snapshot.settingsSources.modelAliases !== "string" || snapshot.settingsSources.skills !== undefined && typeof snapshot.settingsSources.skills !== "string" || snapshot.settingsSources.extensions !== undefined && typeof snapshot.settingsSources.extensions !== "string" || snapshot.settingsSources.tools !== undefined && typeof snapshot.settingsSources.tools !== "string")
|
|
258
266
|
throw new Error("Persisted snapshot settings sources are invalid");
|
|
259
267
|
}
|
|
268
|
+
const settingsRecord = snapshot.settings;
|
|
269
|
+
resourceSelectors({ skills: settingsRecord.skills, extensions: settingsRecord.extensions, tools: settingsRecord.tools }, "Persisted snapshot selectors");
|
|
260
270
|
validateBudget(snapshot.budget);
|
|
261
271
|
if (snapshot.modelAliases !== undefined)
|
|
262
272
|
validateModelAliases(snapshot.modelAliases);
|
|
263
|
-
if (
|
|
264
|
-
validateModelAliases(
|
|
273
|
+
if (settingsRecord.modelAliases !== undefined)
|
|
274
|
+
validateModelAliases(settingsRecord.modelAliases);
|
|
265
275
|
if (snapshot.phases !== undefined)
|
|
266
276
|
stringList(snapshot.phases, "Persisted snapshot phases");
|
|
267
|
-
resourceExclusions(snapshot.settings.disabledAgentResources, "Persisted snapshot disabled resources");
|
|
268
277
|
if (snapshot.roles !== undefined) {
|
|
269
278
|
if (!object(snapshot.roles))
|
|
270
279
|
throw new Error("Persisted snapshot roles are invalid");
|
package/dist/src/doctor.d.ts
CHANGED
|
@@ -38,12 +38,18 @@ export interface DoctorRoleInspection {
|
|
|
38
38
|
};
|
|
39
39
|
tools: readonly string[];
|
|
40
40
|
resources: {
|
|
41
|
+
selectors: {
|
|
42
|
+
skills: readonly string[];
|
|
43
|
+
extensions: readonly string[];
|
|
44
|
+
tools: readonly string[];
|
|
45
|
+
};
|
|
41
46
|
skills: readonly string[];
|
|
42
47
|
extensions: readonly string[];
|
|
43
|
-
|
|
44
|
-
excludedExtensions: readonly string[];
|
|
48
|
+
tools: readonly string[];
|
|
45
49
|
unmatchedSkills: readonly string[];
|
|
46
50
|
unmatchedExtensions: readonly string[];
|
|
51
|
+
unmatchedTools: readonly string[];
|
|
52
|
+
selectorSources?: NonNullable<AgentResourcePolicy["selectorSources"]>;
|
|
47
53
|
};
|
|
48
54
|
systemPrompt: {
|
|
49
55
|
probe: string;
|
package/dist/src/doctor.js
CHANGED
|
@@ -2,10 +2,29 @@ import { readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
|
|
|
2
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
|
-
import { DEFAULT_SETTINGS, createLocalPiSession, errorText, isNodeError, isObject, loadSettings, prepareAgentSetupForInspection, resolveAgentResourcePolicy, resolveWorkflowSettings, resolveModelReference, parseThinking, parseRoleMarkdown, registeredWorkflowFunctions, registeredWorkflowRoleDirectoryRegistrations, workflowRoleDirectories, workflowProjectSettingsPath, workflowSettingsPath, } from "pi-extensible-workflows";
|
|
5
|
+
import { DEFAULT_SETTINGS, createLocalPiSession, errorText, isNodeError, isObject, loadSettings, prepareAgentSetupForInspection, resolveAgentResourcePolicy, resolveWorkflowSettings, resolveModelReference, resourcePatternHasMagic, parseThinking, parseRoleMarkdown, registeredWorkflowFunctions, registeredWorkflowRoleDirectoryRegistrations, workflowRoleDirectories, workflowProjectSettingsPath, workflowSettingsPath, } from "pi-extensible-workflows";
|
|
6
6
|
import { loadingRegistry } from "pi-extensible-workflows";
|
|
7
|
-
import {
|
|
7
|
+
import { selectResourcesByLayers, unmatchedResourcePatterns } from "pi-extensible-workflows";
|
|
8
8
|
const THINKING_HINT = "Use off, minimal, low, medium, high, xhigh, or max.";
|
|
9
|
+
const AGENT_RESOURCE_SELECTOR_MIGRATION_ISSUE = "https://github.com/vekexasia/pi-extensible-workflows/issues/205";
|
|
10
|
+
const AGENT_RESOURCE_SELECTOR_MIGRATION_MESSAGE = `\`disabledAgentResources\` is no longer supported by #205. Migrate to direct \`skills\`, \`extensions\`, and \`tools\` selectors: legacy patterns exclude resources and \`!pattern\` re-enables them, while new selectors include matches and \`!pattern\` excludes them. See ${AGENT_RESOURCE_SELECTOR_MIGRATION_ISSUE}`;
|
|
11
|
+
function usesLegacySettings(path) {
|
|
12
|
+
try {
|
|
13
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
14
|
+
return isObject(parsed) && Object.prototype.hasOwnProperty.call(parsed, "disabledAgentResources");
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function usesLegacyRoleSelectors(path) {
|
|
21
|
+
try {
|
|
22
|
+
return /^\s*disabledAgentResources\s*:/m.test(readFileSync(path, "utf8"));
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
9
28
|
function canonical(path) {
|
|
10
29
|
const absolute = resolve(path);
|
|
11
30
|
try {
|
|
@@ -170,9 +189,17 @@ function roleProvenance(source) {
|
|
|
170
189
|
function diagnostic(severity, code, message, source, hint) {
|
|
171
190
|
return { severity, code, message, ...(source ? { source } : {}), ...(hint ? { hint } : {}) };
|
|
172
191
|
}
|
|
192
|
+
function legacyAgentResourceSelectorDiagnostic(source) {
|
|
193
|
+
return diagnostic("error", "AGENT_RESOURCE_SELECTOR_MIGRATION", AGENT_RESOURCE_SELECTOR_MIGRATION_MESSAGE, source, "Replace disabledAgentResources with direct selectors and use !* before positive allow-list patterns.");
|
|
194
|
+
}
|
|
195
|
+
function positiveOnlyToolSelectorDiagnostic(source, selectors) {
|
|
196
|
+
if (!selectors?.length || selectors.some((selector) => selector.startsWith("!")))
|
|
197
|
+
return undefined;
|
|
198
|
+
return diagnostic("warning", "AGENT_RESOURCE_TOOL_SELECTOR_ALLOWLIST", "Positive-only tool selectors do not restrict the default-enabled candidate set.", `${source}.tools`, "Prepend !* before positive patterns to make this an allow-list.");
|
|
199
|
+
}
|
|
173
200
|
function emptyResourcePolicy(globalSettingsPath, cwd, projectTrusted) {
|
|
174
|
-
const empty = { skills: [], extensions: [] };
|
|
175
|
-
return { globalSettingsPath, projectSettingsPath: workflowProjectSettingsPath(cwd), projectTrusted, global: empty, project: empty, effective: empty, unmatchedSkills: [], unmatchedExtensions: [] };
|
|
201
|
+
const empty = { skills: [], extensions: [], tools: [] };
|
|
202
|
+
return { globalSettingsPath, projectSettingsPath: workflowProjectSettingsPath(cwd), projectTrusted, global: empty, project: empty, effective: empty, unmatchedSkills: [], unmatchedExtensions: [], unmatchedTools: [], selectorSources: { global: {}, project: {} } };
|
|
176
203
|
}
|
|
177
204
|
function validateModel(value, known, available, source, diagnostics, aliases, dynamicAliases, settingsPath) {
|
|
178
205
|
if (isDynamicModelAlias(value, dynamicAliases))
|
|
@@ -194,10 +221,17 @@ function inspectRole(path, activeTools, knownModels, availableModels, diagnostic
|
|
|
194
221
|
definition = parseRoleMarkdown(readFileSync(path, "utf8"), true, path);
|
|
195
222
|
}
|
|
196
223
|
catch (error) {
|
|
224
|
+
if (usesLegacyRoleSelectors(path)) {
|
|
225
|
+
diagnostics.push(legacyAgentResourceSelectorDiagnostic(path));
|
|
226
|
+
return undefined;
|
|
227
|
+
}
|
|
197
228
|
const message = error instanceof Error ? error.message : String(error);
|
|
198
229
|
diagnostics.push(diagnostic("error", "ROLE_FRONTMATTER", source ? `${roleProvenance(source)} contains invalid role at "${path}": ${message}` : message, path, "Fix the role YAML frontmatter."));
|
|
199
230
|
return undefined;
|
|
200
231
|
}
|
|
232
|
+
const toolSelectorDiagnostic = positiveOnlyToolSelectorDiagnostic(path, definition.tools);
|
|
233
|
+
if (toolSelectorDiagnostic)
|
|
234
|
+
diagnostics.push(toolSelectorDiagnostic);
|
|
201
235
|
const body = definition.prompt ?? "";
|
|
202
236
|
if (body.trim() === "")
|
|
203
237
|
diagnostics.push(diagnostic("warning", "ROLE_BODY_EMPTY", "Role body is empty", path));
|
|
@@ -207,15 +241,22 @@ function inspectRole(path, activeTools, knownModels, availableModels, diagnostic
|
|
|
207
241
|
diagnostics.push(diagnostic("warning", "ROLE_PLACEHOLDER", "Role body contains an unsupported placeholder-looking token", path));
|
|
208
242
|
if (definition.model)
|
|
209
243
|
validateModel(definition.model, knownModels, availableModels, path, diagnostics, aliases, dynamicAliases, settingsPath);
|
|
210
|
-
for (const
|
|
211
|
-
|
|
244
|
+
for (const selector of definition.tools ?? []) {
|
|
245
|
+
const tool = selector.startsWith("!") ? selector.slice(1) : selector;
|
|
246
|
+
if (!selector.startsWith("!") && !resourcePatternHasMagic(selector) && !activeTools.has(tool))
|
|
212
247
|
diagnostics.push(diagnostic("error", "ROLE_TOOL_INACTIVE", `Tool is unknown or inactive: ${tool}`, path, "Use a tool listed under Pi active tools or enable its Pi extension."));
|
|
248
|
+
}
|
|
213
249
|
return definition;
|
|
214
250
|
}
|
|
215
251
|
function matchResourcePolicy(policy, pi) {
|
|
216
252
|
const extensions = [...new Set((pi.extensions ?? []).map(canonical))];
|
|
217
253
|
const skills = [...new Set(pi.skills ?? [])];
|
|
218
|
-
|
|
254
|
+
const tools = [...new Set(pi.activeTools)];
|
|
255
|
+
const layers = policy.selectorSources;
|
|
256
|
+
const selectedSkills = selectResourcesByLayers([layers.global.skills, layers.project.skills], skills);
|
|
257
|
+
const selectedExtensions = selectResourcesByLayers([layers.global.extensions, layers.project.extensions], extensions);
|
|
258
|
+
const selectedTools = selectResourcesByLayers([layers.global.tools, layers.project.tools], tools);
|
|
259
|
+
return { ...policy, selectedSkills, selectedExtensions, selectedTools, unmatchedSkills: unmatchedResourcePatterns(policy.effective.skills, skills), unmatchedExtensions: unmatchedResourcePatterns(policy.effective.extensions, extensions), unmatchedTools: unmatchedResourcePatterns(policy.effective.tools ?? [], tools) };
|
|
219
260
|
}
|
|
220
261
|
async function inspectRoleSession(cwd, agentDir, roleName, definition, rolePath, basePolicy, rootModel, activeTools, aliases, knownModels, availableModels, settingsPath, prompt, hooks, diagnostics) {
|
|
221
262
|
const setupDiagnostics = [];
|
|
@@ -260,7 +301,7 @@ async function inspectRoleSession(cwd, agentDir, roleName, definition, rolePath,
|
|
|
260
301
|
const policy = prepared.setup.sessionInput.resourcePolicy ?? basePolicy;
|
|
261
302
|
for (const item of [...resources.diagnostics, ...promptResult.diagnostics])
|
|
262
303
|
setupDiagnostics.push(diagnostic(item.type === "error" ? "error" : "warning", "ROLE_INSPECTION", item.message, item.source));
|
|
263
|
-
return { role: roleName, path: rolePath, model: actualModel, tools: state?.tools.map(({ name }) => name) ?? [...prepared.setup.sessionInput.tools], resources: { skills:
|
|
304
|
+
return { role: roleName, path: rolePath, model: actualModel, tools: state?.tools.map(({ name }) => name) ?? [...prepared.setup.sessionInput.tools], resources: { selectors: { skills: [...policy.effective.skills], extensions: [...policy.effective.extensions], tools: [...(policy.effective.tools ?? [])] }, skills: policy.selectedSkills ?? resources.skills, extensions: policy.selectedExtensions ?? resources.extensions, tools: policy.selectedTools ?? prepared.setup.sessionInput.tools, unmatchedSkills: policy.unmatchedSkills, unmatchedExtensions: policy.unmatchedExtensions, unmatchedTools: policy.unmatchedTools ?? [], selectorSources: policy.selectorSources }, systemPrompt: { probe: prompt, expandedProbe: promptResult.expandedPrompt, text: promptResult.systemPrompt, ...(resources.systemPromptSource ? { source: resources.systemPromptSource } : {}) }, setup: { hooks: prepared.summary.hookNames, diagnostics: setupDiagnostics } };
|
|
264
305
|
}
|
|
265
306
|
catch (error) {
|
|
266
307
|
setupDiagnostics.push(diagnostic("error", "ROLE_INSPECTION", error instanceof Error ? error.message : String(error), rolePath));
|
|
@@ -276,6 +317,8 @@ export async function doctor(options = {}) {
|
|
|
276
317
|
const cwd = canonical(options.cwd ?? process.cwd());
|
|
277
318
|
const agentDir = canonical(options.agentDir ?? getAgentDir());
|
|
278
319
|
const settingsPath = canonical(options.settingsPath ?? workflowSettingsPath(agentDir));
|
|
320
|
+
const projectSettingsPath = workflowProjectSettingsPath(cwd);
|
|
321
|
+
const legacyGlobalSettings = usesLegacySettings(settingsPath);
|
|
279
322
|
const diagnostics = [];
|
|
280
323
|
let settings = DEFAULT_SETTINGS;
|
|
281
324
|
try {
|
|
@@ -284,8 +327,7 @@ export async function doctor(options = {}) {
|
|
|
284
327
|
catch (error) {
|
|
285
328
|
diagnostics.push(diagnostic("error", "SETTINGS_INVALID", errorText(error), settingsPath, "Fix or remove the invalid workflow settings file."));
|
|
286
329
|
}
|
|
287
|
-
let settingsSources = { concurrency: settingsPath, modelAliases: settingsPath,
|
|
288
|
-
const projectSettingsPath = workflowProjectSettingsPath(cwd);
|
|
330
|
+
let settingsSources = { concurrency: settingsPath, modelAliases: settingsPath, skills: settingsPath, extensions: settingsPath, tools: settingsPath };
|
|
289
331
|
let pi;
|
|
290
332
|
try {
|
|
291
333
|
pi = await (options.discoverPi ?? discoverPi)(cwd, agentDir);
|
|
@@ -298,6 +340,11 @@ export async function doctor(options = {}) {
|
|
|
298
340
|
pi = { ...pi, activeTools: options.activeTools.filter((tool) => tool !== "workflow" && tool !== "workflow_respond" && tool !== "workflow_catalog") };
|
|
299
341
|
if (pi.trust.required && !pi.trust.trusted)
|
|
300
342
|
diagnostics.push(diagnostic("warning", "PROJECT_UNTRUSTED", "Pi project resources are inactive because the project is not trusted", cwd, "Open this project in Pi, choose Trust, then rerun doctor."));
|
|
343
|
+
const legacyProjectSettings = pi.trust.trusted && usesLegacySettings(projectSettingsPath);
|
|
344
|
+
if (legacyGlobalSettings)
|
|
345
|
+
diagnostics.push(legacyAgentResourceSelectorDiagnostic(`${settingsPath}.disabledAgentResources`));
|
|
346
|
+
if (legacyProjectSettings)
|
|
347
|
+
diagnostics.push(legacyAgentResourceSelectorDiagnostic(`${projectSettingsPath}.disabledAgentResources`));
|
|
301
348
|
for (const error of pi.extensionErrors)
|
|
302
349
|
diagnostics.push(diagnostic("error", "EXTENSION_LOAD", error.message, error.path, "Fix or disable the failing Pi extension."));
|
|
303
350
|
try {
|
|
@@ -322,10 +369,17 @@ export async function doctor(options = {}) {
|
|
|
322
369
|
diagnostics.push(diagnostic("error", "SETTINGS_INVALID", message, source, "Fix or remove the invalid workflow settings file."));
|
|
323
370
|
resourcePolicy = emptyResourcePolicy(settingsPath, cwd, pi.trust.trusted);
|
|
324
371
|
}
|
|
372
|
+
for (const [source, selectors] of [[resourcePolicy.globalSettingsPath, resourcePolicy.selectorSources.global.tools], [resourcePolicy.projectSettingsPath, resourcePolicy.selectorSources.project.tools]]) {
|
|
373
|
+
const toolSelectorDiagnostic = positiveOnlyToolSelectorDiagnostic(source, selectors);
|
|
374
|
+
if (toolSelectorDiagnostic)
|
|
375
|
+
diagnostics.push(toolSelectorDiagnostic);
|
|
376
|
+
}
|
|
325
377
|
for (const skill of resourcePolicy.unmatchedSkills)
|
|
326
|
-
diagnostics.push(diagnostic("warning", "AGENT_RESOURCE_UNMATCHED", `
|
|
378
|
+
diagnostics.push(diagnostic("warning", "AGENT_RESOURCE_UNMATCHED", `Skill selector currently matches no discovered skill: ${skill}`, `${resourcePolicySource(settingsSources.skills ?? settingsPath)}.skills`));
|
|
327
379
|
for (const extension of resourcePolicy.unmatchedExtensions)
|
|
328
|
-
diagnostics.push(diagnostic("warning", "AGENT_RESOURCE_UNMATCHED", `
|
|
380
|
+
diagnostics.push(diagnostic("warning", "AGENT_RESOURCE_UNMATCHED", `Extension selector currently matches no discovered extension source: ${extension}`, `${resourcePolicySource(settingsSources.extensions ?? settingsPath)}.extensions`));
|
|
381
|
+
for (const tool of resourcePolicy.unmatchedTools ?? [])
|
|
382
|
+
diagnostics.push(diagnostic("warning", "AGENT_RESOURCE_UNMATCHED", `Tool selector currently matches no root tool: ${tool}`, `${resourcePolicySource(settingsSources.tools ?? settingsPath)}.tools`));
|
|
329
383
|
const activeTools = new Set(pi.activeTools);
|
|
330
384
|
const knownModels = new Set(pi.knownModels);
|
|
331
385
|
const availableModels = new Set(pi.availableModels);
|
|
@@ -414,6 +468,9 @@ export async function doctor(options = {}) {
|
|
|
414
468
|
else
|
|
415
469
|
definitions.delete(name);
|
|
416
470
|
}
|
|
471
|
+
const rolePaths = new Set(roles.map(({ path }) => path));
|
|
472
|
+
if (diagnostics.some(({ code, source }) => source !== undefined && rolePaths.has(source) && (code === "ROLE_FRONTMATTER" || code === "AGENT_RESOURCE_SELECTOR_MIGRATION")))
|
|
473
|
+
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."));
|
|
417
474
|
let roleInspection;
|
|
418
475
|
if (options.role !== undefined) {
|
|
419
476
|
const activeRole = roles.find(({ name, active }) => name === options.role && active);
|
|
@@ -459,24 +516,44 @@ export async function doctor(options = {}) {
|
|
|
459
516
|
}
|
|
460
517
|
function count(report, severity) { return report.diagnostics.filter((item) => item.severity === severity).length; }
|
|
461
518
|
export function doctorExitCode(report) { return count(report, "error") > 0 ? 1 : 0; }
|
|
462
|
-
function exposedValues(values, excluded) {
|
|
463
|
-
const disabled = new Set(excluded);
|
|
464
|
-
return values.filter((value) => !disabled.has(value));
|
|
465
|
-
}
|
|
466
519
|
function nestedValues(label, values) {
|
|
467
520
|
return [`- ${label}:`, ...(values.length ? values.map((value) => ` - \`${value}\``) : [" - (none)"])];
|
|
468
521
|
}
|
|
522
|
+
function roleSelectorSourceLines(sources) {
|
|
523
|
+
return [
|
|
524
|
+
...nestedValues("Global skill selectors", sources.global.skills ?? []),
|
|
525
|
+
...nestedValues("Global extension selectors", sources.global.extensions ?? []),
|
|
526
|
+
...nestedValues("Global tool selectors", sources.global.tools ?? []),
|
|
527
|
+
...nestedValues("Project skill selectors", sources.project.skills ?? []),
|
|
528
|
+
...nestedValues("Project extension selectors", sources.project.extensions ?? []),
|
|
529
|
+
...nestedValues("Project tool selectors", sources.project.tools ?? []),
|
|
530
|
+
...(sources.role === undefined ? [] : [
|
|
531
|
+
...nestedValues("Role skill selectors", sources.role.skills ?? []),
|
|
532
|
+
...nestedValues("Role extension selectors", sources.role.extensions ?? []),
|
|
533
|
+
...nestedValues("Role tool selectors", sources.role.tools ?? []),
|
|
534
|
+
]),
|
|
535
|
+
...(sources.call === undefined ? [] : [
|
|
536
|
+
...nestedValues("Call skill selectors", sources.call.skills ?? []),
|
|
537
|
+
...nestedValues("Call extension selectors", sources.call.extensions ?? []),
|
|
538
|
+
...nestedValues("Call tool selectors", sources.call.tools ?? []),
|
|
539
|
+
]),
|
|
540
|
+
];
|
|
541
|
+
}
|
|
469
542
|
function roleInspectionLines(inspection) {
|
|
470
543
|
return [
|
|
471
544
|
`- Role: \`${inspection.role}\` - \`${inspection.path}\``,
|
|
472
545
|
`- Model: \`${inspection.model.provider}/${inspection.model.model}\` (${inspection.model.inherited ? "inherited, " : ""}${inspection.model.thinking ?? "off"})`,
|
|
546
|
+
...(inspection.resources.selectorSources ? roleSelectorSourceLines(inspection.resources.selectorSources) : []),
|
|
473
547
|
...nestedValues("Tools", inspection.tools),
|
|
548
|
+
...nestedValues("Configured skill selectors", inspection.resources.selectors.skills),
|
|
474
549
|
...nestedValues("Effective skills", inspection.resources.skills),
|
|
550
|
+
...nestedValues("Configured extension selectors", inspection.resources.selectors.extensions),
|
|
475
551
|
...nestedValues("Effective extensions", inspection.resources.extensions),
|
|
476
|
-
...nestedValues("
|
|
477
|
-
...nestedValues("
|
|
552
|
+
...nestedValues("Configured tool selectors", inspection.resources.selectors.tools),
|
|
553
|
+
...nestedValues("Effective tools", inspection.resources.tools),
|
|
478
554
|
...nestedValues("Unmatched skills", inspection.resources.unmatchedSkills),
|
|
479
555
|
...nestedValues("Unmatched extensions", inspection.resources.unmatchedExtensions),
|
|
556
|
+
...nestedValues("Unmatched tools", inspection.resources.unmatchedTools),
|
|
480
557
|
`- Prompt probe: ${inspection.systemPrompt.probe ? JSON.stringify(inspection.systemPrompt.probe) : "empty"}`,
|
|
481
558
|
`- Expanded probe: ${JSON.stringify(inspection.systemPrompt.expandedProbe)}`,
|
|
482
559
|
`- System prompt source: ${inspection.systemPrompt.source ?? "(none)"}`,
|
|
@@ -504,6 +581,7 @@ export function formatDoctorReport(report) {
|
|
|
504
581
|
];
|
|
505
582
|
return `${lines.join("\n")}\n`;
|
|
506
583
|
}
|
|
584
|
+
const roleLoadingFailed = report.diagnostics.some(({ code }) => code === "ROLE_LOAD_BLOCKED");
|
|
507
585
|
const lines = [
|
|
508
586
|
"# pi-extensible-workflows doctor",
|
|
509
587
|
"",
|
|
@@ -512,7 +590,7 @@ export function formatDoctorReport(report) {
|
|
|
512
590
|
`- Agent dir: \`${report.agentDir}\``,
|
|
513
591
|
`- Global workflow settings: \`${report.settingsPath}\``,
|
|
514
592
|
`- Project workflow settings: \`${report.resourcePolicy.projectSettingsPath}\` (${report.resourcePolicy.projectTrusted ? "trusted" : "ignored: project untrusted"})`,
|
|
515
|
-
`- Effective setting sources: concurrency=\`${report.settingsSources.concurrency}\`, modelAliases=\`${report.settingsSources.modelAliases}\`,
|
|
593
|
+
`- Effective setting sources: concurrency=\`${report.settingsSources.concurrency}\`, modelAliases=\`${report.settingsSources.modelAliases}\`, skills=\`${report.settingsSources.skills ?? "(none)"}\`, extensions=\`${report.settingsSources.extensions ?? "(none)"}\`, tools=\`${report.settingsSources.tools ?? "(none)"}\``,
|
|
516
594
|
`- Limits: concurrency=${String(report.settings.concurrency)}`,
|
|
517
595
|
"",
|
|
518
596
|
"## Trust/resources",
|
|
@@ -527,24 +605,24 @@ export function formatDoctorReport(report) {
|
|
|
527
605
|
"## Pi active skills",
|
|
528
606
|
...(report.piSkills.length ? report.piSkills.map((skill) => `- \`${skill}\``) : ["- None resolved"]),
|
|
529
607
|
"",
|
|
530
|
-
"## Workflow agent
|
|
608
|
+
"## Workflow agent resource selectors",
|
|
531
609
|
`- Global settings: \`${report.resourcePolicy.globalSettingsPath}\``,
|
|
532
610
|
`- Global skills: ${report.resourcePolicy.global.skills.join(", ") || "(none)"}`,
|
|
533
611
|
`- Global extensions: ${report.resourcePolicy.global.extensions.join(", ") || "(none)"}`,
|
|
612
|
+
`- Global tools: ${(report.resourcePolicy.global.tools ?? []).join(", ") || "(none)"}`,
|
|
534
613
|
`- Project settings: \`${report.resourcePolicy.projectSettingsPath}\` (${report.resourcePolicy.projectTrusted ? "trusted" : "ignored: project untrusted"})`,
|
|
535
614
|
`- Project skills: ${report.resourcePolicy.project.skills.join(", ") || "(none)"}`,
|
|
536
615
|
`- Project extensions: ${report.resourcePolicy.project.extensions.join(", ") || "(none)"}`,
|
|
537
|
-
`-
|
|
538
|
-
`- Effective
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
...nestedValues("Disabled skills", report.resourcePolicy.excludedSkills ?? []),
|
|
542
|
-
...nestedValues("Disabled extensions", report.resourcePolicy.excludedExtensions ?? []),
|
|
616
|
+
`- Project tools: ${(report.resourcePolicy.project.tools ?? []).join(", ") || "(none)"}`,
|
|
617
|
+
`- Effective skills: ${(report.resourcePolicy.selectedSkills ?? []).join(", ") || "(none)"}`,
|
|
618
|
+
`- Effective extensions: ${(report.resourcePolicy.selectedExtensions ?? []).join(", ") || "(none)"}`,
|
|
619
|
+
`- Effective tools: ${(report.resourcePolicy.selectedTools ?? []).join(", ") || "(none)"}`,
|
|
543
620
|
`- Unmatched skills: ${report.resourcePolicy.unmatchedSkills.join(", ") || "(none)"}`,
|
|
544
621
|
`- Unmatched extensions: ${report.resourcePolicy.unmatchedExtensions.join(", ") || "(none)"}`,
|
|
622
|
+
`- Unmatched tools: ${(report.resourcePolicy.unmatchedTools ?? []).join(", ") || "(none)"}`,
|
|
545
623
|
"",
|
|
546
624
|
"## Roles",
|
|
547
|
-
...(report.roles.length ? report.roles.map((role) => `- \`${role.name}\` (${role.scope}, ${role.active ? "active" : role.overriddenBy ? `overridden by ${role.overriddenBy}` : "inactive: project untrusted"}) - \`${role.path}\`${role.extension ? `; ${extensionLabel(role.extension)} role directory "${dirname(role.path)}"` : ""}${role.overrides ? `; overrides \`${role.overrides}\`` : ""}`) : ["- None found"]),
|
|
625
|
+
...(report.roles.length ? report.roles.map((role) => `- \`${role.name}\` (${role.scope}, ${role.active ? roleLoadingFailed ? "unavailable: role loading failed" : "active" : role.overriddenBy ? `overridden by ${role.overriddenBy}` : "inactive: project untrusted"}) - \`${role.path}\`${role.extension ? `; ${extensionLabel(role.extension)} role directory "${dirname(role.path)}"` : ""}${role.overrides ? `; overrides \`${role.overrides}\`` : ""}`) : ["- None found"]),
|
|
548
626
|
"",
|
|
549
627
|
"## Model aliases",
|
|
550
628
|
...(report.modelAliases.length ? report.modelAliases.map((alias) => `- [${alias.kind}] \`${alias.name}\`${alias.kind === "static" ? ` -> ${report.settings.modelAliases?.[alias.name] ?? "(unresolved)"}` : ""} (${alias.provenance})`) : ["- None registered"]),
|
|
@@ -304,13 +304,17 @@ function detailLines(workflow) {
|
|
|
304
304
|
lines.push(`Attempt ${String(attempt.attempt)} · ${attempt.model}${attempt.thinking !== undefined ? `:${attempt.thinking}` : ""} · ${money(attempt.cost)}${attempt.error ? ` · ${attempt.error}` : ""}`, `Prompt: ${attempt.prompt}`, ...(attempt.setup ? [
|
|
305
305
|
`Hooks: ${attempt.setup.hookNames.join(", ") || "(none)"}`,
|
|
306
306
|
`Effective: model=${attempt.setup.model.provider}/${attempt.setup.model.model}${attempt.setup.model.thinking ? `:${attempt.setup.model.thinking}` : ""} tools=${attempt.setup.tools.join(",") || "(none)"} cwd=${attempt.setup.cwd}`,
|
|
307
|
-
...(attempt.setup.
|
|
308
|
-
`
|
|
309
|
-
`
|
|
310
|
-
`
|
|
311
|
-
`
|
|
312
|
-
`
|
|
313
|
-
`
|
|
307
|
+
...(attempt.setup.resourceSelectors ? [
|
|
308
|
+
`Selector sources: global/project/role/call are persisted with the attempt`,
|
|
309
|
+
`Configured skill patterns: ${attempt.setup.resourceSelectors.selectors.skills.join(", ") || "(none)"}`,
|
|
310
|
+
`Effective skills: ${attempt.setup.resourceSelectors.skills.join(", ") || "(none)"}`,
|
|
311
|
+
`Configured extension patterns: ${attempt.setup.resourceSelectors.selectors.extensions.join(", ") || "(none)"}`,
|
|
312
|
+
`Effective extensions: ${attempt.setup.resourceSelectors.extensions.join(", ") || "(none)"}`,
|
|
313
|
+
`Configured tool patterns: ${(attempt.setup.resourceSelectors.selectors.tools ?? []).join(", ") || "(none)"}`,
|
|
314
|
+
`Effective tools: ${attempt.setup.resourceSelectors.tools.join(", ") || "(none)"}`,
|
|
315
|
+
`Unmatched skills: ${attempt.setup.resourceSelectors.unmatchedSkills.join(", ") || "(none)"}`,
|
|
316
|
+
`Unmatched extensions: ${attempt.setup.resourceSelectors.unmatchedExtensions.join(", ") || "(none)"}`,
|
|
317
|
+
`Unmatched tools: ${attempt.setup.resourceSelectors.unmatchedTools.join(", ") || "(none)"}`,
|
|
314
318
|
] : []),
|
|
315
319
|
] : []));
|
|
316
320
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@piewf/cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.5.0",
|
|
4
4
|
"description": "CLI for pi-extensible-workflows",
|
|
5
5
|
"homepage": "https://vekexasia.github.io/pi-extensible-workflows/",
|
|
6
6
|
"repository": {
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
},
|
|
47
47
|
"license": "MIT",
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"pi-extensible-workflows": "5.
|
|
49
|
+
"pi-extensible-workflows": "5.5.0"
|
|
50
50
|
},
|
|
51
51
|
"publishConfig": {
|
|
52
52
|
"access": "public"
|