Skip to content

Commit 77ff1f9

Browse files
authored
feat(skills): add shared sync cli package (#24)
* feat(skills): add shared sync cli package * chore(skills): add package release metadata * docs(skills): remove redundant publish note * fix(skills): infer target repo from git urls * docs(skills): clarify target repo inference * feat(skills): add CLI discovery commands * ci(skills): add MetaMask package release tooling * ci(skills): set least-privilege workflow permissions Add top-level contents: read permissions to main and lint-build-test workflows so GITHUB_TOKEN is least-privilege. Clears CodeQL 'workflow does not contain permissions' alerts; job-level blocks (security-events: write, contents: write) still override where needed. * test(skills): add unit and CLI tests for the sync CLI Guard the CLI entrypoint so the module is importable, export the pure helpers, and add node:test suites (no new deps) covering repo inference, .skills.local parsing, frontmatter, maturity/domain filtering, arg parsing, skill collection, and CLI commands. Wire `test` to `node --test`. * fix(skills): support symlinked bin and Bash 3.2 - Entrypoint guard now compares realpaths so the CLI runs when invoked through an npm-installed symlink (node_modules/.bin); previously it silently no-opped. Adds a symlink-bin regression test. - pickBash() accepts Bash 3.2 (macOS /bin/bash) instead of requiring 4+. The tools/ scripts are deliberately 3.2-compatible, so the >=4 gate wrongly produced 'No supported Bash found'. * fix(skills): require Bash 3.2+ exactly in pickBash gate Parse major.minor so the accepted version matches the '3.2+' message (rejects 3.0/3.1). Addresses cross-review nit.
1 parent e99dc6a commit 77ff1f9

17 files changed

Lines changed: 3487 additions & 30 deletions

.github/actionlint.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
paths:
2+
.github/workflows/publish-release.yml:
3+
ignore:
4+
# Queue option is not supported by actionlint yet.
5+
- 'unexpected key "queue" for "concurrency" section. expected one of "cancel-in-progress", "group"'
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
name: Lint, Build, and Test
2+
3+
on:
4+
workflow_call:
5+
6+
permissions:
7+
contents: read
8+
9+
jobs:
10+
validate-changelog:
11+
name: Validate changelog
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout and setup environment
15+
uses: MetaMask/action-checkout-and-setup@v2
16+
with:
17+
is-high-risk-environment: false
18+
cache-node-modules: true
19+
node-version: 24.x
20+
- run: yarn changelog:validate
21+
- name: Require clean working directory
22+
run: git diff --exit-code
23+
24+
build:
25+
name: Build
26+
runs-on: ubuntu-latest
27+
steps:
28+
- name: Checkout and setup environment
29+
uses: MetaMask/action-checkout-and-setup@v2
30+
with:
31+
is-high-risk-environment: false
32+
cache-node-modules: true
33+
node-version: 24.x
34+
- run: yarn build
35+
- name: Require clean working directory
36+
run: git diff --exit-code
37+
38+
test:
39+
name: Test
40+
runs-on: ubuntu-latest
41+
strategy:
42+
matrix:
43+
node-version: [18.x, 20.x, 22.x, 24.x]
44+
steps:
45+
- name: Checkout and setup environment
46+
uses: MetaMask/action-checkout-and-setup@v2
47+
with:
48+
is-high-risk-environment: false
49+
cache-node-modules: true
50+
node-version: ${{ matrix.node-version }}
51+
- run: yarn test
52+
- name: Require clean working directory
53+
run: git diff --exit-code
54+
55+
pack:
56+
name: Pack dry run
57+
runs-on: ubuntu-latest
58+
steps:
59+
- name: Checkout and setup environment
60+
uses: MetaMask/action-checkout-and-setup@v2
61+
with:
62+
is-high-risk-environment: false
63+
cache-node-modules: true
64+
node-version: 24.x
65+
- run: yarn pack:dry-run
66+
- name: Require clean working directory
67+
run: git diff --exit-code

.github/workflows/main.yml

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
name: Main
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
merge_group:
8+
9+
concurrency:
10+
group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/main' && github.sha || github.ref }}
11+
cancel-in-progress: ${{ !contains(github.ref, 'refs/heads/main') }}
12+
13+
permissions:
14+
contents: read
15+
16+
jobs:
17+
check-workflows:
18+
name: Check workflows
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v5
22+
- name: Download actionlint
23+
id: download-actionlint
24+
run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/914e7df21a07ef503a81201c76d2b11c789d3fca/scripts/download-actionlint.bash) 1.7.12
25+
shell: bash
26+
- name: Check workflow files
27+
run: ${{ steps.download-actionlint.outputs.executable }} -color
28+
shell: bash
29+
30+
analyse-code:
31+
name: Analyse code
32+
needs: check-workflows
33+
uses: MetaMask/action-security-code-scanner/.github/workflows/security-scan.yml@v2
34+
with:
35+
scanner-ref: v2
36+
paths-ignored: |
37+
node_modules/
38+
.skills-cache/
39+
**/*.patch
40+
**/fixtures/
41+
secrets:
42+
project-metrics-token: ${{ secrets.SECURITY_SCAN_METRICS_TOKEN }}
43+
slack-webhook: ${{ secrets.APPSEC_BOT_SLACK_WEBHOOK }}
44+
permissions:
45+
actions: read
46+
contents: read
47+
security-events: write
48+
49+
lint-build-test:
50+
name: Lint, build, and test
51+
needs: check-workflows
52+
uses: ./.github/workflows/lint-build-test.yml
53+
54+
is-release:
55+
name: Determine whether this is a release merge commit
56+
needs: lint-build-test
57+
if: github.event_name == 'push'
58+
runs-on: ubuntu-latest
59+
outputs:
60+
IS_RELEASE: ${{ steps.is-release.outputs.IS_RELEASE }}
61+
steps:
62+
- id: is-release
63+
uses: MetaMask/action-is-release@v2
64+
with:
65+
commit-starts-with: ${{ vars.RELEASE_COMMIT_PREFIX }}
66+
67+
publish-release:
68+
name: Publish release
69+
needs: is-release
70+
if: needs.is-release.outputs.IS_RELEASE == 'true'
71+
permissions:
72+
contents: write
73+
uses: ./.github/workflows/publish-release.yml
74+
secrets:
75+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
76+
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
77+
78+
all-jobs-complete:
79+
name: All jobs complete
80+
runs-on: ubuntu-latest
81+
needs:
82+
- analyse-code
83+
- lint-build-test
84+
outputs:
85+
passed: ${{ steps.set-output.outputs.passed }}
86+
steps:
87+
- name: Set passed output
88+
id: set-output
89+
run: echo "passed=true" >> "$GITHUB_OUTPUT"
90+
91+
all-jobs-pass:
92+
name: All jobs pass
93+
if: ${{ always() }}
94+
runs-on: ubuntu-latest
95+
needs:
96+
- all-jobs-complete
97+
steps:
98+
- name: Check that all jobs have passed
99+
env:
100+
PASSED: ${{ needs.all-jobs-complete.outputs.passed }}
101+
run: |
102+
if [[ "$PASSED" != "true" ]]; then
103+
exit 1
104+
fi
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: Publish Release
2+
3+
on:
4+
workflow_call:
5+
secrets:
6+
NPM_TOKEN:
7+
required: true
8+
SLACK_WEBHOOK_URL:
9+
required: true
10+
11+
concurrency:
12+
group: publish-release
13+
queue: max
14+
15+
jobs:
16+
publish-release:
17+
permissions:
18+
contents: write
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout and setup environment
22+
uses: MetaMask/action-checkout-and-setup@v2
23+
with:
24+
is-high-risk-environment: true
25+
node-version: 24.x
26+
- name: Publish GitHub release
27+
uses: MetaMask/action-publish-release@v3
28+
env:
29+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
30+
- run: yarn build
31+
32+
publish-npm-dry-run:
33+
name: Dry run publish to NPM
34+
runs-on: ubuntu-latest
35+
needs: publish-release
36+
steps:
37+
- name: Checkout and setup environment
38+
uses: MetaMask/action-checkout-and-setup@v2
39+
with:
40+
is-high-risk-environment: true
41+
ref: ${{ github.sha }}
42+
node-version: 24.x
43+
- name: Dry run publish to NPM
44+
uses: MetaMask/action-npm-publish@v5
45+
with:
46+
slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
47+
subteam: S042S7RE4AE # @metamask-npm-publishers
48+
49+
publish-npm:
50+
name: Publish to NPM
51+
environment: npm-publish
52+
runs-on: ubuntu-latest
53+
needs: publish-npm-dry-run
54+
steps:
55+
- name: Checkout and setup environment
56+
uses: MetaMask/action-checkout-and-setup@v2
57+
with:
58+
is-high-risk-environment: true
59+
ref: ${{ github.sha }}
60+
node-version: 24.x
61+
- name: Publish to NPM
62+
uses: MetaMask/action-npm-publish@v5
63+
with:
64+
npm-token: ${{ secrets.NPM_TOKEN }}

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ Thumbs.db
1111

