skillcask 0.6.0 → 0.8.0

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
@@ -60,6 +60,27 @@ build provenance attestations:
60
60
  gh attestation verify skillcask-x86_64-unknown-linux-gnu.tar.xz -R puniserv/skillcask
61
61
  ```
62
62
 
63
+ **GitHub Actions**
64
+
65
+ ```yaml
66
+ permissions:
67
+ contents: read
68
+ attestations: read # for the provenance check
69
+ steps:
70
+ - uses: actions/checkout@<sha>
71
+ - uses: puniserv/skillcask@<commit sha of a release tag> # vX.Y.Z
72
+ with:
73
+ version: X.Y.Z # needed when the action is pinned to a sha, not a tag
74
+ args: install --frozen # default
75
+ # working-directory: .
76
+ # verify-attestation: "true" # default
77
+ ```
78
+
79
+ The action downloads the release archive for the runner (Linux x64 and arm64, macOS, Windows
80
+ x64), checks its sha256 and its build provenance attestation, puts `skillcask` on `PATH` and runs
81
+ it with `args`. It runs no other action. Used at a tag (`@vX.Y.Z`) it takes the version from the
82
+ tag.
83
+
63
84
  ## Use
64
85
 
65
86
  ```sh
@@ -114,8 +135,9 @@ local = ".skills" # project skills
114
135
  min-release-age = "3d" # cooldown for add/update; "0" disables
115
136
  allow-ranges = true # false: every version must be exact
116
137
  packagist = true # false: drop the implicit Packagist repository
117
- github-requires = false # true: dependencies may come from GitHub
138
+ github-requires = false # true: dependencies may come from GitHub or git URLs
118
139
  allow-sources = ["acme", "own", "packagist", "github"] # only these sources; default: all
140
+ deny-patterns = false # true: risky patterns in new skill text fail instead of warning
119
141
  ```
120
142
 
121
143
  ### Agents
@@ -160,8 +182,8 @@ Policy can also come from `config.toml` next to the global manifest (`~/.config/
160
182
  from the environment. The first layer that sets a key wins:
161
183
 
162
184
  1. `SKILLCASK_ALLOW_SOURCES`, `SKILLCASK_MIN_RELEASE_AGE`, `SKILLCASK_ALLOW_RANGES`,
163
- `SKILLCASK_PACKAGIST`, `SKILLCASK_GITHUB_REQUIRES` (overriding an enforced key prints a
164
- warning);
185
+ `SKILLCASK_PACKAGIST`, `SKILLCASK_GITHUB_REQUIRES`, `SKILLCASK_DENY_PATTERNS` (overriding an
186
+ enforced key prints a warning);
165
187
  2. keys `config.toml` lists in `enforce`: a project that sets them otherwise fails;
166
188
  3. `[policy]` in `skills.toml`;
167
189
  4. the other keys of `config.toml`, as defaults.
@@ -200,6 +222,7 @@ version: 1.2.0
200
222
  requires:
201
223
  acme/security-review: ^1.0
202
224
  github:mattpocock/skills/pr: main
225
+ "git:https://git.example.com/team/skills.git#review": ^2.0
203
226
  ---
