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 +28 -3
- package/README.md +10 -9
- package/{Docs → docs}/usage.md +11 -0
- package/docs/verification/pi-sdk-smoke.json +110 -0
- package/package.json +10 -7
- package/src/index.ts +26 -5
- package/src/metrics.ts +65 -1
- package/src/retrieval-expanded.ts +1 -4
- package/src/retrieval.ts +1 -1
- package/src/types.ts +13 -0
- /package/{Docs → docs}/recommendation-surface.md +0 -0
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
|
-
- `
|
|
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 `
|
|
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
|
-
- `
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 [
|
|
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 [`
|
|
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 [`
|
|
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
|
-
| `
|
|
125
|
-
| `
|
|
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 |
|
package/{Docs → docs}/usage.md
RENAMED
|
@@ -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.
|
|
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
|
-
"
|
|
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.
|
|
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-
|
|
58
|
-
"@earendil-works/pi-
|
|
59
|
-
"@earendil-works/pi-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|