@gotgenes/pi-subagents 17.0.1 → 17.1.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 +8 -0
- package/docs/architecture/architecture.md +8 -8
- package/docs/plans/0447-extract-subagents-settings-command.md +220 -0
- package/docs/retro/0447-extract-subagents-settings-command.md +46 -0
- package/docs/retro/0448-block-disabled-agent-spawn.md +33 -0
- package/package.json +1 -1
- package/src/index.ts +12 -0
- package/src/ui/subagents-settings.ts +83 -0
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
|
+
## [17.1.0](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v17.0.1...pi-subagents-v17.1.0) (2026-06-20)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* add SubagentsSettingsHandler for focused settings command ([#447](https://github.com/gotgenes/pi-packages/issues/447)) ([cae804d](https://github.com/gotgenes/pi-packages/commit/cae804d1be7ac437ad7344035b5d99cd1424e0c0))
|
|
14
|
+
* register /subagents-settings command ([#447](https://github.com/gotgenes/pi-packages/issues/447)) ([7735a38](https://github.com/gotgenes/pi-packages/commit/7735a38db2658fd9faca6388ac4bc712fa8e5a86))
|
|
15
|
+
|
|
8
16
|
## [17.0.1](https://github.com/gotgenes/pi-packages/compare/pi-subagents-v17.0.0...pi-subagents-v17.0.1) (2026-06-20)
|
|
9
17
|
|
|
10
18
|
|
|
@@ -950,7 +950,7 @@ Outcome: ADR-0004 updated with all four entry-criteria answers; Step 4 unblocked
|
|
|
950
950
|
|
|
951
951
|
`Release: independent`
|
|
952
952
|
|
|
953
|
-
### Step 2 — Extract settings to a focused `/subagents-settings` command ([#447])
|
|
953
|
+
### ✅ Step 2 — Extract settings to a focused `/subagents-settings` command ([#447])
|
|
954
954
|
|
|
955
955
|
Smell: Category E (naming/organization) — settings are buried inside the monolithic `/agents` command per ADR-0004 Decision C. This step is purely additive: it stands up the new surface without touching `agent-menu.ts`.
|
|
956
956
|
Target files:
|
|
@@ -1061,13 +1061,13 @@ Outcome: test clone groups ≤ 10 (from 16); `subagent-manager.test.ts` uses sha
|
|
|
1061
1061
|
|
|
1062
1062
|
```mermaid
|
|
1063
1063
|
flowchart LR
|
|
1064
|
-
S1[✅ Step 1 - Spike (#446)]
|
|
1065
|
-
S2[Step 2 - Settings command (#447)]
|
|
1066
|
-
S3[Step 3 - Background widget (#444)]
|
|
1067
|
-
S4[Step 4 - Native session nav (#445)]
|
|
1068
|
-
S5[Step 5 - Dissolve /agents + viewer (#442)]
|
|
1069
|
-
S6[Step 6 - Remove definition mgmt (#441)]
|
|
1070
|
-
S7[Step 7 - Test clones (#443)]
|
|
1064
|
+
S1["✅ Step 1 - Spike (#446)"]
|
|
1065
|
+
S2["✅ Step 2 - Settings command (#447)"]
|
|
1066
|
+
S3["Step 3 - Background widget (#444)"]
|
|
1067
|
+
S4["Step 4 - Native session nav (#445)"]
|
|
1068
|
+
S5["Step 5 - Dissolve /agents + viewer (#442)"]
|
|
1069
|
+
S6["Step 6 - Remove definition mgmt (#441)"]
|
|
1070
|
+
S7["Step 7 - Test clones (#443)"]
|
|
1071
1071
|
|
|
1072
1072
|
S1 --> S4
|
|
1073
1073
|
S2 --> S5
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 447
|
|
3
|
+
issue_title: "pi-subagents: extract subagent settings to a focused /subagents-settings command"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Extract subagent settings to a focused `/subagents-settings` command
|
|
7
|
+
|
|
8
|
+
## Release Recommendation
|
|
9
|
+
|
|
10
|
+
**Release:** ship independently
|
|
11
|
+
|
|
12
|
+
Phase 19 Step 2 ([#447]) carries `Release: independent` in the architecture roadmap, and the only batch defined there is "dissolve-agents" (Steps 5–6, [#442]/[#441]).
|
|
13
|
+
This step is purely additive — it stands up the new command without touching `agent-menu.ts` — so it ships on its own with no batch coupling.
|
|
14
|
+
|
|
15
|
+
## Problem Statement
|
|
16
|
+
|
|
17
|
+
The `/agents` command bundles four unrelated jobs (running-agent visibility, agent-type browsing, the creation wizard, and operational settings).
|
|
18
|
+
ADR-0004 Decision C splits them.
|
|
19
|
+
Settings has standalone value but does not belong buried inside an agent-management menu — a focused, top-level command is discoverable on its own without navigating a multi-purpose menu.
|
|
20
|
+
|
|
21
|
+
This step is deliberately **additive**: it stands up the new command without touching `agent-menu.ts`.
|
|
22
|
+
The old in-menu Settings option keeps working until Phase 19 Step 5 ([#442]) deletes `agent-menu.ts` wholesale.
|
|
23
|
+
Keeping the work additive avoids surgery on the doomed module and lets this step run in parallel with the rest of the Phase 19 replacement track.
|
|
24
|
+
|
|
25
|
+
## Goals
|
|
26
|
+
|
|
27
|
+
- Add `src/ui/subagents-settings.ts` — a `SubagentsSettingsHandler` lifted verbatim from `AgentsMenuHandler.showSettings`, carrying its own narrow manager interface (the three `apply*` methods and three readonly accessors only) and a narrow UI interface (only `select`, `input`, `notify`).
|
|
28
|
+
- Register the `/subagents-settings` command in `src/index.ts`, passing the existing `settings` (`SettingsManager`) directly.
|
|
29
|
+
- Add `test/ui/subagents-settings.test.ts` covering the extracted handler's behavior.
|
|
30
|
+
|
|
31
|
+
Non-breaking: this is pure addition.
|
|
32
|
+
No existing export, command, or behavior changes.
|
|
33
|
+
|
|
34
|
+
## Non-Goals
|
|
35
|
+
|
|
36
|
+
- Do **not** remove `showSettings` or `AgentMenuSettings` from `agent-menu.ts` — that file is deleted whole in Phase 19 Step 5 ([#442]).
|
|
37
|
+
- Do **not** modify the `/agents` command, its handler, or any wiring in `index.ts` beyond adding the new registration block.
|
|
38
|
+
- Do **not** add a settings re-show loop or any new settings behavior — the lift preserves the single-selection-then-return semantics of `showSettings` (see Design Overview).
|
|
39
|
+
- Defer the background widget ([#444]), native session navigation ([#445]), and the `/agents` dissolution ([#442]/[#441]) to their own Phase 19 steps.
|
|
40
|
+
|
|
41
|
+
## Background
|
|
42
|
+
|
|
43
|
+
Relevant existing modules:
|
|
44
|
+
|
|
45
|
+
- `src/ui/agent-menu.ts` — `AgentsMenuHandler.showSettings(ui)` is the source of the lift.
|
|
46
|
+
It depends solely on `this.settings` (typed `AgentMenuSettings`) and the `ui` parameter (`MenuUI`).
|
|
47
|
+
It calls `ui.select` once, then `ui.input` + one `settings.apply*` + `ui.notify` for the chosen field.
|
|
48
|
+
It does not loop: it shows the settings list once, applies one change (or none), and returns.
|
|
49
|
+
The re-show in the live menu comes from `showAgentsMenu` re-invoking *itself* after `showSettings` returns — not from `showSettings` looping.
|
|
50
|
+
- `AgentMenuSettings` (in `agent-menu.ts`) — the narrow settings shape `showSettings` reads: `readonly maxConcurrent`, `readonly defaultMaxTurns: number | undefined`, `readonly graceTurns`, plus `applyMaxConcurrent`/`applyDefaultMaxTurns`/`applyGraceTurns`, each returning `{ message: string; level: "info" | "warning" }`.
|
|
51
|
+
- `MenuUI` (in `agent-menu.ts`) — the wide menu UI interface; `showSettings` uses only `select`, `input`, and `notify` from it.
|
|
52
|
+
- `src/settings.ts` — `SettingsManager` is the concrete settings object constructed in `index.ts`.
|
|
53
|
+
It already exposes all six members of `AgentMenuSettings` (the three getters and three `apply*` methods), so it structurally satisfies the new narrow manager interface and can be passed directly.
|
|
54
|
+
- `src/index.ts` — constructs `settings = new SettingsManager(...)`, then `new AgentsMenuHandler(manager, registry, settings, ...)` and `registerCommand("agents", ...)`.
|
|
55
|
+
Sibling commands register flat, hyphenated names (`agents`, `colgrep-reindex`, `permission-system`) — no `:` namespace.
|
|
56
|
+
- `docs/decisions/0004-reconsider-ui-direction.md` — the addendum (2026-06-20) confirms the command name **`/subagents-settings`** (Criterion 4) and rejects `/subagents:settings` and `/agents-settings`.
|
|
57
|
+
|
|
58
|
+
AGENTS.md / package constraints:
|
|
59
|
+
|
|
60
|
+
- pi-subagents is a minimal core; the surviving UI is an in-core reactive consumer.
|
|
61
|
+
This extraction adds a command surface only — no policy, no new core dependency.
|
|
62
|
+
- Modules marked `← removing` (the `/agents` subtree) must not gain features — this plan adds nothing to `agent-menu.ts`.
|
|
63
|
+
|
|
64
|
+
## Design Overview
|
|
65
|
+
|
|
66
|
+
`SubagentsSettingsHandler` is a faithful lift of `showSettings` into a standalone, independently-registered command.
|
|
67
|
+
It owns the settings UI interaction and depends on two narrow interfaces it declares itself (ISP — neither carries a field the handler does not use).
|
|
68
|
+
|
|
69
|
+
### Narrow interfaces
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
/** Narrow settings interface required by the subagents-settings command. */
|
|
73
|
+
export interface SubagentsSettingsManager {
|
|
74
|
+
readonly maxConcurrent: number;
|
|
75
|
+
readonly defaultMaxTurns: number | undefined;
|
|
76
|
+
readonly graceTurns: number;
|
|
77
|
+
applyMaxConcurrent(n: number): { message: string; level: "info" | "warning" };
|
|
78
|
+
applyDefaultMaxTurns(n: number): { message: string; level: "info" | "warning" };
|
|
79
|
+
applyGraceTurns(n: number): { message: string; level: "info" | "warning" };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Narrow UI interface — only the ctx.ui methods the settings handler calls. */
|
|
83
|
+
export interface SubagentsSettingsUI {
|
|
84
|
+
select(title: string, options: string[]): Promise<string | undefined>;
|
|
85
|
+
input(title: string, defaultValue?: string): Promise<string | undefined>;
|
|
86
|
+
notify(message: string, level: "info" | "warning" | "error"): void;
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
`SubagentsSettingsManager` is shape-identical to `AgentMenuSettings` but owned by the new module (zero import from `agent-menu.ts`, which is doomed).
|
|
91
|
+
`SubagentsSettingsUI` is narrower than `MenuUI` — it drops `confirm`, `editor`, and `custom`, which `showSettings` never calls.
|
|
92
|
+
|
|
93
|
+
### Handler
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
export class SubagentsSettingsHandler {
|
|
97
|
+
constructor(private readonly settings: SubagentsSettingsManager) {}
|
|
98
|
+
|
|
99
|
+
async handle({ ui }: { ui: SubagentsSettingsUI }): Promise<void> {
|
|
100
|
+
// verbatim lift of showSettings: one select → input → apply* → notify
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The body is copied character-for-character from `showSettings` (the same three `if` branches, the same `parseInt` / validation / toast wiring), with `this.settings` resolving against the new narrow interface.
|
|
106
|
+
|
|
107
|
+
### Registration call site (index.ts)
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
const subagentsSettings = new SubagentsSettingsHandler(settings);
|
|
111
|
+
pi.registerCommand("subagents-settings", {
|
|
112
|
+
description: "Configure subagent settings (concurrency, turn limits)",
|
|
113
|
+
handler: async (_args, ctx) => {
|
|
114
|
+
await subagentsSettings.handle({ ui: ctx.ui });
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`settings` (the `SettingsManager` instance) is passed directly — it structurally satisfies `SubagentsSettingsManager`.
|
|
120
|
+
The command handler needs only `ctx.ui` (no `modelRegistry`, no `parentSnapshot`), so its params are narrower than the `/agents` handler's.
|
|
121
|
+
|
|
122
|
+
### Design-review findings
|
|
123
|
+
|
|
124
|
+
| Smell | Location | Evidence | Result |
|
|
125
|
+
| --------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- |
|
|
126
|
+
| Wide interface | `SubagentsSettingsManager` (6 members) | `handle` reads all 3 accessors and calls all 3 apply methods | None — 100% usage |
|
|
127
|
+
| Wide interface | `SubagentsSettingsUI` (3 methods) | `handle` calls `select`, `input`, `notify` | None — 100% usage |
|
|
128
|
+
| LoD violation | `subagents-settings.ts` | `this.settings.applyMaxConcurrent(n)` is a direct call; `ui.notify(toast.message, toast.level)` reads a returned value, not a reach-through | None |
|
|
129
|
+
| Output argument | `subagents-settings.ts` | handler reads accessors + calls apply methods; never writes back into `settings` or `ui` | None |
|
|
130
|
+
|
|
131
|
+
The extraction introduces a genuine new collaborator (a focused command handler owning the settings interaction) with its own narrow contracts — it is not procedure-splitting.
|
|
132
|
+
|
|
133
|
+
### Edge cases (preserved verbatim from `showSettings`)
|
|
134
|
+
|
|
135
|
+
- `select` returns `undefined` (operator cancels) → return early, no change.
|
|
136
|
+
- `input` returns falsy (empty / cancel) → no apply call.
|
|
137
|
+
- `maxConcurrent` / `graceTurns`: reject `n < 1` with `"Must be a positive integer."` warning.
|
|
138
|
+
- `defaultMaxTurns`: accept `n >= 0` (0 = unlimited); reject `n < 0` with `"Must be 0 (unlimited) or a positive integer."` warning.
|
|
139
|
+
- Single selection then return — no settings re-show loop (matches current `showSettings`).
|
|
140
|
+
|
|
141
|
+
## Module-Level Changes
|
|
142
|
+
|
|
143
|
+
- **New** `src/ui/subagents-settings.ts` — `SubagentsSettingsHandler` class, `SubagentsSettingsManager` interface, `SubagentsSettingsUI` interface.
|
|
144
|
+
~80 LOC.
|
|
145
|
+
- **Changed** `src/index.ts` — add the `SubagentsSettingsHandler` import, construct it, and register the `subagents-settings` command.
|
|
146
|
+
No other lines change; the `/agents` block stays intact.
|
|
147
|
+
- **New** `test/ui/subagents-settings.test.ts` — unit tests for the handler.
|
|
148
|
+
- **No change** to `src/ui/agent-menu.ts` (and its `AgentMenuSettings` / `showSettings` / `MenuUI` stay as-is — deleted later in [#442]).
|
|
149
|
+
|
|
150
|
+
Doc / skill grep results (no stale references to update in this step):
|
|
151
|
+
|
|
152
|
+
- `architecture.md` Step 2 already names `src/ui/subagents-settings.ts`, `SubagentsSettingsHandler`, `SubagentsSettingsManager`, and the `/subagents-settings` command with `Outcome: new subagents-settings.ts (~80 LOC) and focused command registered; agent-menu.ts untouched` — the file appears as a *planned* addition, so no doc edit is needed now; its status line flips to ✅ at ship time only if the operator wants it folded in.
|
|
153
|
+
- The package skill (`.pi/skills/package-pi-subagents/SKILL.md`) lists the UI domain by count (`ui/` = 10 modules); adding one file makes it 11, but the table is a coarse summary that is not maintained per-file — leave it for a later Phase 19 doc-sync unless the operator asks otherwise.
|
|
154
|
+
- `settings.ts` comments reference `/agents → Settings` as the writer of project settings; those are still accurate (the in-menu path keeps working), so no edit.
|
|
155
|
+
They become stale only when [#442] deletes `agent-menu.ts`; do not pre-edit them here.
|
|
156
|
+
|
|
157
|
+
## Test Impact Analysis
|
|
158
|
+
|
|
159
|
+
1. **New tests enabled by the extraction.**
|
|
160
|
+
`test/ui/subagents-settings.test.ts` can unit-test the settings flow in isolation against the standalone handler — no `AgentsMenuHandler` construction (registry, fileOps, two agent dirs) required.
|
|
161
|
+
This is strictly more focused than the current `agent-menu.test.ts` "agent menu — settings" describe block, which has to drive the full top menu (`"Settings"` from the main menu) before reaching the settings list.
|
|
162
|
+
2. **Existing tests that become redundant.**
|
|
163
|
+
None are removed in this step.
|
|
164
|
+
The "agent menu — settings" tests in `agent-menu.test.ts` still genuinely exercise the live in-menu path, which keeps working until [#442].
|
|
165
|
+
They are superseded only when `agent-menu.ts` is deleted in Step 5 — removing them now would drop coverage of a still-shipping surface.
|
|
166
|
+
3. **Existing tests that must stay as-is.**
|
|
167
|
+
All of `agent-menu.test.ts` stays unchanged — this step does not touch `agent-menu.ts`.
|
|
168
|
+
|
|
169
|
+
## Invariants at risk
|
|
170
|
+
|
|
171
|
+
This step adds a new module and one registration; it does not touch the Phase 18 spine that prior steps refactored.
|
|
172
|
+
The ADR-0004 / Phase 18 invariants (runtime holds zero UI state [#422]; widget is a reactive consumer with no inbound core calls [#423]; the `subagent` tool depends only on manager/runtime/settings/registry [#424]; declared event channels equal emitted channels [#425]) are untouched and stay pinned by their existing suites.
|
|
173
|
+
The new handler adds no inbound call into the core and emits no events — it reads/applies through the existing `SettingsManager` surface only, so it preserves these invariants by construction.
|
|
174
|
+
|
|
175
|
+
## TDD Order
|
|
176
|
+
|
|
177
|
+
1. **Red→Green: extract `SubagentsSettingsHandler` with its narrow interfaces.**
|
|
178
|
+
Test surface: `test/ui/subagents-settings.test.ts`.
|
|
179
|
+
Write tests against a `makeSettings()`-style manager stub (lift the stub shape from `agent-menu.test.ts`) and a narrow UI stub (lift `makeMenuUI`, or reuse it — it already provides `select`/`input`/`notify`):
|
|
180
|
+
- constructable;
|
|
181
|
+
- cancel at the settings list (`select` → `undefined`) → no apply call;
|
|
182
|
+
- `maxConcurrent`: valid input delegates to `applyMaxConcurrent(n)` and notifies the returned toast; `n < 1` notifies the positive-integer warning and does not apply;
|
|
183
|
+
- `defaultMaxTurns`: `0` delegates `applyDefaultMaxTurns(0)`; a positive value delegates; `n < 0` notifies the warning;
|
|
184
|
+
- `graceTurns`: valid input delegates to `applyGraceTurns(n)`; `n < 1` notifies the warning;
|
|
185
|
+
- empty/cancelled `input` → no apply call.
|
|
186
|
+
Implementation: create `src/ui/subagents-settings.ts` with the class and both interfaces; body lifted verbatim from `showSettings`.
|
|
187
|
+
Commit: `feat: add SubagentsSettingsHandler for focused settings command (#447)`.
|
|
188
|
+
2. **Green: register the `/subagents-settings` command in `index.ts`.**
|
|
189
|
+
Test surface: none new (command registration in `index.ts` is exercised by the package's existing index-level wiring tests if present; otherwise verify by `pnpm run check` and the suite).
|
|
190
|
+
Implementation: import `SubagentsSettingsHandler`, construct it with `settings`, and `registerCommand("subagents-settings", ...)` passing `{ ui: ctx.ui }`.
|
|
191
|
+
Run `pnpm run check` immediately (the registration is the only call site of the new export — it must compile alongside the new module).
|
|
192
|
+
Commit: `feat: register /subagents-settings command (#447)`.
|
|
193
|
+
|
|
194
|
+
Both steps may be folded into one commit if preferred — the export and its single call site are small — but keeping the handler (with its tests) separate from the wiring keeps each commit self-contained and reviewable.
|
|
195
|
+
|
|
196
|
+
## Risks and Mitigations
|
|
197
|
+
|
|
198
|
+
- **Risk: the lift diverges from `showSettings`, causing the standalone command to behave differently from the in-menu option while both ship.**
|
|
199
|
+
Mitigation: copy the body verbatim and assert the same validation/toast behavior in the new test file; the two surfaces share the same `SettingsManager`, so applied values are identical.
|
|
200
|
+
- **Risk: passing `SettingsManager` directly fails structural typing against the new narrow interface.**
|
|
201
|
+
Mitigation: `SettingsManager` already declares all six members with matching signatures (verified in `src/settings.ts`); `pnpm run check` in Step 2 confirms it.
|
|
202
|
+
- **Risk: command-name drift from the ADR.**
|
|
203
|
+
Mitigation: the name `subagents-settings` is fixed by the ADR-0004 addendum (Criterion 4) and the roadmap Step 2; use it exactly.
|
|
204
|
+
- **Risk: an unused-import or dead-export flag if the wiring lands in a separate commit from the handler.**
|
|
205
|
+
Mitigation: Step 2 adds the sole call site immediately; run `pnpm fallow dead-code` before pushing.
|
|
206
|
+
|
|
207
|
+
## Open Questions
|
|
208
|
+
|
|
209
|
+
- Whether to also flip the `architecture.md` Step 2 status line to ✅ in this issue or defer to a Phase 19 doc-sync — decide at ship time (the architecture entry currently lists the file as planned, which is harmless until then).
|
|
210
|
+
- Whether a settings re-show loop (apply one setting → re-show the list) is desirable for the standalone command — deferred; the lift preserves single-selection-then-return to match current behavior, and a loop is a separate UX decision.
|
|
211
|
+
|
|
212
|
+
[#441]: https://github.com/gotgenes/pi-packages/issues/441
|
|
213
|
+
[#442]: https://github.com/gotgenes/pi-packages/issues/442
|
|
214
|
+
[#444]: https://github.com/gotgenes/pi-packages/issues/444
|
|
215
|
+
[#445]: https://github.com/gotgenes/pi-packages/issues/445
|
|
216
|
+
[#447]: https://github.com/gotgenes/pi-packages/issues/447
|
|
217
|
+
[#422]: https://github.com/gotgenes/pi-packages/issues/422
|
|
218
|
+
[#423]: https://github.com/gotgenes/pi-packages/issues/423
|
|
219
|
+
[#424]: https://github.com/gotgenes/pi-packages/issues/424
|
|
220
|
+
[#425]: https://github.com/gotgenes/pi-packages/issues/425
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
---
|
|
2
|
+
issue: 447
|
|
3
|
+
issue_title: "pi-subagents: extract subagent settings to a focused /subagents-settings command"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Retro: #447 — pi-subagents: extract subagent settings to a focused /subagents-settings command
|
|
7
|
+
|
|
8
|
+
## Stage: Planning (2026-06-20T00:00:00Z)
|
|
9
|
+
|
|
10
|
+
### Session summary
|
|
11
|
+
|
|
12
|
+
Produced a numbered TDD plan for Phase 19 Step 2: a purely additive extraction of `AgentsMenuHandler.showSettings` into a standalone `SubagentsSettingsHandler` registered as the `/subagents-settings` command.
|
|
13
|
+
Confirmed the command name against the closed spike (#446) and its ADR-0004 addendum (Criterion 4), and verified `SettingsManager` already structurally satisfies the new narrow manager interface so it can be passed directly.
|
|
14
|
+
The plan ships independently (roadmap `Release: independent`).
|
|
15
|
+
|
|
16
|
+
### Observations
|
|
17
|
+
|
|
18
|
+
- The extraction is a faithful verbatim lift — `showSettings` already had zero coupling beyond `this.settings` and `ui`, so the design-review checklist came back clean (100% field usage on both new narrow interfaces, no LoD/output-arg smells).
|
|
19
|
+
Classified as a genuine collaborator extraction, not procedure-splitting.
|
|
20
|
+
- Declared two narrow interfaces owned by the new module: `SubagentsSettingsManager` (shape-identical to the doomed `AgentMenuSettings` but with no import from `agent-menu.ts`) and `SubagentsSettingsUI` (drops `confirm`/`editor`/`custom` from `MenuUI` — ISP).
|
|
21
|
+
- Strictly additive: `agent-menu.ts` is untouched, and its settings tests stay as-is because the in-menu path keeps shipping until Step 5 (#442) deletes the file.
|
|
22
|
+
Removing them now would drop coverage of a live surface.
|
|
23
|
+
- Preserved the single-selection-then-return semantics of `showSettings` verbatim (no re-show loop) — flagged a settings re-show loop as a deferred UX open question.
|
|
24
|
+
- Two small TDD steps (handler+tests, then `index.ts` registration); noted they may fold into one commit since the export and its sole call site are tiny, with `pnpm run check` required right after the wiring step.
|
|
25
|
+
- No third-party `ask_user` gate needed — issue filed by the operator (`gotgenes`), direction fixed by ADR-0004, design unambiguous.
|
|
26
|
+
|
|
27
|
+
## Stage: Implementation — TDD (2026-06-20T14:30:00Z)
|
|
28
|
+
|
|
29
|
+
### Session summary
|
|
30
|
+
|
|
31
|
+
Implemented Phase 19 Step 2 in two TDD cycles plus a doc-sync commit: added `SubagentsSettingsHandler` (with narrow `SubagentsSettingsManager` and `SubagentsSettingsUI` interfaces) lifted verbatim from `AgentsMenuHandler.showSettings`, then registered the `/subagents-settings` command in `index.ts`.
|
|
32
|
+
Test count went 1051 → 1062 (+11, one new file `test/ui/subagents-settings.test.ts`).
|
|
33
|
+
Full suite, `tsc`, root lint, and `fallow dead-code` all green.
|
|
34
|
+
|
|
35
|
+
### Observations
|
|
36
|
+
|
|
37
|
+
- The lift was clean: `SettingsManager` already exposed all six members of the new narrow manager interface, so the `index.ts` wiring passed `settings` directly with zero adapter — `pnpm run check` confirmed structural satisfaction.
|
|
38
|
+
- `makeMenuUI` from `ui-stubs.ts` was reused as the UI stub without modification — its wider shape (`confirm`/`editor`/`custom`) structurally satisfies the narrower `SubagentsSettingsUI`, so no new helper was needed.
|
|
39
|
+
- Kept the two cycles as separate commits rather than folding them; the export and its sole call site were small enough that either would have been valid.
|
|
40
|
+
- Deviation from plan: flipped `✅` on the architecture Step 2 heading and its Mermaid node now (the plan had flagged this as a ship-time open question).
|
|
41
|
+
Applied per the `/tdd-plan` template's roadmap-completion rule; verified the diagram still renders via `mmdc`.
|
|
42
|
+
The phase status row was left unchanged (only 1 of 7 steps done).
|
|
43
|
+
- `agent-menu.ts` was not touched (verified by the reviewer via `git log`), preserving the additive-only constraint.
|
|
44
|
+
- Pre-completion reviewer: WARN.
|
|
45
|
+
Reviewer warnings: one non-blocking finding — `.pi/skills/package-pi-subagents/SKILL.md` records `ui/` as 10 modules (now 11); the plan intentionally deferred this coarse-summary update to a later Phase 19 doc-sync.
|
|
46
|
+
No FAILs; all deterministic checks PASS, verbatim-lift fidelity and ISP of both narrow interfaces confirmed.
|
|
@@ -40,3 +40,36 @@ Test count delta: 1047 → 1051 (+4).
|
|
|
40
40
|
- The `makeAgentConfig` helper was added to `test/tools/spawn-config.test.ts` (mirroring the pattern in `test/config/agent-types.test.ts`) rather than importing from a shared fixture, since the existing spawn-config test fixture infrastructure didn't need modification.
|
|
41
41
|
- All three plan-enumerated cross-step invariants held green throughout: `resolveAgentConfig` disabled-config behavior, unknown-type fallback, and `getAllTypes` disabled-agent listing.
|
|
42
42
|
- Pre-completion reviewer: **PASS** — all deterministic checks, code design, test artifacts, and cross-step invariants clean.
|
|
43
|
+
|
|
44
|
+
## Stage: Land — worktree (2026-06-20T17:57:54Z)
|
|
45
|
+
|
|
46
|
+
### Session summary
|
|
47
|
+
|
|
48
|
+
Ran `/land-worktree 448` from the root checkout: fast-forward merged the peer branch `issue-448-enabled-false-does-not-prevent-explicitl` onto linear `main`, pushed, verified CI green, closed issue #448, merged the release-please PR, and tore down the worktree.
|
|
49
|
+
Released `pi-subagents-v17.0.1` (the plan marked `Release: ship independently`).
|
|
50
|
+
|
|
51
|
+
### Observations
|
|
52
|
+
|
|
53
|
+
#### What went well
|
|
54
|
+
|
|
55
|
+
- The new `/land-worktree` flow (added in `7cbfea46`) ran cleanly end-to-end on a real issue: ff-merge → push → CI → `issue_close` → release → teardown, with no blockers or rework.
|
|
56
|
+
- The release-please PR returned `MERGEABLE` / `UNSTABLE` with an empty `statusCheckRollup` — the documented `GITHUB_TOKEN` no-checks case.
|
|
57
|
+
Falling back from `release_pr_merge` to `gh pr merge 449 --rebase` followed by `git pull --ff-only` worked exactly as the prompt anticipates.
|
|
58
|
+
|
|
59
|
+
#### What caused friction (agent side)
|
|
60
|
+
|
|
61
|
+
- `instruction-violation` (self-identified, in retro) — the prompt's step 6.2 says to check the **full** release-PR body for which packages it bumps **before** merging.
|
|
62
|
+
I requested `body` in `gh pr view 449 --json body,...` but the `--jq` filter only printed `title`/`state`/`mergeStateStatus`/`statusCheckRollup`, so the body was never actually inspected.
|
|
63
|
+
I learned the bumped package (`pi-subagents`) only **after** merging, from the `git pull` output.
|
|
64
|
+
Impact: none this time (a single expected package bumped), but skipping the pre-merge body check means an unexpected sibling-package bump would slip through unnoticed.
|
|
65
|
+
|
|
66
|
+
### Diagnostic details
|
|
67
|
+
|
|
68
|
+
- **Model-performance correlation** — no subagents dispatched; the land flow ran entirely on the parent session model.
|
|
69
|
+
No mismatch.
|
|
70
|
+
- **Escalation-delay tracking** — no rabbit-holes; no error retried more than once.
|
|
71
|
+
- **Feedback-loop gap analysis** — no code changes in the land session, so CI (run on the pushed SHA, conclusion `success`) was the appropriate and only verification gate.
|
|
72
|
+
|
|
73
|
+
### Changes made
|
|
74
|
+
|
|
75
|
+
1. `.pi/prompts/land-worktree.md` — added a one-line nudge to step 6.2 to print the release-PR body explicitly with `gh pr view <N> --json body -q .body`, since a `--jq` that drops `body` skips the package-bump check silently.
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -47,6 +47,7 @@ import { SteerTool } from "#src/tools/steer-tool";
|
|
|
47
47
|
import { FsAgentFileOps } from "#src/ui/agent-file-ops";
|
|
48
48
|
import { AgentsMenuHandler } from "#src/ui/agent-menu";
|
|
49
49
|
import { AgentWidget } from "#src/ui/agent-widget";
|
|
50
|
+
import { SubagentsSettingsHandler } from "#src/ui/subagents-settings";
|
|
50
51
|
|
|
51
52
|
export default function (pi: ExtensionAPI) {
|
|
52
53
|
// ---- Register custom notification renderer ----
|
|
@@ -180,4 +181,15 @@ export default function (pi: ExtensionAPI) {
|
|
|
180
181
|
});
|
|
181
182
|
},
|
|
182
183
|
});
|
|
184
|
+
|
|
185
|
+
// ---- /subagents-settings command ----
|
|
186
|
+
|
|
187
|
+
const subagentsSettings = new SubagentsSettingsHandler(settings);
|
|
188
|
+
|
|
189
|
+
pi.registerCommand("subagents-settings", {
|
|
190
|
+
description: "Configure subagent settings (concurrency, turn limits)",
|
|
191
|
+
handler: async (_args, ctx) => {
|
|
192
|
+
await subagentsSettings.handle({ ui: ctx.ui });
|
|
193
|
+
},
|
|
194
|
+
});
|
|
183
195
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// ---- Narrow interfaces ----
|
|
2
|
+
|
|
3
|
+
/** Narrow settings interface required by the subagents-settings command. */
|
|
4
|
+
export interface SubagentsSettingsManager {
|
|
5
|
+
readonly maxConcurrent: number;
|
|
6
|
+
readonly defaultMaxTurns: number | undefined;
|
|
7
|
+
readonly graceTurns: number;
|
|
8
|
+
applyMaxConcurrent(n: number): { message: string; level: "info" | "warning" };
|
|
9
|
+
applyDefaultMaxTurns(n: number): { message: string; level: "info" | "warning" };
|
|
10
|
+
applyGraceTurns(n: number): { message: string; level: "info" | "warning" };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Narrow UI interface — only the ctx.ui methods the settings handler calls. */
|
|
14
|
+
export interface SubagentsSettingsUI {
|
|
15
|
+
select(title: string, options: string[]): Promise<string | undefined>;
|
|
16
|
+
input(title: string, defaultValue?: string): Promise<string | undefined>;
|
|
17
|
+
notify(message: string, level: "info" | "warning" | "error"): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ---- Class ----
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Handler for the `/subagents-settings` slash command.
|
|
24
|
+
*
|
|
25
|
+
* Call `handle({ ui })` from the Pi command registration to open the interactive
|
|
26
|
+
* settings list. Lifted from `AgentsMenuHandler.showSettings`.
|
|
27
|
+
*/
|
|
28
|
+
export class SubagentsSettingsHandler {
|
|
29
|
+
constructor(private readonly settings: SubagentsSettingsManager) {}
|
|
30
|
+
|
|
31
|
+
async handle({ ui }: { ui: SubagentsSettingsUI }): Promise<void> {
|
|
32
|
+
const choice = await ui.select("Settings", [
|
|
33
|
+
`Max concurrency (current: ${this.settings.maxConcurrent})`,
|
|
34
|
+
`Default max turns (current: ${this.settings.defaultMaxTurns ?? "unlimited"})`,
|
|
35
|
+
`Grace turns (current: ${this.settings.graceTurns})`,
|
|
36
|
+
]);
|
|
37
|
+
if (!choice) return;
|
|
38
|
+
|
|
39
|
+
if (choice.startsWith("Max concurrency")) {
|
|
40
|
+
const val = await ui.input(
|
|
41
|
+
"Max concurrent background agents",
|
|
42
|
+
String(this.settings.maxConcurrent),
|
|
43
|
+
);
|
|
44
|
+
if (val) {
|
|
45
|
+
const n = parseInt(val, 10);
|
|
46
|
+
if (n >= 1) {
|
|
47
|
+
const toast = this.settings.applyMaxConcurrent(n);
|
|
48
|
+
ui.notify(toast.message, toast.level);
|
|
49
|
+
} else {
|
|
50
|
+
ui.notify("Must be a positive integer.", "warning");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
} else if (choice.startsWith("Default max turns")) {
|
|
54
|
+
const val = await ui.input(
|
|
55
|
+
"Default max turns before wrap-up (0 = unlimited)",
|
|
56
|
+
String(this.settings.defaultMaxTurns ?? 0),
|
|
57
|
+
);
|
|
58
|
+
if (val) {
|
|
59
|
+
const n = parseInt(val, 10);
|
|
60
|
+
if (n >= 0) {
|
|
61
|
+
const toast = this.settings.applyDefaultMaxTurns(n);
|
|
62
|
+
ui.notify(toast.message, toast.level);
|
|
63
|
+
} else {
|
|
64
|
+
ui.notify("Must be 0 (unlimited) or a positive integer.", "warning");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
} else if (choice.startsWith("Grace turns")) {
|
|
68
|
+
const val = await ui.input(
|
|
69
|
+
"Grace turns after wrap-up steer",
|
|
70
|
+
String(this.settings.graceTurns),
|
|
71
|
+
);
|
|
72
|
+
if (val) {
|
|
73
|
+
const n = parseInt(val, 10);
|
|
74
|
+
if (n >= 1) {
|
|
75
|
+
const toast = this.settings.applyGraceTurns(n);
|
|
76
|
+
ui.notify(toast.message, toast.level);
|
|
77
|
+
} else {
|
|
78
|
+
ui.notify("Must be a positive integer.", "warning");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|