pi-profiles-manager 1.0.1 → 1.1.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 javinnav
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,96 +1,100 @@
1
- # pi-profiles-manager 🚀
1
+ # pi-profiles-manager
2
2
 
3
- Interactive SDD model profile management built natively for the [Pi Coding Agent](https://github.com/earendil-works/pi-mono).
3
+ `pi-profiles-manager` is a [Pi Coding Agent](https://github.com/badlogic/pi-mono) extension for saving, selecting, editing, importing, and exporting SDD model profiles. A profile can set an orchestrator model and thinking level, plus routes for managed subagents.
4
4
 
5
- Manage your Gentle AI `models.json` configurations dynamically from a beautiful Terminal User Interface (TUI) without ever leaving your session.
5
+ ## Install
6
6
 
7
- ---
7
+ Prerequisites:
8
8
 
9
- ## 🌟 Features
9
+ - Pi Coding Agent
10
+ - Node.js 22.19.0 or later (for development)
10
11
 
11
- ### 1. Unified Profile Management
12
- Manage all your SDD (Spec-Driven Development) profiles via a rich interface:
13
- - **Create:** Read your current `models.json` setup and save it as a new profile instantly.
14
- - **Activate:** Apply a saved profile into the global runtime config. Changes affect the executing session immediately with no restarts required.
15
- - **Edit Agent Nodes:** Individually adjust `model` strings and `thinking` tiers (`low`, `medium`, `high`, `xhigh`, `max`) for any agent (`sdd-*`, `jd-*`, `review-*`, etc.).
16
- - **Scaffold Empty Agents:** Add specific overrides for single agents right from the UI.
17
- - **Delete:** Remove outdated tiers and profiles to keep your workspace tidy.
12
+ Install the published package:
18
13
 
19
- ### 2. Native Pi Integration
20
- - Fully built on top of `@earendil-works/pi-tui`.
21
- - Works flawlessly with overlay navigation, meaning your terminal output isn't erased when interacting with profiles.
14
+ ```bash
15
+ pi install npm:pi-profiles-manager
16
+ ```
22
17
 
23
- ---
18
+ Reload an active Pi session with `/reload` if needed.
24
19
 
25
- ## 📸 Interface Preview (Compact Format)
20
+ ## Pi command: `/profiles`
26
21
 
27
- ```text
28
- Profiles Manager
29
- ──────────────────────────────────────────────────
30
- > ✨ Create New Profile from Current Config
31
- FREE
32
- antigravity
33
- deus
34
-
35
- ↑↓ navigate • enter select • esc close
36
- ```
22
+ Pi registers one command: `/profiles`. Its actions are arguments to that command, not separate Pi commands such as `/profiles:sync`.
37
23
 
38
24
  ```text
39
- Action for 'antigravity'
40
- ──────────────────────────────────────────────────
41
- > ▶ Activate Apply this profile to models.json
42
- ✎ Edit Modify agents in this profile
43
- ✖ Delete Remove this profile
44
- ← Back Return to profile list
25
+ /profiles
45
26
  ```
46
27
 
47
- ```text
48
- Edit Profile 'antigravity'
49
- ──────────────────────────────────────────────────
50
- > orchestrator omni/antigravity/gemini-pro-agent (medium)
51
- jd-fix-agent omniroute/gemini-flash-low (high)
52
- ...
53
- ➕ Add Subagent Add a specific configuration for a subagent
54
- ```
28
+ Open the interactive manager with no arguments. When entering an argument, Pi completion suggests supported actions; after `use` or `save`, it suggests matching saved profile names. Profile names may contain spaces.
55
29
 
56
- ---
30
+ | Command | Action |
31
+ | --- | --- |
32
+ | `/profiles` | Open the interactive profile manager. |
33
+ | `/profiles list` | List saved profiles. |
34
+ | `/profiles status` | Show the active profile for the current session. |
35
+ | `/profiles use <name>` | Activate a saved profile. |
36
+ | `/profiles save <name>` | Save the current configuration as a profile. |
37
+ | `/profiles next` | Activate the next profile in the configured cycle. |
38
+ | `/profiles off` | Disable the active profile for the current session. |
39
+ | `/profiles sync` | Discover managed `agents/*.md` files and reconcile every saved profile's subagent entries. It does not create Pi commands. |
57
40
 
58
- ## 🛠 Installation
41
+ The default shortcut, `ctrl+shift+p`, activates the next profile in the configured cycle.
59
42
 
60
- Clone this repository directly into your Pi extensions directory:
43
+ ## Where data lives
61
44
 
62
- ```bash
63
- git clone https://github.com/javinnav/pi-profiles-manager.git ~/.pi/agent/extensions/profiles-manager
64
- ```
45
+ These locations have different purposes:
46
+
47
+ ### Saved profile persistence
65
48
 
66
- Apply the changes immediately by reloading the active session:
49
+ Saved profiles, the default profile, cycle, and shortcut preference are persisted in:
67
50
 
68
51
  ```text
69
- /reload
52
+ ~/.pi/agent/pi-profiles/config.json
70
53
  ```
71
54
 
72
- ---
73
-
74
- ## 🚀 Usage
55
+ The extension creates this file and its parent directory when it saves a profile.
75
56
 
76
- *Ensure the active Pi Session recognizes the newly linked extension context.*
57
+ ### Managed subagent definitions
77
58
 
78
- Open the Profiles Manager by triggering its slash command:
59
+ Pi agent markdown files live in the Pi agent directory:
79
60
 
80
61
  ```text
81
- /profiles
62
+ ~/.pi/agent/agents/*.md
82
63
  ```
83
64
 
84
- Alternatively, to quickly snapshot your current session's `models.json` without routing through the UI:
65
+ Run `/profiles sync` after adding or removing these files. Sync discovers their names and reconciles each saved profile: new managed agents receive a profile entry, and entries for removed markdown files are removed. Sync updates profile data; it does not register additional Pi slash commands.
66
+
67
+ ### Applied subagent routes
68
+
69
+ When a profile is activated, its subagent routes are applied to Pi's global agent-route configuration:
85
70
 
86
71
  ```text
87
- /profiles save <profile-name>
72
+ ~/.pi/agent/subagents.json
88
73
  ```
89
74
 
90
- ---
75
+ This is separate from `pi-profiles/config.json`: the former is the active routing configuration Pi uses, while the latter stores the profiles that can be selected later.
76
+
77
+ ## Features
78
+
79
+ - Create, activate, edit, and delete profiles.
80
+ - Import and export versioned profile strings.
81
+ - Set model and thinking routes for the orchestrator and managed subagents.
82
+ - Mark a default profile for session startup and view active/default status in the UI.
83
+ - Cycle profiles with a keyboard shortcut.
84
+
85
+ ## Development
86
+
87
+ Clone this repository into your Pi extensions directory to develop it locally:
88
+
89
+ ```bash
90
+ git clone https://github.com/javinnav/pi-profiles-manager.git ~/.pi/agent/extensions/profiles-manager
91
+ cd ~/.pi/agent/extensions/profiles-manager
92
+ npm ci
93
+ npm test
94
+ npm run typecheck
95
+ npm run package-check
96
+ ```
91
97
 
92
- ## 💖 Credits and Acknowledgments
98
+ ## License
93
99
 
94
- - **Inspiration:** Developed mirroring the clean extension and TUI architecture provided by [opencode-sdd-engram-manage](https://github.com/j0k3r-dev-rgl/sdd-engram-plugin) from @j0k3r-dev-rgl.
95
- - **Dependencies:** Built leveraging the core APIs of the [Pi Coding Agent framework](https://github.com/earendil-works/pi-mono).
96
- - **Ecosystem:** Powered by [Gentle AI (OpenCode)](https://github.com/Gentleman-Programming/gentle-ai).
100
+ MIT © javinnav
package/index.ts CHANGED
@@ -1,352 +1 @@
1
- import * as fs from "fs/promises";
2
- import * as os from "os";
3
- import * as path from "path";
4
- import type { ExtensionAPI, TUIContext } from "@earendil-works/pi-coding-agent";
5
- import { DynamicBorder } from "@earendil-works/pi-coding-agent";
6
- import { Container, SelectList, Text, Input } from "@earendil-works/pi-tui";
7
-
8
- const GENTLE_DIR = path.join(os.homedir(), ".pi", "gentle-ai");
9
- const MODELS_PATH = path.join(GENTLE_DIR, "models.json");
10
- const PROFILES_PATH = path.join(GENTLE_DIR, "sdd-profiles-manager.json");
11
-
12
- interface Profile {
13
- name: string;
14
- orchestrator: { model: string; thinking: string };
15
- agents: Record<string, { model: string; thinking: string }>;
16
- }
17
-
18
- async function readJson(fp: string): Promise<any> {
19
- try {
20
- const data = await fs.readFile(fp, "utf-8");
21
- return JSON.parse(data);
22
- } catch (e: any) {
23
- if (e.code === "ENOENT") return {};
24
- throw e;
25
- }
26
- }
27
-
28
- async function writeJson(fp: string, data: any): Promise<void> {
29
- await fs.mkdir(path.dirname(fp), { recursive: true });
30
- await fs.writeFile(fp, JSON.stringify(data, null, 2));
31
- }
32
-
33
- // Helper to ask user for a string
34
- async function promptInput(ctx: any, title: string, initialValue: string = ""): Promise<string | null> {
35
- return await ctx.ui.custom<string | null>((tui: any, theme: any, _kb: any, done: any) => {
36
- const container = new Container() as any;
37
- container.focused = true; // Make container focusable
38
-
39
- container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
40
- container.addChild(new Text(theme.fg("accent", theme.bold(title)), 1, 0));
41
-
42
- const input = new Input();
43
- input.setValue(initialValue);
44
- input.onSubmit = (val) => done(val.trim());
45
- input.onEscape = () => done(null);
46
- input.focused = true;
47
-
48
- // Wire up focus propagation
49
- container.handleInput = (data: string) => {
50
- input.handleInput(data);
51
- tui.requestRender();
52
- };
53
-
54
- container.addChild(input);
55
- container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
56
-
57
- return {
58
- render: (w: number) => container.render(w),
59
- invalidate: () => container.invalidate(),
60
- handleInput: (data: string) => container.handleInput(data),
61
- };
62
- }, { overlay: true });
63
- }
64
-
65
- export default function (pi: ExtensionAPI) {
66
- pi.registerCommand("profiles", {
67
- description: "Manage SDD model profiles",
68
- handler: async (args, ctx) => {
69
- // Subcommand for save
70
- if (args[0] === "save") {
71
- const profileName = args[1];
72
- if (!profileName) {
73
- ctx.ui.notify("Error: Provide a profile name (e.g. /profiles save my-profile)", "error");
74
- return;
75
- }
76
-
77
- const models = await readJson(MODELS_PATH);
78
- const orchestrator = models["gentle-orchestrator"] || { model: "", thinking: "" };
79
- const agents: any = {};
80
- for (const key of Object.keys(models)) {
81
- if (key !== "gentle-orchestrator") {
82
- agents[key] = models[key];
83
- }
84
- }
85
- const profiles = await readJson(PROFILES_PATH);
86
- profiles[profileName] = { name: profileName, orchestrator, agents };
87
- await writeJson(PROFILES_PATH, profiles);
88
- ctx.ui.notify(`Profile '${profileName}' saved`, "success");
89
- return;
90
- }
91
-
92
- while (true) {
93
- let profiles: Record<string, Profile> = await readJson(PROFILES_PATH);
94
- const profileNames = Object.keys(profiles);
95
-
96
- const items = [
97
- { value: "__CREATE__", label: "✨ Create New Profile from Current Config", description: "Saves models.json into a new profile" },
98
- ...profileNames.map((name) => ({
99
- value: name,
100
- label: name,
101
- description: `Orchestrator: ${profiles[name].orchestrator?.model || "none"}`,
102
- }))
103
- ];
104
-
105
- // Pick Profile
106
- const selectedProfile = await ctx.ui.custom<string | null>((tui, theme, _kb, done) => {
107
- const container = new Container();
108
- container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
109
- container.addChild(new Text(theme.fg("accent", theme.bold("Profiles Manager")), 1, 0));
110
-
111
- const list = new SelectList(items, Math.min(items.length, 10), {
112
- selectedPrefix: (t) => theme.fg("accent", t),
113
- selectedText: (t) => theme.fg("accent", t),
114
- description: (t) => theme.fg("muted", t),
115
- scrollInfo: (t) => theme.fg("dim", t),
116
- noMatch: (t) => theme.fg("warning", t),
117
- });
118
- list.onSelect = (item) => done(item.value);
119
- list.onCancel = () => done(null);
120
- container.addChild(list);
121
-
122
- container.addChild(new Text(theme.fg("dim", "↑↓ navigate • enter select • esc close"), 1, 0));
123
- container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
124
-
125
- return {
126
- render: (w) => container.render(w),
127
- invalidate: () => container.invalidate(),
128
- handleInput: (data) => { list.handleInput(data); tui.requestRender(); },
129
- };
130
- }, { overlay: true });
131
-
132
- if (!selectedProfile) break; // Finished and exit loop
133
-
134
- if (selectedProfile === "__CREATE__") {
135
- const newName = await promptInput(ctx, "Enter new profile name:");
136
- if (newName) {
137
- const models = await readJson(MODELS_PATH);
138
- const orchestrator = models["gentle-orchestrator"] || { model: "", thinking: "" };
139
- const agents: any = {};
140
- for (const key of Object.keys(models)) {
141
- if (key !== "gentle-orchestrator") {
142
- agents[key] = models[key];
143
- }
144
- }
145
- profiles[newName] = { name: newName, orchestrator, agents };
146
- await writeJson(PROFILES_PATH, profiles);
147
- ctx.ui.notify(`Created profile '${newName}'`, "success");
148
- }
149
- continue;
150
- }
151
-
152
- // Action menu for existing profile
153
- while (true) {
154
- const actionItems = [
155
- { value: "activate", label: "▶ Activate", description: "Apply this profile to models.json" },
156
- { value: "edit", label: "✎ Edit", description: "Modify agents in this profile" },
157
- { value: "delete", label: "✖ Delete", description: "Remove this profile" },
158
- { value: "back", label: "← Back", description: "Return to profile list" },
159
- ];
160
-
161
- const selectedAction = await ctx.ui.custom<string | null>((tui, theme, _kb, done) => {
162
- const container = new Container();
163
- container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
164
- container.addChild(new Text(theme.fg("accent", theme.bold(`Profile: '${selectedProfile}'`)), 1, 0));
165
-
166
- const list = new SelectList(actionItems, 4, {
167
- selectedPrefix: (t) => theme.fg("accent", t),
168
- selectedText: (t) => theme.fg("accent", t),
169
- description: (t) => theme.fg("muted", t),
170
- scrollInfo: (t) => theme.fg("dim", t),
171
- noMatch: (t) => theme.fg("warning", t),
172
- });
173
- list.onSelect = (item) => done(item.value);
174
- list.onCancel = () => done(null);
175
- container.addChild(list);
176
- container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
177
-
178
- return {
179
- render: (w) => container.render(w),
180
- invalidate: () => container.invalidate(),
181
- handleInput: (data) => { list.handleInput(data); tui.requestRender(); },
182
- };
183
- }, { overlay: true });
184
-
185
- if (!selectedAction || selectedAction === "back") {
186
- break; // Back to profile list
187
- }
188
-
189
- if (selectedAction === "activate") {
190
- const profile = profiles[selectedProfile];
191
- const newModels: any = {};
192
- if (profile.orchestrator) {
193
- newModels["gentle-orchestrator"] = profile.orchestrator;
194
- }
195
- if (profile.agents) {
196
- for (const key of Object.keys(profile.agents)) {
197
- newModels[key] = profile.agents[key];
198
- }
199
- }
200
- await writeJson(MODELS_PATH, newModels);
201
- ctx.ui.notify(`Activated profile '${selectedProfile}'. Run /reload if needed.`, "success");
202
- break; // Back to profile list after activating
203
- }
204
-
205
- if (selectedAction === "delete") {
206
- delete profiles[selectedProfile];
207
- await writeJson(PROFILES_PATH, profiles);
208
- ctx.ui.notify(`Deleted profile '${selectedProfile}'.`, "success");
209
- break; // Back to profile list after deleting
210
- }
211
-
212
- if (selectedAction === "edit") {
213
- // Edit Flow
214
- while (true) {
215
- const currentProfile = profiles[selectedProfile];
216
- const agentKeys = ["orchestrator", ...Object.keys(currentProfile.agents || {})];
217
-
218
- const editItems = agentKeys.map(k => {
219
- const conf = k === "orchestrator" ? currentProfile.orchestrator : currentProfile.agents[k];
220
- return {
221
- value: k,
222
- label: k,
223
- description: `${conf?.model || "none"} (${conf?.thinking || "low"})`
224
- };
225
- });
226
- editItems.push({ value: "__ADD__", label: "➕ Add Subagent", description: "Add a specific configuration for a subagent" });
227
- editItems.push({ value: "back", label: "← Back", description: "Return to profile menu" });
228
-
229
- const pickedAgent = await ctx.ui.custom<string | null>((tui, theme, _kb, done) => {
230
- const container = new Container();
231
- container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
232
- container.addChild(new Text(theme.fg("accent", theme.bold(`Edit Profile '${selectedProfile}'`)), 1, 0));
233
-
234
- const list = new SelectList(editItems, Math.min(editItems.length, 10), {
235
- selectedPrefix: (t) => theme.fg("accent", t),
236
- selectedText: (t) => theme.fg("accent", t),
237
- description: (t) => theme.fg("muted", t),
238
- scrollInfo: (t) => theme.fg("dim", t),
239
- noMatch: (t) => theme.fg("warning", t),
240
- });
241
- list.onSelect = (item) => done(item.value);
242
- list.onCancel = () => done(null);
243
- container.addChild(list);
244
- container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
245
-
246
- return {
247
- render: (w) => container.render(w),
248
- invalidate: () => container.invalidate(),
249
- handleInput: (data) => { list.handleInput(data); tui.requestRender(); },
250
- };
251
- }, { overlay: true });
252
-
253
- if (!pickedAgent || pickedAgent === "back") break;
254
-
255
- let agentName = pickedAgent;
256
- // If adding, ask for agent name
257
- if (agentName === "__ADD__") {
258
- const newName = await promptInput(ctx, "Subagent Name (e.g. sdd-apply):");
259
- if (!newName) continue;
260
- agentName = newName;
261
- if (!currentProfile.agents) currentProfile.agents = {};
262
- currentProfile.agents[agentName] = { model: "omni/antigravity/gemini-3.6-flash-low", thinking: "low" };
263
- await writeJson(PROFILES_PATH, profiles);
264
- }
265
-
266
- // Edit Agent
267
- while (true) {
268
- const conf = agentName === "orchestrator" ? currentProfile.orchestrator : currentProfile.agents[agentName];
269
-
270
- const modifierItems = [
271
- { value: "model", label: "Modify Model", description: conf?.model || "none" },
272
- { value: "thinking", label: "Modify Thinking", description: conf?.thinking || "low" },
273
- { value: "delete", label: "✖ Remove Agent from profile", description: "Delete this configuration" },
274
- { value: "back", label: "← Back", description: "" },
275
- ];
276
-
277
- const pickedMod = await ctx.ui.custom<string | null>((tui, theme, _kb, done) => {
278
- const container = new Container();
279
- container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
280
- container.addChild(new Text(theme.fg("accent", theme.bold(`Edit Agent '${agentName}'`)), 1, 0));
281
- const list = new SelectList(modifierItems, 4, {
282
- selectedPrefix: (t) => theme.fg("accent", t),
283
- selectedText: (t) => theme.fg("accent", t),
284
- description: (t) => theme.fg("muted", t),
285
- scrollInfo: (t) => theme.fg("dim", t),
286
- noMatch: (t) => theme.fg("warning", t),
287
- });
288
- list.onSelect = (item) => done(item.value);
289
- list.onCancel = () => done(null);
290
- container.addChild(list);
291
- container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
292
- return {
293
- render: (w) => container.render(w),
294
- invalidate: () => container.invalidate(),
295
- handleInput: (data) => { list.handleInput(data); tui.requestRender(); },
296
- };
297
- }, { overlay: true });
298
-
299
- if (!pickedMod || pickedMod === "back") break;
300
-
301
- if (pickedMod === "delete") {
302
- if (agentName === "orchestrator") {
303
- ctx.ui.notify("Cannot delete orchestrator", "error");
304
- } else {
305
- delete currentProfile.agents[agentName];
306
- await writeJson(PROFILES_PATH, profiles);
307
- ctx.ui.notify(`Removed ${agentName}`, "success");
308
- break;
309
- }
310
- } else if (pickedMod === "model") {
311
- const newModel = await promptInput(ctx, `Model for ${agentName}:`, conf.model);
312
- if (newModel !== null) {
313
- conf.model = newModel;
314
- await writeJson(PROFILES_PATH, profiles);
315
- }
316
- } else if (pickedMod === "thinking") {
317
- const thinkingLevels = ["low", "medium", "high", "xhigh", "max"].map(t => ({ value: t, label: t }));
318
- const newThinking = await ctx.ui.custom<string | null>((tui, theme, _kb, done) => {
319
- const container = new Container();
320
- container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
321
- container.addChild(new Text(theme.fg("accent", theme.bold(`Thinking for ${agentName}`)), 1, 0));
322
- const list = new SelectList(thinkingLevels, 5, {
323
- selectedPrefix: (t) => theme.fg("accent", t),
324
- selectedText: (t) => theme.fg("accent", t),
325
- description: (t) => theme.fg("muted", t),
326
- scrollInfo: (t) => theme.fg("dim", t),
327
- noMatch: (t) => theme.fg("warning", t),
328
- });
329
- list.onSelect = (item) => done(item.value);
330
- list.onCancel = () => done(null);
331
- container.addChild(list);
332
- container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
333
- return {
334
- render: (w) => container.render(w),
335
- invalidate: () => container.invalidate(),
336
- handleInput: (data) => { list.handleInput(data); tui.requestRender(); },
337
- };
338
- }, { overlay: true });
339
-
340
- if (newThinking) {
341
- conf.thinking = newThinking;
342
- await writeJson(PROFILES_PATH, profiles);
343
- }
344
- }
345
- }
346
- }
347
- }
348
- }
349
- }
350
- },
351
- });
352
- }
1
+ export { default } from "./src/extension.js";
package/package.json CHANGED
@@ -1,20 +1,53 @@
1
1
  {
2
- "name": "pi-profiles-manager",
3
- "version": "1.0.1",
4
- "description": "Interactive SDD model profile management built natively for the Pi Coding Agent.",
5
- "main": "index.ts",
6
- "type": "module",
7
- "keywords": [
8
- "pi-agent-extension",
9
- "pi-package"
10
- ],
11
- "pi": {
12
- "extensions": ["index.ts"]
13
- },
14
- "author": "javinnav",
15
- "license": "MIT",
16
- "repository": {
17
- "type": "git",
18
- "url": "https://github.com/javinnav/pi-profiles-manager.git"
19
- }
20
- }
2
+ "name": "pi-profiles-manager",
3
+ "version": "1.1.1",
4
+ "description": "Interactive SDD model profile management built natively for the Pi Coding Agent.",
5
+ "main": "index.ts",
6
+ "type": "module",
7
+ "files": [
8
+ "index.ts",
9
+ "src"
10
+ ],
11
+ "scripts": {
12
+ "dev": "npm run build -- --watch",
13
+ "build": "tsup index.ts --format esm --clean",
14
+ "typecheck": "tsc --noEmit",
15
+ "test": "vitest run",
16
+ "test:watch": "vitest",
17
+ "lint": "biome check .",
18
+ "format": "biome format --write .",
19
+ "package-check": "node -e \"const pkg = require('./package.json'); if (!pkg.pi || !pkg.pi.extensions) { console.error('Missing pi.extensions in package.json'); process.exit(1); }\""
20
+ },
21
+ "engines": {
22
+ "node": ">=22.19.0"
23
+ },
24
+ "keywords": [
25
+ "pi-agent-extension",
26
+ "pi-package"
27
+ ],
28
+ "pi": {
29
+ "extensions": [
30
+ "index.ts"
31
+ ]
32
+ },
33
+ "author": "javinnav",
34
+ "license": "MIT",
35
+ "homepage": "https://github.com/javinnav/pi-profiles-manager#readme",
36
+ "bugs": {
37
+ "url": "https://github.com/javinnav/pi-profiles-manager/issues"
38
+ },
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/javinnav/pi-profiles-manager.git"
42
+ },
43
+ "dependencies": {
44
+ "@earendil-works/pi-coding-agent": "*",
45
+ "@earendil-works/pi-tui": "*"
46
+ },
47
+ "devDependencies": {
48
+ "@biomejs/biome": "^2.5.11",
49
+ "tsup": "^8.5.1",
50
+ "typescript": "^7.0.2",
51
+ "vitest": "^4.1.11"
52
+ }
53
+ }