pi-crew 0.9.18 → 0.9.19
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/CHANGELOG.md +64 -0
- package/dist/build-meta.json +11 -6
- package/dist/index.mjs +75 -3
- package/dist/index.mjs.map +3 -3
- package/package.json +1 -1
- package/src/extension/team-tool/run.ts +98 -2
- package/src/schema/team-tool-schema.ts +21 -0
- package/workflows/plan-execute.workflow.md +30 -0
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { allAgents, discoverAgents } from "../../agents/discover-agents.ts";
|
|
2
2
|
import { loadConfig } from "../../config/config.ts";
|
|
3
3
|
import { PipelineRunner, type PipelineWorkflow } from "../../runtime/pipeline-runner.ts";
|
|
4
|
+
import { sanitizeTaskText } from "../../runtime/task-packet.ts";
|
|
4
5
|
// Heavy runtime — lazy-loaded to avoid 1.4s import cost at extension registration.
|
|
5
6
|
import type { executeTeamRun as ExecuteTeamRunFn } from "../../runtime/team-runner.ts";
|
|
6
7
|
import type { TeamToolParamsValue } from "../../schema/team-tool-schema.ts";
|
|
@@ -17,7 +18,7 @@ import { assertCleanLeader, findGitRoot } from "../../worktree/worktree-manager.
|
|
|
17
18
|
const _typeCheck: typeof ExecuteTeamRunFn = null as never as typeof ExecuteTeamRunFn;
|
|
18
19
|
|
|
19
20
|
import { logInternalError } from "../../utils/internal-error.ts";
|
|
20
|
-
import { resolveRealContainedPath } from "../../utils/safe-paths.ts";
|
|
21
|
+
import { resolveContainedPath, resolveRealContainedPath } from "../../utils/safe-paths.ts";
|
|
21
22
|
|
|
22
23
|
let _cachedExecuteTeamRun: typeof ExecuteTeamRunFn | undefined;
|
|
23
24
|
async function executeTeamRun(...args: Parameters<typeof ExecuteTeamRunFn>): Promise<Awaited<ReturnType<typeof ExecuteTeamRunFn>>> {
|
|
@@ -127,6 +128,76 @@ function scheduleBackgroundEarlyExitGuard(cwd: string, runId: string, pid: numbe
|
|
|
127
128
|
timer.unref();
|
|
128
129
|
}
|
|
129
130
|
|
|
131
|
+
/**
|
|
132
|
+
* Max analysis size in bytes — matches the `maxLength: 100_000` schema cap on
|
|
133
|
+
* the inline `analysis` param, so the file channel can't smuggle in a larger
|
|
134
|
+
* payload than the inline channel allows (prompt-size blowup guard).
|
|
135
|
+
*/
|
|
136
|
+
const MAX_ANALYSIS_BYTES = 100_000;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Resolve the analysis channel (round-X Y1): inline `analysis` or `analysisPath` file.
|
|
140
|
+
*
|
|
141
|
+
* Called BEFORE `createRunManifest` so validation failures fail-fast and no orphan
|
|
142
|
+
* run state is left on disk. Sanitizes the parsed content with the same
|
|
143
|
+
* {@link sanitizeTaskText} machinery used in `buildTaskPacket` (SEC-007), since the
|
|
144
|
+
* resulting text is injected into worker prompts via standard sharedReads.
|
|
145
|
+
*
|
|
146
|
+
* Mutual exclusivity mirrors the `budgetTotal`/`budgetUnlimited` pattern in
|
|
147
|
+
* `goal.ts` — a cold-review #2 blocking fix pattern.
|
|
148
|
+
*/
|
|
149
|
+
function resolveAnalysisText(
|
|
150
|
+
params: TeamToolParamsValue,
|
|
151
|
+
cwd: string,
|
|
152
|
+
): { text?: string; error?: string; source: "inline" | "path" | "none" } {
|
|
153
|
+
const hasInline = typeof params.analysis === "string" && params.analysis.length > 0;
|
|
154
|
+
const hasPath = typeof params.analysisPath === "string" && params.analysisPath.length > 0;
|
|
155
|
+
|
|
156
|
+
if (hasInline && hasPath) {
|
|
157
|
+
return {
|
|
158
|
+
error: "`analysis` and `analysisPath` are mutually exclusive. Set exactly one.",
|
|
159
|
+
source: "none",
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
if (!hasInline && !hasPath) return { source: "none" };
|
|
163
|
+
|
|
164
|
+
if (hasPath) {
|
|
165
|
+
let resolved: string;
|
|
166
|
+
try {
|
|
167
|
+
resolved = resolveContainedPath(cwd, params.analysisPath as string);
|
|
168
|
+
} catch {
|
|
169
|
+
return {
|
|
170
|
+
error: `analysisPath must be within project directory: ${params.analysisPath}`,
|
|
171
|
+
source: "none",
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
if (!fs.existsSync(resolved)) {
|
|
175
|
+
return {
|
|
176
|
+
error: `Analysis file not found: ${resolved}`,
|
|
177
|
+
source: "none",
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
// Size cap BEFORE reading: mirror the inline schema cap (maxLength 100_000)
|
|
181
|
+
// so a large file can't blow up worker prompts via the sharedReads channel.
|
|
182
|
+
const { size } = fs.statSync(resolved);
|
|
183
|
+
if (size > MAX_ANALYSIS_BYTES) {
|
|
184
|
+
return {
|
|
185
|
+
error: `Analysis file too large: ${size} bytes (max ${MAX_ANALYSIS_BYTES}). Trim the analysis or pass a summary inline.`,
|
|
186
|
+
source: "none",
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
const raw = fs.readFileSync(resolved, "utf-8");
|
|
190
|
+
const sanitized = sanitizeTaskText(raw);
|
|
191
|
+
if (!sanitized) return { source: "none" };
|
|
192
|
+
return { text: sanitized, source: "path" };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// hasInline
|
|
196
|
+
const sanitized = sanitizeTaskText(params.analysis as string);
|
|
197
|
+
if (!sanitized) return { source: "none" };
|
|
198
|
+
return { text: sanitized, source: "inline" };
|
|
199
|
+
}
|
|
200
|
+
|
|
130
201
|
export async function handleRun(params: TeamToolParamsValue, ctx: TeamContext): Promise<PiTeamsToolResult> {
|
|
131
202
|
// CHAIN DISPATCH: runs before goal validation since a chain has no top-level
|
|
132
203
|
// goal. The injected handleRun reference breaks the run.ts ↔ chain-dispatch.ts
|
|
@@ -234,11 +305,18 @@ export async function handleRun(params: TeamToolParamsValue, ctx: TeamContext):
|
|
|
234
305
|
role: params.role ?? "agent",
|
|
235
306
|
task: "{goal}",
|
|
236
307
|
model: params.model,
|
|
308
|
+
reads: params.analysis || params.analysisPath ? ["analysis.md"] : undefined,
|
|
237
309
|
},
|
|
238
310
|
],
|
|
239
311
|
}
|
|
240
312
|
: workflows.find((item) => item.name === workflowName);
|
|
241
313
|
if (!baseWorkflow) return result(`Workflow '${workflowName}' not found.`, { action: "run", status: "error" }, true);
|
|
314
|
+
|
|
315
|
+
// ANALYSIS CHANNEL (round-X Y1): resolve analysis text from inline or file BEFORE
|
|
316
|
+
// createRunManifest so validation errors fail-fast (no orphan run state).
|
|
317
|
+
const analysisParam = resolveAnalysisText(params, resolvedCtx.cwd);
|
|
318
|
+
if (analysisParam.error) return result(analysisParam.error, { action: "run", status: "error" }, true);
|
|
319
|
+
|
|
242
320
|
// LAZY: dodge the jiti ESM/CJS interop TDZ race on the static `import { expandParallelResearchWorkflow }` above (issue #28, RFC 17). At call time the module body has fully evaluated, so the dynamic import returns a live binding. Multi-line form breaks scripts/check-lazy-imports.mjs (which does `lines[lineNum - 2]`), so keep destructuring + await import on one line.
|
|
243
321
|
const { expandParallelResearchWorkflow: expandParallelResearch } = await import("../../runtime/parallel-research.ts");
|
|
244
322
|
const workflow = directAgent ? baseWorkflow : expandParallelResearch(baseWorkflow, resolvedCtx.cwd);
|
|
@@ -276,6 +354,11 @@ export async function handleRun(params: TeamToolParamsValue, ctx: TeamContext):
|
|
|
276
354
|
if (!directAgent && workflow.source === "builtin" && isGoalWrapEnabled(resolvedCtx.cwd, workflow.name)) {
|
|
277
355
|
const decision = shouldGoalWrap(resolvedCtx.cwd, workflow);
|
|
278
356
|
if (decision.enabled) {
|
|
357
|
+
if (analysisParam.text) {
|
|
358
|
+
console.warn(
|
|
359
|
+
`[team-tool.run] analysis param is ignored by goal-wrapped run (workflow=${workflow.name}). The analysis artifact will not be written.`,
|
|
360
|
+
);
|
|
361
|
+
}
|
|
279
362
|
return await startGoalWrappedRun(params, ctx, workflow, goal);
|
|
280
363
|
}
|
|
281
364
|
// goal-wrap disabled for this workflow — fall through silently to normal
|
|
@@ -364,10 +447,23 @@ export async function handleRun(params: TeamToolParamsValue, ctx: TeamContext):
|
|
|
364
447
|
content: `${goal}\n`,
|
|
365
448
|
producer: "team-tool",
|
|
366
449
|
});
|
|
450
|
+
// ANALYSIS CHANNEL (round-X Y1): if analysis was provided, persist as a shared artifact
|
|
451
|
+
// so workflow steps declaring reads: analysis.md receive it via the standard
|
|
452
|
+
// dependency-context injection (collectDependencyOutputContext → renderDependencyOutputContext).
|
|
453
|
+
const analysisArtifacts = analysisParam.text
|
|
454
|
+
? [
|
|
455
|
+
writeArtifact(paths.artifactsRoot, {
|
|
456
|
+
kind: "prompt",
|
|
457
|
+
relativePath: "shared/analysis.md",
|
|
458
|
+
content: `${analysisParam.text}\n`,
|
|
459
|
+
producer: "team-tool",
|
|
460
|
+
}),
|
|
461
|
+
]
|
|
462
|
+
: [];
|
|
367
463
|
const updatedManifest = {
|
|
368
464
|
...manifest,
|
|
369
465
|
...(skillOverride !== undefined ? { skillOverride } : {}),
|
|
370
|
-
artifacts: [goalArtifact],
|
|
466
|
+
artifacts: [goalArtifact, ...analysisArtifacts],
|
|
371
467
|
summary: "Run manifest created; worker execution is not implemented yet.",
|
|
372
468
|
};
|
|
373
469
|
atomicWriteJson(paths.manifestPath, updatedManifest);
|
|
@@ -264,6 +264,19 @@ export const TeamToolParams = Type.Object({
|
|
|
264
264
|
// "description-only schema" strict-provider check.
|
|
265
265
|
Type.Any(),
|
|
266
266
|
),
|
|
267
|
+
analysis: Type.Optional(
|
|
268
|
+
Type.String({
|
|
269
|
+
maxLength: 100_000,
|
|
270
|
+
description:
|
|
271
|
+
"Inline analysis/context notes from the calling session. Persisted to artifacts/{runId}/shared/analysis.md (audit trail) and auto-injected into any workflow step declaring reads: analysis.md (e.g. builtin 'plan-execute'). Mutually exclusive with analysisPath. Ignored by goal-wrapped runs and chain dispatch in v1.",
|
|
272
|
+
}),
|
|
273
|
+
),
|
|
274
|
+
analysisPath: Type.Optional(
|
|
275
|
+
Type.String({
|
|
276
|
+
description:
|
|
277
|
+
"Path to an existing markdown analysis file (resolved within cwd). Copied into shared/analysis.md. Mutually exclusive with analysis.",
|
|
278
|
+
}),
|
|
279
|
+
),
|
|
267
280
|
focus: Type.Optional(
|
|
268
281
|
Type.String({
|
|
269
282
|
description:
|
|
@@ -385,4 +398,12 @@ export interface TeamToolParamsValue {
|
|
|
385
398
|
tokenBudget?: number;
|
|
386
399
|
/** Typed workflow arguments for .dwf.ts scripts, accessible via ctx.args<T>() (round-14 P1-5). */
|
|
387
400
|
args?: unknown;
|
|
401
|
+
/** Inline analysis/context notes from the calling session. Persisted to
|
|
402
|
+
* artifacts/{runId}/shared/analysis.md (audit trail) and auto-injected into
|
|
403
|
+
* any workflow step declaring reads: analysis.md (e.g. builtin 'plan-execute').
|
|
404
|
+
* Mutually exclusive with `analysisPath`. */
|
|
405
|
+
analysis?: string;
|
|
406
|
+
/** Path to an existing analysis file resolved within `cwd`. Copied into
|
|
407
|
+
* shared/analysis.md. Mutually exclusive with `analysis`. */
|
|
408
|
+
analysisPath?: string;
|
|
388
409
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plan-execute
|
|
3
|
+
description: Plan and execute a goal already analyzed by the calling session (no explore step)
|
|
4
|
+
topology: sequential
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## plan
|
|
8
|
+
role: planner
|
|
9
|
+
output: plan.md
|
|
10
|
+
reads: analysis.md
|
|
11
|
+
|
|
12
|
+
Create a concise implementation plan for: {goal}
|
|
13
|
+
|
|
14
|
+
The calling session has already analyzed the problem. Its analysis is provided
|
|
15
|
+
as shared context (analysis.md). Build directly on that analysis; only re-verify
|
|
16
|
+
file paths and facts you need. Do not redo full discovery.
|
|
17
|
+
|
|
18
|
+
## execute
|
|
19
|
+
role: executor
|
|
20
|
+
dependsOn: plan
|
|
21
|
+
|
|
22
|
+
Implement the plan for: {goal}
|
|
23
|
+
|
|
24
|
+
## verify
|
|
25
|
+
role: verifier
|
|
26
|
+
dependsOn: execute
|
|
27
|
+
verify: true
|
|
28
|
+
|
|
29
|
+
Verify completion for: {goal}
|
|
30
|
+
Run tests ONCE (cache to .crew/cache/), read changed files from executor context. Cross-reference test output with the changes. Do NOT re-run tests. Give PASS or FAIL with specific test evidence.
|