@juicesharp/rpiv-pi 1.12.0 → 1.13.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 +1 -0
- package/extensions/rpiv-core/package-checks.test.ts +15 -6
- package/extensions/rpiv-core/package-checks.ts +2 -2
- package/extensions/rpiv-core/prune-legacy-siblings.test.ts +36 -15
- package/extensions/rpiv-core/prune-legacy-siblings.ts +3 -3
- package/extensions/rpiv-core/setup-command.test.ts +13 -0
- package/extensions/rpiv-core/setup-command.ts +5 -4
- package/extensions/rpiv-core/siblings.ts +3 -3
- package/extensions/rpiv-core/utils.test.ts +36 -8
- package/extensions/rpiv-core/utils.ts +15 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -223,6 +223,7 @@ Pi Agent discovers extensions via `"extensions": ["./extensions"]` and skills vi
|
|
|
223
223
|
- **UI language** - run `/languages` to pick the locale for rpiv-* TUI strings, or pass `pi --locale <code>` at startup. Detection priority: flag → `~/.config/rpiv-i18n/locale.json` → `LANG` / `LC_ALL` → English. LLM-facing copy stays English by design
|
|
224
224
|
- **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.
|
|
225
225
|
- **Agent profiles** - synced to `~/.pi/agent/agents/` from bundled defaults; refresh with `/rpiv-update-agents` (overwrites rpiv-managed files, preserves your custom agents).
|
|
226
|
+
- **Non-default agent directory** - if you set `PI_CODING_AGENT_DIR` (e.g. `~/.config/pi/agent` for an XDG-style layout), rpiv-pi reads and writes the same `settings.json` Pi does — sibling detection, `/rpiv-setup`, and `/rpiv-update-agents` all follow the env var. Leading `~` is expanded.
|
|
226
227
|
|
|
227
228
|
## Uninstall
|
|
228
229
|
|
|
@@ -3,12 +3,12 @@ import { dirname, join } from "node:path";
|
|
|
3
3
|
import { describe, expect, it } from "vitest";
|
|
4
4
|
import { findMissingSiblings } from "./package-checks.js";
|
|
5
5
|
import { SIBLINGS } from "./siblings.js";
|
|
6
|
-
|
|
7
|
-
const SETTINGS_PATH = join(process.env.HOME!, ".pi", "agent", "settings.json");
|
|
6
|
+
import { getPiAgentSettingsPath } from "./utils.js";
|
|
8
7
|
|
|
9
8
|
function writeSettings(contents: unknown) {
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
const settingsPath = getPiAgentSettingsPath();
|
|
10
|
+
mkdirSync(dirname(settingsPath), { recursive: true });
|
|
11
|
+
writeFileSync(settingsPath, JSON.stringify(contents), "utf-8");
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
describe("findMissingSiblings", () => {
|
|
@@ -17,8 +17,9 @@ describe("findMissingSiblings", () => {
|
|
|
17
17
|
});
|
|
18
18
|
|
|
19
19
|
it("returns all 7 siblings when JSON is invalid", () => {
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
const settingsPath = getPiAgentSettingsPath();
|
|
21
|
+
mkdirSync(dirname(settingsPath), { recursive: true });
|
|
22
|
+
writeFileSync(settingsPath, "{not json", "utf-8");
|
|
22
23
|
expect(findMissingSiblings()).toHaveLength(SIBLINGS.length);
|
|
23
24
|
});
|
|
24
25
|
|
|
@@ -56,4 +57,12 @@ describe("findMissingSiblings", () => {
|
|
|
56
57
|
});
|
|
57
58
|
expect(findMissingSiblings()).toEqual([]);
|
|
58
59
|
});
|
|
60
|
+
|
|
61
|
+
it("reads settings from PI_CODING_AGENT_DIR when configured", () => {
|
|
62
|
+
process.env.PI_CODING_AGENT_DIR = join(process.env.HOME!, ".config", "pi", "agent");
|
|
63
|
+
writeSettings({
|
|
64
|
+
packages: SIBLINGS.map((s) => s.pkg.replace(/^npm:/, "")),
|
|
65
|
+
});
|
|
66
|
+
expect(findMissingSiblings()).toEqual([]);
|
|
67
|
+
});
|
|
59
68
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Detect which SIBLINGS are installed by reading
|
|
2
|
+
* Detect which SIBLINGS are installed by reading the active Pi settings file.
|
|
3
3
|
* Pure utility — no ExtensionAPI.
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -8,7 +8,7 @@ import { readPiAgentSettings } from "./utils.js";
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Return the SIBLINGS not currently installed.
|
|
11
|
-
* Reads
|
|
11
|
+
* Reads the active Pi settings file once per call — callers that need both the
|
|
12
12
|
* full snapshot and the missing subset should call this once and filter.
|
|
13
13
|
*/
|
|
14
14
|
export function findMissingSiblings(): SiblingPlugin[] {
|
|
@@ -2,16 +2,24 @@ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { describe, expect, it } from "vitest";
|
|
4
4
|
import { findLegacySiblings, pruneLegacySiblings } from "./prune-legacy-siblings.js";
|
|
5
|
+
import { getPiAgentSettingsPath } from "./utils.js";
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
function writeSettingsRaw(raw: string): void {
|
|
8
|
+
const settingsPath = getPiAgentSettingsPath();
|
|
9
|
+
mkdirSync(dirname(settingsPath), { recursive: true });
|
|
10
|
+
writeFileSync(settingsPath, raw, "utf-8");
|
|
11
|
+
}
|
|
7
12
|
|
|
8
13
|
function writeSettings(contents: unknown): void {
|
|
9
|
-
|
|
10
|
-
|
|
14
|
+
writeSettingsRaw(JSON.stringify(contents));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function readSettingsRaw(): string {
|
|
18
|
+
return readFileSync(getPiAgentSettingsPath(), "utf-8");
|
|
11
19
|
}
|
|
12
20
|
|
|
13
21
|
function readSettings(): unknown {
|
|
14
|
-
return JSON.parse(
|
|
22
|
+
return JSON.parse(readSettingsRaw());
|
|
15
23
|
}
|
|
16
24
|
|
|
17
25
|
describe("pruneLegacySiblings", () => {
|
|
@@ -20,10 +28,9 @@ describe("pruneLegacySiblings", () => {
|
|
|
20
28
|
});
|
|
21
29
|
|
|
22
30
|
it("invalid JSON → pruned: [], file byte-exact unchanged", () => {
|
|
23
|
-
|
|
24
|
-
writeFileSync(SETTINGS_PATH, "{not json", "utf-8");
|
|
31
|
+
writeSettingsRaw("{not json");
|
|
25
32
|
expect(pruneLegacySiblings()).toEqual({ pruned: [] });
|
|
26
|
-
expect(
|
|
33
|
+
expect(readSettingsRaw()).toBe("{not json");
|
|
27
34
|
});
|
|
28
35
|
|
|
29
36
|
it("non-object top-level (array) → pruned: [], file unchanged", () => {
|
|
@@ -47,9 +54,9 @@ describe("pruneLegacySiblings", () => {
|
|
|
47
54
|
writeSettings({
|
|
48
55
|
packages: ["npm:pi-perplexity", "npm:@juicesharp/rpiv-todo", "npm:@tintinweb/pi-subagents"],
|
|
49
56
|
});
|
|
50
|
-
const before =
|
|
57
|
+
const before = readSettingsRaw();
|
|
51
58
|
expect(pruneLegacySiblings()).toEqual({ pruned: [] });
|
|
52
|
-
expect(
|
|
59
|
+
expect(readSettingsRaw()).toBe(before);
|
|
53
60
|
});
|
|
54
61
|
|
|
55
62
|
it("legacy-only: removes pi-subagents (nicobailon fork), preserves other top-level keys", () => {
|
|
@@ -93,6 +100,15 @@ describe("pruneLegacySiblings", () => {
|
|
|
93
100
|
});
|
|
94
101
|
});
|
|
95
102
|
|
|
103
|
+
it("prunes settings from PI_CODING_AGENT_DIR when configured", () => {
|
|
104
|
+
process.env.PI_CODING_AGENT_DIR = join(process.env.HOME!, ".config", "pi", "agent");
|
|
105
|
+
writeSettings({
|
|
106
|
+
packages: ["npm:pi-subagents"],
|
|
107
|
+
});
|
|
108
|
+
expect(pruneLegacySiblings().pruned).toEqual(["npm:pi-subagents"]);
|
|
109
|
+
expect(readSettings()).toEqual({ packages: [] });
|
|
110
|
+
});
|
|
111
|
+
|
|
96
112
|
it("idempotent: second call after prune is a no-op", () => {
|
|
97
113
|
writeSettings({
|
|
98
114
|
packages: ["npm:pi-subagents"],
|
|
@@ -115,8 +131,7 @@ describe("findLegacySiblings (read-only scan)", () => {
|
|
|
115
131
|
});
|
|
116
132
|
|
|
117
133
|
it("invalid JSON → []", () => {
|
|
118
|
-
|
|
119
|
-
writeFileSync(SETTINGS_PATH, "{not json", "utf-8");
|
|
134
|
+
writeSettingsRaw("{not json");
|
|
120
135
|
expect(findLegacySiblings()).toEqual([]);
|
|
121
136
|
});
|
|
122
137
|
|
|
@@ -147,16 +162,22 @@ describe("findLegacySiblings (read-only scan)", () => {
|
|
|
147
162
|
defaultProvider: "zai",
|
|
148
163
|
packages: ["npm:pi-subagents", "npm:@juicesharp/rpiv-todo"],
|
|
149
164
|
});
|
|
150
|
-
const before =
|
|
165
|
+
const before = readSettingsRaw();
|
|
166
|
+
expect(findLegacySiblings()).toEqual(["npm:pi-subagents"]);
|
|
167
|
+
expect(readSettingsRaw()).toBe(before);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("reads settings from PI_CODING_AGENT_DIR when configured", () => {
|
|
171
|
+
process.env.PI_CODING_AGENT_DIR = join(process.env.HOME!, ".config", "pi", "agent");
|
|
172
|
+
writeSettings({ packages: ["npm:pi-subagents"] });
|
|
151
173
|
expect(findLegacySiblings()).toEqual(["npm:pi-subagents"]);
|
|
152
|
-
expect(readFileSync(SETTINGS_PATH, "utf-8")).toBe(before);
|
|
153
174
|
});
|
|
154
175
|
|
|
155
176
|
it("idempotent: repeat call returns the same list and does not mutate", () => {
|
|
156
177
|
writeSettings({ packages: ["npm:pi-subagents"] });
|
|
157
|
-
const before =
|
|
178
|
+
const before = readSettingsRaw();
|
|
158
179
|
expect(findLegacySiblings()).toEqual(["npm:pi-subagents"]);
|
|
159
180
|
expect(findLegacySiblings()).toEqual(["npm:pi-subagents"]);
|
|
160
|
-
expect(
|
|
181
|
+
expect(readSettingsRaw()).toBe(before);
|
|
161
182
|
});
|
|
162
183
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Detect + remove deprecated sibling package entries from
|
|
3
|
-
*
|
|
3
|
+
* the active Pi agent settings file.
|
|
4
4
|
*
|
|
5
5
|
* Split into two phases so /rpiv-setup can preview pending changes in the
|
|
6
6
|
* confirmation dialog and apply the mutation only after the user agrees:
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
|
|
24
24
|
import { writeFileSync } from "node:fs";
|
|
25
25
|
import { LEGACY_SIBLINGS } from "./siblings.js";
|
|
26
|
-
import {
|
|
26
|
+
import { getPiAgentSettingsPath, readPiAgentSettings } from "./utils.js";
|
|
27
27
|
|
|
28
28
|
export interface PruneLegacySiblingsResult {
|
|
29
29
|
/** settings.json `packages[]` entries that were removed (empty = no-op). */
|
|
@@ -65,7 +65,7 @@ export function pruneLegacySiblings(): PruneLegacySiblingsResult {
|
|
|
65
65
|
|
|
66
66
|
parsed.settings.packages = kept;
|
|
67
67
|
try {
|
|
68
|
-
writeFileSync(
|
|
68
|
+
writeFileSync(getPiAgentSettingsPath(), `${JSON.stringify(parsed.settings, null, 2)}\n`, "utf-8");
|
|
69
69
|
} catch {
|
|
70
70
|
return { pruned: [] };
|
|
71
71
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
1
2
|
import { createMockCtx, createMockPi } from "@juicesharp/rpiv-test-utils";
|
|
2
3
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
4
|
|
|
@@ -85,6 +86,18 @@ describe("/rpiv-setup — pre-confirm read-only contract", () => {
|
|
|
85
86
|
expect(confirmCall[1]).toContain("npm:pi-subagents");
|
|
86
87
|
});
|
|
87
88
|
|
|
89
|
+
it("shows the PI_CODING_AGENT_DIR settings path in the confirmation body", async () => {
|
|
90
|
+
process.env.PI_CODING_AGENT_DIR = join(process.env.HOME!, ".config", "pi", "agent");
|
|
91
|
+
vi.mocked(findMissingSiblings).mockReturnValue([]);
|
|
92
|
+
vi.mocked(findLegacySiblings).mockReturnValue(["npm:pi-subagents"]);
|
|
93
|
+
const { pi, captured } = createMockPi();
|
|
94
|
+
registerSetupCommand(pi);
|
|
95
|
+
const ctx = createMockCtx({ hasUI: true });
|
|
96
|
+
await captured.commands.get("rpiv-setup")?.handler("", ctx as never);
|
|
97
|
+
const confirmCall = (ctx.ui.confirm as ReturnType<typeof vi.fn>).mock.calls[0]!;
|
|
98
|
+
expect(confirmCall[1]).toContain(join(process.env.HOME!, ".config", "pi", "agent", "settings.json"));
|
|
99
|
+
});
|
|
100
|
+
|
|
88
101
|
it("includes pending installs in the confirmation body", async () => {
|
|
89
102
|
vi.mocked(findMissingSiblings).mockReturnValue([{ pkg: "npm:@x/a", matches: /./, provides: "A" }]);
|
|
90
103
|
vi.mocked(findLegacySiblings).mockReturnValue([]);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* /rpiv-setup — installs any SIBLINGS not present in
|
|
2
|
+
* /rpiv-setup — installs any SIBLINGS not present in the active Pi settings file
|
|
3
3
|
* and prunes deprecated entries (e.g. the unscoped `npm:pi-subagents` from
|
|
4
4
|
* the rpiv-pi 0.12.x → 0.14.0 line). Both mutations are previewed in the
|
|
5
5
|
* confirmation dialog and only executed after the user agrees.
|
|
@@ -13,7 +13,7 @@ import { findMissingSiblings } from "./package-checks.js";
|
|
|
13
13
|
import { spawnPiInstall } from "./pi-installer.js";
|
|
14
14
|
import { findLegacySiblings, pruneLegacySiblings } from "./prune-legacy-siblings.js";
|
|
15
15
|
import type { SiblingPlugin } from "./siblings.js";
|
|
16
|
-
import { toErrorMessage } from "./utils.js";
|
|
16
|
+
import { getPiAgentSettingsPath, toErrorMessage } from "./utils.js";
|
|
17
17
|
|
|
18
18
|
const INSTALL_TIMEOUT_MS = 120_000;
|
|
19
19
|
const STDERR_SNIPPET_CHARS = 300;
|
|
@@ -43,12 +43,13 @@ function buildConfirmBody(missing: SiblingPlugin[], legacyEntries: string[]): st
|
|
|
43
43
|
for (const m of missing) lines.push(` • ${m.pkg} (required — provides ${m.provides})`);
|
|
44
44
|
lines.push("");
|
|
45
45
|
}
|
|
46
|
+
const settingsPath = getPiAgentSettingsPath();
|
|
46
47
|
if (legacyEntries.length > 0) {
|
|
47
|
-
lines.push(
|
|
48
|
+
lines.push(`Remove from \`${settingsPath}\` (deprecated):`);
|
|
48
49
|
for (const entry of legacyEntries) lines.push(` • ${entry}`);
|
|
49
50
|
lines.push("");
|
|
50
51
|
}
|
|
51
|
-
lines.push(
|
|
52
|
+
lines.push(`Your \`${settingsPath}\` will be updated. Proceed?`);
|
|
52
53
|
return lines.join("\n");
|
|
53
54
|
}
|
|
54
55
|
|
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
* /rpiv-setup installer (setup-command.ts). Add a sibling here and every
|
|
7
7
|
* consumer picks it up automatically.
|
|
8
8
|
*
|
|
9
|
-
* Detection is filesystem-based via a regex over
|
|
9
|
+
* Detection is filesystem-based via a regex over the active Pi settings file
|
|
10
10
|
* — no runtime import of sibling packages (keeps rpiv-core pure-orchestrator).
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
export interface SiblingPlugin {
|
|
14
14
|
/** Install spec passed to `pi install`. Prefixed with `npm:` for Pi's installer. */
|
|
15
15
|
readonly pkg: string;
|
|
16
|
-
/** Case-insensitive regex that matches the package in
|
|
16
|
+
/** Case-insensitive regex that matches the package in settings.json. */
|
|
17
17
|
readonly matches: RegExp;
|
|
18
18
|
/** What the sibling provides — shown in /rpiv-setup confirmation and reports. */
|
|
19
19
|
readonly provides: string;
|
|
@@ -59,7 +59,7 @@ export const SIBLINGS: readonly SiblingPlugin[] = [
|
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
61
|
* Deprecated sibling packages that `/rpiv-setup` actively prunes from
|
|
62
|
-
*
|
|
62
|
+
* the active Pi settings file (so upgraders don't end up with superseded
|
|
63
63
|
* libraries loaded alongside their replacements). Single source of truth
|
|
64
64
|
* for `prune-legacy-siblings.ts`.
|
|
65
65
|
*/
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname } from "node:path";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
3
|
import { describe, expect, it } from "vitest";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
import {
|
|
5
|
+
getPiAgentSettingsPath,
|
|
6
|
+
isPlainObject,
|
|
7
|
+
PI_AGENT_SETTINGS,
|
|
8
|
+
readPiAgentSettings,
|
|
9
|
+
toErrorMessage,
|
|
10
|
+
} from "./utils.js";
|
|
11
|
+
|
|
12
|
+
function writeSettingsRaw(raw: string, path = getPiAgentSettingsPath()): void {
|
|
13
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
14
|
+
writeFileSync(path, raw, "utf-8");
|
|
9
15
|
}
|
|
10
16
|
|
|
11
|
-
function writeSettings(contents: unknown): void {
|
|
12
|
-
writeSettingsRaw(JSON.stringify(contents));
|
|
17
|
+
function writeSettings(contents: unknown, path = getPiAgentSettingsPath()): void {
|
|
18
|
+
writeSettingsRaw(JSON.stringify(contents), path);
|
|
13
19
|
}
|
|
14
20
|
|
|
15
21
|
describe("isPlainObject", () => {
|
|
@@ -65,6 +71,22 @@ describe("toErrorMessage", () => {
|
|
|
65
71
|
});
|
|
66
72
|
});
|
|
67
73
|
|
|
74
|
+
describe("getPiAgentSettingsPath", () => {
|
|
75
|
+
it("uses ~/.pi/agent/settings.json when PI_CODING_AGENT_DIR is unset", () => {
|
|
76
|
+
expect(getPiAgentSettingsPath()).toBe(PI_AGENT_SETTINGS);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("uses PI_CODING_AGENT_DIR/settings.json when configured", () => {
|
|
80
|
+
process.env.PI_CODING_AGENT_DIR = join(process.env.HOME!, ".config", "pi", "agent");
|
|
81
|
+
expect(getPiAgentSettingsPath()).toBe(join(process.env.HOME!, ".config", "pi", "agent", "settings.json"));
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("expands a leading ~ in PI_CODING_AGENT_DIR", () => {
|
|
85
|
+
process.env.PI_CODING_AGENT_DIR = "~/.config/pi/agent";
|
|
86
|
+
expect(getPiAgentSettingsPath()).toBe(join(process.env.HOME!, ".config", "pi", "agent", "settings.json"));
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
68
90
|
describe("readPiAgentSettings", () => {
|
|
69
91
|
it("returns undefined when the settings file is missing", () => {
|
|
70
92
|
rmSync(PI_AGENT_SETTINGS, { force: true });
|
|
@@ -105,6 +127,12 @@ describe("readPiAgentSettings", () => {
|
|
|
105
127
|
});
|
|
106
128
|
});
|
|
107
129
|
|
|
130
|
+
it("reads from PI_CODING_AGENT_DIR/settings.json when configured", () => {
|
|
131
|
+
process.env.PI_CODING_AGENT_DIR = join(process.env.HOME!, ".config", "pi", "agent");
|
|
132
|
+
writeSettings({ packages: ["npm:@juicesharp/rpiv-todo"] });
|
|
133
|
+
expect(readPiAgentSettings()?.packages).toEqual(["npm:@juicesharp/rpiv-todo"]);
|
|
134
|
+
});
|
|
135
|
+
|
|
108
136
|
it("preserves non-string entries inside packages (caller responsibility to filter)", () => {
|
|
109
137
|
writeSettings({ packages: [null, 42, "npm:pi-subagents"] });
|
|
110
138
|
const result = readPiAgentSettings();
|
|
@@ -7,14 +7,24 @@
|
|
|
7
7
|
import { existsSync, readFileSync } from "node:fs";
|
|
8
8
|
import { homedir } from "node:os";
|
|
9
9
|
import { join } from "node:path";
|
|
10
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
10
11
|
|
|
11
12
|
// ---------------------------------------------------------------------------
|
|
12
13
|
// PI Agent Settings path
|
|
13
14
|
// ---------------------------------------------------------------------------
|
|
14
15
|
|
|
15
|
-
/**
|
|
16
|
+
/** Default Pi agent settings path when PI_CODING_AGENT_DIR is not configured. */
|
|
16
17
|
export const PI_AGENT_SETTINGS = join(homedir(), ".pi", "agent", "settings.json");
|
|
17
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Resolve the active Pi agent settings file. Delegates the agent-dir lookup to
|
|
21
|
+
* Pi's `getAgentDir()` so PI_CODING_AGENT_DIR handling (including tilde
|
|
22
|
+
* expansion) stays in one place across rpiv-pi and Pi itself.
|
|
23
|
+
*/
|
|
24
|
+
export function getPiAgentSettingsPath(): string {
|
|
25
|
+
return join(getAgentDir(), "settings.json");
|
|
26
|
+
}
|
|
27
|
+
|
|
18
28
|
// ---------------------------------------------------------------------------
|
|
19
29
|
// Type guards
|
|
20
30
|
// ---------------------------------------------------------------------------
|
|
@@ -48,15 +58,16 @@ interface PiAgentSettingsResult {
|
|
|
48
58
|
}
|
|
49
59
|
|
|
50
60
|
/**
|
|
51
|
-
* Read and parse
|
|
61
|
+
* Read and parse the active Pi agent settings file.
|
|
52
62
|
* Returns undefined if the file is missing, has invalid JSON, or is not a plain object
|
|
53
63
|
* with a packages array. Fail-soft — never throws.
|
|
54
64
|
*/
|
|
55
65
|
export function readPiAgentSettings(): PiAgentSettingsResult | undefined {
|
|
56
|
-
|
|
66
|
+
const settingsPath = getPiAgentSettingsPath();
|
|
67
|
+
if (!existsSync(settingsPath)) return undefined;
|
|
57
68
|
let parsed: unknown;
|
|
58
69
|
try {
|
|
59
|
-
parsed = JSON.parse(readFileSync(
|
|
70
|
+
parsed = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
60
71
|
} catch {
|
|
61
72
|
return undefined;
|
|
62
73
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juicesharp/rpiv-pi",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "A skill-based development workflow for Pi Agent. Five skills (research, design, plan, implement, validate) and the shared subagents that compose its ship-loop.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|