204
227
  ```
205
228
 
@@ -207,7 +230,7 @@ requires:
207
230
  cooldown, same hashes, one version per package (the highest one every requirer accepts). They
208
231
  stay out of `skills.toml`; the lock records each package's `requires` and switches to
209
232
  `version = 2`. A dependency is looked up only in your repositories, in order, then Packagist;
210
- a `github:` one needs `github-requires = true`. `remove` drops dependencies nobody needs any
233
+ a `github:` or `git:<url>[#skill]` one needs `github-requires = true`. `remove` drops dependencies nobody needs any
211
234
  more, and `status` shows them as `via <requirer>`.
212
235
 
213
236
  ### Name conflicts
@@ -239,7 +262,10 @@ skillcask -g install --frozen # e.g. from a dotfiles repository
239
262
  - Skills go to `~/.agents/skills` (read by most agents), with links in `~/.claude/skills`
240
263
  (`[agents] targets`, see Agents). Links are absolute, so a symlinked
241
264
  `~/.claude` works. `[agents] local` does not apply.
242
- - Personal skills you wrote by hand stay where they are and are not linked anywhere.
265
+ - Personal skills you wrote by hand stay where they are and are not linked anywhere. `status`
266
+ lists them, and copies other tools put in a target, as `not managed`; `status --json` names the
267
+ source `npx skills` recorded for one. `skillcask -g add --adopt <source>` moves such a skill to
268
+ `~/.config/skillcask/adopted/` and installs the package's skill of the same name in its place.
243
269
  - The list of managed paths is `~/.agents/skills/.skillcask-installed`, local to the machine;
244
270
  nothing touches git.
245
271
 
@@ -252,20 +278,27 @@ Every command except `publish` and `validate` takes `-g, --global`.
252
278
  | `init` | Create `skills.toml`. |
253
279
  | `add <vendor/name>[@constraint] [--repository r]` | Resolve, lock and install. |
254
280
  | `add github:<owner/repo>[/skill][@version-or-ref]` | The same from a GitHub repository, see below. |
281
+ | `add git:<https-url>[@version-or-ref]` | The same from any git repository over HTTPS, see below. |
255
282
  | `remove <vendor/name>…` | Drop from manifest and lock; delete files, links and gitignore lines. |
256
283
  | `install [--frozen]` | Install the lock. `--frozen` (implied by `CI`) fails when the lock is out of date. |
257
284
  | `update [vendor/name…] [--diff]` | Re-resolve within constraints. `--diff` prints what changes in each updated or added package; it does not ask before installing. |
258
- | `outdated [--strict]` | Newer versions, and when cooled-down ones become eligible. `--strict` exits 1 when `update` would lock a newer version. |
259
- | `diff [vendor/name] [--to constraint]` | Unified diff of skill files from the locked version to the one `update` would lock, see below. |
260
- | `info <vendor/name[@constraint]\|github:owner/repo\|skill>` | A package's skills, versions, description, license and requirements, or which package installs a skill. |
285
+ | `outdated [--strict]` | Newer versions, when cooled-down ones become eligible, and GitHub or git tags moved since they were locked. `--strict` exits 1 when `update` would lock a newer version. |
286
+ | `diff [vendor/name] [--to constraint\|--from old.lock]` | Unified diff of skill files from the locked version to the one `update` would lock, or from an older lock to `skills.lock`, see below. |
287
+ | `info <vendor/name[@constraint]\|github:owner/repo\|git:url\|skill>` | A package's skills, versions, description, license and requirements, or which package installs a skill. |
261
288
  | `status` | Exit 1 when manifest, lock or installed files disagree. |
262
289
  | `doctor [--fix]` | Broken or committed links, stale gitignore block, modified skills; lists each check it ran. |
290
+ | `audit [--json\|--sarif]` | What each installed skill reaches (commands, hosts, paths, env vars, scripts) and its findings: modified files, risky lines, invisible characters. Exit 1 on any finding. |
263
291
  | `search <query>` | Packagist (`type=ai-agent-skill`) and skill packages of the other repositories, GitLab included. |
264
292
  | `auth set\|remove <host>` | Keep a registry token in git's credential helper (Keychain, Credential Manager, libsecret). |
265
293
  | `publish --vendor v --base-url URL --out DIR [--inline] [--dist-url TPL] [paths…]` | Build a static Composer repository from a monorepo. |
266
294
  | `validate [paths…]` | Check `SKILL.md` files against the Agent Skills spec, see below. |
267
295
  | `<other>` | Runs `skillcask-<other>` from `PATH`, like git and cargo plugins. |
268
296
 
297
+ `status`, `outdated`, `info`, `search`, `doctor` and `audit` take `--json`: one JSON document on one line of
298
+ stdout, with the same exit code; warnings and errors stay on stderr. Package text in it is
299
+ escaped as in the terminal (`<U+202E>`). For example `skillcask outdated --json | jq
300
+ '.packages[] | select(.within != .locked)'`.
301
+
269
302
  Exit codes: 0 ok, 1 manifest/lock/status problems, 2 authentication, 3 network, 4 hash mismatch,
270
303
  5 IO or archive error.
271
304
 
@@ -294,6 +327,26 @@ before it writes the lock, then installs without asking: run `skillcask diff` fi
294
327
  and decide. A scheduled CI job can run `skillcask outdated --strict` (exit 1 when something is to
295
328
  update), then `skillcask update --diff` and open a pull request with the output.
296
329
 
330
+ **Reviewing a lock change:** `skillcask diff --from old.lock` prints the same diff from the
331
+ versions in another lock to those in `skills.lock`, plus the packages it drops, so a reviewer
332
+ sees the skill text a pull request brings in instead of new versions and hashes. Each new dist
333
+ is verified against `skills.lock` as `install` would, and the policy decides which repositories
334
+ may be fetched. skillcask does not run git; the job fetches the old lock:
335
+
336
+ ```yaml
337
+ # GitLab CI: print the diff in the merge request pipeline
338
+ skills-diff:
339
+ rules:
340
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event"
341
+ changes: [skills.lock]
342
+ script:
343
+ - git fetch --depth 1 origin "$CI_MERGE_REQUEST_TARGET_BRANCH_NAME"
344
+ - git show FETCH_HEAD:skills.lock | skillcask diff --from -
345
+ ```
346
+
347
+ `--from -` reads the old lock from stdin. On GitHub the same lines run in a `pull_request` job with `$GITHUB_BASE_REF`; posting the
348
+ output as a comment is up to the CI.
349
+
297
350
  **Where a skill comes from:** `skillcask info vendor/name` shows the package's skills with their
298
351
  descriptions, its versions, description, license, `abandoned` and `requires`, for the locked
299
352
  version or else the newest one `add` would take (`@constraint` for another); `info
@@ -345,8 +398,29 @@ entry; `install` from the lock uses none, it downloads the pinned archive. Set `
345
398
  for a higher limit and for private repositories. `SKILLCASK_GITHUB_API` points at GitHub
346
399
  Enterprise (`https://ghe.example.com/api/v3`).
347
400
 
348
- skills.sh itself is not queried: its API needs a Vercel token. Search on the website, then add
349
- what you picked.
401
+ skills.sh itself is not queried: its API is undocumented and may change without notice. Search
402
+ on the website, then add what you picked.
403
+
404
+ ### Skills from any git repository
405
+
406
+ GitLab, Bitbucket, Azure DevOps, Gitea or a self-hosted server work without a Composer registry:
407
+
408
+ ```sh
409
+ skillcask add git:https://gitlab.example.com/acme/skills.git
410
+ skillcask add git:https://gitlab.example.com/acme/skills.git@main
411
+ ```
412
+
413
+ ```toml
414
+ "acme/skills" = { git = "https://gitlab.example.com/acme/skills.git", version = "^1.0" }
415
+ ```
416
+
417
+ Versions, `ref` and `skills = [...]` work as for GitHub; the entry is named after the last two
418
+ parts of the URL. skillcask speaks git's HTTPS protocol itself: no git binary runs, so no hook or
419
+ filter of the repository can. It fetches the one commit it needs and checks every file against
420
+ the commit's object ids. Credentials come from the same places as for registries (`auth.json`,
421
+ the credential helper, `$CI_JOB_TOKEN` in GitLab CI). Git servers keep no release or push times,
422
+ so `min-release-age` uses commit dates, which the committer sets; a branch whose newest commit is
423
+ too young falls back to an older commit when the server supports partial fetches (`filter`).
350
424
 
