@gotgenes/pi-subagents 5.8.1 → 5.8.2

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
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [5.8.2](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v5.8.1...pi-subagents-v5.8.2) (2026-05-20)
9
+
10
+
11
+ ### Documentation
12
+
13
+ * plan inject projectAgentsDir into AgentMenuDeps ([#77](https://github.com/gotgenes/pi-packages/issues/77)) ([d8cf039](https://github.com/gotgenes/pi-packages/commit/d8cf03944d0abc6230541d67655d89582665a615))
14
+ * **retro:** add retro notes for issue [#66](https://github.com/gotgenes/pi-packages/issues/66) ([ce0f04a](https://github.com/gotgenes/pi-packages/commit/ce0f04a3d84523a4c2e8b7bc998b5bec0f16970f))
15
+
8
16
  ## [5.8.1](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v5.8.0...pi-subagents-v5.8.1) (2026-05-20)
9
17
 
10
18
 
@@ -0,0 +1,115 @@
1
+ ---
2
+ issue: 77
3
+ issue_title: "refactor: add projectAgentsDir to AgentMenuDeps instead of reading process.cwd() inline"
4
+ ---
5
+
6
+ # Inject projectAgentsDir into AgentMenuDeps
7
+
8
+ ## Problem Statement
9
+
10
+ `createAgentsMenuHandler` computes the project agents directory by reading `process.cwd()` inline via a lambda on line 63 of `ui/agent-menu.ts`:
11
+
12
+ ```typescript
13
+ const projectAgentsDir = () => join(process.cwd(), ".pi", "agents");
14
+ ```
15
+
16
+ The `AgentMenuDeps` interface already carries `personalAgentsDir: string` as an explicit field, but the project-side equivalent bypasses the injection boundary entirely.
17
+ `projectAgentsDir()` is called in at least five places inside the handler (`findAgentFile`, `ejectAgent`, `disableAgent`, `showCreateWizard`, `showManualWizard`).
18
+ This violates the code-style rule: "Do not read `process.cwd()` inside library/utility functions — accept the value as a parameter."
19
+
20
+ ## Goals
21
+
22
+ - Add `projectAgentsDir: string` to `AgentMenuDeps`.
23
+ - Remove the inline `projectAgentsDir` lambda from `createAgentsMenuHandler`.
24
+ - Replace all five call sites with `deps.projectAgentsDir`.
25
+ - Wire the value at the call site in `index.ts` as `join(process.cwd(), ".pi", "agents")`.
26
+ - Update the test helper `makeDeps()` in `agent-menu.test.ts` to supply the new field.
27
+
28
+ ## Non-Goals
29
+
30
+ - Removing other `process.cwd()` calls in `index.ts` (e.g., `loadCustomAgents`, `GitWorktreeManager`).
31
+ Those are separate concerns tracked or already addressed elsewhere (e.g., #76 for `AgentManager`).
32
+ - Changing `personalAgentsDir` wiring or any other `AgentMenuDeps` fields.
33
+
34
+ ## Background
35
+
36
+ ### Relevant modules
37
+
38
+ | Module | Role |
39
+ | ---------------------------- | -------------------------------------------------------------------------------------------------------------------- |
40
+ | `src/ui/agent-menu.ts` | Contains `AgentMenuDeps` interface and `createAgentsMenuHandler` factory. Owns the inline `projectAgentsDir` lambda. |
41
+ | `src/index.ts` | Extension entry point. Constructs the `AgentMenuDeps` object at line 228. Already passes `personalAgentsDir`. |
42
+ | `test/ui/agent-menu.test.ts` | Tests for the menu handler. Has a `makeDeps()` helper that constructs `AgentMenuDeps`. |
43
+
44
+ ### Constraints
45
+
46
+ From code-style skill:
47
+
48
+ > Do not read `process.env`, `process.cwd()`, or `process.platform` inside library/utility functions — accept the value as a parameter.
49
+
50
+ This is the same pattern applied in #76 (inject `cwd` into `AgentManager`), now applied to the UI layer.
51
+
52
+ `AgentMenuDeps` is internal — the public API surface (`exports` in `package.json`) is `service.ts` only, so this is a non-breaking change for consumers.
53
+
54
+ ## Design Overview
55
+
56
+ The change is mechanical — add a field, remove a lambda, replace call sites:
57
+
58
+ 1. Add `projectAgentsDir: string` to `AgentMenuDeps` (mirrors the existing `personalAgentsDir` field).
59
+ 2. Delete the `const projectAgentsDir = () => join(process.cwd(), ".pi", "agents");` lambda inside `createAgentsMenuHandler`.
60
+ 3. Replace all five `projectAgentsDir()` call sites with `deps.projectAgentsDir`:
61
+ - `findAgentFile` (line 68)
62
+ - `ejectAgent` (line 312)
63
+ - `disableAgent` (line 378)
64
+ - `showCreateWizard` (line 416)
65
+ - `showManualWizard` — uses the `targetDir` value from `showCreateWizard`, so not a direct call site
66
+ 4. At the call site in `index.ts`, add `projectAgentsDir: join(process.cwd(), ".pi", "agents")` to the deps object.
67
+ 5. In `makeDeps()`, add `projectAgentsDir: "/test-project/.pi/agents"`.
68
+
69
+ No new types, no interface changes beyond the one added field.
70
+
71
+ ## Module-Level Changes
72
+
73
+ ### `src/ui/agent-menu.ts`
74
+
75
+ - Add `projectAgentsDir: string` to the `AgentMenuDeps` interface.
76
+ - Remove the `const projectAgentsDir = () => join(process.cwd(), ".pi", "agents");` lambda.
77
+ - Replace `projectAgentsDir()` with `deps.projectAgentsDir` at all call sites inside the factory closure.
78
+ - The `join` import from `node:path` may become unused in this file if no other call uses it — verify and remove if so.
79
+
80
+ ### `src/index.ts`
81
+
82
+ - Add `projectAgentsDir: join(process.cwd(), ".pi", "agents")` to the `createAgentsMenuHandler({...})` call.
83
+ - Import `join` from `node:path` if not already imported.
84
+
85
+ ### `test/ui/agent-menu.test.ts`
86
+
87
+ - Add `projectAgentsDir: "/test-project/.pi/agents"` to `makeDeps()`.
88
+ - No other test logic changes.
89
+
90
+ ## Test Impact Analysis
91
+
92
+ 1. No new unit tests are strictly required — the refactoring is mechanical and preserves behavior.
93
+ However, a targeted test verifying that `findAgentFile` uses the injected `projectAgentsDir` (not `process.cwd()`) is valuable to prevent regression.
94
+ 2. No existing tests become redundant.
95
+ 3. All existing tests stay as-is; only the `makeDeps()` helper needs the new field.
96
+
97
+ ## TDD Order
98
+
99
+ 1. **Red: test that the injected projectAgentsDir is used** — add a test in `agent-menu.test.ts` that mocks `existsSync` and verifies `findAgentFile` (exercised via the menu handler) checks a path under the injected `projectAgentsDir`, not under `process.cwd()`.
100
+ Commit message: `test: verify projectAgentsDir injection in agent menu (#77)`
101
+
102
+ 2. **Green: add projectAgentsDir to AgentMenuDeps and wire it** — add the field to `AgentMenuDeps`, remove the inline lambda, replace all call sites with `deps.projectAgentsDir`, wire the value in `index.ts`, and update `makeDeps()` in the test helper.
103
+ Commit message: `refactor: inject projectAgentsDir into AgentMenuDeps (#77)`
104
+
105
+ ## Risks and Mitigations
106
+
107
+ | Risk | Mitigation |
108
+ | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
109
+ | Missing a `projectAgentsDir()` call site | `grep 'projectAgentsDir'` in `agent-menu.ts` confirms exactly five call sites (one definition + four usages). After the change, grep again to verify no `process.cwd()` calls remain. |
110
+ | `join` import becomes unused in `agent-menu.ts` | Check whether `join` is used elsewhere in the file (it is — `findAgentFile` uses `join` for the personal path). The import stays. |
111
+ | Behavioral change | Production call site passes `join(process.cwd(), ".pi", "agents")`, which produces the same value the lambda computed. No behavioral change. |
112
+
113
+ ## Open Questions
114
+
115
+ None — the issue's proposed change is unambiguous and mirrors the established `personalAgentsDir` pattern.
@@ -0,0 +1,44 @@
1
+ ---
2
+ issue: 66
3
+ issue_title: "refactor: replace `as any` casts in extracted tool/menu factories with proper SDK types"
4
+ ---
5
+
6
+ # Retro: #66 — replace `as any` casts with proper SDK types
7
+
8
+ ## Final Retrospective (2026-05-20T18:50:00Z)
9
+
10
+ ### Session summary
11
+
12
+ Replaced all 14 `as any` casts in `src/index.ts` by typing 5 factory dep interfaces with proper SDK types (`ExtensionContext`, `AgentSession`, `ExtensionAPI`, `ModelRegistry`) and the newly-exported `SpawnOptions`.
13
+ Released as `pi-subagents-v5.8.1` with zero behavioral change across 520 tests.
14
+ The plan, implementation, and shipping were completed in a single session with 6 implementation commits.
15
+
16
+ ### Observations
17
+
18
+ #### What went well
19
+
20
+ - Thorough context-gathering during planning paid off: reading all 5 dep interfaces, the SDK `.d.ts` files, test mock helpers, and the `ExtensionUIContext` shape before writing the plan meant most steps landed on first `pnpm run check`.
21
+ - The plan's risk table anticipated the cascading `as any` issue on `createAgentTool` and prepared a mitigation ("add explicit `satisfies ToolDefinition<…>` if needed"), which was close to the actual fix needed.
22
+ - Steps 4 and 5 (`GetResultDeps`, `SteerToolDeps`) were trivially clean — single-type-import changes that compiled immediately.
23
+
24
+ #### What caused friction (agent side)
25
+
26
+ - `missing-context` — The plan assumed `NotificationDeps.sendMessage.display` could be optional (`display?: boolean`) but the SDK's `CustomMessage.display` is required `boolean`.
27
+ First `pnpm run check` after step 2 failed; required one extra edit-verify cycle.
28
+ Impact: added friction but no rework (fixed in the same commit).
29
+
30
+ - `missing-context` — The plan did not check the `execute` function's full signature in `agent-tool.ts`.
31
+ Removing the outer `as any` on `createAgentTool({...})` exposed three additional type mismatches: `onUpdate` parameter type (`unknown` vs `AgentToolResult<any>`), `signal` optionality (`AbortSignal` vs `AbortSignal | undefined`), and `params.description` (`unknown` vs `string`).
32
+ The plan flagged cascading risk but assumed only the return type would be affected.
33
+ Impact: step 3 required three extra edits and two extra `pnpm run check` cycles, noted as a deviation in the commit body.
34
+
35
+ - `missing-context` — Steps 6 and 7 had to be folded into one commit.
36
+ The plan listed them as independent steps but typing `AgentMenuManager.spawnAndWait(ctx: ExtensionContext)` immediately made `MenuContext` incompatible — `showGenerateWizard` passes `ctx: MenuContext` to the dep callback that now expects `ExtensionContext`.
37
+ The testing skill already warns: "when a TDD plan lists separate steps that share a type definition, changing that type in step N breaks steps N+1…N+k."
38
+ The planner failed to recognize that `MenuContext` and `AgentMenuManager.spawnAndWait` share a type dependency through `ctx`.
39
+ Impact: no rework — the steps were folded successfully — but the plan's step count was inaccurate.
40
+
41
+ #### What caused friction (user side)
42
+
43
+ - No user-side friction observed.
44
+ The user ran three sequential prompts (`/plan-issue 66`, `/tdd-plan`, `/ship-issue`) with no corrections or redirects needed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gotgenes/pi-subagents",
3
- "version": "5.8.1",
3
+ "version": "5.8.2",
4
4
  "exports": {
5
5
  ".": "./src/service.ts"
6
6
  },
package/src/index.ts CHANGED
@@ -264,6 +264,7 @@ export default function (pi: ExtensionAPI) {
264
264
  ),
265
265
  emitEvent: (name, data) => pi.events.emit(name, data),
266
266
  personalAgentsDir: join(getAgentDir(), 'agents'),
267
+ projectAgentsDir: join(process.cwd(), '.pi', 'agents'),
267
268
  });
268
269
 
269
270
  pi.registerCommand('agents', {
@@ -41,6 +41,7 @@ export interface AgentMenuDeps {
41
41
  ) => { message: string; level: string };
42
42
  emitEvent: (name: string, data: unknown) => void;
43
43
  personalAgentsDir: string;
44
+ projectAgentsDir: string;
44
45
  /** Returns the runtime default max turns (undefined = unlimited). */
45
46
  getDefaultMaxTurns: () => number | undefined;
46
47
  /** Returns the runtime grace turns value. */
@@ -60,12 +61,10 @@ export interface AgentMenuDeps {
60
61
  * Returns a function suitable for `pi.registerCommand("agents", { handler })`.
61
62
  */
62
63
  export function createAgentsMenuHandler(deps: AgentMenuDeps) {
63
- const projectAgentsDir = () => join(process.cwd(), ".pi", "agents");
64
-
65
64
  function findAgentFile(
66
65
  name: string,
67
66
  ): { path: string; location: "project" | "personal" } | undefined {
68
- const projectPath = join(projectAgentsDir(), `${name}.md`);
67
+ const projectPath = join(deps.projectAgentsDir, `${name}.md`);
69
68
  if (existsSync(projectPath)) return { path: projectPath, location: "project" };
70
69
  const personalPath = join(deps.personalAgentsDir, `${name}.md`);
71
70
  if (existsSync(personalPath)) return { path: personalPath, location: "personal" };
@@ -309,7 +308,7 @@ export function createAgentsMenuHandler(deps: AgentMenuDeps) {
309
308
  if (!location) return;
310
309
 
311
310
  const targetDir = location.startsWith("Project")
312
- ? projectAgentsDir()
311
+ ? deps.projectAgentsDir
313
312
  : deps.personalAgentsDir;
314
313
  mkdirSync(targetDir, { recursive: true });
315
314
 
@@ -375,7 +374,7 @@ export function createAgentsMenuHandler(deps: AgentMenuDeps) {
375
374
  if (!location) return;
376
375
 
377
376
  const targetDir = location.startsWith("Project")
378
- ? projectAgentsDir()
377
+ ? deps.projectAgentsDir
379
378
  : deps.personalAgentsDir;
380
379
  mkdirSync(targetDir, { recursive: true });
381
380
 
@@ -413,7 +412,7 @@ export function createAgentsMenuHandler(deps: AgentMenuDeps) {
413
412
  if (!location) return;
414
413
 
415
414
  const targetDir = location.startsWith("Project")
416
- ? projectAgentsDir()
415
+ ? deps.projectAgentsDir
417
416
  : deps.personalAgentsDir;
418
417
 
419
418
  const method = await ctx.ui.select("Creation method", [