1212
# Node modules (if any scripts use npm)
1313
node_modules/
14+
.yarn/*
15+
!.yarn/patches
16+
!.yarn/plugins
17+
!.yarn/releases
18+
!.yarn/sdks
19+
!.yarn/versions
1420

1521
# Python
1622
__pycache__/

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
lts/*

.yarnrc.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
compressionLevel: mixed
2+
3+
enableGlobalCache: false
4+
5+
enableScripts: false
6+
7+
enableTelemetry: false
8+
9+
nodeLinker: node-modules
10+
11+
npmMinimalAgeGate: 4320
12+
13+
npmPreapprovedPackages:
14+
- "@metamask/*"
15+
- "@lavamoat/*"

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
## [0.1.0]
11+
12+
### Added
13+
14+
- Add CLI discovery commands: `list`, `search`, and `describe`.
15+
- Add `@metamask/skills` package metadata for publishing the public MetaMask skills repo to npm.
16+
- Add `metamask-skills` CLI with `sync`, `postinstall`, and `install` commands.
17+
- Add repo inference, repo-local skills cache support, bundled package fallback, and `SKILLS_AUTO_UPDATE=1` handling for consumer repos.
18+
19+
[Unreleased]: https://github.com/MetaMask/skills/compare/@metamask/skills@0.1.0...HEAD
20+
[0.1.0]: https://github.com/MetaMask/skills/releases/tag/@metamask/skills@0.1.0

0 commit comments

Comments
 (0)