pi-cohort 4.0.0 → 5.0.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/CHANGELOG.md +10 -0
- package/README.md +2 -0
- package/package.json +1 -1
- package/skills/pi-cohort/SKILL.md +2 -2
- package/src/extension/fanout-child.ts +3 -1
- package/src/extension/index.ts +3 -1
- package/src/extension/schemas.ts +1 -1
- package/src/runs/background/async-execution.ts +6 -0
- package/src/runs/background/subagent-runner.ts +6 -0
- package/src/runs/foreground/chain-execution.ts +7 -1
- package/src/runs/foreground/execution.ts +1 -0
- package/src/runs/foreground/subagent-executor.ts +13 -0
- package/src/runs/shared/forward-flags.ts +82 -0
- package/src/runs/shared/pi-args.ts +6 -0
- package/src/shared/types.ts +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [5.0.0] - 2026-08-06
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
- **BREAKING:** Programmatic chains now launch directly when `clarify` is omitted. Pass `clarify: true` to keep the foreground preview/edit flow.
|
|
7
|
+
|
|
8
|
+
## [4.1.0] - 2026-07-19
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- **Forward parent pi CLI flags into children.** Extension flags the parent `pi` was launched with (e.g. pi-lens's `--no-autofix`) are now auto-forwarded into spawned children - foreground, sync-background, and detached-async, at any nesting depth - derived universally from the parent's argv. Pi core flags are never forwarded. Opt out with `forwardParentFlags: false`. ([#2](https://github.com/jjuraszek/pi-cohort/issues/2))
|
|
12
|
+
|
|
3
13
|
## [4.0.0] - 2026-07-16
|
|
4
14
|
|
|
5
15
|
### Changed
|
package/README.md
CHANGED
|
@@ -117,6 +117,8 @@ Full reference: agent/chain authoring in [doc/agents-and-chains.md](doc/agents-a
|
|
|
117
117
|
|
|
118
118
|
Most installs need zero configuration. Full env/JSON reference: [doc/configuration.md](doc/configuration.md).
|
|
119
119
|
|
|
120
|
+
Parent extension CLI flags (e.g. pi-lens's `--no-autofix`) are forwarded into spawned children by default, so subagents inherit the same tooling behavior you launched with; toggle with [`forwardParentFlags`](doc/configuration.md#forwardparentflags).
|
|
121
|
+
|
|
120
122
|
Optional companions:
|
|
121
123
|
|
|
122
124
|
- [pi-intercom](https://github.com/jjuraszek/pi-intercom) - lets a blocked child ask the parent a question instead of guessing. [doc/skills-and-companions.md](doc/skills-and-companions.md#optional-pi-intercom-companion)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-cohort",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "Delegate Pi work to focused child agents: code review, scouting, implementation, parallel audits, saved chains, and background jobs.",
|
|
5
5
|
"author": "Jacek Juraszek",
|
|
6
6
|
"license": "MIT",
|
|
@@ -400,8 +400,8 @@ subagent({
|
|
|
400
400
|
})
|
|
401
401
|
```
|
|
402
402
|
|
|
403
|
-
|
|
404
|
-
For programmatic background launches, use `async: true`.
|
|
403
|
+
Programmatic chains launch directly by default. Set `clarify: true` to preview or edit a sequential chain before launch; chains containing parallel steps skip the UI. Clarify edits affect only the next run; use management actions, settings, or markdown files for persistent changes.
|
|
404
|
+
For programmatic background launches, use `async: true`. Explicit `clarify: true` keeps the run foreground for the clarify UI.
|
|
405
405
|
|
|
406
406
|
|
|
407
407
|
## Worktree Isolation
|
|
@@ -11,7 +11,8 @@ import { deliverSubagentIntercomMessageEvent } from "../intercom/result-intercom
|
|
|
11
11
|
import { resolveSubagentIntercomTarget } from "../intercom/intercom-bridge.ts";
|
|
12
12
|
import { SubagentParams } from "./schemas.ts";
|
|
13
13
|
import { loadConfig } from "./config.ts";
|
|
14
|
-
import {
|
|
14
|
+
import { deriveForwardedFlags } from "../runs/shared/forward-flags.ts";
|
|
15
|
+
import type { Details, SubagentState } from "../shared/types.ts";
|
|
15
16
|
|
|
16
17
|
function getSubagentSessionRoot(parentSessionFile: string | null): string {
|
|
17
18
|
if (parentSessionFile) {
|
|
@@ -148,6 +149,7 @@ export default function registerFanoutChildSubagentExtension(pi: ExtensionAPI):
|
|
|
148
149
|
getSubagentSessionRoot,
|
|
149
150
|
expandTilde,
|
|
150
151
|
discoverAgents,
|
|
152
|
+
forwardedFlags: deriveForwardedFlags(process.argv, config),
|
|
151
153
|
allowMutatingManagementActions: false,
|
|
152
154
|
});
|
|
153
155
|
|
package/src/extension/index.ts
CHANGED
|
@@ -16,7 +16,7 @@ import * as fs from "node:fs";
|
|
|
16
16
|
import * as os from "node:os";
|
|
17
17
|
import * as path from "node:path";
|
|
18
18
|
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
19
|
-
import {
|
|
19
|
+
import type { ExtensionAPI, ExtensionContext, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
20
20
|
import { Box, Container, Spacer, Text, truncateToWidth, visibleWidth, wrapTextWithAnsi, type Component } from "@earendil-works/pi-tui";
|
|
21
21
|
import { discoverAgents, discoverAgentsAll } from "../agents/agents.ts";
|
|
22
22
|
import { cleanupAllArtifactDirs, cleanupOldArtifacts, getArtifactsDir } from "../shared/artifacts.ts";
|
|
@@ -37,6 +37,7 @@ import { SUBAGENT_CHILD_ENV, SUBAGENT_FANOUT_CHILD_ENV } from "../runs/shared/pi
|
|
|
37
37
|
import registerFanoutChildSubagentExtension from "./fanout-child.ts";
|
|
38
38
|
import { formatDuration, shortenPath } from "../shared/formatters.ts";
|
|
39
39
|
import { loadConfig } from "./config.ts";
|
|
40
|
+
import { deriveForwardedFlags } from "../runs/shared/forward-flags.ts";
|
|
40
41
|
import {
|
|
41
42
|
type Details,
|
|
42
43
|
type SubagentState,
|
|
@@ -315,6 +316,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
315
316
|
getSubagentSessionRoot,
|
|
316
317
|
expandTilde,
|
|
317
318
|
discoverAgents,
|
|
319
|
+
forwardedFlags: deriveForwardedFlags(process.argv, config),
|
|
318
320
|
});
|
|
319
321
|
|
|
320
322
|
pi.registerMessageRenderer<SlashMessageDetails>(SLASH_RESULT_TYPE, (message, options, theme) => {
|
package/src/extension/schemas.ts
CHANGED
|
@@ -289,7 +289,7 @@ export const SubagentParams = Type.Object({
|
|
|
289
289
|
Type.String({ description: "Directory to store session logs (default: temp; enables sessions even if share=false)" }),
|
|
290
290
|
),
|
|
291
291
|
// Clarification TUI
|
|
292
|
-
clarify: Type.Optional(Type.Boolean({ description: "Show TUI to preview/edit before execution. Explicit clarify: true keeps the run foreground for the clarify UI;
|
|
292
|
+
clarify: Type.Optional(Type.Boolean({ description: "Show TUI to preview/edit before execution. Omitted or false launches directly. Explicit clarify: true keeps the run foreground for the clarify UI when supported; chains containing parallel steps skip the UI." })),
|
|
293
293
|
control: Type.Optional(ControlOverrides),
|
|
294
294
|
// Solo agent overrides
|
|
295
295
|
output: Type.Optional(Type.Unsafe({
|
|
@@ -120,6 +120,7 @@ interface AsyncChainParams {
|
|
|
120
120
|
controlIntercomTarget?: string;
|
|
121
121
|
childIntercomTarget?: (agent: string, index: number) => string | undefined;
|
|
122
122
|
nestedRoute?: NestedRouteInfo;
|
|
123
|
+
forwardedFlags?: string[];
|
|
123
124
|
acceptance?: AcceptanceInput;
|
|
124
125
|
}
|
|
125
126
|
|
|
@@ -147,6 +148,7 @@ interface AsyncSingleParams {
|
|
|
147
148
|
controlIntercomTarget?: string;
|
|
148
149
|
childIntercomTarget?: (agent: string, index: number) => string | undefined;
|
|
149
150
|
nestedRoute?: NestedRouteInfo;
|
|
151
|
+
forwardedFlags?: string[];
|
|
150
152
|
acceptance?: AcceptanceInput;
|
|
151
153
|
}
|
|
152
154
|
|
|
@@ -249,6 +251,7 @@ export function executeAsyncChain(
|
|
|
249
251
|
controlIntercomTarget,
|
|
250
252
|
childIntercomTarget,
|
|
251
253
|
nestedRoute,
|
|
254
|
+
forwardedFlags,
|
|
252
255
|
} = params;
|
|
253
256
|
const resultMode = params.resultMode ?? "chain";
|
|
254
257
|
const chainSkills = params.chainSkills ?? [];
|
|
@@ -481,6 +484,7 @@ export function executeAsyncChain(
|
|
|
481
484
|
sessionId: ctx.currentSessionId,
|
|
482
485
|
piPackageRoot,
|
|
483
486
|
piArgv1: process.argv[1],
|
|
487
|
+
forwardedFlags,
|
|
484
488
|
worktreeSetupHook,
|
|
485
489
|
worktreeSetupHookTimeoutMs,
|
|
486
490
|
controlConfig,
|
|
@@ -629,6 +633,7 @@ export function executeAsyncSingle(
|
|
|
629
633
|
controlIntercomTarget,
|
|
630
634
|
childIntercomTarget,
|
|
631
635
|
nestedRoute,
|
|
636
|
+
forwardedFlags,
|
|
632
637
|
} = params;
|
|
633
638
|
const task = params.task ?? "";
|
|
634
639
|
const runnerCwd = resolveChildCwd(ctx.cwd, cwd);
|
|
@@ -716,6 +721,7 @@ export function executeAsyncSingle(
|
|
|
716
721
|
sessionId: ctx.currentSessionId,
|
|
717
722
|
piPackageRoot,
|
|
718
723
|
piArgv1: process.argv[1],
|
|
724
|
+
forwardedFlags,
|
|
719
725
|
worktreeSetupHook,
|
|
720
726
|
worktreeSetupHookTimeoutMs,
|
|
721
727
|
controlConfig,
|
|
@@ -97,6 +97,7 @@ interface SubagentRunConfig {
|
|
|
97
97
|
sessionId?: string | null;
|
|
98
98
|
piPackageRoot?: string;
|
|
99
99
|
piArgv1?: string;
|
|
100
|
+
forwardedFlags?: string[];
|
|
100
101
|
worktreeSetupHook?: string;
|
|
101
102
|
worktreeSetupHookTimeoutMs?: number;
|
|
102
103
|
controlConfig?: ResolvedControlConfig;
|
|
@@ -574,6 +575,7 @@ interface SingleStepContext {
|
|
|
574
575
|
outputFile: string;
|
|
575
576
|
piPackageRoot?: string;
|
|
576
577
|
piArgv1?: string;
|
|
578
|
+
forwardedFlags?: string[];
|
|
577
579
|
registerInterrupt?: (interrupt: (() => void) | undefined) => void;
|
|
578
580
|
childIntercomTarget?: string;
|
|
579
581
|
orchestratorIntercomTarget?: string;
|
|
@@ -677,6 +679,7 @@ async function runSingleStep(
|
|
|
677
679
|
parentRootRunId: ctx.nestedRoute?.rootRunId,
|
|
678
680
|
parentCapabilityToken: ctx.nestedRoute?.capabilityToken,
|
|
679
681
|
structuredOutput: effectiveStructuredOutput,
|
|
682
|
+
forwardedFlags: ctx.forwardedFlags,
|
|
680
683
|
});
|
|
681
684
|
const run = await runPiStreaming(
|
|
682
685
|
args,
|
|
@@ -1610,6 +1613,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1610
1613
|
outputFile: path.join(asyncDir, `output-${fi}.log`),
|
|
1611
1614
|
piPackageRoot: config.piPackageRoot,
|
|
1612
1615
|
piArgv1: config.piArgv1,
|
|
1616
|
+
forwardedFlags: config.forwardedFlags,
|
|
1613
1617
|
childIntercomTarget: config.childIntercomTargets?.[fi],
|
|
1614
1618
|
orchestratorIntercomTarget: config.controlIntercomTarget,
|
|
1615
1619
|
nestedRoute: config.nestedRoute,
|
|
@@ -1857,6 +1861,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
1857
1861
|
outputFile: path.join(asyncDir, `output-${fi}.log`),
|
|
1858
1862
|
piPackageRoot: config.piPackageRoot,
|
|
1859
1863
|
piArgv1: config.piArgv1,
|
|
1864
|
+
forwardedFlags: config.forwardedFlags,
|
|
1860
1865
|
childIntercomTarget: config.childIntercomTargets?.[fi],
|
|
1861
1866
|
orchestratorIntercomTarget: config.controlIntercomTarget,
|
|
1862
1867
|
nestedRoute: config.nestedRoute,
|
|
@@ -2024,6 +2029,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
|
|
|
2024
2029
|
outputFile: path.join(asyncDir, `output-${flatIndex}.log`),
|
|
2025
2030
|
piPackageRoot: config.piPackageRoot,
|
|
2026
2031
|
piArgv1: config.piArgv1,
|
|
2032
|
+
forwardedFlags: config.forwardedFlags,
|
|
2027
2033
|
childIntercomTarget: config.childIntercomTargets?.[flatIndex],
|
|
2028
2034
|
orchestratorIntercomTarget: config.controlIntercomTarget,
|
|
2029
2035
|
nestedRoute: config.nestedRoute,
|
|
@@ -136,6 +136,7 @@ interface ParallelChainRunInput {
|
|
|
136
136
|
worktreeSetup?: WorktreeSetup;
|
|
137
137
|
maxSubagentDepth: number;
|
|
138
138
|
nestedRoute?: NestedRouteInfo;
|
|
139
|
+
forwardedFlags?: string[];
|
|
139
140
|
}
|
|
140
141
|
|
|
141
142
|
function buildChainExecutionDetails(input: ChainExecutionDetailsInput): Details {
|
|
@@ -290,6 +291,7 @@ async function runParallelChainTasks(input: ParallelChainRunInput): Promise<Sing
|
|
|
290
291
|
structuredOutput: structuredRuntime,
|
|
291
292
|
acceptance: task.acceptance,
|
|
292
293
|
acceptanceContext: { mode: "chain" },
|
|
294
|
+
forwardedFlags: input.forwardedFlags,
|
|
293
295
|
onUpdate: input.onUpdate
|
|
294
296
|
? (progressUpdate) => {
|
|
295
297
|
const stepResults = progressUpdate.details?.results || [];
|
|
@@ -392,6 +394,7 @@ interface ChainExecutionParams {
|
|
|
392
394
|
nestedRoute?: NestedRouteInfo;
|
|
393
395
|
worktreeSetupHook?: string;
|
|
394
396
|
worktreeSetupHookTimeoutMs?: number;
|
|
397
|
+
forwardedFlags?: string[];
|
|
395
398
|
}
|
|
396
399
|
|
|
397
400
|
interface ChainExecutionResult {
|
|
@@ -490,7 +493,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
490
493
|
const chainDir = createChainDir(runId, chainDirBase);
|
|
491
494
|
const hasParallelSteps = chainSteps.some((step) => isParallelStep(step) || isDynamicParallelStep(step));
|
|
492
495
|
let templates: ResolvedTemplates = resolveChainTemplates(chainSteps);
|
|
493
|
-
const shouldClarify = clarify
|
|
496
|
+
const shouldClarify = clarify === true && ctx.hasUI && !hasParallelSteps;
|
|
494
497
|
let tuiBehaviorOverrides: (BehaviorOverride | undefined)[] | undefined;
|
|
495
498
|
const availableModels: ModelInfo[] = ctx.modelRegistry.getAvailable().map(toModelInfo);
|
|
496
499
|
const availableSkills = discoverAvailableSkills(cwd ?? ctx.cwd);
|
|
@@ -644,6 +647,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
644
647
|
cwd,
|
|
645
648
|
runId,
|
|
646
649
|
globalTaskIndex,
|
|
650
|
+
forwardedFlags: params.forwardedFlags,
|
|
647
651
|
sessionDirForIndex,
|
|
648
652
|
sessionFileForIndex,
|
|
649
653
|
shareEnabled,
|
|
@@ -850,6 +854,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
850
854
|
cwd,
|
|
851
855
|
runId,
|
|
852
856
|
globalTaskIndex,
|
|
857
|
+
forwardedFlags: params.forwardedFlags,
|
|
853
858
|
sessionDirForIndex,
|
|
854
859
|
sessionFileForIndex,
|
|
855
860
|
shareEnabled,
|
|
@@ -1062,6 +1067,7 @@ export async function executeChain(params: ChainExecutionParams): Promise<ChainE
|
|
|
1062
1067
|
artifactConfig,
|
|
1063
1068
|
outputPath,
|
|
1064
1069
|
outputMode: behavior.outputMode,
|
|
1070
|
+
forwardedFlags: params.forwardedFlags,
|
|
1065
1071
|
maxSubagentDepth,
|
|
1066
1072
|
controlConfig,
|
|
1067
1073
|
onControlEvent,
|
|
@@ -179,6 +179,7 @@ async function runSingleAttempt(
|
|
|
179
179
|
parentRootRunId: options.nestedRoute?.rootRunId,
|
|
180
180
|
parentCapabilityToken: options.nestedRoute?.capabilityToken,
|
|
181
181
|
structuredOutput: options.structuredOutput,
|
|
182
|
+
forwardedFlags: options.forwardedFlags,
|
|
182
183
|
});
|
|
183
184
|
|
|
184
185
|
const result: SingleResult = {
|
|
@@ -145,6 +145,7 @@ interface ExecutorDeps {
|
|
|
145
145
|
pi: ExtensionAPI;
|
|
146
146
|
state: SubagentState;
|
|
147
147
|
config: ExtensionConfig;
|
|
148
|
+
forwardedFlags: string[];
|
|
148
149
|
asyncByDefault: boolean;
|
|
149
150
|
tempArtifactsDir: string;
|
|
150
151
|
getSubagentSessionRoot: (parentSessionFile: string | null) => string;
|
|
@@ -665,6 +666,7 @@ async function resumeAsyncRun(input: {
|
|
|
665
666
|
controlIntercomTarget: intercomBridge.active ? intercomBridge.orchestratorTarget : undefined,
|
|
666
667
|
childIntercomTarget: intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(runId, agent, index) : undefined,
|
|
667
668
|
availableModels,
|
|
669
|
+
forwardedFlags: input.deps.forwardedFlags,
|
|
668
670
|
});
|
|
669
671
|
if (result.isError) return result;
|
|
670
672
|
|
|
@@ -1133,6 +1135,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
1133
1135
|
controlIntercomTarget,
|
|
1134
1136
|
childIntercomTarget,
|
|
1135
1137
|
nestedRoute,
|
|
1138
|
+
forwardedFlags: deps.forwardedFlags,
|
|
1136
1139
|
});
|
|
1137
1140
|
}
|
|
1138
1141
|
|
|
@@ -1162,6 +1165,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
1162
1165
|
controlIntercomTarget,
|
|
1163
1166
|
childIntercomTarget,
|
|
1164
1167
|
nestedRoute,
|
|
1168
|
+
forwardedFlags: deps.forwardedFlags,
|
|
1165
1169
|
});
|
|
1166
1170
|
}
|
|
1167
1171
|
|
|
@@ -1205,6 +1209,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
|
|
|
1205
1209
|
childIntercomTarget: childIntercomTarget ? (agent, index) => childIntercomTarget(agent, index) : undefined,
|
|
1206
1210
|
nestedRoute,
|
|
1207
1211
|
acceptance: params.acceptance,
|
|
1212
|
+
forwardedFlags: deps.forwardedFlags,
|
|
1208
1213
|
});
|
|
1209
1214
|
}
|
|
1210
1215
|
|
|
@@ -1264,6 +1269,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
1264
1269
|
maxSubagentDepth: currentMaxSubagentDepth,
|
|
1265
1270
|
worktreeSetupHook: deps.config.worktreeSetupHook,
|
|
1266
1271
|
worktreeSetupHookTimeoutMs: deps.config.worktreeSetupHookTimeoutMs,
|
|
1272
|
+
forwardedFlags: deps.forwardedFlags,
|
|
1267
1273
|
});
|
|
1268
1274
|
|
|
1269
1275
|
if (chainResult.requestedAsync) {
|
|
@@ -1304,6 +1310,7 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
|
|
|
1304
1310
|
controlIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
1305
1311
|
childIntercomTarget: data.intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(id, agent, index) : undefined,
|
|
1306
1312
|
nestedRoute: data.nestedRoute,
|
|
1313
|
+
forwardedFlags: deps.forwardedFlags,
|
|
1307
1314
|
});
|
|
1308
1315
|
}
|
|
1309
1316
|
|
|
@@ -1363,6 +1370,7 @@ interface ForegroundParallelRunInput {
|
|
|
1363
1370
|
liveProgress: (AgentProgress | undefined)[];
|
|
1364
1371
|
onUpdate?: (r: AgentToolResult<Details>) => void;
|
|
1365
1372
|
worktreeSetup?: WorktreeSetup;
|
|
1373
|
+
forwardedFlags?: string[];
|
|
1366
1374
|
}
|
|
1367
1375
|
|
|
1368
1376
|
function buildParallelModeError(message: string): AgentToolResult<Details> {
|
|
@@ -1523,6 +1531,7 @@ async function runForegroundParallelTasks(input: ForegroundParallelRunInput): Pr
|
|
|
1523
1531
|
skills: effectiveSkills === false ? [] : effectiveSkills,
|
|
1524
1532
|
acceptance: task.acceptance,
|
|
1525
1533
|
acceptanceContext: { mode: "parallel" },
|
|
1534
|
+
forwardedFlags: input.forwardedFlags,
|
|
1526
1535
|
onUpdate: input.onUpdate
|
|
1527
1536
|
? (progressUpdate) => {
|
|
1528
1537
|
const stepResults = progressUpdate.details?.results || [];
|
|
@@ -1735,6 +1744,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
1735
1744
|
controlConfig,
|
|
1736
1745
|
controlIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
1737
1746
|
childIntercomTarget: data.intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(id, agent, index) : undefined,
|
|
1747
|
+
forwardedFlags: deps.forwardedFlags,
|
|
1738
1748
|
});
|
|
1739
1749
|
}
|
|
1740
1750
|
}
|
|
@@ -1818,6 +1828,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
|
|
|
1818
1828
|
liveProgress,
|
|
1819
1829
|
onUpdate: onUpdateWithCost,
|
|
1820
1830
|
worktreeSetup,
|
|
1831
|
+
forwardedFlags: deps.forwardedFlags,
|
|
1821
1832
|
});
|
|
1822
1833
|
if (foregroundControl) updateForegroundNestedProjection(foregroundControl);
|
|
1823
1834
|
recordSyncCost(deps.state.grandTotal, runId, results.reduce((sum, r) => sum + r.usage.cost, 0) + sumNestedCost(foregroundControl?.nestedChildren));
|
|
@@ -2013,6 +2024,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
2013
2024
|
controlConfig,
|
|
2014
2025
|
controlIntercomTarget: data.intercomBridge.active ? data.intercomBridge.orchestratorTarget : undefined,
|
|
2015
2026
|
childIntercomTarget: data.intercomBridge.active ? (agent, index) => resolveSubagentIntercomTarget(id, agent, index) : undefined,
|
|
2027
|
+
forwardedFlags: deps.forwardedFlags,
|
|
2016
2028
|
});
|
|
2017
2029
|
}
|
|
2018
2030
|
}
|
|
@@ -2101,6 +2113,7 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
|
|
|
2101
2113
|
skills: effectiveSkills,
|
|
2102
2114
|
acceptance: params.acceptance,
|
|
2103
2115
|
acceptanceContext: { mode: "single" },
|
|
2116
|
+
forwardedFlags: deps.forwardedFlags,
|
|
2104
2117
|
});
|
|
2105
2118
|
if (foregroundControl?.currentIndex === 0) {
|
|
2106
2119
|
foregroundControl.interrupt = undefined;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export interface ForwardFlagsConfig {
|
|
2
|
+
forwardParentFlags?: boolean;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
// Core pi flag names -> value arity. This is the UNION of recognized core flags
|
|
6
|
+
// across the supported pi range (0.74-0.80), transcribed from
|
|
7
|
+
// @earendil-works/pi-coding-agent dist/cli/args.js. A name present here is never
|
|
8
|
+
// forwarded to children. Direction matters: an entry that is core in a NEWER pi
|
|
9
|
+
// but absent from an OLDER installed pi is safe (worst case we skip forwarding a
|
|
10
|
+
// hypothetical extension flag of the same name); a MISSING entry is the real
|
|
11
|
+
// hazard - it would leak a real core flag into the child. The drift tripwire in
|
|
12
|
+
// test/unit/forward-flags.test.ts asserts installed-core is a subset of this table.
|
|
13
|
+
type Arity = "boolean" | "value" | "value-guarded" | "value-guarded-print";
|
|
14
|
+
export const RECOGNIZED_PI_FLAGS: Record<string, Arity> = {
|
|
15
|
+
help: "boolean", version: "boolean", continue: "boolean", resume: "boolean",
|
|
16
|
+
"no-session": "boolean", "no-tools": "boolean", "no-builtin-tools": "boolean",
|
|
17
|
+
"no-extensions": "boolean", "no-skills": "boolean", "no-prompt-templates": "boolean",
|
|
18
|
+
"no-themes": "boolean", "no-context-files": "boolean", verbose: "boolean",
|
|
19
|
+
approve: "boolean", "no-approve": "boolean", offline: "boolean",
|
|
20
|
+
mode: "value", provider: "value", model: "value", "api-key": "value",
|
|
21
|
+
"system-prompt": "value", "append-system-prompt": "value", name: "value",
|
|
22
|
+
session: "value", "session-id": "value", fork: "value", "session-dir": "value",
|
|
23
|
+
models: "value", tools: "value", "exclude-tools": "value", thinking: "value",
|
|
24
|
+
export: "value", extension: "value", skill: "value", "prompt-template": "value",
|
|
25
|
+
theme: "value",
|
|
26
|
+
print: "value-guarded-print", "list-models": "value-guarded",
|
|
27
|
+
};
|
|
28
|
+
// Short aliases -> long name, so `-p foo` / `-e x` classify correctly.
|
|
29
|
+
const SHORT_ALIASES: Record<string, string> = {
|
|
30
|
+
h: "help", v: "version", c: "continue", r: "resume", nt: "no-tools",
|
|
31
|
+
nbt: "no-builtin-tools", ne: "no-extensions", ns: "no-skills",
|
|
32
|
+
np: "no-prompt-templates", nc: "no-context-files", a: "approve", na: "no-approve",
|
|
33
|
+
n: "name", t: "tools", xt: "exclude-tools", p: "print", e: "extension",
|
|
34
|
+
};
|
|
35
|
+
const EXTENSION_CUSTOMIZATION = new Set(["--extension", "-e", "--no-extensions", "-ne"]);
|
|
36
|
+
|
|
37
|
+
function consumesNext(arity: Arity, next: string | undefined): boolean {
|
|
38
|
+
if (arity === "value") return next !== undefined;
|
|
39
|
+
if (arity === "value-guarded") return next !== undefined && !next.startsWith("-") && !next.startsWith("@");
|
|
40
|
+
if (arity === "value-guarded-print") return next !== undefined && !next.startsWith("@") && (!next.startsWith("-") || next.startsWith("---"));
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function deriveForwardedFlags(argv: string[], config: ForwardFlagsConfig): string[] {
|
|
45
|
+
if (config.forwardParentFlags === false) return [];
|
|
46
|
+
if (argv.some((tok) => EXTENSION_CUSTOMIZATION.has(tok))) return [];
|
|
47
|
+
|
|
48
|
+
const collected: Array<{ name: string; tokens: string[] }> = [];
|
|
49
|
+
for (let i = 2; i < argv.length; i++) {
|
|
50
|
+
const tok = argv[i]!;
|
|
51
|
+
if (tok.startsWith("@") || !tok.startsWith("-")) continue;
|
|
52
|
+
if (tok.startsWith("-") && !tok.startsWith("--")) {
|
|
53
|
+
const alias = SHORT_ALIASES[tok.slice(1)];
|
|
54
|
+
if (alias && consumesNext(RECOGNIZED_PI_FLAGS[alias]!, argv[i + 1])) i++;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const body = tok.slice(2);
|
|
58
|
+
const eq = body.indexOf("=");
|
|
59
|
+
const name = eq === -1 ? body : body.slice(0, eq);
|
|
60
|
+
const arity = RECOGNIZED_PI_FLAGS[name];
|
|
61
|
+
if (arity) {
|
|
62
|
+
if (eq === -1 && consumesNext(arity, argv[i + 1])) i++;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (eq !== -1) {
|
|
66
|
+
collected.push({ name, tokens: [tok] });
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const next = argv[i + 1];
|
|
70
|
+
if (next !== undefined && !next.startsWith("-") && !next.startsWith("@")) {
|
|
71
|
+
collected.push({ name, tokens: [tok, next] });
|
|
72
|
+
i++;
|
|
73
|
+
} else {
|
|
74
|
+
collected.push({ name, tokens: [tok] });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Dedup by name, last value wins; first-occurrence position is preserved.
|
|
79
|
+
const lastByName = new Map<string, string[]>();
|
|
80
|
+
for (const { name, tokens } of collected) lastByName.set(name, tokens);
|
|
81
|
+
return [...lastByName.values()].flat();
|
|
82
|
+
}
|
|
@@ -38,6 +38,7 @@ interface BuildPiArgsInput {
|
|
|
38
38
|
inheritSkills: boolean;
|
|
39
39
|
tools?: string[];
|
|
40
40
|
extensions?: string[];
|
|
41
|
+
forwardedFlags?: string[];
|
|
41
42
|
systemPrompt?: string | null;
|
|
42
43
|
cwd?: string;
|
|
43
44
|
promptFileStem?: string;
|
|
@@ -76,6 +77,11 @@ export function applyThinkingSuffix(model: string | undefined, thinking: string
|
|
|
76
77
|
|
|
77
78
|
export function buildPiArgs(input: BuildPiArgsInput): BuildPiArgsResult {
|
|
78
79
|
const args = [...input.baseArgs];
|
|
80
|
+
// Forwarded flags are already name-deduped by deriveForwardedFlags; append only
|
|
81
|
+
// when the child inherits full extension discovery (extensions === undefined).
|
|
82
|
+
if (input.extensions === undefined && input.forwardedFlags?.length) {
|
|
83
|
+
args.push(...input.forwardedFlags);
|
|
84
|
+
}
|
|
79
85
|
|
|
80
86
|
if (input.sessionFile) {
|
|
81
87
|
fs.mkdirSync(path.dirname(input.sessionFile), { recursive: true });
|
package/src/shared/types.ts
CHANGED
|
@@ -796,6 +796,8 @@ export interface RunSyncOptions {
|
|
|
796
796
|
outputMode?: OutputMode;
|
|
797
797
|
maxSubagentDepth?: number;
|
|
798
798
|
nestedRoute?: NestedRouteInfo;
|
|
799
|
+
/** Parent extension flags to forward into the child (already derived; empty = none). */
|
|
800
|
+
forwardedFlags?: string[];
|
|
799
801
|
/** Override the agent's default model (format: "provider/id" or just "id") */
|
|
800
802
|
modelOverride?: string;
|
|
801
803
|
/** Registry models available for heuristic bare-model resolution */
|
|
@@ -849,6 +851,8 @@ export interface ExtensionConfig {
|
|
|
849
851
|
intercomBridge?: IntercomBridgeConfig;
|
|
850
852
|
/** Print a roster of discovered subagents/chains into chat at session start. Default: true. */
|
|
851
853
|
showRosterOnStart?: boolean;
|
|
854
|
+
/** Forward the parent pi's extension CLI flags (e.g. --no-autofix) into spawned children. Default: true. */
|
|
855
|
+
forwardParentFlags?: boolean;
|
|
852
856
|
}
|
|
853
857
|
|
|
854
858
|
// ============================================================================
|