@matyah00/openpi 0.1.3 → 0.1.4

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 haytamAroui
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,14 +1,54 @@
1
- # openpi
1
+ <div align="center">
2
+ <h1>OpenPi</h1>
3
+ <p><strong>A Pi-native command, skill, agent, workflow, and theme pack for serious coding sessions.</strong></p>
4
+ <p>
5
+ <a href="https://www.npmjs.com/package/@matyah00/openpi"><img src="https://img.shields.io/npm/v/@matyah00/openpi.svg" alt="npm version"></a>
6
+ <a href="https://github.com/haytamAroui/OpenPi/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="license"></a>
7
+ <a href="https://github.com/haytamAroui/OpenPi"><img src="https://img.shields.io/badge/Pi-native-111827" alt="Pi native"></a>
8
+ <a href="https://www.npmjs.com/package/@matyah00/openpi"><img src="https://img.shields.io/badge/install-pi%20install%20npm%3A%40matyah00%2Fopenpi-0f766e" alt="Pi install"></a>
9
+ </p>
10
+ <p>
11
+ <a href="#the-core-idea">Core Idea</a>
12
+ <a href="#install">Install</a>
13
+ <a href="#quick-start">Quick Start</a>
14
+ <a href="#what-you-get">What You Get</a>
15
+ <a href="#profiles">Profiles</a>
16
+ <a href="#commands">Commands</a>
17
+ <a href="#agent-workflows">Agents</a>
18
+ <a href="#tooling">Tools</a>
19
+ <a href="#architecture">Architecture</a>
20
+ </p>
21
+ </div>
2
22
 
3
- Pi-native package for reusable commands, skills, agents, and workflows.
23
+ ---
4
24
 
5
- This package uses Pi-owned paths only:
25
+ ## The Core Idea
26
+
27
+ **OpenPi turns Pi into a richer engineering environment without copying another assistant's file layout.**
28
+
29
+ It uses Pi-owned package surfaces:
6
30
 
7
31
  - `prompts/*.md` for slash-command prompt templates
8
32
  - `skills/*/SKILL.md` for Pi skills
9
- - `agents/*.md` for system prompts and specialist agents
10
- - `agents/teams.yaml` for multi-agent dispatcher teams
11
- - `agents/agent-chain.yaml` for sequential workflows
33
+ - `agents/*.md` for specialist role prompts
34
+ - `extensions/*.ts` for native Pi commands, tools, profile switching, themes, and workflow orchestration
35
+ - `themes/*.json` for terminal UI themes
36
+
37
+ ```text
38
+ Plain Pi session OpenPi session
39
+
40
+ Manual project scan /prime and /explore orient the session
41
+ Ad hoc planning /spec, /clarify, /blueprint, /debate
42
+ One long assistant context spawn_agents delegates to role agents
43
+ Manual grep/tree work project_tree and code_search_batch
44
+ Manual safety checks secret_scan, env_scan, ghost_test_scan
45
+ Lost session reasoning /snapshot, goal_state, write_snapshot
46
+ One profile for every task /openpi use <profile>
47
+ ```
48
+
49
+ OpenPi is not a project template. It is a reusable Pi package you install once and activate per project or globally.
50
+
51
+ ---
12
52
 
13
53
  ## Install
14
54
 
@@ -22,96 +62,318 @@ For Pi:
22
62
  pi install npm:@matyah00/openpi
