pi-skill-shiori 0.6.12 → 0.6.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## [0.6.16] - 2026-10-17
6
+
7
+ ### Changed
8
+
9
+ - Share the retrieval-index contract between standard and expanded retrieval.
10
+
11
+ ## [0.6.15] - 2026-10-16
12
+
13
+ ### Changed
14
+
15
+ - Pin Pi SDK development and smoke-test dependencies to 1.2.0 and guard user-only prompt handling for the new `before_agent_start` message union.
16
+
17
+ ## [0.6.14] - 2026-10-12
18
+
19
+ ### Changed
20
+
21
+ - Pin the Pi SDK development and smoke-test dependencies to 1.1.0.
22
+ - Standardize the documentation directory to lowercase so the Pi SDK smoke report is available at `docs/verification/pi-sdk-smoke.json` on case-sensitive hosts.
23
+
24
+ ## [0.6.13] - 2026-10-05
25
+
26
+ ### Added
27
+
28
+ - Add session-local `/shiori:stats` timing diagnostics for inventory refresh and skill retrieval, with additive JSON counters and no prompt, skill-body, or cross-session analytics storage.
29
+
5
30
  ## [0.6.12] - 2026-09-30
6
31
 
7
32
  ### Changed
@@ -78,14 +103,14 @@ This project follows semantic versioning.
78
103
 
79
104
  ### Changed
80
105
 
81
- - `Docs/recommendation-surface.md` documents the dogfood review outcome and recommendation to keep badges on default surfaces.
106
+ - `docs/recommendation-surface.md` documents the dogfood review outcome and recommendation to keep badges on default surfaces.
82
107
 
83
108
  ## [0.6.1] - 2026-07-01
84
109
 
85
110
  ### Changed
86
111
 
87
112
  - README aligned with Pi OSS minimal-docs policy: badges, required entrypoint sections, Quick start, Package contents, Links.
88
- - Detailed configuration and operational notes moved to `Docs/usage.md`.
113
+ - Detailed configuration and operational notes moved to `docs/usage.md`.
89
114
  - README install examples and status text updated to match package version `0.6.1`.
90
115
 
91
116
  ## [0.6.0] - 2026-06-29
@@ -93,7 +118,7 @@ This project follows semantic versioning.
93
118
  ### Added
94
119
 
95
120
  - Bounded recommendation reason badges (`[trigger]`, `[description]`, `[low match]`) on recommended skills in auto-inject, `/shiori:recommend`, and `shiori_recommend` surfaces.
96
- - `Docs/recommendation-surface.md` describing badge vocabulary, a multi-skill example, and how badges relate to Always Visible Skill policy and recommendation metrics.
121
+ - `docs/recommendation-surface.md` describing badge vocabulary, a multi-skill example, and how badges relate to Always Visible Skill policy and recommendation metrics.
97
122
  - Tests for reason present, absent, low-confidence, and badge formatting.
98
123
 
99
124
  ### Changed
package/README.md CHANGED
@@ -19,7 +19,7 @@ For maintenance priorities and the phased plan, see [`ROADMAP.md`](ROADMAP.md).
19
19
 
