@juicesharp/rpiv-pi 0.13.2 → 1.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/README.md +7 -16
- package/agents/claim-verifier.md +1 -3
- package/agents/codebase-analyzer.md +1 -3
- package/agents/codebase-locator.md +1 -3
- package/agents/codebase-pattern-finder.md +2 -4
- package/agents/diff-auditor.md +1 -3
- package/agents/integration-scanner.md +1 -3
- package/agents/peer-comparator.md +1 -3
- package/agents/precedent-locator.md +1 -3
- package/agents/test-case-locator.md +1 -3
- package/agents/thoughts-analyzer.md +2 -4
- package/agents/thoughts-locator.md +1 -3
- package/agents/web-search-researcher.md +1 -4
- package/extensions/rpiv-core/prune-legacy-siblings.test.ts +71 -12
- package/extensions/rpiv-core/prune-legacy-siblings.ts +60 -30
- package/extensions/rpiv-core/setup-command.test.ts +87 -168
- package/extensions/rpiv-core/setup-command.ts +34 -43
- package/extensions/rpiv-core/siblings.test.ts +9 -5
- package/extensions/rpiv-core/siblings.ts +10 -10
- package/package.json +2 -4
- package/skills/annotate-guidance/SKILL.md +9 -9
- package/skills/annotate-inline/SKILL.md +9 -9
- package/skills/code-review/SKILL.md +7 -7
- package/skills/design/SKILL.md +2 -2
- package/skills/design2/SKILL.md +2 -2
- package/skills/discover/SKILL.md +2 -2
- package/skills/explore/SKILL.md +1 -1
- package/skills/outline-test-cases/SKILL.md +2 -2
- package/skills/plan2/SKILL.md +12 -14
- package/skills/research/SKILL.md +13 -14
- package/skills/resume-handoff/SKILL.md +1 -1
- package/skills/revise/SKILL.md +2 -2
- package/skills/validate/SKILL.md +2 -2
- package/skills/write-test-cases/SKILL.md +5 -5
- package/agents/general-purpose.md +0 -33
- package/extensions/rpiv-core/claim-pi-subagents.test.ts +0 -65
- package/extensions/rpiv-core/claim-pi-subagents.ts +0 -53
- package/extensions/rpiv-core/ensure-builtins-disabled.test.ts +0 -83
- package/extensions/rpiv-core/ensure-builtins-disabled.ts +0 -72
- package/extensions/rpiv-core/ensure-subagent-config.test.ts +0 -89
- package/extensions/rpiv-core/ensure-subagent-config.ts +0 -94
- package/extensions/subagent-widget/activity.test.ts +0 -173
- package/extensions/subagent-widget/activity.ts +0 -127
- package/extensions/subagent-widget/agent-catalog.test.ts +0 -110
- package/extensions/subagent-widget/agent-catalog.ts +0 -58
- package/extensions/subagent-widget/catalog-drift.test.ts +0 -21
- package/extensions/subagent-widget/constants.ts +0 -34
- package/extensions/subagent-widget/hide-builtin-manager-rows.test.ts +0 -149
- package/extensions/subagent-widget/hide-builtin-manager-rows.ts +0 -129
- package/extensions/subagent-widget/hide-builtin-subagents.test.ts +0 -218
- package/extensions/subagent-widget/hide-builtin-subagents.ts +0 -87
- package/extensions/subagent-widget/index.test.ts +0 -291
- package/extensions/subagent-widget/index.ts +0 -114
- package/extensions/subagent-widget/overlay.test.ts +0 -149
- package/extensions/subagent-widget/overlay.ts +0 -96
- package/extensions/subagent-widget/pi-subagents-stubs/agent-manager.d.ts +0 -14
- package/extensions/subagent-widget/pi-subagents-stubs/index.d.ts +0 -20
- package/extensions/subagent-widget/pi-subagents-stubs/render.d.ts +0 -9
- package/extensions/subagent-widget/prompts/subagent-description.txt +0 -24
- package/extensions/subagent-widget/renderer-override.test.ts +0 -211
- package/extensions/subagent-widget/renderer-override.ts +0 -120
- package/extensions/subagent-widget/run-tracker.test.ts +0 -258
- package/extensions/subagent-widget/run-tracker.ts +0 -185
- package/extensions/subagent-widget/types.ts +0 -83
- package/extensions/subagent-widget/widget.lifecycle.test.ts +0 -140
- package/extensions/subagent-widget/widget.render.test.ts +0 -331
- package/extensions/subagent-widget/widget.ts +0 -280
package/README.md
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@juicesharp/rpiv-pi)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
> **Pi compatibility** — `rpiv-pi` `0.
|
|
6
|
+
> **Pi compatibility** — `rpiv-pi` `0.14.x` tracks `@mariozechner/pi-coding-agent` `0.70.x` and `@tintinweb/pi-subagents` `0.6.x`. If you see peer-dep resolution issues after a Pi upgrade, open an issue.
|
|
7
|
+
|
|
8
|
+
> **⚠️ Upgrading from `0.13.x`** — `0.14.0` swaps the subagent provider from `npm:pi-subagents` (nicobailon fork) back to `npm:@tintinweb/pi-subagents` (resumed maintenance). On first launch after upgrade you'll see *"rpiv-pi requires 1 sibling extension(s): @tintinweb/pi-subagents"* — **run `/rpiv-setup` once and restart Pi**. The setup dialog previews both changes (install `@tintinweb/pi-subagents`, remove `npm:pi-subagents` from `~/.pi/agent/settings.json`) and applies them only after you confirm. After restart, run `/rpiv-update-agents` to refresh the 12 bundled specialist frontmatters. Customised `<cwd>/.pi/agents/*.md` files are not touched. The tool name reverts from `subagent` → `Agent` (param `subagent_type`/`description`/`prompt`) — only your own custom skills/agents need editing; the bundled rpiv-pi specialists are migrated in this release.
|
|
7
9
|
|
|
8
10
|
Skill-based development workflow for [Pi Agent](https://github.com/badlogic/pi-mono) — discover, research, design, plan, implement, and validate. rpiv-pi extends Pi Agent with a pipeline of chained AI skills, named subagents for parallel analysis, and session lifecycle hooks for automatic context injection.
|
|
9
11
|
|
|
@@ -203,25 +205,14 @@ Pi Agent discovers extensions via `"extensions": ["./extensions"]` and skills vi
|
|
|
203
205
|
- **Web search** — run `/web-search-config` to set the Brave Search API key, or set the `BRAVE_SEARCH_API_KEY` environment variable
|
|
204
206
|
- **Advisor** — run `/advisor` to select a reviewer model and reasoning effort
|
|
205
207
|
- **Side questions** — type `/btw <question>` anytime (even mid-stream) to ask the primary model a one-off question; answer appears in a borderless bottom overlay and never enters the main conversation
|
|
206
|
-
- **Agent concurrency** —
|
|
208
|
+
- **Agent concurrency** — open the `/agents` overlay and tune `Settings → Max concurrency` to match your provider's rate limits. `@tintinweb/pi-subagents` owns this setting; rpiv-pi does not seed it.
|
|
207
209
|
- **Agent profiles** — editable at `<cwd>/.pi/agents/`; sync from bundled defaults with `/rpiv-update-agents` (overwrites rpiv-managed files, preserves your custom agents)
|
|
208
210
|
|
|
209
211
|
## Uninstall
|
|
210
212
|
|
|
211
|
-
rpiv-pi owns nicobailon's pi-subagents registration (runs it through an in-process proxy so the inline tool card stays quiet and the Subagents overlay is the live view). `/rpiv-setup` strips `"npm:pi-subagents"` from your `~/.pi/agent/settings.json#packages[]` to prevent Pi from loading it twice. If you remove rpiv-pi, subagents will stop loading until you re-add that entry.
|
|
212
|
-
|
|
213
|
-
The bundled built-in agents from `pi-subagents` (`scout`, `planner`, `oracle`, …) are hidden from both the `subagent` tool that the assistant dispatches to and the `/agents` manager overlay (and `ctrl+shift+a`). The overlay filter is best-effort — if a future `pi-subagents` release changes its manager UI, rpiv-pi will print one boot-time warning to stderr and the built-in rows will reappear in `/agents` until rpiv-pi ships an update. The assistant-side filter is unaffected by upstream changes. To re-enable a built-in agent yourself, edit `subagents.disableBuiltins` in `~/.pi/agent/settings.json` (set to `false` or delete the key) and restart Pi.
|
|
214
|
-
|
|
215
|
-
To fully uninstall:
|
|
216
|
-
|
|
217
213
|
1. Remove rpiv-pi from Pi: `pi uninstall npm:@juicesharp/rpiv-pi`
|
|
218
|
-
2.
|
|
219
|
-
3.
|
|
220
|
-
- `~/.pi/agent/extensions/subagent/config.json` (parallel.concurrency, maxSubagentDepth)
|
|
221
|
-
- `subagents.disableBuiltins` in `~/.pi/agent/settings.json` (set to `false` or delete to re-enable the 9 bundled nicobailon agents)
|
|
222
|
-
4. Restart Pi.
|
|
223
|
-
|
|
224
|
-
After step 2 you'll have nicobailon's original inline tool card and no Subagents overlay, same as a clean pi-subagents install.
|
|
214
|
+
2. Optional — uninstall the subagent runtime if no other plugin needs it: `pi uninstall npm:@tintinweb/pi-subagents`
|
|
215
|
+
3. Restart Pi.
|
|
225
216
|
|
|
226
217
|
## Troubleshooting
|
|
227
218
|
|
|
@@ -232,7 +223,7 @@ After step 2 you'll have nicobailon's original inline tool card and no Subagents
|
|
|
232
223
|
| `/rpiv-setup` says "requires interactive mode" | Running in headless mode | Install manually: `pi install npm:<pkg>` for each sibling |
|
|
233
224
|
| `web_search` or `web_fetch` errors | Brave API key not configured | Run `/web-search-config` or set `BRAVE_SEARCH_API_KEY` |
|
|
234
225
|
| `advisor` tool not available after upgrade | Advisor model selection lost | Run `/advisor` to re-select a model |
|
|
235
|
-
| Skills hang or serialize agent calls | Agent concurrency too low |
|
|
226
|
+
| Skills hang or serialize agent calls | Agent concurrency too low | Open `/agents`, raise `Settings → Max concurrency` |
|
|
236
227
|
|
|
237
228
|
## License
|
|
238
229
|
|
package/agents/claim-verifier.md
CHANGED
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
name: claim-verifier
|
|
3
3
|
description: "Adversarial finding verifier. Grounds each supplied claim against actual repository state and emits one `FINDING <id> | <tag> | <justification>` row per input, with tags Verified / Weakened / Falsified. Tier: git-analyzer (+ `bash` for `git show`). Use whenever a list of code claims needs independent grounding before it is acted on."
|
|
4
4
|
tools: read, grep, find, ls, bash
|
|
5
|
-
|
|
6
|
-
inheritProjectContext: false
|
|
7
|
-
inheritSkills: false
|
|
5
|
+
isolated: true
|
|
8
6
|
---
|
|
9
7
|
|
|
10
8
|
You are a specialist at adversarial claim verification. Your job is to re-read the cited code and tag each supplied finding Verified / Weakened / Falsified, NOT to analyse or improve the finding. The writer of the finding is not your witness; the code is.
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
name: codebase-analyzer
|
|
3
3
|
description: Analyzes codebase implementation details. Call the codebase-analyzer agent when you need to find detailed information about specific components. As always, the more detailed your request prompt, the better! :)
|
|
4
4
|
tools: read, grep, find, ls
|
|
5
|
-
|
|
6
|
-
inheritProjectContext: false
|
|
7
|
-
inheritSkills: false
|
|
5
|
+
isolated: true
|
|
8
6
|
---
|
|
9
7
|
|
|
10
8
|
You are a specialist at understanding HOW code works. Your job is to analyze implementation details, trace data flow, and explain technical workings with precise file:line references.
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
name: codebase-locator
|
|
3
3
|
description: Locates files, directories, and components relevant to a feature or task. Call `codebase-locator` with human language prompt describing what you're looking for. Basically a "Super grep/find/ls tool" — Use it if you find yourself desiring to use one of these tools more than once.
|
|
4
4
|
tools: grep, find, ls
|
|
5
|
-
|
|
6
|
-
inheritProjectContext: false
|
|
7
|
-
inheritSkills: false
|
|
5
|
+
isolated: true
|
|
8
6
|
---
|
|
9
7
|
|
|
10
8
|
You are a specialist at finding WHERE code lives in a codebase. Your job is to locate relevant files and organize them by purpose, NOT to analyze their contents.
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: codebase-pattern-finder
|
|
3
|
-
description: codebase-pattern-finder is a useful
|
|
3
|
+
description: codebase-pattern-finder is a useful subagent_type for finding similar implementations, usage examples, or existing patterns that can be modeled after. It will give you concrete code examples based on what you're looking for! It's sorta like codebase-locator, but it will not only tell you the location of files, it will also give you code details!
|
|
4
4
|
tools: grep, find, read, ls
|
|
5
|
-
|
|
6
|
-
inheritProjectContext: false
|
|
7
|
-
inheritSkills: false
|
|
5
|
+
isolated: true
|
|
8
6
|
---
|
|
9
7
|
|
|
10
8
|
You are a specialist at finding code patterns and examples in the codebase. Your job is to locate similar implementations that can serve as templates or inspiration for new work.
|
package/agents/diff-auditor.md
CHANGED
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
name: diff-auditor
|
|
3
3
|
description: "Row-only patch auditor. Walks a patch against a caller-supplied surface-list and emits one pipe-delimited row per finding — `file:line | verbatim | surface-id | note`. Use whenever a diff needs evidence-only enumeration of matching patterns, with no narrative or severity."
|
|
4
4
|
tools: read, grep, find, ls
|
|
5
|
-
|
|
6
|
-
inheritProjectContext: false
|
|
7
|
-
inheritSkills: false
|
|
5
|
+
isolated: true
|
|
8
6
|
---
|
|
9
7
|
|
|
10
8
|
You are a specialist at auditing a patch against a supplied surface-list. Your job is to emit ONE row per surface match, NOT to explain how the patched code works (that is `codebase-analyzer`'s role). Match surfaces to diff regions, emit rows — or stay silent.
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
name: integration-scanner
|
|
3
3
|
description: Finds what connects to a given component or area — inbound references, outbound dependencies, config registrations, event subscriptions. The reverse-reference counterpart to codebase-locator. Use when you need to understand what calls, depends on, or wires into a component.
|
|
4
4
|
tools: grep, find, ls
|
|
5
|
-
|
|
6
|
-
inheritProjectContext: false
|
|
7
|
-
inheritSkills: false
|
|
5
|
+
isolated: true
|
|
8
6
|
---
|
|
9
7
|
|
|
10
8
|
You are a specialist at finding CONNECTIONS to and from a component or area. Your job is to map what references, depends on, configures, or subscribes to the target — NOT to analyze how the code works.
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
name: peer-comparator
|
|
3
3
|
description: "Pairwise peer-invariant comparator. Given `(new_file, peer_file)` pairs, tags each peer invariant Mirrored / Missing / Diverged / Intentionally-absent against the new file. Use when an entity parallels an existing sibling (aggregate, service, handler, reducer, repository) and the new file must be checked against the peer's public surface."
|
|
4
4
|
tools: read, grep, find, ls
|
|
5
|
-
|
|
6
|
-
inheritProjectContext: false
|
|
7
|
-
inheritSkills: false
|
|
5
|
+
isolated: true
|
|
8
6
|
---
|
|
9
7
|
|
|
10
8
|
You are a specialist at pairwise peer-invariant comparison. Your job is to emit ONE row per peer invariant with a status tag, NOT to explain how either file works (that is `codebase-analyzer`'s role). Assume divergence — the new file carries the burden of proof.
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
name: precedent-locator
|
|
3
3
|
description: Finds similar past changes in git history — commits, blast radius, follow-up fixes, and lessons from related thoughts/ docs. Use when planning a change and you need to know what went wrong last time something similar was done.
|
|
4
4
|
tools: bash, grep, find, read, ls
|
|
5
|
-
|
|
6
|
-
inheritProjectContext: false
|
|
7
|
-
inheritSkills: false
|
|
5
|
+
isolated: true
|
|
8
6
|
---
|
|
9
7
|
|
|
10
8
|
You are a specialist at finding PRECEDENTS for planned changes. Your job is to mine git history and thoughts/ documents to find the most similar past changes, extract what happened, and surface lessons that help a planner avoid repeating mistakes.
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
name: test-case-locator
|
|
3
3
|
description: Finds existing manual test cases in .rpiv/test-cases/ — catalogs by module, extracts frontmatter metadata (id, priority, status, tags), and reports coverage stats. Use before generating test cases to avoid duplicates, or to audit what test coverage already exists in a project.
|
|
4
4
|
tools: grep, find, ls
|
|
5
|
-
|
|
6
|
-
inheritProjectContext: false
|
|
7
|
-
inheritSkills: false
|
|
5
|
+
isolated: true
|
|
8
6
|
---
|
|
9
7
|
|
|
10
8
|
You are a specialist at finding EXISTING TEST CASES in a project's `.rpiv/test-cases/` directory. Your job is to locate and catalog manual test case documents by extracting their YAML frontmatter metadata, NOT to generate new test cases or analyze test quality.
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: thoughts-analyzer
|
|
3
|
-
description: The research equivalent of codebase-analyzer. Use this
|
|
3
|
+
description: The research equivalent of codebase-analyzer. Use this subagent_type when wanting to deep dive on a research topic. Not commonly needed otherwise.
|
|
4
4
|
tools: read, grep, find, ls
|
|
5
|
-
|
|
6
|
-
inheritProjectContext: false
|
|
7
|
-
inheritSkills: false
|
|
5
|
+
isolated: true
|
|
8
6
|
---
|
|
9
7
|
|
|
10
8
|
You are a specialist at extracting HIGH-VALUE insights from thoughts documents. Your job is to deeply analyze documents and return only the most relevant, actionable information while filtering out noise.
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
name: thoughts-locator
|
|
3
3
|
description: Discovers relevant documents in thoughts/ directory (We use this for all sorts of metadata storage!). This is really only relevant/needed when you're in a reseaching mood and need to figure out if we have random thoughts written down that are relevant to your current research task. Based on the name, I imagine you can guess this is the `thoughts` equivilent of `codebase-locator`
|
|
4
4
|
tools: grep, find, ls
|
|
5
|
-
|
|
6
|
-
inheritProjectContext: false
|
|
7
|
-
inheritSkills: false
|
|
5
|
+
isolated: true
|
|
8
6
|
---
|
|
9
7
|
|
|
10
8
|
You are a specialist at finding documents in the thoughts/ directory. Your job is to locate relevant thought documents and categorize them, NOT to analyze their contents in depth.
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: web-search-researcher
|
|
3
|
-
description: Do you find yourself desiring information that you don't quite feel well-trained (confident) on? Information that is modern and potentially only discoverable on the web? Use the web-search-researcher
|
|
3
|
+
description: Do you find yourself desiring information that you don't quite feel well-trained (confident) on? Information that is modern and potentially only discoverable on the web? Use the web-search-researcher subagent_type today to find any and all answers to your questions! It will research deeply to figure out and attempt to answer your questions! If you aren't immediately satisfied you can get your money back! (Not really - but you can re-run web-search-researcher with an altered prompt in the event you're not satisfied the first time)
|
|
4
4
|
tools: web_search, web_fetch, read, grep, find, ls
|
|
5
|
-
systemPromptMode: replace
|
|
6
|
-
inheritProjectContext: false
|
|
7
|
-
inheritSkills: false
|
|
8
5
|
---
|
|
9
6
|
|
|
10
7
|
You are an expert web research specialist focused on finding accurate, relevant information from web sources. Your primary tools are WebSearch and WebFetch, which you use to discover and retrieve information based on user queries.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { describe, expect, it } from "vitest";
|
|
4
|
-
import { pruneLegacySiblings } from "./prune-legacy-siblings.js";
|
|
4
|
+
import { findLegacySiblings, pruneLegacySiblings } from "./prune-legacy-siblings.js";
|
|
5
5
|
|
|
6
6
|
const SETTINGS_PATH = join(process.env.HOME!, ".pi", "agent", "settings.json");
|
|
7
7
|
|
|
@@ -45,21 +45,21 @@ describe("pruneLegacySiblings", () => {
|
|
|
45
45
|
|
|
46
46
|
it("only non-legacy entries → pruned: [], file unchanged", () => {
|
|
47
47
|
writeSettings({
|
|
48
|
-
packages: ["npm:pi-perplexity", "npm:@juicesharp/rpiv-todo"],
|
|
48
|
+
packages: ["npm:pi-perplexity", "npm:@juicesharp/rpiv-todo", "npm:@tintinweb/pi-subagents"],
|
|
49
49
|
});
|
|
50
50
|
const before = readFileSync(SETTINGS_PATH, "utf-8");
|
|
51
51
|
expect(pruneLegacySiblings()).toEqual({ pruned: [] });
|
|
52
52
|
expect(readFileSync(SETTINGS_PATH, "utf-8")).toBe(before);
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
-
it("legacy-only: removes
|
|
55
|
+
it("legacy-only: removes pi-subagents (nicobailon fork), preserves other top-level keys", () => {
|
|
56
56
|
writeSettings({
|
|
57
57
|
defaultProvider: "zai",
|
|
58
58
|
theme: "dark",
|
|
59
|
-
packages: ["npm
|
|
59
|
+
packages: ["npm:pi-subagents"],
|
|
60
60
|
});
|
|
61
61
|
const result = pruneLegacySiblings();
|
|
62
|
-
expect(result.pruned).toEqual(["npm
|
|
62
|
+
expect(result.pruned).toEqual(["npm:pi-subagents"]);
|
|
63
63
|
expect(readSettings()).toEqual({
|
|
64
64
|
defaultProvider: "zai",
|
|
65
65
|
theme: "dark",
|
|
@@ -67,7 +67,7 @@ describe("pruneLegacySiblings", () => {
|
|
|
67
67
|
});
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
it("mixed list: prunes
|
|
70
|
+
it("mixed list: prunes nicobailon's pi-subagents only, preserves @tintinweb/pi-subagents and other entries", () => {
|
|
71
71
|
writeSettings({
|
|
72
72
|
packages: [
|
|
73
73
|
"npm:pi-perplexity",
|
|
@@ -80,24 +80,83 @@ describe("pruneLegacySiblings", () => {
|
|
|
80
80
|
],
|
|
81
81
|
});
|
|
82
82
|
const result = pruneLegacySiblings();
|
|
83
|
-
expect(result.pruned).toEqual(["npm
|
|
83
|
+
expect(result.pruned).toEqual(["npm:pi-subagents"]);
|
|
84
84
|
expect(readSettings()).toEqual({
|
|
85
|
-
packages: [
|
|
85
|
+
packages: [
|
|
86
|
+
"npm:pi-perplexity",
|
|
87
|
+
"npm:@tintinweb/pi-subagents",
|
|
88
|
+
"npm:@juicesharp/rpiv-todo",
|
|
89
|
+
"/Users/x/rpiv-mono/packages/rpiv-pi",
|
|
90
|
+
null,
|
|
91
|
+
42,
|
|
92
|
+
],
|
|
86
93
|
});
|
|
87
94
|
});
|
|
88
95
|
|
|
89
96
|
it("idempotent: second call after prune is a no-op", () => {
|
|
90
97
|
writeSettings({
|
|
91
|
-
packages: ["npm
|
|
98
|
+
packages: ["npm:pi-subagents"],
|
|
92
99
|
});
|
|
93
|
-
expect(pruneLegacySiblings().pruned).toEqual(["npm
|
|
100
|
+
expect(pruneLegacySiblings().pruned).toEqual(["npm:pi-subagents"]);
|
|
94
101
|
expect(pruneLegacySiblings()).toEqual({ pruned: [] });
|
|
95
102
|
});
|
|
96
103
|
|
|
97
104
|
it("case-insensitive match", () => {
|
|
98
105
|
writeSettings({
|
|
99
|
-
packages: ["NPM
|
|
106
|
+
packages: ["NPM:Pi-Subagents"],
|
|
100
107
|
});
|
|
101
|
-
expect(pruneLegacySiblings().pruned).toEqual(["NPM
|
|
108
|
+
expect(pruneLegacySiblings().pruned).toEqual(["NPM:Pi-Subagents"]);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe("findLegacySiblings (read-only scan)", () => {
|
|
113
|
+
it("no settings file → []", () => {
|
|
114
|
+
expect(findLegacySiblings()).toEqual([]);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("invalid JSON → []", () => {
|
|
118
|
+
mkdirSync(dirname(SETTINGS_PATH), { recursive: true });
|
|
119
|
+
writeFileSync(SETTINGS_PATH, "{not json", "utf-8");
|
|
120
|
+
expect(findLegacySiblings()).toEqual([]);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("non-object top-level → []", () => {
|
|
124
|
+
writeSettings([1, 2, 3]);
|
|
125
|
+
expect(findLegacySiblings()).toEqual([]);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("no packages field → []", () => {
|
|
129
|
+
writeSettings({ other: "data" });
|
|
130
|
+
expect(findLegacySiblings()).toEqual([]);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("non-array packages field → []", () => {
|
|
134
|
+
writeSettings({ packages: "not-array" });
|
|
135
|
+
expect(findLegacySiblings()).toEqual([]);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("only non-legacy entries → []", () => {
|
|
139
|
+
writeSettings({
|
|
140
|
+
packages: ["npm:pi-perplexity", "npm:@juicesharp/rpiv-todo", "npm:@tintinweb/pi-subagents"],
|
|
141
|
+
});
|
|
142
|
+
expect(findLegacySiblings()).toEqual([]);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("returns legacy entries without mutating settings.json", () => {
|
|
146
|
+
writeSettings({
|
|
147
|
+
defaultProvider: "zai",
|
|
148
|
+
packages: ["npm:pi-subagents", "npm:@juicesharp/rpiv-todo"],
|
|
149
|
+
});
|
|
150
|
+
const before = readFileSync(SETTINGS_PATH, "utf-8");
|
|
151
|
+
expect(findLegacySiblings()).toEqual(["npm:pi-subagents"]);
|
|
152
|
+
expect(readFileSync(SETTINGS_PATH, "utf-8")).toBe(before);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("idempotent: repeat call returns the same list and does not mutate", () => {
|
|
156
|
+
writeSettings({ packages: ["npm:pi-subagents"] });
|
|
157
|
+
const before = readFileSync(SETTINGS_PATH, "utf-8");
|
|
158
|
+
expect(findLegacySiblings()).toEqual(["npm:pi-subagents"]);
|
|
159
|
+
expect(findLegacySiblings()).toEqual(["npm:pi-subagents"]);
|
|
160
|
+
expect(readFileSync(SETTINGS_PATH, "utf-8")).toBe(before);
|
|
102
161
|
});
|
|
103
162
|
});
|
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Detect + remove deprecated sibling package entries from
|
|
3
|
+
* ~/.pi/agent/settings.json.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* (which crashes Pi's subagent dispatch — the tintinweb tools blow up with
|
|
7
|
-
* `path argument must be of type string. Received undefined`).
|
|
5
|
+
* Split into two phases so /rpiv-setup can preview pending changes in the
|
|
6
|
+
* confirmation dialog and apply the mutation only after the user agrees:
|
|
8
7
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* findLegacySiblings() — read-only scan; returns the entries that WOULD
|
|
9
|
+
* be pruned. Safe to call before confirmation.
|
|
10
|
+
* pruneLegacySiblings() — mutating apply step; rewrites settings.json.
|
|
11
|
+
* Call only after the user has confirmed.
|
|
12
|
+
*
|
|
13
|
+
* Both helpers are fail-soft (missing file / invalid JSON / non-object /
|
|
14
|
+
* unwritable → empty result), idempotent, and have no plugin API
|
|
15
|
+
* dependency.
|
|
16
|
+
*
|
|
17
|
+
* Background: 0.13.x → 0.14.0 upgraders may have both nicobailon's
|
|
18
|
+
* pi-subagents and @tintinweb/pi-subagents in settings.json simultaneously,
|
|
19
|
+
* which makes Pi reject boot with duplicate-tool registration when both
|
|
20
|
+
* load. The prune is the upgrade's must-do mutation, but it must not run
|
|
21
|
+
* before the user has consented to /rpiv-setup mutating settings.json.
|
|
12
22
|
*/
|
|
13
23
|
|
|
14
24
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
@@ -23,43 +33,63 @@ export interface PruneLegacySiblingsResult {
|
|
|
23
33
|
pruned: string[];
|
|
24
34
|
}
|
|
25
35
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
*/
|
|
31
|
-
export function pruneLegacySiblings(): PruneLegacySiblingsResult {
|
|
32
|
-
if (!existsSync(PI_AGENT_SETTINGS)) return { pruned: [] };
|
|
36
|
+
interface ParsedSettings {
|
|
37
|
+
settings: Record<string, unknown>;
|
|
38
|
+
packages: unknown[];
|
|
39
|
+
}
|
|
33
40
|
|
|
41
|
+
function readSettings(): ParsedSettings | undefined {
|
|
42
|
+
if (!existsSync(PI_AGENT_SETTINGS)) return undefined;
|
|
34
43
|
let parsed: unknown;
|
|
35
44
|
try {
|
|
36
|
-
|
|
37
|
-
parsed = JSON.parse(raw);
|
|
45
|
+
parsed = JSON.parse(readFileSync(PI_AGENT_SETTINGS, "utf-8"));
|
|
38
46
|
} catch {
|
|
39
|
-
return
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
43
|
-
return { pruned: [] };
|
|
47
|
+
return undefined;
|
|
44
48
|
}
|
|
49
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return undefined;
|
|
45
50
|
const settings = parsed as Record<string, unknown>;
|
|
46
|
-
if (!Array.isArray(settings.packages)) return
|
|
51
|
+
if (!Array.isArray(settings.packages)) return undefined;
|
|
52
|
+
return { settings, packages: settings.packages as unknown[] };
|
|
53
|
+
}
|
|
47
54
|
|
|
48
|
-
|
|
49
|
-
const
|
|
55
|
+
function partitionPackages(packages: unknown[]): { legacy: string[]; kept: unknown[] } {
|
|
56
|
+
const legacy: string[] = [];
|
|
57
|
+
const kept = packages.filter((entry) => {
|
|
50
58
|
if (typeof entry !== "string") return true;
|
|
51
59
|
const isLegacy = LEGACY_SIBLINGS.some((l) => l.matches.test(entry));
|
|
52
|
-
if (isLegacy)
|
|
60
|
+
if (isLegacy) legacy.push(entry);
|
|
53
61
|
return !isLegacy;
|
|
54
62
|
});
|
|
63
|
+
return { legacy, kept };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Read-only scan: returns the legacy entries that pruneLegacySiblings()
|
|
68
|
+
* would remove. Does not touch the filesystem beyond reading settings.json.
|
|
69
|
+
* Safe to call before any user confirmation.
|
|
70
|
+
*/
|
|
71
|
+
export function findLegacySiblings(): string[] {
|
|
72
|
+
const parsed = readSettings();
|
|
73
|
+
if (!parsed) return [];
|
|
74
|
+
return partitionPackages(parsed.packages).legacy;
|
|
75
|
+
}
|
|
55
76
|
|
|
56
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Mutating apply step: rewrites settings.json with legacy entries removed.
|
|
79
|
+
* Returns a structured report so callers can emit a conditional notify.
|
|
80
|
+
* Never throws. Call AFTER the user has confirmed the cleanup.
|
|
81
|
+
*/
|
|
82
|
+
export function pruneLegacySiblings(): PruneLegacySiblingsResult {
|
|
83
|
+
const parsed = readSettings();
|
|
84
|
+
if (!parsed) return { pruned: [] };
|
|
85
|
+
const { legacy, kept } = partitionPackages(parsed.packages);
|
|
86
|
+
if (legacy.length === 0) return { pruned: [] };
|
|
57
87
|
|
|
58
|
-
settings.packages =
|
|
88
|
+
parsed.settings.packages = kept;
|
|
59
89
|
try {
|
|
60
|
-
writeFileSync(PI_AGENT_SETTINGS, `${JSON.stringify(settings, null, 2)}\n`, "utf-8");
|
|
90
|
+
writeFileSync(PI_AGENT_SETTINGS, `${JSON.stringify(parsed.settings, null, 2)}\n`, "utf-8");
|
|
61
91
|
} catch {
|
|
62
92
|
return { pruned: [] };
|
|
63
93
|
}
|
|
64
|
-
return { pruned };
|
|
94
|
+
return { pruned: legacy };
|
|
65
95
|
}
|