@maxiaochao/pi-toolkit 0.11.1 → 0.11.3

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 CHANGED
@@ -1,3 +1,16 @@
1
+ ## 2026-10-12 - v0.11.3
2
+
3
+ - harden coordinated publishing and make local release checks authoritative
4
+
5
+ ## Unreleased
6
+
7
+ - Replace split child/root publishing with one idempotent root-tag workflow, make final-version local checks authoritative, keep CI publication-only, and remove timing-sensitive Herdr lifecycle assertions.
8
+
9
+ ## 2026-10-12 - v0.11.2
10
+
11
+ - bundle herdr-subagents v0.4.12: enable codemode for child agents while preserving persona tool boundaries
12
+ - Enable `codemode` for bundled Herdr Subagents children while preserving persona tool boundaries.
13
+
1
14
  ## 2026-10-12 - v0.11.1
2
15
 
3
16
  - bundle herdr-subagents v0.4.11: add architecture documentation and clear-output guidance evaluation
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maxiaochao/pi-toolkit",
3
- "version": "0.11.1",
3
+ "version": "0.11.3",
4
4
  "description": "Personal pi coding agent toolkit: scheduler, /btw, /cache_export, apply_patch, browser automation, and bundled theme/skills.",
5
5
  "type": "module",
6
6
  "workspaces": [
@@ -1,3 +1,12 @@
1
+ ## Unreleased
2
+
3
+ - Make lifecycle tests accept both valid report-first and observation-first execution orderings.
4
+
5
+ ## 2026-10-12 - v0.4.12
6
+
7
+ - enable codemode for child agents while preserving persona tool boundaries
8
+ - Enable `codemode` for every child Pi while preserving each persona's existing tool allowlist and read/write boundary.
9
+
1
10
  ## 2026-10-12 - v0.4.11
2
11
 
3
12
  - add architecture documentation and clear-output guidance evaluation
@@ -172,6 +172,8 @@ Each `personas.<name>` object supports exactly these overrides:
172
172
 
173
173
  JSON persona overrides cannot change `name`, `description`, `access`, `tools`, or the system prompt; define those in persona Markdown.
174
174
 
175
+ Every child Pi explicitly loads and enables `codemode`. Built-in personas include it in their tool allowlists. Custom personas may omit `tools` and inherit Pi's defaults plus `codemode`, or list `codemode` explicitly alongside their allowed tools. A read-only persona remains read-only because codemode can call only the tools enabled for that child session.
176
+
175
177
  A global configuration can establish shared defaults:
176
178
 
177
179
  ```json
@@ -2,7 +2,7 @@
2
2
  name: analyst
3
3
  description: Neutral read-only analyst for dynamically assigned roles
4
4
  access: read
5
- tools: read, bash, grep, find, ls
5
+ tools: read, bash, grep, find, ls, codemode
6
6
  ---
7
7
 
8
8
  Act as an independent analyst and follow the role and evaluation method in the task prompt. Do not modify project files. Separate direct evidence, inference, and unknowns, and report concrete evidence with useful paths.
@@ -2,7 +2,7 @@
2
2
  name: explorer
3
3
  description: Read-only explorer for fast codebase investigation
4
4
  access: read
5
- tools: read, bash, grep, find, ls
5
+ tools: read, bash, grep, find, ls, codemode
6
6
  ---
7
7
 
8
8
  Investigate the assigned question without modifying files. Report concrete evidence and useful paths.
@@ -2,7 +2,7 @@
2
2
  name: reviewer
3
3
  description: Read-only reviewer for correctness and maintainability risks
4
4
  access: read
5
- tools: read, bash, grep, find, ls
5
+ tools: read, bash, grep, find, ls, codemode
6
6
  ---
7
7
 
8
8
  Review the assigned work without modifying files. Report actionable findings with concrete evidence.
@@ -2,7 +2,7 @@
2
2
  name: worker
3
3
  description: General-purpose worker for one focused task
4
4
  access: write
5
- tools: read, bash, edit, write, grep, find, ls
5
+ tools: read, bash, edit, write, grep, find, ls, codemode
6
6
  ---
7
7
 
8
8
  You are a focused worker subagent.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maxiaochao/pi-herdr-subagents",
3
- "version": "0.4.11",
3
+ "version": "0.4.12",
4
4
  "description": "Run inspectable interactive Pi subagent batches through Herdr.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -36,13 +36,15 @@ export function childPiArgs(task: LiveTaskRecord, systemPromptFile: string): str
36
36
  "--session", task.sessionFile,
37
37
  "--name", `Subagent: ${task.name ?? task.agent ?? "worker"}`,
38
38
  "--no-extensions",
39
+ "--extension", "builtin:codemode",
39
40
  "--extension", fileURLToPath(new URL("../extensions/subagent-report.ts", import.meta.url)),
40
41
  "--no-skills",
41
42
  "--append-system-prompt", systemPromptFile,
42
43
  ];
43
44
  if (task.model) args.push("--model", task.model);
44
45
  if (task.thinking) args.push("--thinking", task.thinking);
45
- if (task.tools?.length) args.push("--tools", [...new Set([...task.tools, "subagent_report"])].join(","));
46
+ if (task.tools?.length) args.push("--tools", [...new Set([...task.tools, "codemode", "subagent_report"])].join(","));
47
+ else args.push("--tools", "+codemode");
46
48
  for (const skill of task.skills ?? []) args.push("--skill", skill);
47
49
  return args;
48
50
  }
@@ -10,6 +10,7 @@ export const BUILTIN_TOOLS = new Set([
10
10
  "find",
11
11
  "ls",
12
12
  "powershell",
13
+ "codemode",
13
14
  ]);
14
15
 
15
16
  export interface AgentDefinition {
@@ -24,6 +24,7 @@ import { RunPaneAllocator } from "../src/run-pane-allocator.ts";
24
24
  import { aggregateTaskStatus } from "../src/run-status.ts";
25
25
  import { isAbortError } from "../src/errors.ts";
26
26
  import { acquireDirectoryLock, withRunStateLock } from "../src/run-state.ts";
27
+ import { childPiArgs } from "../src/live-agent.ts";
27
28
  import registerSubagentReport from "../extensions/subagent-report.ts";
28
29
 
29
30
  /** Drives one task through the batch runner and projects its single result like a task-scoped run. */
@@ -457,13 +458,13 @@ try {
457
458
  herdr,
458
459
  });
459
460
  check("compact successful report", success.ok && success.status === "completed" && success.summary === "Authentication review completed." && success.documents.length === 1 && !success.output.includes("FULL RESULT"));
460
- check("Herdr lifecycle", herdr.calls.map(([name]) => name).join(",") === "createTab,renamePane,startAgent,promptAgent,getAgentState,isTabFocused,closeTab");
461
+ check("Herdr lifecycle", herdr.calls.map(([name]) => name).filter((name) => name !== "getAgentState").join(",") === "createTab,renamePane,startAgent,promptAgent,isTabFocused,closeTab");
461
462
  const createRequest = herdr.calls.find(([name]) => name === "createTab")[1];
462
463
  const startRequest = herdr.calls.find(([name]) => name === "startAgent")[1];
463
464
  check("named unfocused tab in parent workspace", createRequest.workspaceId === "w1" && createRequest.cwd === root && createRequest.label === "SA · auth-review" && createRequest.focus === false);
464
465
  check("ordinary execution never steals pane focus", !herdr.calls.some(([name]) => name === "focusPane"));
465
466
  check("child environment points at durable task and parent pane", createRequest.env.PI_HERDR_SUBAGENTS_CHILD === "1" && createRequest.env.PI_HERDR_SUBAGENTS_TASK_DIR.includes(join(".pi", "herdr-subagents", "runs")) && createRequest.env.PI_HERDR_SUBAGENTS_PARENT_PANE_ID === "w1:p-parent");
466
- check("persistent isolated child Pi", startRequest.kind === "pi" && startRequest.paneId === "w1:p2" && startRequest.args.includes("--session") && startRequest.args.includes("--no-extensions") && startRequest.args.includes("--extension") && startRequest.args.includes("--no-skills") && startRequest.args.includes("--model") && startRequest.args.includes("fake/model") && startRequest.args.includes("--tools") && startRequest.args.includes("read,grep,subagent_report"));
467
+ check("persistent isolated child Pi", startRequest.kind === "pi" && startRequest.paneId === "w1:p2" && startRequest.args.includes("--session") && startRequest.args.includes("--no-extensions") && startRequest.args.includes("builtin:codemode") && startRequest.args.includes("--extension") && startRequest.args.includes("--no-skills") && startRequest.args.includes("--model") && startRequest.args.includes("fake/model") && startRequest.args.includes("--tools") && startRequest.args.includes("read,grep,codemode,subagent_report"));
467
468
  const promptRequest = herdr.calls.find(([name]) => name === "promptAgent")[1];
468
469
  check("prompt submitted through Herdr", promptRequest.prompt === "Review authentication" && promptRequest.target === startRequest.name);
469
470
 
@@ -480,7 +481,7 @@ try {
480
481
  model: { provider: "fake", id: "model" },
481
482
  thinkingLevel: "medium",
482
483
  }, { agentsDirectory: join(process.cwd(), "agents"), herdr: extensionHerdr });
483
- check("public extension behavior uses injectable Herdr", !publicResult.isError && publicResult.content[0].text.includes("Authentication review completed.") && !publicResult.content[0].text.includes("FULL RESULT") && extensionHerdr.calls.map(([name]) => name).join(",") === "createTab,renamePane,startAgent,promptAgent,getAgentState,isTabFocused,closeTab", JSON.stringify({ publicResult, calls: extensionHerdr.calls.map(([name]) => name) }));
484
+ check("public extension behavior uses injectable Herdr", !publicResult.isError && publicResult.content[0].text.includes("Authentication review completed.") && !publicResult.content[0].text.includes("FULL RESULT") && extensionHerdr.calls.map(([name]) => name).filter((name) => name !== "getAgentState").join(",") === "createTab,renamePane,startAgent,promptAgent,isTabFocused,closeTab", JSON.stringify({ publicResult, calls: extensionHerdr.calls.map(([name]) => name) }));
484
485
  let singleStalledWarning = "";
485
486
  await runSingleTask("single slow task", { cwd: root, herdr: fakeHerdr(), stalledWarningSeconds: 0.001, onStalled: (message) => { singleStalledWarning = message; } });
486
487
  check("single-task execution emits configured stalled warning", singleStalledWarning.includes("appears stalled"));
@@ -570,6 +571,10 @@ try {
570
571
 
571
572
  const definition = parseAgentMarkdown(`---\nname: reviewer\ndescription: Review code\nmodel: fake/model\ntools: read, grep\n---\nReview carefully.`, "reviewer.md");
572
573
  check("persona frontmatter parsing", definition.name === "reviewer" && definition.model === "fake/model" && definition.tools?.join(",") === "read,grep" && definition.systemPrompt === "Review carefully.");
574
+ const inheritedToolArgs = childPiArgs({ id: "inherited", sessionFile: "/tmp/inherited.jsonl" }, "/tmp/system.md");
575
+ check("child Pi enables codemode when a persona inherits default tools", inheritedToolArgs.includes("builtin:codemode") && inheritedToolArgs[inheritedToolArgs.indexOf("--tools") + 1] === "+codemode");
576
+ const explicitToolArgs = childPiArgs({ id: "explicit", sessionFile: "/tmp/explicit.jsonl", tools: ["read"] }, "/tmp/system.md");
577
+ check("child Pi enables codemode with an explicit tool allowlist", explicitToolArgs.includes("builtin:codemode") && explicitToolArgs.includes("read,codemode,subagent_report"));
573
578
  const agentsDir = join(root, "agents");
574
579
  await import("node:fs/promises").then(({ mkdir }) => mkdir(agentsDir));
575
580
  writeFileSync(join(agentsDir, "worker.md"), `---\nname: worker\ndescription: Worker\n---\nDo work.`);
@@ -578,7 +583,7 @@ try {
578
583
  check("persona discovery and diagnostics", discovery.agents.length === 1 && discovery.agents[0].name === "worker" && discovery.diagnostics.length === 1);
579
584
  const packageDiscovery = discoverPackageAgents(join(process.cwd(), "agents"));
580
585
  const analystPersona = packageDiscovery.agents.find((persona) => persona.name === "analyst");
581
- check("built-in analyst is neutral and read-only", packageDiscovery.diagnostics.length === 0 && analystPersona?.access === "read" && analystPersona.model === undefined && analystPersona.tools?.join(",") === "read,bash,grep,find,ls" && /independent analyst/.test(analystPersona.systemPrompt) && /direct evidence, inference, and unknowns/.test(analystPersona.systemPrompt) && !/auditor|replicator|adversary/.test(analystPersona.systemPrompt));
586
+ check("built-in analyst is neutral and read-only", packageDiscovery.diagnostics.length === 0 && analystPersona?.access === "read" && analystPersona.model === undefined && analystPersona.tools?.join(",") === "read,bash,grep,find,ls,codemode" && /independent analyst/.test(analystPersona.systemPrompt) && /direct evidence, inference, and unknowns/.test(analystPersona.systemPrompt) && !/auditor|replicator|adversary/.test(analystPersona.systemPrompt));
582
587
 
583
588
  const configRoot = join(root, "configuration");
584
589
  const packageAgents = join(configRoot, "package-agents");