20
20
  Pi Skill Shiori is a [Pi](https://pi.dev/) package for vaults or projects with many Agent Skills where the default catalog becomes noisy and expensive. It hides the normal skill catalog when safe, recommends triggerable skills with compact summaries, and loads full `SKILL.md` bodies on demand.
21
21
 
22
- `0.6.12` is the current working release. Prompt-boundary suppression is intentionally conservative: if Shiori cannot safely recognize a catalog boundary, it leaves the prompt untouched and warns instead of deleting too much.
22
+ `0.6.16` is the current working release. Prompt-boundary suppression is intentionally conservative: if Shiori cannot safely recognize a catalog boundary, it leaves the prompt untouched and warns instead of deleting too much.
23
23
 
24
24
  ## Features
25
25
 
@@ -31,13 +31,14 @@ Pi Skill Shiori is a [Pi](https://pi.dev/) package for vaults or projects with m
31
31
  - **SQLite FTS retrieval** — indexes skill names, descriptions, and policy triggers with `node:sqlite` + FTS5, with token-match fallback.
32
32
  - **Recommendation reason badges** — compact `[trigger]`, `[description]`, and `[low match]` labels on surfaced skills.
33
33
  - **Session-local feedback metrics** — `/shiori:stats` tracks offers, loads, and follow-through without storing prompts.
34
+ - **Timing diagnostics** — `/shiori:stats` separates inventory refresh and retrieval cost without storing prompts, skill bodies, or cross-session analytics.
34
35
 
35
36
  ## Install
36
37
 
37
38
  ### Global install from npm
38
39
 
39
40
  ```bash
40
- pi install npm:pi-skill-shiori@0.6.12
41
+ pi install npm:pi-skill-shiori@0.6.16
41
42
  ```
42
43
 
43
44
  Without a version pin:
@@ -49,7 +50,7 @@ pi install npm:pi-skill-shiori
49
50
  ### Project-local install from npm
50
51
 
51
52
  ```bash
52
- pi install -l npm:pi-skill-shiori@0.6.12
53
+ pi install -l npm:pi-skill-shiori@0.6.16
53
54
  ```
54
55
 
55
56
  Without a version pin:
@@ -61,7 +62,7 @@ pi install -l npm:pi-skill-shiori
61
62
  ### Install from GitHub
62
63
 
63
64
  ```bash
64
- pi install git:github.com/eiei114/pi-skill-shiori@v0.6.12
65
+ pi install git:github.com/eiei114/pi-skill-shiori@v0.6.16
65
66
  pi install -l git:github.com/eiei114/pi-skill-shiori
66
67
  ```
67
68
 
@@ -81,7 +82,7 @@ pi install -l /absolute/path/to/pi-skill-shiori
81
82
  ## Quick start
82
83
 
83
84
  1. Install the package (see [Install](#install)).
84
- 2. Create `.pi/skill-shiori.yml` in your Pi project (see [Docs/usage.md](Docs/usage.md#configure)).
85
+ 2. Create `.pi/skill-shiori.yml` in your Pi project (see [docs/usage.md](docs/usage.md#configure)).
85
86
  3. Start Pi and run `/shiori:doctor` to confirm policy path, indexed roots, and suppression status.
86
87
  4. Use `/shiori:bootstrap` to write `.pi/skill-shiori.generated.yml`, review it, copy needed sections into `.pi/skill-shiori.yml`, then run `/shiori:reload`.
87
88
  5. Ask the agent for help; Shiori injects compact candidates or use `/shiori:recommend` for an interactive flow.
@@ -112,17 +113,17 @@ shiori_load_skill({ skill: "reddit-research" })
112
113
 
113
114
  `shiori_load_skill` loads one skill by name. The Pi UI shows a compact result like `✓ Loaded reddit-research (6.3KB)`.
114
115
 
115
- Recommended skills show compact reason badges (`[trigger]`, `[description]`, `[low match]`). See [`Docs/recommendation-surface.md`](Docs/recommendation-surface.md) for badge vocabulary and metrics interplay.
116
+ Recommended skills show compact reason badges (`[trigger]`, `[description]`, `[low match]`). See [`docs/recommendation-surface.md`](docs/recommendation-surface.md) for badge vocabulary and metrics interplay.
116
117
 
117
- For inventory roots, policy YAML, suppression statuses, and feedback metrics, see [`Docs/usage.md`](Docs/usage.md).
118
+ For inventory roots, policy YAML, suppression statuses, and feedback metrics, see [`docs/usage.md`](docs/usage.md).
118
119
 
119
120
  ## Package contents
120
121
 
121
122
  | Path | Purpose |
122
123
  |---|---|
123
124
  | `src/` | Pi extension entrypoint and Shiori runtime |
124
- | `Docs/usage.md` | Configuration, inventory, suppression, and metrics details |
125
- | `Docs/recommendation-surface.md` | Recommendation badges and surface behavior |
125
+ | `docs/usage.md` | Configuration, inventory, suppression, and metrics details |
126
+ | `docs/recommendation-surface.md` | Recommendation badges and surface behavior |
126
127
  | `ROADMAP.md` | Maintenance direction and phased plan |
127
128
  | `CHANGELOG.md` | Version history |
128
129
  | `SECURITY.md` | Vulnerability reporting |
@@ -167,6 +167,17 @@ command: offers=1, loaded=2, zeroHits=1
167
167
  top skills: auth-helper(1/2), deploy-kit(1/1)
168
168
  ```
169
169
 
170
+ ## Timing diagnostics
171
+
172
+ Run `/shiori:stats` to see a compact timing line alongside the existing recommendation feedback summary. The line separates inventory refresh cost from retrieval cost; the JSON payload exposes the same machine-readable counters under `timing.inventoryRefresh` and `timing.retrieval`:
173
+
174
+ - `count` — successful operations recorded in the current session
175
+ - `lastMs` — duration of the most recent successful operation
176
+ - `totalMs` — cumulative duration of successful operations
177
+ - `maxMs` — longest successful operation
178
+
179
+ Inventory refresh timing covers the successful session-start, manual `/shiori:reload`, and automatic refresh rebuilds. Retrieval timing covers successful auto-injection, `shiori_recommend`, and `/shiori:recommend` retrievals. These are in-memory, session-local diagnostics: Shiori does not store prompt text, full queries, skill bodies, or cross-session analytics.
180
+
170
181
  ## Agent tools
171
182
 
172
183
  Shiori registers two tools the agent can call after candidates appear:
@@ -0,0 +1,110 @@
1
+ {
2
+ "schema_version": 1,
3
+ "verification_contract": "pi-sdk-behavior-v2",
4
+ "sdk_versions": {
5
+ "@earendil-works/pi-agent-core": "1.2.0",
6
+ "@earendil-works/pi-ai": "1.2.0",
7
+ "@earendil-works/pi-coding-agent": "1.2.0",
8
+ "@earendil-works/pi-tui": "1.2.0"
9
+ },
10
+ "platform": "win32",
11
+ "model_inference": "none; actual Pi host executes local Shiori slash commands in RPC mode",
12
+ "boundary": "The Pi SDK host, extension loader, extension lifecycle and Shiori commands are real. The deterministic smoke skill is filesystem input; no provider/model call or mocked host is used. Pi settings, home and temp paths are isolated and removed after the checks.",
13
+ "unhandled_errors": 0,
14
+ "extension_load_errors": 0,
15
+ "cases": [
16
+ {
17
+ "name": "extension_load",
18
+ "command": "npm run smoke:pi (actual locally pinned Pi RPC host and Shiori extension)",
19
+ "exit_code": 0,
20
+ "passed": true,
21
+ "evidence": "[extension_load] Pi SDK 1.2.0\n[first process stdout]\n{\"id\":\"load-commands\",\"type\":\"response\",\"command\":\"get_commands\",\"success\":true,\"data\":{\"commands\":[{\"name\":\"shiori:doctor\",\"description\":\"Show Pi Skill Shiori configuration and inventory status\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}},{\"name\":\"shiori:reload\",\"description\":\"Rebuild Pi Skill Shiori inventory and optional Pi runtime resources\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}},{\"name\":\"shiori:recommend\",\"description\":\"Recommend Agent Skills for a natural-language task and queue it for the agent\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}},{\"name\":\"shiori:stats\",\"description\":\"Show Pi Skill Shiori operational counters and recommendation feedback\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}},{\"name\":\"shiori:bootstrap\",\"description\":\"Generate a review policy draft from discovered skill descriptions\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}}]}}\n\n[first process stderr]\n",
22
+ "assertions": [
23
+ {
24
+ "id": "extension_registered",
25
+ "expected": "The actual Pi host lists Shiori's registered doctor command.",
26
+ "observed": "{\"commands\":[\"shiori:doctor\",\"shiori:reload\",\"shiori:recommend\",\"shiori:stats\",\"shiori:bootstrap\"]}",
27
+ "passed": true
28
+ }
29
+ ]
30
+ },
31
+ {
32
+ "name": "happy_path",
33
+ "command": "npm run smoke:pi (actual locally pinned Pi RPC host and Shiori extension)",
34
+ "exit_code": 0,
35
+ "passed": true,
36
+ "evidence": "[happy_path] Pi SDK 1.2.0\n[first process stdout]\n{\"id\":\"happy-commands\",\"type\":\"response\",\"command\":\"get_commands\",\"success\":true,\"data\":{\"commands\":[{\"name\":\"shiori:doctor\",\"description\":\"Show Pi Skill Shiori configuration and inventory status\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}},{\"name\":\"shiori:reload\",\"description\":\"Rebuild Pi Skill Shiori inventory and optional Pi runtime resources\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}},{\"name\":\"shiori:recommend\",\"description\":\"Recommend Agent Skills for a natural-language task and queue it for the agent\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}},{\"name\":\"shiori:stats\",\"description\":\"Show Pi Skill Shiori operational counters and recommendation feedback\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}},{\"name\":\"shiori:bootstrap\",\"description\":\"Generate a review policy draft from discovered skill descriptions\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}}]}}\n{\"type\":\"extension_ui_request\",\"id\":\"<RPC_ID>\",\"method\":\"input\",\"title\":\"What do you want help with?\",\"placeholder\":\"Describe your task in natural language…\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<RPC_ID>\",\"method\":\"select\",\"title\":\"How should Shiori use these recommendations?\",\"options\":[\"Pre-load matches and queue task for agent\",\"Review recommendations only\",\"Cancel\"]}\n{\"type\":\"extension_ui_request\",\"id\":\"<RPC_ID>\",\"method\":\"notify\",\"message\":\"Matched 1 skill(s): smoke-skill [trigger]\",\"notifyType\":\"info\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<RPC_ID>\",\"method\":\"select\",\"title\":\"Follow up on a recommendation:\",\"options\":[\"smoke-skill [trigger]\",\"Done\"]}\n{\"id\":\"happy-prompt\",\"type\":\"response\",\"command\":\"prompt\",\"success\":true,\"data\":{\"disposition\":\"handled\"}}\n\n[first process stderr]\n",
37
+ "assertions": [
38
+ {
39
+ "id": "feature_response",
40
+ "expected": "Pi handles Shiori's interactive recommendation command and the real extension returns its discovered smoke-skill.",
41
+ "observed": "{\"response\":{\"id\":\"happy-prompt\",\"type\":\"response\",\"command\":\"prompt\",\"success\":true,\"data\":{\"disposition\":\"handled\"}},\"notification\":{\"type\":\"extension_ui_request\",\"id\":\"<RPC_ID>\",\"method\":\"notify\",\"message\":\"Matched 1 skill(s): smoke-skill [trigger]\",\"notifyType\":\"info\"}}",
42
+ "passed": true
43
+ }
44
+ ]
45
+ },
46
+ {
47
+ "name": "error_path",
48
+ "command": "npm run smoke:pi (actual locally pinned Pi RPC host and Shiori extension)",
49
+ "exit_code": 0,
50
+ "passed": true,
51
+ "evidence": "[error_path] Pi SDK 1.2.0\n[first process stdout]\n{\"id\":\"error-commands\",\"type\":\"response\",\"command\":\"get_commands\",\"success\":true,\"data\":{\"commands\":[{\"name\":\"shiori:doctor\",\"description\":\"Show Pi Skill Shiori configuration and inventory status\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}},{\"name\":\"shiori:reload\",\"description\":\"Rebuild Pi Skill Shiori inventory and optional Pi runtime resources\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}},{\"name\":\"shiori:recommend\",\"description\":\"Recommend Agent Skills for a natural-language task and queue it for the agent\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}},{\"name\":\"shiori:stats\",\"description\":\"Show Pi Skill Shiori operational counters and recommendation feedback\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}},{\"name\":\"shiori:bootstrap\",\"description\":\"Generate a review policy draft from discovered skill descriptions\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}}]}}\n{\"type\":\"extension_ui_request\",\"id\":\"<RPC_ID>\",\"method\":\"input\",\"title\":\"What do you want help with?\",\"placeholder\":\"Describe your task in natural language…\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<RPC_ID>\",\"method\":\"notify\",\"message\":\"Recommendation cancelled.\",\"notifyType\":\"info\"}\n{\"id\":\"error-prompt\",\"type\":\"response\",\"command\":\"prompt\",\"success\":true,\"data\":{\"disposition\":\"handled\"}}\n\n[first process stderr]\n",
52
+ "assertions": [
53
+ {
54
+ "id": "handled_error",
55
+ "expected": "Cancelling Shiori's actual RPC input dialog is handled with its cancellation notice and no model call.",
56
+ "observed": "{\"response\":{\"id\":\"error-prompt\",\"type\":\"response\",\"command\":\"prompt\",\"success\":true,\"data\":{\"disposition\":\"handled\"}},\"cancelled\":{\"type\":\"extension_ui_request\",\"id\":\"<RPC_ID>\",\"method\":\"notify\",\"message\":\"Recommendation cancelled.\",\"notifyType\":\"info\"}}",
57
+ "passed": true
58
+ }
59
+ ]
60
+ },
61
+ {
62
+ "name": "reload_cleanup",
63
+ "command": "npm run smoke:pi (actual locally pinned Pi RPC host and Shiori extension)",
64
+ "exit_code": 0,
65
+ "passed": true,
66
+ "evidence": "[reload sequence]\nfirst process:\n reload-stats-before: success=true disposition=handled\n reload-inventory: success=true disposition=handled\n reload-stats-after: success=true disposition=handled\n reload-recommend: success=true disposition=handled\n firstExitCode=0\nsecond process:\n reload-recommend: success=true disposition=handled\n secondExitCode=0\n[reload_cleanup] Pi SDK 1.2.0\n[first process stdout]\n{\"id\":\"reload-first-commands\",\"type\":\"response\",\"command\":\"get_commands\",\"success\":true,\"data\":{\"commands\":[{\"name\":\"shiori:doctor\",\"description\":\"Show Pi Skill Shiori configuration and inventory status\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}},{\"name\":\"shiori:reload\",\"description\":\"Rebuild Pi Skill Shiori inventory and optional Pi runtime resources\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}},{\"name\":\"shiori:recommend\",\"description\":\"Recommend Agent Skills for a natural-language task and queue it for the agent\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}},{\"name\":\"shiori:stats\",\"description\":\"Show Pi Skill Shiori operational counters and recommendation feedback\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}},{\"name\":\"shiori:bootstrap\",\"description\":\"Generate a review policy draft from discovered skill descriptions\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}}]}}\n{\"type\":\"extension_ui_request\",\"id\":\"<RPC_ID>\",\"method\":\"notify\",\"message\":\"Recommendation feedback (session-local, no prompts stored)\\noffers: 0 | candidate slots: 0 | loaded: 0\\nabandoned: 0 | zero-candidate queries: 0 | follow-through: n/a\\nTiming (session-local) | inventory refresh: count=1 last=<DURATION> total=<DURATION> max=<DURATION> | retrieval: count=0 last=<DURATION> total=<DURATION> max=<DURATION>\\n\\n{\\n \\\"inventoryCount\\\": 1,\\n \\\"duplicateCount\\\": 0,\\n \\\"candidateHitCount\\\": 0,\\n \\\"zeroCandidateCount\\\": 0,\\n \\\"loadedSkillCount\\\": 0,\\n \\\"explicitInvocationCount\\\": 0,\\n \\\"retrievalBackend\\\": \\\"sqlite-fts\\\",\\n \\\"suppressionStatus\\\": \\\"disabled\\\",\\n \\\"inventoryRefreshCount\\\": 1,\\n \\\"inventoryAutoRefreshCount\\\": 0,\\n \\\"timing\\\": {\\n \\\"inventoryRefresh\\\": {\\n \\\"lastMs\\\": \"<DURATION>\",\\n \\\"totalMs\\\": \"<DURATION>\",\\n \\\"maxMs\\\": \"<DURATION>\",\\n \\\"count\\\": 1\\n },\\n \\\"retrieval\\\": {\\n \\\"lastMs\\\": \"<DURATION>\",\\n \\\"totalMs\\\": \"<DURATION>\",\\n \\\"maxMs\\\": \"<DURATION>\",\\n \\\"count\\\": 0\\n }\\n },\\n \\\"lastReloadAt\\\": \\\"<TIMESTAMP>\\\",\\n \\\"recommendationFeedback\\\": {\\n \\\"totalOffers\\\": 0,\\n \\\"totalCandidateSlots\\\": 0,\\n \\\"totalLoaded\\\": 0,\\n \\\"totalAbandoned\\\": 0,\\n \\\"zeroCandidateQueries\\\": 0,\\n \\\"followThroughRate\\\": null,\\n \\\"bySource\\\": {\\n \\\"auto-inject\\\": {\\n \\\"offers\\\": 0,\\n \\\"candidateSlots\\\": 0,\\n \\\"loaded\\\": 0,\\n \\\"zeroHits\\\": 0\\n },\\n \\\"command\\\": {\\n \\\"offers\\\": 0,\\n \\\"candidateSlots\\\": 0,\\n \\\"loaded\\\": 0,\\n \\\"zeroHits\\\": 0\\n },\\n \\\"tool\\\": {\\n \n…[elided 3910 characters]…\n,\"command\":\"prompt\",\"success\":true,\"data\":{\"disposition\":\"handled\"}}\n{\"type\":\"extension_ui_request\",\"id\":\"<RPC_ID>\",\"method\":\"input\",\"title\":\"What do you want help with?\",\"placeholder\":\"Describe your task in natural language…\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<RPC_ID>\",\"method\":\"select\",\"title\":\"How should Shiori use these recommendations?\",\"options\":[\"Pre-load matches and queue task for agent\",\"Review recommendations only\",\"Cancel\"]}\n{\"type\":\"extension_ui_request\",\"id\":\"<RPC_ID>\",\"method\":\"notify\",\"message\":\"Matched 1 skill(s): smoke-skill [trigger]\",\"notifyType\":\"info\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<RPC_ID>\",\"method\":\"select\",\"title\":\"Follow up on a recommendation:\",\"options\":[\"smoke-skill [trigger]\",\"Done\"]}\n{\"id\":\"reload-recommend\",\"type\":\"response\",\"command\":\"prompt\",\"success\":true,\"data\":{\"disposition\":\"handled\"}}\n\n[first process stderr]\n\n[second process stdout]\n{\"id\":\"reload-second-commands\",\"type\":\"response\",\"command\":\"get_commands\",\"success\":true,\"data\":{\"commands\":[{\"name\":\"shiori:doctor\",\"description\":\"Show Pi Skill Shiori configuration and inventory status\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}},{\"name\":\"shiori:reload\",\"description\":\"Rebuild Pi Skill Shiori inventory and optional Pi runtime resources\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}},{\"name\":\"shiori:recommend\",\"description\":\"Recommend Agent Skills for a natural-language task and queue it for the agent\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}},{\"name\":\"shiori:stats\",\"description\":\"Show Pi Skill Shiori operational counters and recommendation feedback\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}},{\"name\":\"shiori:bootstrap\",\"description\":\"Generate a review policy draft from discovered skill descriptions\",\"source\":\"extension\",\"sourceInfo\":{\"path\":\"<REPOSITORY>\\\\src\\\\index.ts\",\"source\":\"cli\",\"scope\":\"temporary\",\"origin\":\"top-level\"}}]}}\n{\"type\":\"extension_ui_request\",\"id\":\"<RPC_ID>\",\"method\":\"input\",\"title\":\"What do you want help with?\",\"placeholder\":\"Describe your task in natural language…\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<RPC_ID>\",\"method\":\"select\",\"title\":\"How should Shiori use these recommendations?\",\"options\":[\"Pre-load matches and queue task for agent\",\"Review recommendations only\",\"Cancel\"]}\n{\"type\":\"extension_ui_request\",\"id\":\"<RPC_ID>\",\"method\":\"notify\",\"message\":\"Matched 1 skill(s): smoke-skill [trigger]\",\"notifyType\":\"info\"}\n{\"type\":\"extension_ui_request\",\"id\":\"<RPC_ID>\",\"method\":\"select\",\"title\":\"Follow up on a recommendation:\",\"options\":[\"smoke-skill [trigger]\",\"Done\"]}\n{\"id\":\"reload-recommend\",\"type\":\"response\",\"command\":\"prompt\",\"success\":true,\"data\":{\"disposition\":\"handled\"}}\n\n[second process stderr]\n",
67
+ "assertions": [
68
+ {
69
+ "id": "inventory_reload",
70
+ "expected": "Runtime reload re-registers Shiori and resets its session-local inventory counter to the single new session_start refresh.",
71
+ "observed": "{\"post_reload_commands\":[\"shiori:doctor\",\"shiori:reload\",\"shiori:recommend\",\"shiori:stats\",\"shiori:bootstrap\"],\"post_reload_inventory_refresh_count\":1}",
72
+ "passed": true
73
+ },
74
+ {
75
+ "id": "reloaded_extension_registered",
76
+ "expected": "A second actual Pi process registers Shiori again after the first process shuts down.",
77
+ "observed": "{\"commands\":[\"shiori:doctor\",\"shiori:reload\",\"shiori:recommend\",\"shiori:stats\",\"shiori:bootstrap\"]}",
78
+ "passed": true
79
+ },
80
+ {
81
+ "id": "reloaded_feature_response",
82
+ "expected": "The second process handles Shiori's interactive recommendation and returns the discovered skill.",
83
+ "observed": "{\"response\":{\"id\":\"reload-recommend\",\"type\":\"response\",\"command\":\"prompt\",\"success\":true,\"data\":{\"disposition\":\"handled\"}},\"notification\":{\"type\":\"extension_ui_request\",\"id\":\"<RPC_ID>\",\"method\":\"notify\",\"message\":\"Matched 1 skill(s): smoke-skill [trigger]\",\"notifyType\":\"info\"}}",
84
+ "passed": true
85
+ },
86
+ {
87
+ "id": "clean_shutdown",
88
+ "expected": "Both actual Pi processes exit with status 0 and emit no unhandled or shutdown errors.",
89
+ "observed": "{\"first_exit_code\":0,\"second_exit_code\":0,\"runtime_errors\":null,\"extension_load_errors\":null}",
90
+ "passed": true
91
+ }
92
+ ]
93
+ },
94
+ {
95
+ "name": "host_shell",
96
+ "command": "npm run smoke:pi (native host shell launches the locally pinned Pi CLI)",
97
+ "exit_code": 0,
98
+ "passed": true,
99
+ "evidence": "1.2.0\n",
100
+ "assertions": [
101
+ {
102
+ "id": "native_shell_boundary",
103
+ "expected": "The native host shell launches the locally pinned Pi CLI and returns exactly its SDK version.",
104
+ "observed": "{\"exit_code\":0,\"output\":\"1.2.0\",\"expectedVersion\":\"1.2.0\"}",
105
+ "passed": true
106
+ }
107
+ ]
108
+ }
109
+ ]
110
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-skill-shiori",
3
- "version": "0.6.12",
3
+ "version": "0.6.16",
4
4
  "description": "Pi extension that reduces Agent Skill catalog tokens with policy-based skill retrieval and on-demand skill loading.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -27,17 +27,19 @@
27
27
  "LICENSE",
28
28
  "SECURITY.md",
29
29
  "CHANGELOG.md",
30
- "Docs",
30
+ "docs",
31
31
  "src"
32
32
  ],
33
33
  "scripts": {
34
34
  "typecheck": "tsc --noEmit",
35
35
  "build": "tsc --noEmit",
36
36
  "test": "npm run typecheck && tsx --test tests/*.test.mjs",
37
+ "smoke:pi": "node scripts/pi-sdk-smoke.mjs",
37
38
  "prepublishOnly": "npm test",
38
39
  "release:npm:dry": "npm pack --dry-run",
39
40
  "release:npm": "npm publish --access public",
40
- "version:check": "node scripts/check-version-bump.mjs"
41
+ "version:check": "node scripts/check-version-bump.mjs",
42
+ "smoke:pi:curated": "node scripts/pi-sdk-smoke.mjs --update-curated-report"
41
43
  },
42
44
  "pi": {
43
45
  "extensions": [
@@ -45,7 +47,7 @@
45
47
  ]
46
48
  },
47
49
  "dependencies": {
48
- "yaml": "^2.8.1"
50
+ "yaml": "^2.9.1"
49
51
  },
50
52
  "peerDependencies": {
51
53
  "@earendil-works/pi-ai": "*",
@@ -54,9 +56,10 @@
54
56
  "typebox": "*"
55
57
  },
56
58
  "devDependencies": {
57
- "@earendil-works/pi-ai": "^0.99.1",
58
- "@earendil-works/pi-coding-agent": "^0.99.1",
59
- "@earendil-works/pi-tui": "^0.99.1",
59
+ "@earendil-works/pi-agent-core": "1.2.0",
60
+ "@earendil-works/pi-ai": "1.2.0",
61
+ "@earendil-works/pi-coding-agent": "1.2.0",
62
+ "@earendil-works/pi-tui": "1.2.0",
60
63
  "@types/node": "^26.0.0",
61
64
  "tsx": "^4.22.4",
62
65
  "typebox": "latest",
package/src/index.ts CHANGED
@@ -7,7 +7,7 @@ import { findDuplicates, formatInventoryRoots, isAutoRefreshEnabled, isInventory
7
7
  import type { SkillIndex } from "./indexer.js";
8
8
  import { loadRecommendedSkills } from "./load-skills.js";
9
9
  import { formatPlanningKickoffMessage, isPlanningIntent } from "./planning-kickoff.js";
10
- import { createStats } from "./metrics.js";
10
+ import { createStats, formatTimingSummary, measureTiming } from "./metrics.js";
11
11
  import { createRecommendationFeedback } from "./recommendation-feedback.js";
12
12
  import { getPolicyPath, loadPolicyWithSource, loadedPolicySignature, type LoadedPolicy } from "./policy.js";
13
13
  import {
@@ -48,7 +48,10 @@ export default function piSkillShiori(pi: ExtensionAPI) {
48
48
  async function reload(cwd: string, currentPolicy?: LoadedPolicy): Promise<SkillIndex> {
49
49
  currentPolicy ??= await loadPolicyWithSource(cwd);
50
50
  const policy = currentPolicy.policy;
51
- const inventory = await refreshSkillInventory(cwd, policy, index);
51
+ const inventory = await measureTiming(
52
+ stats.timing.inventoryRefresh,
53
+ () => refreshSkillInventory(cwd, policy, index),
54
+ );
52
55
  index = inventory.index;
53
56
  inventoryFingerprint = inventory.fingerprint;
54
57
  inventoryRoots = inventory.roots;
@@ -139,7 +142,14 @@ export default function piSkillShiori(pi: ExtensionAPI) {
139
142
  ctx.ui.notify("Pi Skill Shiori: Skill Catalog suppression pattern not found; leaving system prompt unchanged.", "warning");
140
143
  }
141
144
 
142
- const candidates = retrieveCandidatesExpanded(event.prompt, current.skills, current.policy, current);
145
+ if (event.messageType === "custom") {
146
+ return { systemPrompt: suppression.systemPrompt };
147
+ }
148
+
149
+ const candidates = await measureTiming(
150
+ stats.timing.retrieval,
151
+ () => retrieveCandidatesExpanded(event.prompt, current.skills, current.policy, current),
152
+ );
143
153
  if (candidates.length === 0) {
144
154
  stats.zeroCandidateCount += 1;
145
155
  recommendationFeedback.recordZeroCandidates("auto-inject");
@@ -174,7 +184,10 @@ export default function piSkillShiori(pi: ExtensionAPI) {
174
184
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
175
185
  const current = await ensureIndex(ctx.cwd);
176
186
  const policy = withRecommendLimits(current);
177
- const candidates = retrieveCandidatesExpanded(params.task, current.skills, policy, current);
187
+ const candidates = await measureTiming(
188
+ stats.timing.retrieval,
189
+ () => retrieveCandidatesExpanded(params.task, current.skills, policy, current),
190
+ );
178
191
  const candidateNames = candidates.map((candidate) => candidate.skill.name);
179
192
  const loaded = await loadRecommendedSkills(candidates);
180
193
  recommendationFeedback.recordRecommendation(
@@ -340,7 +353,10 @@ export default function piSkillShiori(pi: ExtensionAPI) {
340
353
  }
341
354
 
342
355
  const policy = withRecommendLimits(current);
343
- const candidates = retrieveCandidatesExpanded(query, current.skills, policy, current);
356
+ const candidates = await measureTiming(
357
+ stats.timing.retrieval,
358
+ () => retrieveCandidatesExpanded(query, current.skills, policy, current),
359
+ );
344
360
  const candidateNames = candidates.map((candidate) => candidate.skill.name);
345
361
  const planning = isPlanningIntent(query);
346
362
  const reviewOnly = action === RECOMMEND_REVIEW_ONLY;
@@ -417,11 +433,16 @@ export default function piSkillShiori(pi: ExtensionAPI) {
417
433
  handler: async (_args, ctx) => {
418
434
  const payload = {
419
435
  ...stats,
436
+ timing: {
437
+ inventoryRefresh: { ...stats.timing.inventoryRefresh },
438
+ retrieval: { ...stats.timing.retrieval },
439
+ },
420
440
  recommendationFeedback: recommendationFeedback.summarize(),
421
441
  };
422
442
  ctx.ui.notify(
423
443
  [
424
444
  recommendationFeedback.formatSummary(),
445
+ formatTimingSummary(stats.timing),
425
446
  "",
426
447
  JSON.stringify(payload, null, 2),
427
448
  ].join("\n"),
package/src/metrics.ts CHANGED
@@ -1,4 +1,26 @@
1
- import type { ShioriStats } from "./types.js";
1
+ import { performance } from "node:perf_hooks";
2
+ import type { ShioriStats, ShioriTiming, TimingCounter } from "./types.js";
3
+
4
+ const TIMING_DECIMAL_PLACES = 2;
5
+ const TIMING_SCALE = 10 ** TIMING_DECIMAL_PLACES;
6
+
7
+ type MonotonicClock = () => number;
8
+
9
+ export function createTimingCounter(): TimingCounter {
10
+ return {
11
+ lastMs: 0,
12
+ totalMs: 0,
13
+ maxMs: 0,
14
+ count: 0,
15
+ };
16
+ }
17
+
18
+ export function createTiming(): ShioriTiming {
19
+ return {
20
+ inventoryRefresh: createTimingCounter(),
21
+ retrieval: createTimingCounter(),
22
+ };
23
+ }
2
24
 
3
25
  export function createStats(): ShioriStats {
4
26
  return {
@@ -12,5 +34,47 @@ export function createStats(): ShioriStats {
12
34
  suppressionStatus: "disabled",
13
35
  inventoryRefreshCount: 0,
14
36
  inventoryAutoRefreshCount: 0,
37
+ timing: createTiming(),
15
38
  };
16
39
  }
40
+
41
+ export function recordTiming(counter: TimingCounter, elapsedMs: number): void {
42
+ const duration = roundMilliseconds(elapsedMs);
43
+ counter.lastMs = duration;
44
+ counter.totalMs = roundMilliseconds(counter.totalMs + duration);
45
+ counter.maxMs = Math.max(counter.maxMs, duration);
46
+ counter.count += 1;
47
+ }
48
+
49
+ export async function measureTiming<T>(
50
+ counter: TimingCounter,
51
+ operation: () => T | PromiseLike<T>,
52
+ now: MonotonicClock = () => performance.now(),
53
+ ): Promise<T> {
54
+ const startedAt = now();
55
+ const result = await operation();
56
+ recordTiming(counter, now() - startedAt);
57
+ return result;
58
+ }
59
+
60
+ export function formatTimingSummary(timing: ShioriTiming): string {
61
+ return [
62
+ "Timing (session-local)",
63
+ `inventory refresh: ${formatTimingCounter(timing.inventoryRefresh)}`,
64
+ `retrieval: ${formatTimingCounter(timing.retrieval)}`,
65
+ ].join(" | ");
66
+ }
67
+
68
+ function formatTimingCounter(counter: TimingCounter): string {
69
+ const count = Number.isFinite(counter.count) && counter.count > 0 ? Math.trunc(counter.count) : 0;
70
+ return `count=${count} last=${formatMilliseconds(counter.lastMs)} total=${formatMilliseconds(counter.totalMs)} max=${formatMilliseconds(counter.maxMs)}`;
71
+ }
72
+
73
+ function formatMilliseconds(value: number): string {
74
+ return `${roundMilliseconds(value).toFixed(TIMING_DECIMAL_PLACES)}ms`;
75
+ }
76
+
77
+ function roundMilliseconds(value: number): number {
78
+ if (!Number.isFinite(value) || value <= 0) return 0;
79
+ return Math.round((value + Number.EPSILON) * TIMING_SCALE) / TIMING_SCALE;
80
+ }
@@ -1,11 +1,8 @@
1
1
  import { buildRetrievalQueries } from "./query.js";
2
2
  import { compareCandidates, excludedSkillNames, retrieveCandidates } from "./retrieval.js";
3
+ import type { RetrievalIndex } from "./retrieval.js";
3
4
  import type { ShioriPolicy, SkillCandidate, SkillRecord } from "./types.js";
4
5
 
5
- interface RetrievalIndex {
6
- search(query: string, limit: number): SkillRecord[];
7
- }
8
-
9
6
  export function retrieveCandidatesExpanded(
10
7
  query: string,
11
8
  skills: SkillRecord[],
package/src/retrieval.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { attachRecommendationReason } from "./recommendation-reason.js";
2
2
  import type { ShioriPolicy, SkillCandidate, SkillRecord } from "./types.js";
3
3
 
4
- interface RetrievalIndex {
4
+ export interface RetrievalIndex {
5
5
  search(query: string, limit: number): SkillRecord[];
6
6
  }
7
7
 
package/src/types.ts CHANGED
@@ -48,6 +48,18 @@ export interface SkillCandidate {
48
48
  reason: RecommendationReasonKind | null;
49
49
  }
50
50
 
51
+ export interface TimingCounter {
52
+ lastMs: number;
53
+ totalMs: number;
54
+ maxMs: number;
55
+ count: number;
56
+ }
57
+
58
+ export interface ShioriTiming {
59
+ inventoryRefresh: TimingCounter;
60
+ retrieval: TimingCounter;
61
+ }
62
+
51
63
  export interface ShioriStats {
52
64
  inventoryCount: number;
53
65
  duplicateCount: number;
@@ -60,4 +72,5 @@ export interface ShioriStats {
60
72
  lastReloadAt?: string;
61
73
  inventoryRefreshCount: number;
62
74
  inventoryAutoRefreshCount: number;
75
+ timing: ShioriTiming;
63
76
  }
File without changes