23
63
  ```
24
64
 
25
- Restart Pi after install.
65
+ Restart Pi after install, then activate a profile:
66
+
67
+ ```text
68
+ /openpi use full
69
+ /reload
70
+ ```
71
+
72
+ The npm names `openpi` and `open-pi` are blocked by npm's package-name similarity policy, so the public npm package is:
73
+
74
+ ```text
75
+ @matyah00/openpi
76
+ ```
77
+
78
+ ---
79
+
80
+ ## Quick Start
81
+
82
+ ```text
83
+ /openpi list show every profile
84
+ /openpi use commands enable prompt commands and core tools
85
+ /openpi use workflow enable /add, /fix, /review and spawn_agents
86
+ /openpi use guard enable security, dependency, and ship gates
87
+ /openpi use full enable the broad OpenPi surface
88
+ /openpi clear remove OpenPi-managed extensions
89
+ ```
90
+
91
+ Use `--global` to install the selected profile into your global Pi settings:
92
+
93
+ ```text
94
+ /openpi use full --global
95
+ ```
96
+
97
+ Profiles update `.pi/settings.json`. Run `/reload` or restart Pi after switching.
98
+
99
+ ---
100
+
101
+ ## What You Get
102
+
103
+ ```text
104
+ OpenPi
105
+ 13 profiles choose a focused runtime surface per task
106
+ 19 prompt commands /prime, /blueprint, /deep, /ship, /parallel, ...
107
+ 36 agent prompts planner, reviewer, tester, security-auditor, Pi experts
108
+ 7 skills ultrathink, test-first, security-guard, bowser, ...
109
+ 11 themes tokyo-night, rose-pine, gruvbox, nord, dracula, ...
110
+ Native tools search, audit, state, snapshot, dispatch, chains
111
+ ```
112
+
113
+ OpenPi is designed around one practical rule: load only the surface you need.
114
+
115
+ | Need | Profile | What it adds |
116
+ |------|---------|--------------|
117
+ | Slash commands | `commands` | Prompt commands, search tools, state tools, theme switching |
118
+ | Explore a repo | `explore` | Tree/search/discovery workflows |
119
+ | Implement work | `workflow` | `/add`, `/fix`, `/review`, `spawn_agents` |
120
+ | Pre-ship safety | `guard` | Security, dependency, environment, and test-integrity tools |
121
+ | Focused UI | `focus` | Minimal Pi UI plus theme defaults |
122
+ | Session purpose | `purpose` | Keeps task purpose visible while working |
123
+ | Metrics | `metrics` | Model, context, branch, token, cost, and tool counters |
124
+ | Personas | `system` | Switch active system persona from bundled agents |
125
+ | Teams | `team` | Dispatcher-only role team with `dispatch_agent` |
126
+ | Chains | `chain` | Sequential workflow runner with `run_chain` |
127
+ | Everything | `full` | Commands, workflow, safety, metrics, personas, teams, chains |
128
+
129
+ ---
130
+
131
+ ## Commands
132
+
133
+ OpenPi prompt commands are markdown templates with frontmatter metadata. They are loaded from the package and can be extended by project-local `.pi/prompts/*.md`.
134
+
135
+ ### Planning and reasoning
136
+
137
+ | Command | Purpose |
138
+ |---------|---------|
139
+ | `/prime` | Load project orientation before work |
140
+ | `/spec` | Turn a vague feature into a structured spec |
141
+ | `/clarify` | Ask focused clarification questions before coding |
142
+ | `/blueprint` | Build an implementation blueprint before risky work |
143
+ | `/debate` | Run adversarial decision analysis for tradeoffs |
144
+ | `/deep` | Use deeper evidence-led reasoning for hard bugs or architecture |
145
+ | `/plan-team` | Create a team-oriented implementation plan |
146
+
147
+ ### Discovery and state
148
+
149
+ | Command | Purpose |
150
+ |---------|---------|
151
+ | `/explore` | Map relevant files and code paths |
152
+ | `/goal` | Manage durable task goals in `.pi/memory/goals.md` |
153
+ | `/snapshot` | Save a continuation checkpoint before compaction |
154
+ | `/compress` | Compress current task context into a handoff brief |
155
+ | `/commands` | List loaded OpenPi and project prompt commands |
156
+ | `/command:status` | Show command loader status and collisions |
157
+
158
+ ### Quality and release
159
+
160
+ | Command | Purpose |
161
+ |---------|---------|
162
+ | `/code-review` | Review code or changes for bugs and missing tests |
163
+ | `/test` | Select or design focused validation |
164
+ | `/validate` | Run targeted validation for current work |
165
+ | `/deps` | Audit dependency manifests, lockfiles, pins, and update risk |
166
+ | `/ghost-test` | Detect vacuous tests and reward-hacking test patterns |
167
+ | `/sentinel` | Read-only security and environment safety scan |
168
+ | `/ship` | Pre-ship gate for tests, security, dependency risk, and git readiness |
169
+ | `/parallel` | Plan safe parallel work with ownership checks |
170
+
171
+ ---
172
+
173
+ ## Agent Workflows
174
+
175
+ The `workflow` profile adds high-level commands that prompt Pi to use role agents and verification steps.
176
+
177
+ ```text
178
+ /add <feature> discover files -> plan -> edit -> test -> review
179
+ /fix <bug> reproduce/inspect -> plan -> patch -> validate -> review
180
+ /review [scope] read-only review over diff or requested scope
181
+ /openpi-agents list available role agents
182
+ ```
183
+
184
+ OpenPi role agents include:
185
+
186
+ | Agent | Role |
187
+ |-------|------|
188
+ | `file-picker` | Finds relevant files and line ranges before edits |
189
+ | `planner` | Produces scoped implementation plans |
190
+ | `editor` | Performs isolated edits when the plan is clear |
191
+ | `tester` | Selects and runs targeted validation |
192
+ | `reviewer` | Reviews diffs for bugs, regressions, and missing tests |
193
+ | `security-auditor` | Checks secrets, risky automation, and security-sensitive changes |
194
+ | `problem-architect` | Turns ambiguous work into a concrete team spec |
195
+ | `spec-reviewer` | Challenges unclear requirements before implementation |
196
+ | `ship-guard` | Reviews release readiness |
197
+ | `red-team` | Challenges plans and assumptions |
198
+
199
+ The `spawn_agents` tool can run agents sequentially or in parallel as isolated Pi subprocesses. It returns structured outputs: files, line ranges, commands, exact validation output, findings, and assumptions.
200
+
201
+ ---
26
202
 
27
- The npm names `openpi` and `open-pi` are blocked by npm's package-name
28
- similarity policy, so this package uses the public scoped name
29
- `@matyah00/openpi`.
203
+ ## Team and Chain Modes
30
204
 
31
- ## Profiles
205
+ OpenPi has two orchestration modes for larger work.
32
206
 
33
- Use `/openpi` to list available profiles.
207
+ ### Team dispatcher
34
208
 
35
209
  ```text
36
- /openpi use commands
37
- /openpi use explore
38
- /openpi use guard
39
- /openpi use workflow
40
- /openpi use system
41
210
  /openpi use team
211
+ /agents-list
212
+ /agents-team guard
213
+ ```
214
+
215
+ The `team` profile registers `dispatch_agent` and uses teams from `agents/teams.yaml`, including:
216
+
217
+ | Team | Agents |
218
+ |------|--------|
219
+ | `research` | scout, directory-lister, glob-matcher, code-searcher, librarian, documenter, red-team |
220
+ | `validation` | tester, basher, reviewer |
221
+ | `guard` | security-auditor, rule-verifier, ship-guard, spec-reviewer |
222
+ | `frontend` | scout, frontend, reviewer |
223
+ | `backend` | scout, backend, reviewer |
224
+ | `pi-pi` | Pi package, extension, skill, prompt, config, theme, TUI, CLI, and keybinding experts |
225
+
226
+ ### Chain runner
227
+
228
+ ```text
42
229
  /openpi use chain
43
- /openpi use full
44
- /openpi clear
230
+ /chain-list
45
231
  ```
46
232
 
47
- Profiles write selected extension paths into the current project's `.pi/settings.json`.
48
- Run `/reload` or restart Pi after changing profiles.
233
+ The `chain` profile registers `run_chain` for sequential workflows from `agents/agent-chain.yaml`:
49
234
 
50
- ## Commands
235
+ | Chain | Flow |
236
+ |-------|------|
237
+ | `plan-build-review` | planner -> builder -> reviewer |
238
+ | `research-plan` | scout -> red-team -> planner |
239
+ | `deep-explore` | directory-lister -> code-searcher -> thinker |
240
+ | `evidence-validate` | code-searcher -> tester -> reviewer |
241
+ | `spec-to-plan` | problem-architect -> spec-reviewer -> planner |
242
+ | `ship-gate` | security-auditor -> ship-guard -> reviewer |
243
+ | `pi-package-design` | Pi experts -> planner |
244
+
245
+ ---
246
+
247
+ ## Tooling
248
+
249
+ OpenPi registers native Pi tools through profiles.
250
+
251
+ | Tool | Purpose |
252
+ |------|---------|
253
+ | `project_tree` | Return a scoped project tree with ignore handling |
254
+ | `code_search_batch` | Run multiple code searches in one call |
255
+ | `env_scan` | Detect stack, package managers, scripts, and environment clues |
256
+ | `secret_scan` | Search for common secret and credential patterns |
257
+ | `ghost_test_scan` | Find weak, vacuous, or reward-hacked tests |
258
+ | `dependency_inventory` | Summarize dependency manifests and lockfiles |
259
+ | `session_state` | Read current session state |
260
+ | `goal_state` | Read goal memory state |
261
+ | `write_snapshot` | Write a continuation snapshot |
262
+ | `parallel_safety_check` | Check file ownership overlap before parallel work |
263
+ | `spawn_agents` | Run role agents as isolated Pi subprocesses |
264
+ | `dispatch_agent` | Dispatch to the active specialist team |
265
+ | `run_chain` | Run a named sequential agent chain |
266
+
267
+ ---
268
+
269
+ ## Skills
270
+
271
+ OpenPi ships focused Pi skills:
272
+
273
+ | Skill | Use it for |
274
+ |-------|------------|
275
+ | `ultrathink` | Hard debugging, architecture tradeoffs, root-cause analysis |
276
+ | `test-first` | Production code, bug fixes, refactors, validation planning |
277
+ | `security-guard` | Credentials, env files, external code, automation, deploy risk |
278
+ | `spec-driven` | Vague requirements, unclear features, acceptance criteria |
279
+ | `session-continuity` | Long context, resuming, stopping, compaction handoffs |
280
+ | `env-scanner` | Unknown repos, setup issues, stack detection |
281
+ | `bowser` | Playwright-powered browser automation and UI testing |
282
+
283
+ ---
284
+
285
+ ## Themes and UI Profiles
51
286
 
52
- When the `commands` profile is active:
287
+ OpenPi includes 11 bundled themes:
53
288
 
54
289
  ```text
55
- /prime
56
- /blueprint
57
- /code-review
58
- /explore
59
- /deep
60
- /compress
61
- /goal
62
- /snapshot
63
- /parallel
64
- /validate
65
- /clarify
66
- /spec
67
- /debate
68
- /deps
69
- /ghost-test
70
- /sentinel
71
- /ship
72
- /commands
73
- /command:status
74
- ```
75
-
76
- The `commands`, `explore`, `workflow`, and `full` profiles also register:
290
+ catppuccin-mocha cyberpunk dracula everforest gruvbox
291
+ midnight-ocean nord ocean-breeze rose-pine
292
+ synthwave tokyo-night
293
+ ```
294
+
295
+ Use the theme profile or the `full` profile, then:
296
+
297
+ ```text
298
+ /theme
299
+ /theme tokyo-night
300
+ ```
301
+
302
+ UI-oriented profiles:
303
+
304
+ | Profile | Purpose |
305
+ |---------|---------|
306
+ | `focus` | Minimal, distraction-free Pi UI |
307
+ | `metrics` | Status bar and usage metrics |
308
+ | `tool-widget` | Live per-tool usage counts |
309
+ | `purpose` | Session purpose gate and visible task focus |
310
+ | `safety` | Damage-control rules and actionable blocked-tool feedback |
311
+
312
+ ---
313
+
314
+ ## Architecture
315
+
316
+ OpenPi is a package, not a project scaffold.
77
317
 
78
318
  ```text
79
- project_tree
80
- code_search_batch
81
- env_scan
82
- secret_scan
83
- ghost_test_scan
84
- dependency_inventory
85
- session_state
86
- goal_state
87
- write_snapshot
88
- parallel_safety_check
319
+ @matyah00/openpi
320
+ package.json Pi package manifest
321
+ prompts/ prompt commands
322
+ skills/ Pi skills
323
+ agents/ role agents, teams, chains
324
+ extensions/ native Pi extensions and tools
325
+ themes/ terminal UI themes
326
+ damage-control-rules.yaml safety feedback rules
89
327
  ```
90
328
 
91
- When the `workflow` profile is active:
329
+ The activation flow is explicit:
92
330
 
93
331
  ```text
94
- /add <feature>
95
- /fix <bug>
96
- /review [scope]
97
- /openpi-agents
332
+ 1. Install package with Pi
333
+ 2. Run /openpi use <profile>
334
+ 3. OpenPi writes selected extension paths into .pi/settings.json
335
+ 4. Run /reload or restart Pi
336
+ 5. Use only the commands/tools for that profile
98
337
  ```
99
338
 
100
- The workflow profile also registers the `spawn_agents` tool for isolated role-agent
101
- delegation through `file-picker`, `planner`, `editor`, `worker`, `tester`, and
102
- `reviewer` agents. The extended discovery roles include `code-searcher`,
103
- `directory-lister`, `glob-matcher`, `basher`, `thinker`, `context-pruner`, and
104
- `librarian`.
339
+ This keeps OpenPi portable across projects. You can add project-specific prompts and agents in `.pi/prompts` and `.pi/agents`; OpenPi will load them alongside the bundled resources where the active profile supports it.
340
+
341
+ ---
342
+
343
+ ## Why It Is Different
105
344
 
106
- The `guard` profile adds pre-ship and governance workflows inspired by mature
107
- agent environments: spec gates, dependency inventory, secret scanning, test
108
- integrity checks, and ship readiness.
345
+ | Without OpenPi | With OpenPi |
346
+ |----------------|-------------|
347
+ | One generic assistant flow | Profiles for planning, workflow, guard, team, chain, UI |
348
+ | Manual project discovery | `/explore`, `project_tree`, `code_search_batch` |
349
+ | Vague implementation prompts | `/spec`, `/clarify`, `/blueprint`, `problem-architect` |
350
+ | Manual review discipline | `/review`, `reviewer`, `ship-guard`, `ghost_test_scan` |
351
+ | One large context | `spawn_agents`, `dispatch_agent`, `run_chain` |
352
+ | Repeated session loss | `/snapshot`, `write_snapshot`, `goal_state` |
353
+ | Ad hoc security checks | `secret_scan`, `/sentinel`, `security-guard` |
354
+ | Same UI for every task | focus, metrics, purpose, tool-widget, safety profiles |
109
355
 
110
- ## Agents
356
+ ---
357
+
358
+ ## Verified
359
+
360
+ Before publish, the package is checked with:
361
+
362
+ ```bash
363
+ npm pack --dry-run
364
+ npx tsc --noEmit
365
+ npm install @matyah00/openpi@latest --dry-run
366
+ ```
367
+
368
+ Current npm package:
369
+
370
+ ```bash
371
+ npm i @matyah00/openpi
372
+ pi install npm:@matyah00/openpi
373
+ ```
111
374
 
112
- When the `system` profile is active, use `/system` to select a persona.
375
+ ---
113
376
 
114
- When the `team` profile is active, the main agent becomes a dispatcher and can only use
115
- `dispatch_agent` to delegate work to the selected team.
377
+ ## License
116
378
 
117
- When the `chain` profile is active, use `run_chain` for sequential workflows.
379
+ MIT - [haytamAroui](https://github.com/haytamAroui)
@@ -1,7 +1,8 @@
1
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
2
  import { existsSync, readdirSync, readFileSync } from "node:fs";
3
3
  import { basename, join } from "node:path";
4
4
  import { bundledPromptsDir } from "./lib/packagePaths.ts";
5
+ import { arrayField, parseMarkdownFrontmatter, stringField } from "./lib/markdown.ts";
5
6
 
6
7
  type CommandDef = {
7
8
  name: string;
@@ -12,42 +13,6 @@ type CommandDef = {
12
13
  source: string;
13
14
  };
14
15
 
15
- function parseFrontmatter(raw: string): { fields: Record<string, string | string[]>; body: string } {
16
- const match = raw.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
17
- if (!match) return { fields: {}, body: raw };
18
-
19
- const fields: Record<string, string | string[]> = {};
20
- let currentListKey = "";
21
- for (const line of match[1].split("\n")) {
22
- const listMatch = line.match(/^\s+-\s+(.+)$/);
23
- if (listMatch && currentListKey) {
24
- const existing = fields[currentListKey];
25
- fields[currentListKey] = Array.isArray(existing) ? [...existing, listMatch[1].trim()] : [listMatch[1].trim()];
26
- continue;
27
- }
28
-
29
- const idx = line.indexOf(":");
30
- if (idx > 0) {
31
- const key = line.slice(0, idx).trim();
32
- const value = line.slice(idx + 1).trim();
33
- currentListKey = key;
34
- fields[key] = value;
35
- }
36
- }
37
-
38
- return { fields, body: match[2].trim() };
39
- }
40
-
41
- function stringField(value: string | string[] | undefined): string {
42
- return typeof value === "string" ? value : "";
43
- }
44
-
45
- function arrayField(value: string | string[] | undefined): string[] {
46
- if (Array.isArray(value)) return value;
47
- if (typeof value === "string" && value.trim()) return value.split(",").map((item) => item.trim()).filter(Boolean);
48
- return [];
49
- }
50
-
51
16
  function expandArgs(template: string, args: string): string {
52
17
  const parts = args.split(/\s+/).filter(Boolean);
53
18
  let result = template.replace(/\$ARGUMENTS|\$@/g, args);
@@ -64,13 +29,13 @@ function scanPromptDir(dir: string, source: string): CommandDef[] {
64
29
  for (const file of readdirSync(dir)) {
65
30
  if (!file.endsWith(".md")) continue;
66
31
  const raw = readFileSync(join(dir, file), "utf-8");
67
- const { fields, body } = parseFrontmatter(raw);
32
+ const { frontmatter, body } = parseMarkdownFrontmatter(raw);
68
33
  const firstLine = body.split("\n").find((line) => line.trim())?.trim() || "";
69
34
  commands.push({
70
- name: stringField(fields.name) || basename(file, ".md"),
71
- description: stringField(fields.description) || firstLine.slice(0, 120),
72
- category: stringField(fields.category) || "general",
73
- aliases: arrayField(fields.aliases),
35
+ name: stringField(frontmatter.name) || basename(file, ".md"),
36
+ description: stringField(frontmatter.description) || firstLine.slice(0, 120),
37
+ category: stringField(frontmatter.category) || "general",
38
+ aliases: arrayField(frontmatter.aliases),
74
39
  body,
75
40
  source,
76
41
  });
@@ -82,8 +47,8 @@ function scanPromptDir(dir: string, source: string): CommandDef[] {
82
47
  export default function (pi: ExtensionAPI) {
83
48
  const cwd = process.cwd();
84
49
  const commands = [
85
- ...scanPromptDir(bundledPromptsDir, "openpi"),
86
50
  ...scanPromptDir(join(cwd, ".pi", "prompts"), ".pi"),
51
+ ...scanPromptDir(bundledPromptsDir, "openpi"),
87
52
  ];
88
53
  const byName = new Map<string, CommandDef>();
89
54
  const aliases = new Map<string, string>();
@@ -0,0 +1,27 @@
1
+ import { parse as parseYaml } from "yaml";
2
+
3
+ export type FrontmatterValue = string | string[] | number | boolean | null | undefined;
4
+
5
+ export function parseMarkdownFrontmatter<T extends Record<string, FrontmatterValue> = Record<string, FrontmatterValue>>(
6
+ raw: string,
7
+ ): { frontmatter: T; body: string } {
8
+ const match = raw.match(/^---\s*\r?\n([\s\S]*?)\r?\n---\s*\r?\n([\s\S]*)$/);
9
+ if (!match) return { frontmatter: {} as T, body: raw };
10
+
11
+ const parsed = parseYaml(match[1]);
12
+ const frontmatter = parsed && typeof parsed === "object" && !Array.isArray(parsed)
13
+ ? parsed as T
14
+ : {} as T;
15
+
16
+ return { frontmatter, body: match[2].trim() };
17
+ }
18
+
19
+ export function stringField(value: FrontmatterValue): string {
20
+ return typeof value === "string" ? value : "";
21
+ }
22
+
23
+ export function arrayField(value: FrontmatterValue): string[] {
24
+ if (Array.isArray(value)) return value.filter((item): item is string => typeof item === "string");
25
+ if (typeof value === "string" && value.trim()) return value.split(",").map((item) => item.trim()).filter(Boolean);
26
+ return [];
27
+ }
@@ -1,8 +1,9 @@
1
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
2
  import { existsSync, readdirSync, readFileSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
4
  import { basename, join } from "node:path";
5
5
  import { bundledAgentsDir, bundledPiPiAgentsDir } from "./lib/packagePaths.ts";
6
+ import { arrayField, parseMarkdownFrontmatter, stringField } from "./lib/markdown.ts";
6
7
 
7
8
  type AgentDef = {
8
9
  name: string;
@@ -12,28 +13,17 @@ type AgentDef = {
12
13
  source: string;
13
14
  };
14
15
 
15
- function parseFrontmatter(raw: string): { fields: Record<string, string>; body: string } {
16
- const match = raw.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
17
- if (!match) return { fields: {}, body: raw };
18
- const fields: Record<string, string> = {};
19
- for (const line of match[1].split("\n")) {
20
- const idx = line.indexOf(":");
21
- if (idx > 0) fields[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
22
- }
23
- return { fields, body: match[2] };
24
- }
25
-
26
16
  function scanAgents(dir: string, source: string): AgentDef[] {
27
17
  if (!existsSync(dir)) return [];
28
18
  const agents: AgentDef[] = [];
29
19
  for (const file of readdirSync(dir)) {
30
20
  if (!file.endsWith(".md")) continue;
31
21
  const raw = readFileSync(join(dir, file), "utf-8");
32
- const { fields, body } = parseFrontmatter(raw);
22
+ const { frontmatter, body } = parseMarkdownFrontmatter(raw);
33
23
  agents.push({
34
- name: fields.name || basename(file, ".md"),
35
- description: fields.description || "",
36
- tools: fields.tools ? fields.tools.split(",").map((tool) => tool.trim()).filter(Boolean) : [],
24
+ name: stringField(frontmatter.name) || basename(file, ".md"),
25
+ description: stringField(frontmatter.description),
26
+ tools: arrayField(frontmatter.tools),
37
27
  body: body.trim(),
38
28
  source,
39
29
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@matyah00/openpi",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "description": "Pi-native commands, skills, agents, and workflows.",
6
6
  "keywords": [
@@ -28,6 +28,7 @@
28
28
  "themes",
29
29
  "types",
30
30
  "damage-control-rules.yaml",
31
+ "LICENSE",
31
32
  "README.md",
32
33
  "tsconfig.json"
33
34
  ],