@nailuogg/pi-find-packages 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,10 +21,27 @@ pi install /path/to/pi-find-packages # local trial
21
21
 
22
22
  `<PI_CODING_AGENT_DIR>/data/pi-find-packages/` (default `~/.pi/agent/data/pi-find-packages/`)
23
23
 
24
- - `catalog.jsonl` — catalog data (one package per line: name/version/description/date/author/keywords/repo)
24
+ - `catalog.jsonl` — catalog data (one package per line: name/version/description/date/author/publisher/keywords/repo)
25
25
  - `config.json` — `{"isolation": "docker" | "off", "semantic": "auto" | "on" | "off"}`
26
26
  - `isolation` defaults to `docker` (sandboxed source analysis)
27
27
  - `semantic` controls semantic search over lazily-cached READMEs (via [qmd](https://github.com/tobi/qmd)); defaults to `auto`: enabled automatically when the `qmd` binary is present, force with `"on"`, disable with `"off"`
28
+ - `readmes/` — READMEs of reviewed candidates, the corpus behind semantic search (see below)
29
+
30
+ ## Semantic search setup (optional)
31
+
32
+ Semantic search reads a qmd collection named `pi-pkg-readmes`, pointed at the `readmes/`
33
+ directory above. Nothing in this package registers that collection, and qmd does not create
34
+ it on demand: an unregistered directory simply stays empty, so semantic search degrades to
35
+ lexical search with no error. Register it once:
36
+
37
+ ```bash
38
+ qmd collection add "$HOME/.pi/agent/data/pi-find-packages/readmes" --name pi-pkg-readmes
39
+ qmd collection show pi-pkg-readmes # confirm the path matches your data directory
40
+ ```
41
+
42
+ Substitute your real data directory when `PI_CODING_AGENT_DIR` is set. Afterwards
43
+ `/find-packages` keeps the corpus current on its own: each reviewed candidate's README is
44
+ saved into `readmes/` and indexed with `qmd update && qmd embed`.
28
45
 
29
46
  ## Refreshing the catalog
30
47
 
@@ -58,8 +75,10 @@ The image contains no pi and no credentials: it exists solely to clone/unpack/re
58
75
 
59
76
  Publishing is automated: push a tag `v<version>` matching `package.json`, and the
60
77
  `publish.yml` workflow verifies the version, publishes `@nailuogg/pi-find-packages`
61
- to npm, and creates a GitHub release. The `NPM_TOKEN` repository secret must hold a
62
- granular npm access token scoped to the `@nailuogg` packages (npmjs.com Access Keys).
78
+ to npm, and creates a GitHub release. Publishing uses npm **Trusted Publishing (OIDC)**: the package's npm settings list
79
+ this repository and `publish.yml` as a trusted publisher, so CI authenticates with a
80
+ short-lived OIDC token — no `NPM_TOKEN` secret involved. Requires the workflow to run
81
+ with `id-token: write` (set) and npm >= 11.5.1 (the workflow installs the latest npm).
63
82
 
64
83
  ## Security boundaries
65
84
 
@@ -163,7 +163,7 @@ export default function activate(pi: ExtensionAPI) {
163
163
  "",
164
164
  "⚠️ **Isolation disabled (isolation: off)**: candidate source will be cloned/unpacked on the host.",
165
165
  "Third-party packages may contain malicious install scripts or build logic; host-side analysis is not sandboxed.",
166
- "This mode is not recommended. Delete data/pi-find-packages/config.json or set isolation:\"docker\" to restore the default.",
166
+ "This mode is not recommended. Delete " + configFile + " or set isolation:\"docker\" to restore the default.",
167
167
  ].join("\n")
168
168
  : "";
169
169
  const prompt = [
@@ -171,15 +171,17 @@ export default function activate(pi: ExtensionAPI) {
171
171
  `**${query}**`,
172
172
  "",
173
173
  "Steps:",
174
- "1. Search: run multiple jq/grep keyword passes over `~/.pi/agent/data/pi-find-packages/catalog.jsonl` against description/keywords/name, trying synonyms as needed; "
174
+ "1. Search: run multiple jq/grep keyword passes over `" + catalog + "` against description/keywords/name, trying synonyms as needed; "
175
175
  + (semanticEnabled()
176
176
  ? "also run `qmd query --collection pi-pkg-readmes` for semantic search over cached READMEs (skip if empty); "
177
177
  : "")
178
178
  + "pick the 3-5 most relevant candidates.",
179
- "2. Review each candidate (read-only): `npm view <pkg>` for version/deps/peer; clone or download source via the repo link; read entry points, extension points, and the README; judge maintenance activity, dependency surface, and supply-chain signals. "
180
- + "After reviewing, save the package README to `~/.pi/agent/data/pi-find-packages/readmes/<name with / replaced by __>.md` (first line `# <name> <version> <date>`)"
181
- + (semanticEnabled() ? ", then run `qmd index pi-pkg-readmes` to update the index" : "") + ".",
182
- `3. Execution environment: ${iso === "docker" ? "all cloning/unpacking/source analysis must run inside the Docker container (see docker/Dockerfile.analysis); the host only receives analysis text; never run a candidate's install scripts." : "not isolated — analyze read-only on the host (see the risk note above)."}`,
179
+ "2. Review each candidate: `npm view <pkg>` for version/deps/peer, plus the README and registry metadata on the host; judge maintenance activity, dependency surface, and supply-chain signals. Any cloning or unpacking of candidate source happens in step 3's sandbox only — never fetch candidate source on the host. "
180
+ + "After reviewing, save the package README to `" + join(dataDir, "readmes") + "/<name with / replaced by __>.md` (first line `# <name> <version> <date>`) "
181
+ + (semanticEnabled() ? "then refresh the semantic index with `qmd update && qmd embed`" : "") + ".",
182
+ `3. Execution environment: ${iso === "docker"
183
+ ? "all cloning/unpacking/source analysis must run inside the Docker container (see " + join(pkgDir, "docker/Dockerfile.analysis") + "); the host only receives analysis text; never run a candidate's install scripts."
184
+ : "not isolated — analyze read-only on the host (see the risk note above)."}`,
183
185
  "4. Criteria: feature overlap with the current setup / installed packages (Unix philosophy: features must not cross); pi compatibility (peer ranges); maintenance activity; dependency and supply-chain safety.",
184
186
  "5. Output: a candidate comparison table (name/version/activity/fit/risk) + a clear recommendation with reasons. The analysis report is not install authorization — I decide whether to integrate.",
185
187
  riskNote,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nailuogg/pi-find-packages",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Local catalog of the pi package ecosystem with /find-packages: offline search, cold-start data, and sandboxed source analysis for integration review",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -37,5 +37,14 @@
37
37
  },
38
38
  "publishConfig": {
39
39
  "access": "public"
40
- }
40
+ },
41
+ "files": [
42
+ "extensions/",
43
+ "skills/",
44
+ "scripts/",
45
+ "data/",
46
+ "docker/",
47
+ "README.md",
48
+ "LICENSE"
49
+ ]
41
50
  }
@@ -1,37 +1,162 @@
1
- # find-packages: pi package catalog search & integration review
1
+ ---
2
+ name: find-packages
3
+ description: >-
4
+ Search a local offline catalog of the pi package ecosystem (npm keywords:pi-package) and review a
5
+ candidate package before installing it. Use when the user wants to find, compare, or evaluate a pi
6
+ package, extension, or skill to install: lexical and semantic catalog search over cached READMEs,
7
+ catalog refresh when stale, and a fixed integration review (feature overlap with pi built-ins and
8
+ installed packages, peer compatibility, maintenance activity, supply chain) plus sandboxed Docker
9
+ source analysis.
10
+ license: MIT
11
+ compatibility: >-
12
+ Requires jq, node >= 22 and npm; docker for source analysis; network access for catalog refresh and
13
+ npm view. qmd is optional and only enables semantic search over cached READMEs. POSIX shell assumed.
14
+ ---
2
15
 
3
- Maintains a local catalog of the pi package ecosystem (same source as pi.dev/packages: npm `keywords:pi-package`) for offline/semantic package discovery, plus a standard integration-evaluation workflow.
16
+ # find-packages
4
17
 
5
- ## Data
18
+ Find pi packages in a local, offline catalog of the pi ecosystem (npm `keywords:pi-package`) and review
19
+ a candidate before it gets installed. Search is lexical (`jq`), optionally semantic; review is a fixed
20
+ rubric with sandboxed Docker source analysis.
6
21
 
7
- - Catalog file: `~/.pi/agent/data/pi-find-packages/catalog.jsonl` (one package per line: name/version/description/date/author/keywords/npm/repo)
8
- - Refresh (pick the first that works):
9
- 1. `/find-packages update` — pulls the latest snapshot from the jsDelivr `data` branch (falls back to the npmmirror tarball of the latest published version); checksum-verified, atomic replace
10
- 2. `node <package-dir>/scripts/sync-catalog.mjs` full rebuild from the npm registry search API (~40 requests). Only run when the file is older than 30 days or the user asks; avoid hammering the registry.
22
+ Catalog: `${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}/data/pi-find-packages/catalog.jsonl` one JSON object
23
+ per line, with fields `name`, `version`, `description`, `date` (last publish), `author` (frequently
24
+ empty), `publisher` (use this as the maintainer), `keywords`, `npm`, `repo`, `homepage`. The same
25
+ directory holds `config.json` and the `readmes/` cache.
11
26
 
12
- ## Search methods
27
+ Throughout this file, paths are relative to this skill directory; `../../` is the package root.
13
28
 
14
- 1. Lexical: `jq -r 'select(.description|test("keyword";"i")) | [.name,.version,.description] | @tsv' catalog.jsonl` — try multiple synonym groups (English-first; cover channel words like remote/telegram/discord/web).
15
- 2. Semantic (optional): `qmd query --collection pi-pkg-readmes "need description"` — searches cached READMEs of previously reviewed packages (grows over time; skip when the cache is empty). Only use when the `qmd` binary is available and not disabled via `"semantic":"off"` in `config.json`; otherwise skip this step and rely on lexical search.
16
- 3. When both come up empty, fall back to `npm search` and note the catalog may be stale.
29
+ ## Workflow
17
30
 
18
- ## README cache (written as a side effect of review)
31
+ 1. **Search** the catalog for candidates §1.
32
+ 2. **Shortlist** the 3–5 closest matches.
33
+ 3. **Review** each candidate against the rubric — §3. Feature overlap is a hard rule.
34
+ 4. **Analyze sources** inside the Docker sandbox when the review needs code-level evidence — §4.
35
+ 5. **Report** a comparison table plus a recommendation — §5.
19
36
 
20
- After reviewing a candidate, save its README to `~/.pi/agent/data/pi-find-packages/readmes/<name with / replaced by __>.md`, first line `# <name> <version> <date>`; then run `qmd index pi-pkg-readmes` to update the index. **Never bulk-fetch all READMEs** the corpus grows with real reviews.
37
+ Complete the steps in order; never skip the overlap check in step 3.
21
38
 
22
- ## Integration review (check every candidate)
39
+ ## 1. Search the catalog
23
40
 
24
- - **Feature overlap**: does it overlap installed packages (`packages` arrays in `~/.pi/agent/settings.json` and the project `.pi/settings.json`) or pi built-ins? No overlap is a hard rule.
25
- - **Compatibility**: does `npm view <pkg> peerDependencies` cover the current pi version?
26
- - **Activity**: latest publish date, latest repo commit.
27
- - **Supply chain**: author/maintainers, dependency count, install/preinstall scripts in package.json, any data-exfiltration paths.
41
+ Run one pass per synonym group, English first, and always include channel/transport words
42
+ (remote, telegram, discord, web, vnc) when the need is about access or reach:
28
43
 
29
- ## Execution environment
44
+ ```bash
45
+ CAT="${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}/data/pi-find-packages/catalog.jsonl"
46
+ jq -r 'select((.name + " " + (.description // "") + " " + ((.keywords // [])|join(" ")))
47
+ | test("remote|telegram|discord";"i"))
48
+ | [.name, .version, (.publisher // ""), .date] | @tsv' "$CAT"
49
+ ```
30
50
 
31
- - **Source analysis must run inside Docker by default** (clone/unpack candidate code via `docker/Dockerfile.analysis`: node24-slim + git + ripgrep, no credentials). The host only receives analysis text. **Never execute a candidate's install scripts or build artifacts.**
32
- - `"isolation":"off"` in `data/pi-find-packages/config.json` disables isolation — a risk warning is shown on every use; not recommended.
33
- - Shallow lookups (`npm view`, registry JSON) do not need the container.
51
+ Expected output tab-separated, one candidate per line:
34
52
 
35
- ## Output convention
53
+ ```text
54
+ @99percentpeople/pi-ssh-remote 0.6.2 GitHub Actions 2026-09-08T08:29:12.985Z
55
+ @bacnh85/pi-a2a 0.7.11 bacnh85 2026-09-21T03:51:02.441Z
56
+ ```
36
57
 
37
- Candidate comparison table (name/version/last publish/activity/fit/risk) + a clear recommendation with reasons. **An analysis report is not install authorization** — the user decides whether to `pi install`.
58
+ `.description` and `.keywords` can be `null`, hence the `// ""` guards. Keep `test()` case-insensitive.
59
+
60
+ **Semantic search (optional).** Only when `qmd` is installed and `semantic` is not `"off"` — see
61
+ Configuration. It searches cached READMEs of previously reviewed packages only, so check the collection
62
+ first and skip semantic search while it is empty — `qmd query` spends an LLM expansion pass per call,
63
+ `qmd ls` is free:
64
+
65
+ ```bash
66
+ qmd ls pi-pkg-readmes # "No files found" → skip
67
+ qmd query --collection pi-pkg-readmes "<one sentence>"
68
+ ```
69
+
70
+ **When both come up empty**, fall back to `npm search` and say in the report that the catalog may be
71
+ stale.
72
+
73
+ ## 2. Refresh the catalog
74
+
75
+ Refresh only when the catalog is missing, older than 30 days (`find "$CAT" -mtime +30`), or the user
76
+ asks. Avoid hammering the registry.
77
+
78
+ 1. `/find-packages update` — the command provided by this package's extension. Pulls the snapshot from
79
+ the jsDelivr `data` branch (checksum-verified against the published `.sha256`), then falls back to
80
+ the npmmirror tarball of the latest published version.
81
+ 2. Last resort, when both CDNs fail: run `node ../../scripts/sync-catalog.mjs` to rebuild the catalog
82
+ locally from the npm registry search API (~40 requests). It writes `catalog.jsonl`, its `.gz` and
83
+ `.sha256` into the catalog directory; CI invokes the same script with `--out <dir>`.
84
+
85
+ ## 3. Integration review
86
+
87
+ Run every candidate through all four checks; record the evidence for each one.
88
+
89
+ | Check | How to verify | Verdict |
90
+ |---|---|---|
91
+ | **Feature overlap** (hard rule) | Compare against pi built-ins and the packages installed in the `packages` array of `$PI_CODING_AGENT_DIR/settings.json` and of the project `.pi/settings.json` | Any overlap → recommend against, whatever the other checks say |
92
+ | **pi compatibility** | `npm view <pkg> peerDependencies` | Peer range must cover the current pi version |
93
+ | **Maintenance activity** | Catalog `date` (last publish) plus the last commit in `repo` | Stale on both → note as risk, not a blocker |
94
+ | **Supply chain** | `npm view <pkg> maintainers dependencies scripts` | Flag `install`/`preinstall` scripts, large dependency trees, and any path that sends data off-host |
95
+
96
+ ## 4. Analyze sources in the sandbox
97
+
98
+ Source analysis runs in Docker by default, using `../../docker/Dockerfile.analysis` (node24-slim + git +
99
+ ripgrep, no credentials). Clone, unpack and read candidate code **inside the container**; only analysis
100
+ text reaches the host.
101
+
102
+ **Never execute a candidate's install scripts or build artifacts** — not on the host and not in the
103
+ container. Reading `package.json`, the README or registry metadata needs no container, so shallow
104
+ lookups stay on the host.
105
+
106
+ `isolation: "off"` in `config.json` disables isolation and moves analysis to the host; every use shows a
107
+ risk warning. Do not recommend it, and do not fall back to host-side analysis on your own when Docker is
108
+ unavailable — report the blocker instead.
109
+
110
+ ## 5. Report
111
+
112
+ Compare the shortlist in a table, then give one recommendation with reasons:
113
+
114
+ | name | version | last publish | activity | fit | risk |
115
+ |---|---|---|---|---|---|
116
+ | @acme/pi-search | 1.2.0 | 2026-09-05 | active (7d) | no overlap | low |
117
+
118
+ **A report is not install authorization.** The user decides whether to run `pi install`.
119
+
120
+ ## README cache
121
+
122
+ After reviewing a candidate, save its README to
123
+ `<catalog dir>/readmes/<name with / replaced by __>.md`, first line `# <name> <version> <date>`, then
124
+ refresh the semantic index:
125
+
126
+ ```bash
127
+ qmd update && qmd embed
128
+ ```
129
+
130
+ This cache is the corpus behind semantic search. Never bulk-fetch READMEs — it grows one real review at
131
+ a time.
132
+
133
+ **One-time setup.** Semantic search needs this directory registered as the qmd collection
134
+ `pi-pkg-readmes`, and nothing in this package registers it: an unregistered directory stays empty and
135
+ semantic search then degrades to lexical search without any error. Register it once:
136
+
137
+ ```bash
138
+ qmd collection add "<catalog dir>/readmes" --name pi-pkg-readmes
139
+ ```
140
+
141
+ Check where the collection points with `qmd collection show pi-pkg-readmes`. If it points at another
142
+ directory, say so and give the user the command above instead of saving READMEs into a directory that is
143
+ never indexed.
144
+
145
+ ## Configuration
146
+
147
+ `<catalog dir>/config.json` (absent file → defaults apply):
148
+
149
+ - `isolation`: `"docker"` (default) | `"off"` — source-analysis environment.
150
+ - `semantic`: `"auto"` (default) | `"on"` | `"off"` — `auto` enables semantic search exactly when the
151
+ `qmd` binary is present.
152
+
153
+ ## Edge cases
154
+
155
+ | Situation | Action |
156
+ |---|---|
157
+ | `catalog.jsonl` missing | Cold start extracts it automatically; if it is still missing, run `/find-packages update` |
158
+ | Catalog older than 30 days, or no matches | Refresh (§2), then search again; note staleness in the report if the refresh fails |
159
+ | `qmd` missing, or `semantic: "off"` | Skip semantic search; rely on lexical passes |
160
+ | `pi-pkg-readmes` empty or registered against another directory | Still skip semantic search, but tell the user the one-time setup command — an unindexed cache never becomes searchable on its own |
161
+ | Docker unavailable | Stop before source analysis, report the blocker, and ask the user how to proceed |
162
+ | `config.json` absent | Defaults apply: Docker isolation on, semantic `auto` |
@@ -1,40 +0,0 @@
1
- name: publish
2
-
3
- on:
4
- push:
5
- tags: ["v*"]
6
-
7
- permissions:
8
- contents: write # create the GitHub release
9
- id-token: write # reserved for a future Trusted-Publishing (OIDC) switch
10
-
11
- jobs:
12
- publish:
13
- runs-on: ubuntu-latest
14
- steps:
15
- - uses: actions/checkout@v4
16
-
17
- - uses: actions/setup-node@v4
18
- with:
19
- node-version: 24
20
- registry-url: https://registry.npmjs.org/
21
- # reads NODE_AUTH_TOKEN from env to write ~/.npmrc
22
-
23
- - name: Verify version matches tag
24
- run: |
25
- PKG_VERSION="${GITHUB_REF_NAME#v}"
26
- TAG_VERSION="$(node -p "require('./package.json').version")"
27
- if [ "${PKG_VERSION}" != "${TAG_VERSION}" ]; then
28
- echo "::error::tag ${GITHUB_REF_NAME} != package.json version ${TAG_VERSION}"
29
- exit 1
30
- fi
31
-
32
- - name: Publish
33
- run: npm publish
34
- env:
35
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
36
-
37
- - name: Create GitHub release
38
- run: gh release create "${GITHUB_REF_NAME}" --verify-tag --generate-notes
39
- env:
40
- GH_TOKEN: ${{ github.token }}
@@ -1,45 +0,0 @@
1
- name: update-data
2
-
3
- on:
4
- schedule:
5
- - cron: "17 3 * * *" # daily, off the hour
6
- workflow_dispatch:
7
-
8
- permissions:
9
- contents: write
10
-
11
- jobs:
12
- refresh:
13
- runs-on: ubuntu-latest
14
- steps:
15
- - uses: actions/checkout@v4
16
-
17
- - uses: actions/setup-node@v4
18
- with:
19
- node-version: 24
20
-
21
- - name: Fetch latest catalog
22
- run: node scripts/sync-catalog.mjs --out "${RUNNER_TEMP}/catalog"
23
-
24
- - name: Skip if unchanged
25
- id: diff
26
- run: |
27
- NEW="${RUNNER_TEMP}/catalog/catalog.jsonl.gz"
28
- OLD="$(curl -fsSL --max-time 30 https://cdn.jsdelivr.net/gh/${GITHUB_REPOSITORY}@data/data/catalog.jsonl.gz 2>/dev/null | sha256sum | cut -d' ' -f1 || true)"
29
- CUR="$(sha256sum "${NEW}" | cut -d' ' -f1)"
30
- echo "changed=$([[ "${CUR}" != "${OLD}" ]] && echo true || echo false)" >> "$GITHUB_OUTPUT"
31
-
32
- - name: Push to data branch
33
- if: steps.diff.outputs.changed == 'true'
34
- run: |
35
- git config user.name "github-actions[bot]"
36
- git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
37
- git fetch origin data || true
38
- git checkout -B data origin/data 2>/dev/null || git checkout -B data
39
- mkdir -p data
40
- cp "${RUNNER_TEMP}/catalog/catalog.jsonl" data/
41
- cp "${RUNNER_TEMP}/catalog/catalog.jsonl.gz" data/
42
- cp "${RUNNER_TEMP}/catalog/catalog.jsonl.gz.sha256" data/
43
- git add data/
44
- git commit -qm "chore(data): refresh catalog ($(date -u +%Y-%m-%d))"
45
- git push origin data