@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
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', {
|
package/src/ui/agent-menu.ts
CHANGED
|
@@ -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
|
|
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", [
|