351
425
  ## Security
352
426
 
@@ -369,6 +443,40 @@ what you picked.
369
443
  RIGHT-TO-LEFT OVERRIDE`, at most five per file. A leading byte order mark and a zero width
370
444
  joiner inside an emoji are not reported; right-to-left and Indic text can trigger warnings.
371
445
  Installing from the lock does not warn again.
446
+ - The same commands warn about lines that usually mean harm, as `warning: acme/x:
447
+ skills/foo/SKILL.md:12: downloads code into a shell`, at most five per file:
448
+
449
+ | Warning | Matches (case-insensitive) |
450
+ |---|---|
451
+ | downloads code into a shell | `curl`, `wget`, `irm`, `iwr`, `Invoke-WebRequest` or `Invoke-RestMethod` piped (`\|`) into `sh`, `bash`, `zsh`, `dash`, `iex`, `Invoke-Expression`, `python`, `perl`, `ruby` or `node`, also after `sudo` |
452
+ | decodes base64 and runs it | `base64 -d`, `base64 --decode`, `b64decode` or `FromBase64String` with a pipe into a shell, `eval`, `exec(` or `iex` |
453
+ | reads SSH keys | `~/.ssh`, `$HOME/.ssh`, `id_rsa`, `id_ed25519` |
454
+ | reads system account files | `/etc/passwd`, `/etc/shadow` |
455
+ | tells the agent to ignore its instructions | "ignore (all) previous/prior instructions", "disregard all previous/prior" |
456
+
457
+ Skills that teach shell setup trigger some of these legitimately, so they are warnings. With
458
+ `deny-patterns = true` under `[policy]` (or enforced in `config.toml`) they fail the command
459
+ with exit 1 and nothing is written.
460
+ - `skillcask audit` reviews what is installed now, every skill in `.agents/skills` and the project
461
+ skills: the commands its shell code blocks run, the hosts of its URLs, the paths in the home
462
+ directory or under system roots, the environment variables it reads and the files an agent could
463
+ run. That is read from the text, so it is a hint for review, not a sandbox. It also reports a
464
+ locked skill that differs from the lock, and every risky line and invisible character above,
465
+ with no limit per file, and exits 1 on any of them. `--sarif` prints a SARIF 2.1.0 log for code
466
+ scanning, e.g. in GitHub Actions:
467
+
468
+ ```yaml
469
+ - uses: puniserv/skillcask@v0.8.0
470
+ with:
471
+ args: install --frozen
472
+ - run: skillcask audit --sarif > skills.sarif
473
+ - uses: github/codeql-action/upload-sarif@v3
474
+ if: always()
475
+ with:
476
+ sarif_file: skills.sarif
477
+ ```
478
+ - Plain git sources pin the commit; the lock's `dist-sha256` hashes the commit's file list
479
+ (path, mode, sha256 of the content), so it does not depend on how a server packs objects.
372
480
  - `min-release-age` for GitHub sources uses times GitHub sets: release publication for tags,
373
481
  push times from the Activity API for branches. A tag without a release, or a ref GitHub lists no
374
482
  pushes for, falls back to commit dates, which the committer sets; reviewing what `add`/`update`
@@ -19,7 +19,7 @@
19
19
  "hasInstallScript": true,
20
20
  "license": "MIT OR Apache-2.0",
21
21
  "name": "skillcask",
22
- "version": "0.6.0"
22
+ "version": "0.8.0"
23
23
  },
24
24
  "node_modules/detect-libc": {
25
25
  "engines": {
@@ -48,5 +48,5 @@
48
48
  }
49
49
  },
50
50
  "requires": true,
51
- "version": "0.6.0"
51
+ "version": "0.8.0"
52
52
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "artifactDownloadUrls": [
3
- "https://github.com/puniserv/skillcask/releases/download/v0.6.0"
3
+ "https://github.com/puniserv/skillcask/releases/download/v0.8.0"
4
4
  ],
5
5
  "bin": {
6
6
  "skillcask": "run-skillcask.js"
@@ -104,7 +104,7 @@
104
104
  "zipExt": ".tar.xz"
105
105
  }
106
106
  },
107
- "version": "0.6.0",
107
+ "version": "0.8.0",
108
108
  "volta": {
109
109
  "node": "18.14.1",
110
110
  "npm": "9.5.0"