pi-workflow-engine 1.1.0 → 1.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.
- package/.pi/extensions/pi-workflow-engine/index.ts +199 -85
- package/.pi/extensions/pi-workflow-engine/src/advisory-challenge.ts +5 -2
- package/.pi/extensions/pi-workflow-engine/src/advisory-schema.ts +8 -0
- package/.pi/extensions/pi-workflow-engine/src/agent-limits.ts +4 -0
- package/.pi/extensions/pi-workflow-engine/src/agent-runner-types.ts +3 -1
- package/.pi/extensions/pi-workflow-engine/src/agent-runner.ts +18 -4
- package/.pi/extensions/pi-workflow-engine/src/agent-session.ts +34 -7
- package/.pi/extensions/pi-workflow-engine/src/background-workflow-tool.ts +9 -3
- package/.pi/extensions/pi-workflow-engine/src/background-workflows.ts +51 -8
- package/.pi/extensions/pi-workflow-engine/src/cancellation.ts +14 -0
- package/.pi/extensions/pi-workflow-engine/src/codemode-slots.ts +73 -0
- package/.pi/extensions/pi-workflow-engine/src/config-file.ts +52 -0
- package/.pi/extensions/pi-workflow-engine/src/discovery.ts +127 -58
- package/.pi/extensions/pi-workflow-engine/src/dynamax.ts +112 -31
- package/.pi/extensions/pi-workflow-engine/src/engine.ts +75 -18
- package/.pi/extensions/pi-workflow-engine/src/filesystem-error.ts +5 -0
- package/.pi/extensions/pi-workflow-engine/src/inline-workflow.ts +7 -1
- package/.pi/extensions/pi-workflow-engine/src/journal.ts +22 -4
- package/.pi/extensions/pi-workflow-engine/src/model-profiles.ts +9 -47
- package/.pi/extensions/pi-workflow-engine/src/progress-types.ts +1 -1
- package/.pi/extensions/pi-workflow-engine/src/progress.ts +32 -4
- package/.pi/extensions/pi-workflow-engine/src/prompt-sections.ts +15 -0
- package/.pi/extensions/pi-workflow-engine/src/research-evidence.ts +4 -4
- package/.pi/extensions/pi-workflow-engine/src/resume-context.ts +1 -1
- package/.pi/extensions/pi-workflow-engine/src/types.ts +54 -9
- package/.pi/extensions/pi-workflow-engine/src/ui/kit.ts +1 -0
- package/.pi/extensions/pi-workflow-engine/src/ui/workflow-format.ts +9 -3
- package/.pi/extensions/pi-workflow-engine/src/ui/workflow-inspector.ts +69 -18
- package/.pi/extensions/pi-workflow-engine/src/ui/workflow-runs-browser.ts +17 -6
- package/.pi/extensions/pi-workflow-engine/src/ui/workflow-tool-renderer.ts +8 -3
- package/.pi/extensions/pi-workflow-engine/src/ui/workflow-widget.ts +16 -8
- package/.pi/extensions/pi-workflow-engine/src/workflow-advisory-utils.ts +82 -23
- package/.pi/extensions/pi-workflow-engine/src/workflow-catalog.ts +79 -0
- package/.pi/extensions/pi-workflow-engine/src/workflow-execution.ts +6 -2
- package/.pi/extensions/pi-workflow-engine/src/workflow-input.ts +13 -0
- package/.pi/extensions/pi-workflow-engine/src/workflow-module.ts +21 -4
- package/.pi/extensions/pi-workflow-engine/src/workflow-result-context.ts +26 -6
- package/.pi/extensions/pi-workflow-engine/src/workflow-run-background.ts +2 -0
- package/.pi/extensions/pi-workflow-engine/src/workflow-run-controller.ts +69 -13
- package/.pi/extensions/pi-workflow-engine/src/workflow-run-history.ts +23 -4
- package/.pi/extensions/pi-workflow-engine/src/workflow-run-record.ts +27 -1
- package/.pi/extensions/pi-workflow-engine/src/workflow-run-seal.ts +83 -0
- package/.pi/extensions/pi-workflow-engine/src/workflow-run-store.ts +30 -4
- package/.pi/extensions/pi-workflow-engine/src/workflow-script-file.ts +50 -0
- package/.pi/extensions/pi-workflow-engine/src/workflow-settings.ts +147 -0
- package/.pi/extensions/pi-workflow-engine/src/workflow-tool-request.ts +61 -0
- package/.pi/extensions/pi-workflow-engine/workflows/code-review.ts +59 -22
- package/.pi/extensions/pi-workflow-engine/workflows/diagnose.ts +7 -4
- package/.pi/extensions/pi-workflow-engine/workflows/perf-review.ts +7 -4
- package/.pi/extensions/pi-workflow-engine/workflows/refactor-scout.ts +7 -4
- package/.pi/extensions/pi-workflow-engine/workflows/research.ts +12 -4
- package/README.md +11 -3
- package/USAGE.md +124 -23
- package/package.json +13 -4
- package/skills/workflow-authoring/SKILL.md +162 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { challengeFindings, parseChallengeArgs } from "../src/advisory-challenge.ts";
|
|
2
|
-
import { Type } from "typebox";
|
|
2
|
+
import { Type, type Static } from "typebox";
|
|
3
3
|
import {
|
|
4
4
|
type AdvisoryVerified,
|
|
5
5
|
type AdvisoryLens,
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
formatEvidence,
|
|
10
10
|
formatLocation,
|
|
11
11
|
runLensVerificationPipeline,
|
|
12
|
+
verifierCandidates,
|
|
12
13
|
EMPTY_LENS_REVIEW_STATS,
|
|
13
14
|
verdictConfidence,
|
|
14
15
|
DEFAULT_ADVISORY_TOOL_HINTS,
|
|
@@ -23,6 +24,7 @@ import type { WorkflowApi, WorkflowMeta } from "../src/types.ts";
|
|
|
23
24
|
export const meta: WorkflowMeta = {
|
|
24
25
|
name: "code-review",
|
|
25
26
|
description: "Fan-out review of the branch's open PR (or branch vs main): scope → per-angle find → independent verify → synthesize.",
|
|
27
|
+
whenToUse: "Use to review a PR, branch diff, ref range, or focused target for correctness bugs and cleanups, with every finding independently verified; args are the target or review instructions, blank auto-detects the diff.",
|
|
26
28
|
phases: [{ title: "Scope" }, { title: "Find" }, { title: "Verify" }, { title: "Challenge" }, { title: "Synthesize" }],
|
|
27
29
|
};
|
|
28
30
|
|
|
@@ -34,6 +36,16 @@ const ScopeSchema = Type.Object({
|
|
|
34
36
|
conventions: Type.Optional(Type.String({ description: "Relevant AGENTS.md / project conventions" })),
|
|
35
37
|
});
|
|
36
38
|
|
|
39
|
+
// With review instructions, scope splits them by stage: finders get only where to look, because a reporting bar in a
|
|
40
|
+
// finder prompt makes finders filter their own candidates before any verifier sees them; the bar goes to synthesis.
|
|
41
|
+
const InstructedScopeSchema = Type.Object({
|
|
42
|
+
...ScopeSchema.properties,
|
|
43
|
+
conventions: Type.Optional(Type.String({ description: "Repository conventions from AGENTS.md and project docs only. Do not restate the user's review instructions, filtering rules or already-reported findings here." })),
|
|
44
|
+
focus: Type.String({ description: "Where the user wants reviewers to look: areas, files, risks and lenses named in the instructions. Exclude any filtering, evidence, reporting or deduplication rules; write (none) if the instructions name no focus." }),
|
|
45
|
+
reportingBar: Type.String({ description: "The user's filtering, evidence, severity and reporting rules restated so a reader without file access can apply them, listing every already-reported issue to exclude (read any files the instructions reference). Write (none) if the instructions set no bar." }),
|
|
46
|
+
});
|
|
47
|
+
type Scope = Static<typeof ScopeSchema> & Partial<Pick<Static<typeof InstructedScopeSchema>, "focus" | "reportingBar">>;
|
|
48
|
+
|
|
37
49
|
// The review lenses — this is the part you customise to your codebase's real failure modes.
|
|
38
50
|
const ANGLES: AdvisoryLens[] = [
|
|
39
51
|
{ label: "logic-bugs", category: "bug", text: "Off-by-one errors, wrong conditionals, incorrect return values, broken control flow." },
|
|
@@ -53,7 +65,7 @@ export function buildCodeReviewScopeBlock(input: {
|
|
|
53
65
|
readonly summary: string;
|
|
54
66
|
readonly conventions?: string;
|
|
55
67
|
readonly diffText: string;
|
|
56
|
-
readonly
|
|
68
|
+
readonly instructions?: { readonly heading: string; readonly text: string };
|
|
57
69
|
}): string {
|
|
58
70
|
const diffBlock = input.diffText
|
|
59
71
|
? `\n## Diff (review is bounded to these changed lines)\n\`\`\`diff\n${
|
|
@@ -66,7 +78,7 @@ export function buildCodeReviewScopeBlock(input: {
|
|
|
66
78
|
`## Diff command\n${input.diffCommand}\n\n## Changed files\n${input.files.map((file) => `- ${file}`).join("\n")}\n\n` +
|
|
67
79
|
`## Summary\n${input.summary}\n\n## Conventions\n${input.conventions ?? "(none noted)"}\n` +
|
|
68
80
|
diffBlock +
|
|
69
|
-
(input.
|
|
81
|
+
(input.instructions?.text ? `\n## ${input.instructions.heading}\n${input.instructions.text}\n` : "")
|
|
70
82
|
);
|
|
71
83
|
}
|
|
72
84
|
|
|
@@ -81,7 +93,7 @@ export default async function run(api: WorkflowApi, dependencies: CodeReviewDepe
|
|
|
81
93
|
|
|
82
94
|
// ─── Phase 0: Scope ───
|
|
83
95
|
phase("Scope");
|
|
84
|
-
const
|
|
96
|
+
const scopePrompt =
|
|
85
97
|
"Establish the scope of a code review.\n" +
|
|
86
98
|
(target
|
|
87
99
|
? `Target / instructions (verbatim): "${target}". If it names a PR number, branch, ref range, or files, build the matching diff command (use 'gh pr diff <number>' for a PR). Otherwise use the default selection below.\n`
|
|
@@ -97,9 +109,14 @@ export default async function run(api: WorkflowApi, dependencies: CodeReviewDepe
|
|
|
97
109
|
"4. Run the chosen command to confirm the diff is non-empty.\n\n" +
|
|
98
110
|
"Then: list the changed files, summarize the change in one paragraph (mention the PR if one was found), " +
|
|
99
111
|
"and read any relevant AGENTS.md or project docs noting conventions a reviewer should know.\n" +
|
|
100
|
-
"Return diffCommand exactly as a reviewer should run it. Structured output only."
|
|
101
|
-
|
|
102
|
-
|
|
112
|
+
"Return diffCommand exactly as a reviewer should run it. Structured output only.";
|
|
113
|
+
const scopeOptions = { phase: "Scope", label: "scope", tools: DEFAULT_ADVISORY_TOOLS, toolHints: DEFAULT_ADVISORY_TOOL_HINTS, profile: "medium" } as const;
|
|
114
|
+
const scope: Scope = target
|
|
115
|
+
? await agent(
|
|
116
|
+
`${scopePrompt}\nKeep the user's instructions out of conventions. Return focus (where to look) and reportingBar (the self-contained filtering and reporting bar, with every already-reported issue spelled out) as separate fields.`,
|
|
117
|
+
{ ...scopeOptions, schema: InstructedScopeSchema },
|
|
118
|
+
)
|
|
119
|
+
: await agent(scopePrompt, { ...scopeOptions, schema: ScopeSchema });
|
|
103
120
|
|
|
104
121
|
progress({ type: "summary", key: "files", value: scope.files.join(", ") || "(none)" });
|
|
105
122
|
const diffTarget = parseAllowedDiffCommand(scope.diffCommand);
|
|
@@ -147,19 +164,27 @@ export default async function run(api: WorkflowApi, dependencies: CodeReviewDepe
|
|
|
147
164
|
: {}),
|
|
148
165
|
};
|
|
149
166
|
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
167
|
+
const blockFor = (instructions?: { heading: string; text: string }) =>
|
|
168
|
+
`Reviewed snapshot identity: ${JSON.stringify(reviewContext.snapshot ?? "unavailable")}\n` + buildCodeReviewScopeBlock({
|
|
169
|
+
diffCommand,
|
|
170
|
+
files: scope.files,
|
|
171
|
+
summary: scope.summary,
|
|
172
|
+
conventions: scope.conventions,
|
|
173
|
+
diffText,
|
|
174
|
+
...(instructions ? { instructions } : {}),
|
|
175
|
+
});
|
|
176
|
+
const scopeBlock = blockFor({ heading: "User instructions (verbatim)", text: target });
|
|
177
|
+
const focus = stated(scope.focus);
|
|
178
|
+
const reportingBar = stated(scope.reportingBar);
|
|
179
|
+
const finderBlock = blockFor(focus ? { heading: "Review focus (from the user's instructions)", text: focus } : undefined);
|
|
180
|
+
if (focus) log(`review focus: ${focus}`);
|
|
158
181
|
|
|
159
182
|
const pipelineResult = await runLensVerificationPipeline({
|
|
160
183
|
api,
|
|
161
184
|
lenses: ANGLES,
|
|
162
185
|
perLens: PER_ANGLE,
|
|
186
|
+
// Finders on `small` (thinking low by default) returned nothing on real PRs; they are the stage that must dig.
|
|
187
|
+
finderProfile: "medium",
|
|
163
188
|
boundCandidate: (candidate, lens) => {
|
|
164
189
|
const reviewAnchor = candidate.reviewAnchor
|
|
165
190
|
? diffAnchor(changed, candidate.reviewAnchor)
|
|
@@ -167,18 +192,18 @@ export default async function run(api: WorkflowApi, dependencies: CodeReviewDepe
|
|
|
167
192
|
return reviewAnchor ? { ...candidate, category: lens.category, reviewAnchor } : undefined;
|
|
168
193
|
},
|
|
169
194
|
finderPrompt: (lens) =>
|
|
170
|
-
`## Code-review finder — ${lens.label}\n\n${
|
|
195
|
+
`## Code-review finder — ${lens.label}\n\n${finderBlock}\n` +
|
|
171
196
|
`Review the change through ONLY this lens:\n${lens.text}\n` +
|
|
172
197
|
"Only flag issues on lines that are part of the diff above (run the diff command if it is not shown). " +
|
|
173
198
|
"Set reviewAnchor to the changed line causing the issue; locations and discoveryEvidence may include unchanged callers and other files. " +
|
|
174
199
|
`Surface up to ${PER_ANGLE} candidates. Use category exactly "${lens.category}". Each candidate must include a one-line summary, ` +
|
|
175
200
|
"locations with the changed file and a line that appears in the diff, and impact describing the concrete failure or maintenance scenario. " +
|
|
176
201
|
"Pass through anything with a nameable impact — a separate verifier judges them next. Structured output only.",
|
|
177
|
-
verifierPrompt: (
|
|
178
|
-
`## Code-review verifier\n\n${scopeBlock}\n
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
"Run the diff command, read the relevant file(s), and return exactly one verdict (CONFIRMED / PLAUSIBLE / NOT_SUBSTANTIATED / REFUTED) " +
|
|
202
|
+
verifierPrompt: (candidates) =>
|
|
203
|
+
`## Code-review verifier\n\n${scopeBlock}\n` +
|
|
204
|
+
verifierCandidates(candidates, { one: "Candidate", many: "Candidates" }, (candidate) =>
|
|
205
|
+
`Location: ${formatLocation(candidate)}\nCategory: ${candidate.category}\nSummary: ${candidate.summary}\nImpact: ${candidate.impact}`) + "\n\n" +
|
|
206
|
+
"Run the diff command, read the relevant file(s), and return exactly one verdict per candidate (CONFIRMED / PLAUSIBLE / NOT_SUBSTANTIATED / REFUTED) " +
|
|
182
207
|
"with evidence quoting the line(s). Use NOT_SUBSTANTIATED when evidence is insufficient; use REFUTED only for concrete disproof. Structured output only.",
|
|
183
208
|
});
|
|
184
209
|
const verified = await challengeFindings(api, pipelineResult.verified, scopeBlock, challengeConfig.options, pipelineResult.coverage);
|
|
@@ -193,11 +218,23 @@ export default async function run(api: WorkflowApi, dependencies: CodeReviewDepe
|
|
|
193
218
|
synthesisPrompt: (block, ranked) =>
|
|
194
219
|
`## Synthesis: final code-review report\n\n${ranked.length} findings survived independent verification.\n\n${block}\n\n` +
|
|
195
220
|
"Merge findings with the same root cause, rank most-severe first (correctness bugs above cleanups), and produce the final advisory report. " +
|
|
196
|
-
"Return summary, ID selections with severity (low/medium/high) and advisory recommendation, and nextSteps. Evidence and confidence are reconstructed from verified records. Structured output only."
|
|
221
|
+
"Return summary, ID selections with severity (low/medium/high) and advisory recommendation, and nextSteps. Evidence and confidence are reconstructed from verified records. Structured output only." +
|
|
222
|
+
// Only a stated bar filters: a focus alone must not drop verified findings outside it.
|
|
223
|
+
(reportingBar
|
|
224
|
+
? `\n\n## Reporting bar (the user's rules with referenced context resolved)\n${reportingBar}` +
|
|
225
|
+
`\n\n## User instructions (verbatim)\n${target}\n\n` +
|
|
226
|
+
"Apply the reporting bar when choosing which IDs to report: leave out findings it excludes, and say in the summary what you left out and why."
|
|
227
|
+
: ""),
|
|
197
228
|
});
|
|
198
229
|
return { ...report, reviewContext };
|
|
199
230
|
}
|
|
200
231
|
|
|
232
|
+
/** A scope field's text, or undefined when the scope agent left it blank or wrote (none). */
|
|
233
|
+
function stated(value: string | undefined): string | undefined {
|
|
234
|
+
const text = value?.trim();
|
|
235
|
+
return text && text !== "(none)" ? text : undefined;
|
|
236
|
+
}
|
|
237
|
+
|
|
201
238
|
function rank(finding: AdvisoryVerified): number {
|
|
202
239
|
return (finding.category === "cleanup" ? 2 : 0) + (finding.verdict !== "CONFIRMED" ? 1 : 0);
|
|
203
240
|
}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
formatLocation,
|
|
9
9
|
formatRankedFinding,
|
|
10
10
|
runLensVerificationPipeline,
|
|
11
|
+
verifierCandidates,
|
|
11
12
|
DEFAULT_ADVISORY_TOOL_HINTS,
|
|
12
13
|
DEFAULT_ADVISORY_TOOLS,
|
|
13
14
|
} from "../src/workflow-advisory-utils.ts";
|
|
@@ -16,6 +17,7 @@ import type { WorkflowApi, WorkflowMeta } from "../src/types.ts";
|
|
|
16
17
|
export const meta: WorkflowMeta = {
|
|
17
18
|
name: "diagnose",
|
|
18
19
|
description: "Advisory-only bug diagnosis: scope symptoms → competing hypotheses → independent verify → synthesize likely root causes.",
|
|
20
|
+
whenToUse: "Use when a bug, failing command, flaky test, or regression has an unclear cause and competing hypotheses should be checked independently before anyone edits code; args describe the symptom.",
|
|
19
21
|
phases: [{ title: "Scope" }, { title: "Hypothesize" }, { title: "Verify" }, { title: "Challenge" }, { title: "Synthesize" }],
|
|
20
22
|
};
|
|
21
23
|
|
|
@@ -76,10 +78,11 @@ export default async function run(api: WorkflowApi): Promise<unknown> {
|
|
|
76
78
|
`Consider ONLY this hypothesis lens:\n${lens.text}\n\n` +
|
|
77
79
|
`Surface up to ${PER_LENS} root-cause hypotheses. Use category exactly "${lens.category}". ` +
|
|
78
80
|
"Each hypothesis must include a one-line summary, locations, impact explaining how it produces the symptom, and an optional recommendation for the next validation step. Structured output only.",
|
|
79
|
-
verifierPrompt: (
|
|
80
|
-
`## Diagnose verifier\n\n${scopeBlock}\n
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
verifierPrompt: (candidates) =>
|
|
82
|
+
`## Diagnose verifier\n\n${scopeBlock}\n` +
|
|
83
|
+
verifierCandidates(candidates, { one: "Hypothesis", many: "Hypotheses" }, (candidate) =>
|
|
84
|
+
`Location: ${formatLocation(candidate)}\nCategory: ${candidate.category}\nSummary: ${candidate.summary}\nImpact: ${candidate.impact}\n` +
|
|
85
|
+
`Recommended validation: ${candidate.recommendation ?? "(none supplied)"}`) + "\n\n" +
|
|
83
86
|
"Read relevant files and, when useful, run only safe read-only diagnostic commands from the scoped command list or commands explicitly requested by the user. " +
|
|
84
87
|
"Do not run mutation, install, commit, network, or destructive commands. Return CONFIRMED, PLAUSIBLE, NOT_SUBSTANTIATED, or REFUTED with evidence. " +
|
|
85
88
|
"Use NOT_SUBSTANTIATED when evidence is missing; REFUTED requires disproof. Structured output only.",
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
EMPTY_LENS_REVIEW_STATS,
|
|
10
10
|
formatLocation,
|
|
11
11
|
runLensVerificationPipeline,
|
|
12
|
+
verifierCandidates,
|
|
12
13
|
DEFAULT_ADVISORY_TOOL_HINTS,
|
|
13
14
|
DEFAULT_ADVISORY_TOOLS,
|
|
14
15
|
} from "../src/workflow-advisory-utils.ts";
|
|
@@ -17,6 +18,7 @@ import type { WorkflowApi, WorkflowMeta } from "../src/types.ts";
|
|
|
17
18
|
export const meta: WorkflowMeta = {
|
|
18
19
|
name: "perf-review",
|
|
19
20
|
description: "Advisory-only performance review: scope slow path → per-lens bottleneck hypotheses → verify evidence → synthesize measurements and safe optimizations.",
|
|
21
|
+
whenToUse: "Use for a slow path, startup cost, or performance concern where measured bottlenecks and safe optimizations matter more than guesses; args name the path, workload, or command.",
|
|
20
22
|
phases: [{ title: "Scope" }, { title: "Find" }, { title: "Verify" }, { title: "Challenge" }, { title: "Synthesize" }],
|
|
21
23
|
};
|
|
22
24
|
|
|
@@ -84,10 +86,11 @@ export default async function run(api: WorkflowApi): Promise<unknown> {
|
|
|
84
86
|
`Surface up to ${PER_LENS} candidates. Use category exactly "${lens.category}". ` +
|
|
85
87
|
"Each candidate must include a one-line summary, locations, impact stating the suspected performance consequence and workload where it matters, " +
|
|
86
88
|
"and an optional recommendation. Prefer measurement recommendations when evidence is weak. Structured output only.",
|
|
87
|
-
verifierPrompt: (
|
|
88
|
-
`## Perf-review verifier\n\n${scopeBlock}\n
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
verifierPrompt: (candidates) =>
|
|
90
|
+
`## Perf-review verifier\n\n${scopeBlock}\n` +
|
|
91
|
+
verifierCandidates(candidates, { one: "Candidate", many: "Candidates" }, (candidate) =>
|
|
92
|
+
`Location: ${formatLocation(candidate)}\nCategory: ${candidate.category}\nSummary: ${candidate.summary}\nImpact: ${candidate.impact}\n` +
|
|
93
|
+
`Recommendation: ${candidate.recommendation ?? "(none supplied)"}`) + "\n\n" +
|
|
91
94
|
"Read relevant files and package/scripts. Run only safe read-only measurement or inspection commands when useful. " +
|
|
92
95
|
"Return CONFIRMED, PLAUSIBLE, NOT_SUBSTANTIATED, or REFUTED with evidence from code, scripts, config, or measurement output. " +
|
|
93
96
|
"Default toward PLAUSIBLE or REFUTED when no measurement exists; do not overstate a bottleneck. Structured output only.",
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
formatLocation,
|
|
11
11
|
formatRankedFinding,
|
|
12
12
|
runLensVerificationPipeline,
|
|
13
|
+
verifierCandidates,
|
|
13
14
|
DEFAULT_ADVISORY_TOOL_HINTS,
|
|
14
15
|
DEFAULT_ADVISORY_TOOLS,
|
|
15
16
|
} from "../src/workflow-advisory-utils.ts";
|
|
@@ -18,6 +19,7 @@ import type { WorkflowApi, WorkflowMeta } from "../src/types.ts";
|
|
|
18
19
|
export const meta: WorkflowMeta = {
|
|
19
20
|
name: "refactor-scout",
|
|
20
21
|
description: "Advisory-only refactor scout: scope → per-lens find → independent verify → synthesize safe refactor opportunities.",
|
|
22
|
+
whenToUse: "Use to find small, behaviour-preserving refactors (duplication, complexity, type safety, boundaries, dead code) in a path or module without changing it; args name the target.",
|
|
21
23
|
phases: [{ title: "Scope" }, { title: "Find" }, { title: "Verify" }, { title: "Challenge" }, { title: "Synthesize" }],
|
|
22
24
|
};
|
|
23
25
|
|
|
@@ -82,10 +84,11 @@ export default async function run(api: WorkflowApi): Promise<unknown> {
|
|
|
82
84
|
`Surface up to ${PER_LENS} candidates. Use category exactly "${lens.category}". ` +
|
|
83
85
|
"Each candidate must include a one-line summary, locations, impact on maintainability or future correctness, and an optional safe first recommendation. " +
|
|
84
86
|
"Only include opportunities where a small, reviewable first step is plausible. Structured output only.",
|
|
85
|
-
verifierPrompt: (
|
|
86
|
-
`## Refactor-scout verifier\n\n${scopeBlock}\n
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
verifierPrompt: (candidates) =>
|
|
88
|
+
`## Refactor-scout verifier\n\n${scopeBlock}\n` +
|
|
89
|
+
verifierCandidates(candidates, { one: "Candidate", many: "Candidates" }, (candidate) =>
|
|
90
|
+
`Location: ${formatLocation(candidate)}\nCategory: ${candidate.category}\nSummary: ${candidate.summary}\nImpact: ${candidate.impact}\n` +
|
|
91
|
+
`Recommendation: ${candidate.recommendation ?? "(none supplied)"}`) + "\n\n" +
|
|
89
92
|
"Read the relevant files and return CONFIRMED, PLAUSIBLE, NOT_SUBSTANTIATED, or REFUTED. " +
|
|
90
93
|
"Default toward REFUTED if the opportunity is generic, too broad, not evidenced by code, or lacks a safe first step. " +
|
|
91
94
|
"Evidence must quote or cite code. Structured output only.",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
MAX_RESEARCH_LANES,
|
|
3
|
+
MAX_VERIFICATION_CLAIMS,
|
|
3
4
|
ResearchLaneResultSchema,
|
|
4
5
|
ResearchPlanSchema,
|
|
5
6
|
ResearchReportSchema,
|
|
@@ -23,6 +24,7 @@ import type { WorkflowApi, WorkflowMeta } from "../src/types.ts";
|
|
|
23
24
|
export const meta: WorkflowMeta = {
|
|
24
25
|
name: "research",
|
|
25
26
|
description: "Source-grounded external research: decompose → gather direct-page evidence → independently verify claims → cited synthesis.",
|
|
27
|
+
whenToUse: "Use for questions that need current external sources and cited, independently verified claims; needs an installed web search or browsing tool; args are the question and any scope constraints.",
|
|
26
28
|
phases: [{ title: "Plan" }, { title: "Gather" }, { title: "Verify" }, { title: "Synthesize" }],
|
|
27
29
|
};
|
|
28
30
|
|
|
@@ -105,10 +107,11 @@ export default async function run(api: WorkflowApi): Promise<ResearchReport> {
|
|
|
105
107
|
const gathered = gatherResults.flatMap((result) => (result.ok ? [result.value] : []));
|
|
106
108
|
const failedLanes = lanes.filter((_, index) => !gatherResults[index]!.ok);
|
|
107
109
|
|
|
108
|
-
const candidates = buildClaimCandidates(gathered);
|
|
110
|
+
const { candidates, overCap } = buildClaimCandidates(gathered);
|
|
109
111
|
if (candidates.length === 0) return unavailableResearchReport(failedLanes.length > 0 ? "lanes-failed" : "no-evidence");
|
|
110
112
|
progress({ type: "counter", key: "research.claims", label: "claims to verify", value: candidates.length });
|
|
111
113
|
log(`${candidates.length} bounded claim(s) selected for independent verification`);
|
|
114
|
+
if (overCap > 0) log(`Dropped ${overCap} lower-priority claim(s) over the verification cap of ${MAX_VERIFICATION_CLAIMS}`);
|
|
112
115
|
|
|
113
116
|
phase("Verify");
|
|
114
117
|
const verificationResults = await parallel(
|
|
@@ -162,9 +165,14 @@ export default async function run(api: WorkflowApi): Promise<ResearchReport> {
|
|
|
162
165
|
}
|
|
163
166
|
|
|
164
167
|
const report = sanitizeResearchReport(synthesis, synthesisInputs);
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
+
const gaps: string[] = [];
|
|
169
|
+
if (failedLanes.length > 0) {
|
|
170
|
+
gaps.push(`${failedLanes.length} of ${lanes.length} research lane(s) failed (${failedLanes.map((lane) => lane.title).join(", ")}); coverage is incomplete.`);
|
|
171
|
+
}
|
|
172
|
+
if (overCap > 0) {
|
|
173
|
+
gaps.push(`${overCap} lower-priority claim(s) were not independently verified; verification is capped at ${MAX_VERIFICATION_CLAIMS} claims.`);
|
|
174
|
+
}
|
|
175
|
+
return gaps.length === 0 ? report : { ...report, limitations: [...report.limitations, ...gaps] };
|
|
168
176
|
}
|
|
169
177
|
|
|
170
178
|
function verificationPrompt(question: string, candidate: ResearchClaimCandidate): string {
|
package/README.md
CHANGED
|
@@ -72,15 +72,21 @@ dynamax investigate this flaky test using separate timing, fixture, diff, and ex
|
|
|
72
72
|
dynamax argue both implementation options, then have another agent challenge the recommendation
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
The token enables Dynamax for one
|
|
76
|
-
session
|
|
75
|
+
The token enables Dynamax for that one request. Sticky mode instead makes
|
|
76
|
+
workflow orchestration the default for every substantive task in the pi session;
|
|
77
|
+
`/workflow:dynamax` with no argument toggles it:
|
|
77
78
|
|
|
78
79
|
```text
|
|
80
|
+
/workflow:dynamax
|
|
79
81
|
/workflow:dynamax on
|
|
80
82
|
/workflow:dynamax off
|
|
81
83
|
/workflow:dynamax status
|
|
82
84
|
```
|
|
83
85
|
|
|
86
|
+
`/workflow:size small|medium|large|off` sets how many agents pi aims to keep
|
|
87
|
+
each authored workflow under (5, 10 by default, or 25); see the
|
|
88
|
+
[size guideline](USAGE.md#workflow-size-guideline).
|
|
89
|
+
|
|
84
90
|
In the TUI, a standalone `dynamax` token receives a short moving-shine sweep and
|
|
85
91
|
then stays highlighted. Use `PI_DYNAMAX_EFFECT=static` or
|
|
86
92
|
`PI_DYNAMAX_EFFECT=off` to reduce or disable the cue; the
|
|
@@ -108,7 +114,9 @@ covers review actions and recent run history.
|
|
|
108
114
|
## Create your own workflows
|
|
109
115
|
|
|
110
116
|
Saved workflows are TypeScript modules for repeatable orchestration; inline
|
|
111
|
-
workflows are useful for one-off Dynamax tasks.
|
|
117
|
+
workflows are useful for one-off Dynamax tasks. An inline script that proves
|
|
118
|
+
useful can be saved as a `.js` file in a project's `.pi/workflows/` or in
|
|
119
|
+
`~/.pi/agent/workflows/` and run by name. The
|
|
112
120
|
[workflow authoring guide](USAGE.md#author-a-saved-workflow) covers the API,
|
|
113
121
|
typed handoffs, model profiles, tool access, composition, and registration.
|
|
114
122
|
|