skillex 0.3.1 → 0.4.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/CHANGELOG.md +262 -1
- package/README.md +57 -10
- package/dist/auto-sync.d.ts +66 -0
- package/dist/auto-sync.js +91 -0
- package/dist/catalog.js +5 -29
- package/dist/cli.d.ts +13 -0
- package/dist/cli.js +247 -141
- package/dist/confirm.js +3 -1
- package/dist/direct-github.d.ts +60 -0
- package/dist/direct-github.js +177 -0
- package/dist/doctor.d.ts +31 -0
- package/dist/doctor.js +172 -0
- package/dist/downloader.d.ts +42 -0
- package/dist/downloader.js +41 -0
- package/dist/fs.d.ts +21 -1
- package/dist/fs.js +30 -3
- package/dist/http.d.ts +28 -7
- package/dist/http.js +143 -42
- package/dist/install.d.ts +23 -9
- package/dist/install.js +75 -348
- package/dist/lockfile.d.ts +46 -0
- package/dist/lockfile.js +169 -0
- package/dist/output.d.ts +11 -0
- package/dist/output.js +49 -0
- package/dist/recommended.d.ts +13 -0
- package/dist/recommended.js +21 -0
- package/dist/runner.js +9 -9
- package/dist/skill.d.ts +2 -0
- package/dist/skill.js +3 -0
- package/dist/sync.js +12 -9
- package/dist/types.d.ts +39 -0
- package/dist/types.js +28 -0
- package/dist/ui.js +1 -1
- package/dist/user-config.d.ts +5 -0
- package/dist/user-config.js +22 -1
- package/dist/web-ui.js +5 -0
- package/dist-ui/assets/CatalogPage-CbtMTkxd.js +1 -0
- package/dist-ui/assets/CatalogPage-W5MqylAz.css +1 -0
- package/dist-ui/assets/DoctorPage-oUZyX91t.js +1 -0
- package/dist-ui/assets/Skeleton-B_xm5L3P.js +1 -0
- package/dist-ui/assets/Skeleton-_Ooiw1nN.css +1 -0
- package/dist-ui/assets/SkillDetailPage-5JHQLq3q.js +1 -0
- package/dist-ui/assets/SkillDetailPage-CBAaWpcc.css +1 -0
- package/dist-ui/assets/{index-UBECch6X.css → index-CWm7zQTg.css} +1 -1
- package/dist-ui/assets/index-I0b-syhc.js +26 -0
- package/dist-ui/assets/recommended-D_i10hwH.js +1 -0
- package/dist-ui/index.html +2 -2
- package/package.json +2 -2
- package/dist-ui/assets/CatalogPage-B_qic36n.js +0 -1
- package/dist-ui/assets/SkillDetailPage-BJ3onKk4.js +0 -1
- package/dist-ui/assets/index-DN-z--cR.js +0 -25
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,259 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.4.0] - 2026-05-02
|
|
11
|
+
|
|
12
|
+
A substantial release focused on **reliability, security, and UX**. The CLI is
|
|
13
|
+
now production-grade (HTTP timeouts, host-restricted token, parallel
|
|
14
|
+
downloads, hardened parser); the Web UI grew bulk actions, keyboard
|
|
15
|
+
shortcuts, multi-select, persistent state, an accessible doctor panel, and a
|
|
16
|
+
mobile drawer; and the codebase was refactored for maintainability without
|
|
17
|
+
breaking the public API.
|
|
18
|
+
|
|
19
|
+
Test count went from 63 → 88 (no regressions).
|
|
20
|
+
|
|
21
|
+
### Highlights
|
|
22
|
+
|
|
23
|
+
- **Reliability & security pass** — HTTP timeouts everywhere, GitHub token
|
|
24
|
+
scoped to GitHub-owned hosts, lockfile path safety, symlink confinement,
|
|
25
|
+
ref-character validation, mode `0o600` on `~/.askillrc.json`, parallel
|
|
26
|
+
file downloads (5–10× speedup on multi-file skills).
|
|
27
|
+
- **CLI parser hardened** — unknown flags rejected with "did you mean"
|
|
28
|
+
suggestions, `--` sentinel honored, missing-value detection, unknown
|
|
29
|
+
commands suggest the closest match, `parseBooleanFlag` names the flag and
|
|
30
|
+
lists accepted values, `skillex doctor` differentiates DNS / TLS /
|
|
31
|
+
refused / timeout failures.
|
|
32
|
+
- **Three new top-level commands / flags** — `skillex show <id>` previews a
|
|
33
|
+
skill's SKILL.md without installing, `skillex init --install-recommended`
|
|
34
|
+
ships a curated starter pack, `skillex sync --dry-run --exit-code`
|
|
35
|
+
mirrors `git diff --exit-code` for CI drift detection.
|
|
36
|
+
- **Web UI marketplace overhaul** — Install all + Install recommended +
|
|
37
|
+
Remove all bulk actions; per-card optimistic UI; Shift+click multi-select
|
|
38
|
+
with range select; sticky selection bar; persistent selection across
|
|
39
|
+
refreshes; search highlight; group-by-source; related skills; mobile
|
|
40
|
+
drawer; Doctor panel + sidebar health dot; breadcrumbs; first-load
|
|
41
|
+
skeletons; `⌘K` and `⇧⌘A` shortcuts.
|
|
42
|
+
- **Internationalization & regression guard** — every user-facing string
|
|
43
|
+
is now English; `scripts/check-language.mjs` runs in `npm test` and fails
|
|
44
|
+
CI if banned Portuguese tokens reappear without an explicit
|
|
45
|
+
`i18n-allow:` annotation.
|
|
46
|
+
- **Refactor** — `src/install.ts` (1326 LOC) split into focused modules
|
|
47
|
+
(`lockfile.ts`, `direct-github.ts`, `auto-sync.ts`, `downloader.ts`,
|
|
48
|
+
`doctor.ts`) with re-exports preserving every existing import path.
|
|
49
|
+
|
|
50
|
+
### Added — Core / CLI
|
|
51
|
+
|
|
52
|
+
- `skillex show <skill-id>` — preview a skill's manifest + rendered SKILL.md
|
|
53
|
+
from the configured sources without installing. `--raw` prints the
|
|
54
|
+
markdown verbatim; `--json` returns the manifest plus the entry content
|
|
55
|
+
as a single object.
|
|
56
|
+
- `skillex init --install-recommended` — after writing the lockfile, install
|
|
57
|
+
a curated 5-skill starter pack (`commit-craft`, `code-review`,
|
|
58
|
+
`secure-defaults`, `error-handling`, `test-discipline`) using the same
|
|
59
|
+
progress bar as `install --all`. The recommended list lives in
|
|
60
|
+
`src/recommended.ts` (single source of truth).
|
|
61
|
+
- `skillex sync --dry-run --exit-code` — exit `1` whenever the dry-run
|
|
62
|
+
would change at least one adapter (mirrors `git diff --exit-code`). CI
|
|
63
|
+
scripts can detect drift without parsing the diff output.
|
|
64
|
+
- `--tags <tag>` is accepted as a hidden alias of `--tag` on
|
|
65
|
+
`skillex search` so previously documented examples keep working.
|
|
66
|
+
- `src/doctor.ts` exports `runDoctorChecks(options): Promise<DoctorReport>`
|
|
67
|
+
— the canonical six health checks reused by both the CLI and the Web UI.
|
|
68
|
+
- `HttpError` typed error class with codes `HTTP_TIMEOUT`,
|
|
69
|
+
`HTTP_RATE_LIMIT`, `HTTP_AUTH_FAILED`, `HTTP_NOT_FOUND`,
|
|
70
|
+
`HTTP_SERVER_ERROR`, and `HTTP_ERROR`.
|
|
71
|
+
- `RemoveSkillsResult.autoSyncs: SyncCommandResult[]` — full per-adapter
|
|
72
|
+
sync aggregate (the existing `autoSync` field is preserved as the first
|
|
73
|
+
result for backward compat).
|
|
74
|
+
- `createSymlink(target, link, { allowedRoot })` overload that refuses
|
|
75
|
+
targets resolving outside the managed root.
|
|
76
|
+
- `SkillManifest.category?: string` (optional). Catalog publishers can
|
|
77
|
+
group skills explicitly instead of relying on consumer-side regex
|
|
78
|
+
inference. Read from both `skill.json` and SKILL.md frontmatter.
|
|
79
|
+
- `suggestClosest(actual, candidates, threshold = 2)` helper in
|
|
80
|
+
`src/output.ts` powers "did you mean" hints across the parser and
|
|
81
|
+
dispatcher.
|
|
82
|
+
|
|
83
|
+
### Added — Web UI
|
|
84
|
+
|
|
85
|
+
- **Doctor panel** + `GET /api/doctor` endpoint mirroring the CLI's six
|
|
86
|
+
checks, with a sidebar status dot (green / yellow / red + pulse)
|
|
87
|
+
refreshed after every mutation.
|
|
88
|
+
- **Bulk install / remove / install-recommended** buttons in the catalog
|
|
89
|
+
hero, each guarded by an accessible `ConfirmDialog` (role=dialog,
|
|
90
|
+
aria-modal, focus on Cancel, Esc cancels, Enter only fires when no
|
|
91
|
+
button has focus).
|
|
92
|
+
- **Per-card optimistic UI** — single-skill install / remove / update show
|
|
93
|
+
a localized spinner inside the card and dim only that card. Backed by
|
|
94
|
+
`state.busyCards: Set<string>` and a `runCardAction(skillId, label, fn)`
|
|
95
|
+
store helper. Bulk actions still use the global overlay.
|
|
96
|
+
- **Bulk select via Shift+click** — sticky selection bar shows
|
|
97
|
+
`N selected`, `Select all visible (M)` link, and `Install N` /
|
|
98
|
+
`Remove N` buttons that count only the eligible subset. Plain click in
|
|
99
|
+
selection mode toggles. Esc clears (priority cascade — see Changed).
|
|
100
|
+
- **Range select** — Shift+click on a second card selects the range
|
|
101
|
+
between the anchor and the target in the visible-id ordering
|
|
102
|
+
(Finder/Excel behavior). The anchor moves with each interaction so
|
|
103
|
+
successive Shift+clicks extend from the latest endpoint.
|
|
104
|
+
- **Persistent selection** — selection (ids + anchor + scope) is mirrored
|
|
105
|
+
to `localStorage["skillex.selection"]`. Restored on startup, filtered to
|
|
106
|
+
ids that still exist in the loaded catalog under the same scope.
|
|
107
|
+
- **Search highlight** — query matches in skill name and description are
|
|
108
|
+
wrapped in `<mark class="search-mark">` with an accent background. Pure
|
|
109
|
+
template rendering (no `v-html`) — XSS-safe.
|
|
110
|
+
- **Group-by-source toggle** — when more than one source is configured, a
|
|
111
|
+
toggle in the category-pill row buckets the grid by source.repo with
|
|
112
|
+
per-bucket headers and counts.
|
|
113
|
+
- **Related skills** on the SkillDetailPage — up to 4 cards scored by tag
|
|
114
|
+
overlap (+ 0.5 bonus for matching `category`). Cards are keyboard
|
|
115
|
+
accessible, show a 2-line description clamp, up to 3 tags, and a ✓ when
|
|
116
|
+
the related skill is already installed.
|
|
117
|
+
- **Breadcrumbs** on the SkillDetailPage —
|
|
118
|
+
`Catalog (link, home icon) / category / skill-name (current)`.
|
|
119
|
+
Category resolves from the explicit manifest field first, falls back to
|
|
120
|
+
the regex inference, hidden when no signal.
|
|
121
|
+
- **Onboarding card** for fresh workspaces — replaces the generic empty
|
|
122
|
+
state with a bright Install-all CTA + count when the workspace has zero
|
|
123
|
+
installed skills and no filter is active.
|
|
124
|
+
- **Installed-only filter** — the "Installed" overview card is now a
|
|
125
|
+
toggle (`role=button`, Enter/Space, `aria-pressed`); active state filters
|
|
126
|
+
the grid to installed skills only. The stat now reads `N / total`.
|
|
127
|
+
- **Inferred category chip** on cards whose category came from regex
|
|
128
|
+
fallback rather than an explicit manifest field.
|
|
129
|
+
- **First-load skeletons** — new `Skeleton.vue` (variants `card` / `row`).
|
|
130
|
+
Catalog page renders 6 card-skeletons until `state.catalog` resolves;
|
|
131
|
+
Doctor page renders 4 row-skeletons; SkillDetailPage renders metadata
|
|
132
|
+
+ body + related skeletons during its first fetch.
|
|
133
|
+
- **Mobile drawer** — sidebar slides in/out on viewports ≤680 px instead
|
|
134
|
+
of being hidden entirely. Triggered by a topbar hamburger; tap backdrop
|
|
135
|
+
or Esc to close; route changes auto-close.
|
|
136
|
+
- **Adapter dropdown** for compact viewports (≤1100 px) — replaces the 7
|
|
137
|
+
icon row with a single trigger + listbox menu (`role=listbox`,
|
|
138
|
+
`role=option`, `aria-selected`).
|
|
139
|
+
- **`⌘K` / `Ctrl+K` shortcut** — focuses the topbar search. Navigates back
|
|
140
|
+
to `/` first when invoked from another route. Adaptive hint badge.
|
|
141
|
+
- **`⇧⌘A` / `Ctrl+Shift+A` shortcut** — opens the Install-all confirm on
|
|
142
|
+
the catalog page. Skipped when typing in inputs. Shift required so the
|
|
143
|
+
browser's native "select all" (Cmd+A) is preserved. Discoverable via the
|
|
144
|
+
shortcut chip on the Install-all button.
|
|
145
|
+
- **Demo media placeholder** — new `docs/media/` with a README describing
|
|
146
|
+
how to regenerate `tui.gif`, `web-ui.png`, `web-ui-doctor.png` (vhs +
|
|
147
|
+
asciinema). Main README has a "Demo" section that references the media
|
|
148
|
+
via raw GitHub URLs (no binaries in the npm tarball).
|
|
149
|
+
- **"Why Skillex" section** in the README plus a Quick Start that leads
|
|
150
|
+
with `npx skillex@latest` (the TUI) and frames the
|
|
151
|
+
`init` → `install` chain as scriptable mode.
|
|
152
|
+
|
|
153
|
+
### Changed
|
|
154
|
+
|
|
155
|
+
- **Reliability:** all HTTP helpers now abort after a default 30-second
|
|
156
|
+
timeout when the caller does not provide an `AbortSignal`, raising
|
|
157
|
+
`HttpError("HTTP_TIMEOUT")` instead of hanging.
|
|
158
|
+
- **HTTP errors:** 403 responses split into `HTTP_RATE_LIMIT` (when
|
|
159
|
+
`X-RateLimit-Remaining: 0`) and `HTTP_AUTH_FAILED`, each with an
|
|
160
|
+
actionable message. The rate-limit message includes the reset hint.
|
|
161
|
+
- **`maybeSyncAfterRemove`** now runs adapter syncs in parallel via
|
|
162
|
+
`Promise.all` and returns the full aggregate; the CLI prints one line
|
|
163
|
+
per adapter (was previously dropping all but the last adapter's
|
|
164
|
+
outcome).
|
|
165
|
+
- **`toInstallError`** preserves the underlying `error.code` (HTTP error
|
|
166
|
+
codes, `EACCES`, `ENOENT`, etc.) on the wrapped `InstallError`.
|
|
167
|
+
- **CLI parser** is now schema-driven via `STRING_FLAGS` / `BOOLEAN_FLAGS`
|
|
168
|
+
/ `KNOWN_FLAGS` sets. Unknown flags raise `CliError("UNKNOWN_FLAG")`
|
|
169
|
+
with a Levenshtein-based "did you mean" suggestion (typos like
|
|
170
|
+
`--scop=global` are no longer silently accepted).
|
|
171
|
+
- **CLI parser** detects missing values for string flags and raises
|
|
172
|
+
`CliError("MISSING_FLAG_VALUE")` naming the offending flag.
|
|
173
|
+
- **CLI parser** honors the literal `--` end-of-options sentinel and
|
|
174
|
+
exposes everything after it via `ParsedArgs.positionalAfter`, so
|
|
175
|
+
`skillex run x:cmd -- --foo` forwards `--foo` to the underlying script
|
|
176
|
+
without flag interpretation.
|
|
177
|
+
- **CLI dispatcher** suggests the closest match for unknown commands
|
|
178
|
+
(e.g. `skillex insall git-master` → `Did you mean: install?`).
|
|
179
|
+
- **`parseBooleanFlag`** error now names the flag and lists every accepted
|
|
180
|
+
value: `Invalid value "maybe" for --auto-sync. Use true, false, yes,
|
|
181
|
+
no, on, off, 1, or 0.`
|
|
182
|
+
- **`INSTALL_NO_TARGETS`** (`skillex install` with no args) now prints a
|
|
183
|
+
3-line inline usage block instead of a one-liner hint.
|
|
184
|
+
- **`skillex doctor`** differentiates DNS, connection-refused, TLS,
|
|
185
|
+
timeout, and 5xx failures and surfaces the underlying `error.message`
|
|
186
|
+
instead of collapsing every failure into "GitHub API is unreachable".
|
|
187
|
+
- **`skillex init`** ends with a three-line "Next steps" block (TUI /
|
|
188
|
+
starter pack / full catalog) instead of a single line. Suppressed when
|
|
189
|
+
`--install-recommended` was used.
|
|
190
|
+
- **SkillDetailPage** action order rewritten following "primary action
|
|
191
|
+
last": Sync (ghost) → Update (secondary, only when installed) →
|
|
192
|
+
Install (primary) or Remove (danger).
|
|
193
|
+
- **Web UI Esc cascade** — the Esc key now follows a priority chain:
|
|
194
|
+
ConfirmDialog → adapter dropdown → mobile drawer → CatalogPage
|
|
195
|
+
selection. Each handler checks `event.defaultPrevented` before acting,
|
|
196
|
+
so only one consumer fires per keypress.
|
|
197
|
+
- **Refactor:** `src/install.ts` (1326 LOC) split into `src/lockfile.ts`,
|
|
198
|
+
`src/direct-github.ts`, `src/auto-sync.ts`, `src/downloader.ts`. Public
|
|
199
|
+
`package.json#exports` and import paths preserved via re-exports.
|
|
200
|
+
- Consolidated SKILL.md frontmatter parsing on `parseSkillFrontmatter`
|
|
201
|
+
(`src/skill.ts`); the duplicated inline parser in `src/catalog.ts` was
|
|
202
|
+
removed.
|
|
203
|
+
|
|
204
|
+
### Fixed
|
|
205
|
+
|
|
206
|
+
- All remaining Portuguese user-facing strings translated to English: TUI
|
|
207
|
+
prompt label, sync symlink-fallback warnings, runner errors,
|
|
208
|
+
direct-install confirmation, non-TTY confirm prompt, filesystem path
|
|
209
|
+
errors, and Web UI labels (sidebar, catalog, skill card buttons,
|
|
210
|
+
detail page).
|
|
211
|
+
- Sync warnings now route through `output.warn` instead of bare
|
|
212
|
+
`console.error` so they respect color and stream conventions.
|
|
213
|
+
- New `scripts/check-language.mjs` regression guard runs as part of
|
|
214
|
+
`npm test` and fails CI if banned Portuguese tokens reappear in
|
|
215
|
+
`src/**/*.ts` or `ui/src/**/*.{vue,ts}` without an explicit
|
|
216
|
+
`i18n-allow:` annotation.
|
|
217
|
+
- `toLockfileSource` boolean expression no longer contains a duplicated
|
|
218
|
+
`(label || repo === DEFAULT_REPO)` test (copy-paste artifact); behavior
|
|
219
|
+
unchanged.
|
|
220
|
+
- **README** example at the search section now uses the canonical
|
|
221
|
+
`--tag <tag>` flag instead of the silently-ignored `--tags`.
|
|
222
|
+
- **Web UI:** version badge in the sidebar reads the actual
|
|
223
|
+
`package.json#version` at build time via
|
|
224
|
+
`import.meta.env.VITE_SKILLEX_VERSION` instead of the previously
|
|
225
|
+
hardcoded `v0.2.4` (with a tautological ternary).
|
|
226
|
+
- **Web UI:** dead `⌘K` hint badge removed (no shortcut was wired) — and
|
|
227
|
+
brought back once `Cmd+K` was actually implemented.
|
|
228
|
+
- **Web UI:** misleading "Oficial" badge that every skill card displayed
|
|
229
|
+
(without any verification model) removed.
|
|
230
|
+
- **Web UI:** mobile (≤680 px) sidebar no longer disappears entirely.
|
|
231
|
+
- **Web UI:** `Remover` and `Carregando detalhes...` strings on the detail
|
|
232
|
+
page translated to English.
|
|
233
|
+
|
|
234
|
+
### Security
|
|
235
|
+
|
|
236
|
+
- **Token confinement:** `Authorization: Bearer ${GITHUB_TOKEN}` is only
|
|
237
|
+
attached when the request host is `api.github.com`,
|
|
238
|
+
`raw.githubusercontent.com`, or any `*.githubusercontent.com` mirror.
|
|
239
|
+
Tokens are no longer leaked to third-party `--catalog-url` targets.
|
|
240
|
+
- **File mode:** `~/.askillrc.json` (which may contain `githubToken`) is
|
|
241
|
+
written with mode `0o600`. Existing world-readable files are tightened
|
|
242
|
+
on next save with a one-time warning.
|
|
243
|
+
- **Lockfile path safety:** `removeSkill` validates `metadata.path`
|
|
244
|
+
against the managed skills store before deleting; tampered lockfile
|
|
245
|
+
paths raise `INSTALL_PATH_UNSAFE` instead of removing arbitrary
|
|
246
|
+
directories.
|
|
247
|
+
- **Symlink confinement:** sync per-skill symlinks now refuse targets
|
|
248
|
+
that resolve outside the workspace state directory.
|
|
249
|
+
- **Direct-install ref:** `parseDirectGitHubRef` validates the ref segment
|
|
250
|
+
against `^[A-Za-z0-9_.\-/]+$` and rejects empty refs after `@`;
|
|
251
|
+
previously dangerous refs would land in the lockfile.
|
|
252
|
+
- **Web UI avatars:** skill author avatars are now a deterministic
|
|
253
|
+
CSS-only initials chip. The previous `<img>` to `dicebear.com` is gone
|
|
254
|
+
— the UI works offline and no longer leaks page-load telemetry to a
|
|
255
|
+
third-party host.
|
|
256
|
+
|
|
257
|
+
### Performance
|
|
258
|
+
|
|
259
|
+
- **Parallel file downloads:** `downloadSkillFiles` fetches every file in
|
|
260
|
+
a skill via `Promise.all` instead of a sequential loop. Multi-file
|
|
261
|
+
skills now scale with bandwidth instead of file count.
|
|
262
|
+
|
|
10
263
|
## [0.3.1] - 2026-04-08
|
|
11
264
|
|
|
12
265
|
### Fixed
|
|
@@ -127,7 +380,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
127
380
|
- Adapter detection and managed-block sync for Copilot, Cline, Cursor, Claude, Gemini, Windsurf, Codex
|
|
128
381
|
- Lockfile-based workspace state at `.agent-skills/skills.json`
|
|
129
382
|
|
|
130
|
-
[Unreleased]: https://github.com/lgili/skillex/compare/v0.
|
|
383
|
+
[Unreleased]: https://github.com/lgili/skillex/compare/v0.4.0...HEAD
|
|
384
|
+
[0.4.0]: https://github.com/lgili/skillex/compare/v0.3.1...v0.4.0
|
|
385
|
+
[0.3.1]: https://github.com/lgili/skillex/compare/v0.3.0...v0.3.1
|
|
386
|
+
[0.3.0]: https://github.com/lgili/skillex/compare/v0.2.5...v0.3.0
|
|
387
|
+
[0.2.5]: https://github.com/lgili/skillex/compare/v0.2.4...v0.2.5
|
|
388
|
+
[0.2.4]: https://github.com/lgili/skillex/compare/v0.2.3...v0.2.4
|
|
389
|
+
[0.2.3]: https://github.com/lgili/skillex/compare/v0.2.2...v0.2.3
|
|
390
|
+
[0.2.2]: https://github.com/lgili/skillex/compare/v0.2.1...v0.2.2
|
|
391
|
+
[0.2.1]: https://github.com/lgili/skillex/compare/v0.2.0...v0.2.1
|
|
131
392
|
[0.2.0]: https://github.com/lgili/skillex/compare/v0.1.1...v0.2.0
|
|
132
393
|
[0.1.1]: https://github.com/lgili/skillex/compare/v0.1.0...v0.1.1
|
|
133
394
|
[0.1.0]: https://github.com/lgili/skillex/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -28,21 +28,40 @@
|
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
|
31
|
+
## Why Skillex?
|
|
32
|
+
|
|
33
|
+
- **One install, every agent.** Skills get exposed to Claude / Codex / Cursor / Copilot / Cline / Gemini / Windsurf at once, instead of copy-pasting the same prompt into seven config files.
|
|
34
|
+
- **Catalog-driven, not folder-driven.** Skills live in versioned GitHub repos with manifests, descriptions, and tags. Update them with one command instead of `git submodule` gymnastics.
|
|
35
|
+
- **Lockfile + auto-sync.** Skillex tracks what's installed and re-syncs after every change so the agent surface in your repo always matches the lockfile.
|
|
36
|
+
|
|
31
37
|
## Quick Start
|
|
32
38
|
|
|
33
39
|
Get up and running in under two minutes using the built-in first-party skills catalog:
|
|
34
40
|
|
|
35
41
|
```bash
|
|
36
|
-
#
|
|
37
|
-
npx skillex@latest
|
|
42
|
+
# Easiest: open the interactive terminal browser
|
|
43
|
+
npx skillex@latest
|
|
38
44
|
|
|
39
|
-
#
|
|
40
|
-
npx skillex@latest
|
|
41
|
-
|
|
42
|
-
#
|
|
43
|
-
npx skillex@latest
|
|
45
|
+
# Or, scriptable mode:
|
|
46
|
+
npx skillex@latest init # set up the workspace
|
|
47
|
+
npx skillex@latest init --install-recommended # ...with a curated starter pack
|
|
48
|
+
npx skillex@latest install create-skills # install one skill by id
|
|
49
|
+
npx skillex@latest show code-review # preview a skill before installing
|
|
50
|
+
npx skillex@latest list # list every available skill
|
|
44
51
|
```
|
|
45
52
|
|
|
53
|
+
> **Tip:** after the first `npx skillex@latest` call, the binary is cached on your machine, so subsequent invocations can drop the `npx skillex@latest` prefix and just call `skillex`.
|
|
54
|
+
|
|
55
|
+
## Demo
|
|
56
|
+
|
|
57
|
+
| Surface | Preview |
|
|
58
|
+
|---------|---------|
|
|
59
|
+
| Terminal browser (`skillex` with no args) |  |
|
|
60
|
+
| Web UI catalog page (`skillex ui`) |  |
|
|
61
|
+
| Web UI Doctor panel |  |
|
|
62
|
+
|
|
63
|
+
> The media files live in [`docs/media/`](https://github.com/lgili/skillex/tree/main/docs/media) and are referenced via raw GitHub URLs so the npm tarball stays small. See [`docs/media/README.md`](https://github.com/lgili/skillex/blob/main/docs/media/README.md) for re-recording instructions.
|
|
64
|
+
|
|
46
65
|
> **Important:** auto-sync is enabled by default. After `init`, `install`, `update`, and `remove`, Skillex automatically synchronizes skills into every detected adapter target. For directory-native adapters such as Codex, Claude, and Gemini, this materializes one folder per skill under the agent's `skills/` directory. For file-based adapters such as Copilot, Cursor, Cline, and Windsurf, it updates the adapter config file. Use `skillex sync` when you want to preview, re-run manually, or target a specific adapter.
|
|
47
66
|
|
|
48
67
|
After `init`, Skillex saves the configured source list in the local lockfile. New workspaces start with `lgili/skillex@main` by default, and you can add more sources later with `skillex source add`.
|
|
@@ -110,6 +129,7 @@ skillex init --global --adapter codex
|
|
|
110
129
|
| `--repo <owner/repo>` | Optional. Overrides the default first-party source for this workspace. |
|
|
111
130
|
| `--adapter <id>` | Force a specific adapter instead of auto-detecting. |
|
|
112
131
|
| `--auto-sync` | Enable or disable automatic sync after install, update, and remove. Default: `true`. |
|
|
132
|
+
| `--install-recommended` | After init, install a curated starter pack (`commit-craft`, `code-review`, `secure-defaults`, `error-handling`, `test-discipline`). |
|
|
113
133
|
| `--ref <branch>` | Use a specific branch or tag (default: `main`). |
|
|
114
134
|
| `--scope <local\|global>` | Choose whether Skillex manages workspace or user-global state. |
|
|
115
135
|
| `--global` | Shortcut for `--scope global`. |
|
|
@@ -150,7 +170,7 @@ skillex search code-review --repo myorg/my-skills
|
|
|
150
170
|
|------|-------------|
|
|
151
171
|
| `--repo <owner/repo>` | Limit the command to a single source instead of aggregating all configured sources. |
|
|
152
172
|
| `--compatibility <adapter>` | Filter by adapter (e.g. `cursor`, `claude`, `codex`). |
|
|
153
|
-
| `--
|
|
173
|
+
| `--tag <tag>` | Filter by tag. (`--tags` is also accepted for backward compatibility with earlier docs.) |
|
|
154
174
|
| `--json` | Print raw JSON. |
|
|
155
175
|
|
|
156
176
|
---
|
|
@@ -271,11 +291,34 @@ Execute a script declared in a skill's `skill.json`.
|
|
|
271
291
|
|
|
272
292
|
```bash
|
|
273
293
|
skillex run git-master:cleanup
|
|
274
|
-
skillex run git-master:cleanup --yes
|
|
294
|
+
skillex run git-master:cleanup --yes # skip confirmation
|
|
295
|
+
skillex run git-master:cleanup -- --extra=arg # forward "--extra=arg" to the script
|
|
275
296
|
```
|
|
276
297
|
|
|
277
298
|
---
|
|
278
299
|
|
|
300
|
+
### `show`
|
|
301
|
+
|
|
302
|
+
Print a skill's manifest summary plus its rendered `SKILL.md` content from the
|
|
303
|
+
configured catalog sources, **without installing anything**. Use this to
|
|
304
|
+
preview a skill before deciding to install.
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
skillex show git-master # human-friendly summary + content
|
|
308
|
+
skillex show code-review --raw # SKILL.md verbatim, no header
|
|
309
|
+
skillex show secure-defaults --json # manifest + entry content as JSON
|
|
310
|
+
skillex show foo --repo myorg/my-skills # disambiguate when multiple sources match
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
| Flag | Description |
|
|
314
|
+
|------|-------------|
|
|
315
|
+
| `--repo <owner/repo>` | Limit resolution to one source. |
|
|
316
|
+
| `--raw` | Print SKILL.md verbatim with no manifest header. |
|
|
317
|
+
| `--json` | Print the resolved manifest plus the raw SKILL.md as a single JSON object. |
|
|
318
|
+
| `--no-cache` | Bypass local catalog cache. |
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
279
322
|
### Default terminal browser
|
|
280
323
|
|
|
281
324
|
Running `skillex` with no subcommand now opens the interactive terminal browser by default.
|
|
@@ -481,6 +524,7 @@ catalog.json ← optional but recommended
|
|
|
481
524
|
"description": "Teaches the agent to write semantic commits and manage branches.",
|
|
482
525
|
"author": "your-name",
|
|
483
526
|
"tags": ["git", "workflow"],
|
|
527
|
+
"category": "workflow",
|
|
484
528
|
"compatibility": ["codex", "copilot", "cline", "cursor", "claude", "gemini", "windsurf"],
|
|
485
529
|
"entry": "SKILL.md",
|
|
486
530
|
"files": ["SKILL.md", "tools/git-cleanup.js"],
|
|
@@ -490,12 +534,15 @@ catalog.json ← optional but recommended
|
|
|
490
534
|
}
|
|
491
535
|
```
|
|
492
536
|
|
|
537
|
+
The `category` field is **optional**. When present, the Web UI groups the skill under that category explicitly. When absent, the catalog falls back to a regex-based inference and tags the resulting badge with a small `(inferred)` chip so contributors can see when their metadata is incomplete.
|
|
538
|
+
|
|
493
539
|
### `SKILL.md` frontmatter
|
|
494
540
|
|
|
495
541
|
```markdown
|
|
496
542
|
---
|
|
497
543
|
name: "git-master"
|
|
498
544
|
description: "Git workflow instructions"
|
|
545
|
+
category: "workflow"
|
|
499
546
|
autoInject: true
|
|
500
547
|
activationPrompt: "Always apply Git Master rules when the user asks for Git help."
|
|
501
548
|
---
|
|
@@ -505,7 +552,7 @@ activationPrompt: "Always apply Git Master rules when the user asks for Git help
|
|
|
505
552
|
Your skill content goes here...
|
|
506
553
|
```
|
|
507
554
|
|
|
508
|
-
When `autoInject: true` and `activationPrompt` are set, `skillex sync` injects the activation prompt in a separate managed block for adapters that use a shared or dedicated config file.
|
|
555
|
+
When `autoInject: true` and `activationPrompt` are set, `skillex sync` injects the activation prompt in a separate managed block for adapters that use a shared or dedicated config file. `category` is optional and behaves the same whether declared in `skill.json` or in the SKILL.md frontmatter.
|
|
509
556
|
|
|
510
557
|
---
|
|
511
558
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-sync orchestration after install / update / remove operations.
|
|
3
|
+
*
|
|
4
|
+
* Decoupled from `install.ts` so the install/update/remove handlers focus on
|
|
5
|
+
* lockfile mutations only. The `syncFn` parameter (typically
|
|
6
|
+
* `syncInstalledSkills` from `install.ts`) is injected to avoid an import
|
|
7
|
+
* cycle.
|
|
8
|
+
*/
|
|
9
|
+
import type { InstallScope, LockfileState, NowFn, SyncCommandResult, SyncHistory, SyncWriteMode } from "./types.js";
|
|
10
|
+
/**
|
|
11
|
+
* Resolves the list of adapter ids that should receive a sync. When an
|
|
12
|
+
* explicit override is provided, only that adapter is targeted. Otherwise,
|
|
13
|
+
* the active adapter (if any) is listed first followed by the remaining
|
|
14
|
+
* detected adapters in workspace order.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveSyncAdapterIds(adapters: LockfileState["adapters"], adapterOverride?: string): string[];
|
|
17
|
+
/** Options passed to `maybeAutoSync` after install / update operations. */
|
|
18
|
+
export interface MaybeAutoSyncOptions {
|
|
19
|
+
cwd: string;
|
|
20
|
+
scope?: InstallScope | undefined;
|
|
21
|
+
agentSkillsDir?: string | undefined;
|
|
22
|
+
adapters: LockfileState["adapters"];
|
|
23
|
+
adapterOverride?: string | undefined;
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
now: NowFn;
|
|
26
|
+
changed: boolean;
|
|
27
|
+
mode?: SyncWriteMode | undefined;
|
|
28
|
+
}
|
|
29
|
+
/** Sync function shape injected by callers (typically `syncInstalledSkills`). */
|
|
30
|
+
export type SyncFn = (options: {
|
|
31
|
+
cwd: string;
|
|
32
|
+
scope: InstallScope;
|
|
33
|
+
agentSkillsDir?: string;
|
|
34
|
+
adapter?: string;
|
|
35
|
+
mode?: SyncWriteMode;
|
|
36
|
+
now: NowFn;
|
|
37
|
+
}) => Promise<SyncCommandResult>;
|
|
38
|
+
/**
|
|
39
|
+
* Triggers a sync after install/update when auto-sync is enabled and at
|
|
40
|
+
* least one skill changed. Returns `null` when no sync was performed.
|
|
41
|
+
*/
|
|
42
|
+
export declare function maybeAutoSync(options: MaybeAutoSyncOptions, syncFn: SyncFn): Promise<SyncCommandResult | null>;
|
|
43
|
+
/** Options passed to `maybeSyncAfterRemove`. */
|
|
44
|
+
export interface MaybeSyncAfterRemoveOptions {
|
|
45
|
+
cwd: string;
|
|
46
|
+
scope?: InstallScope | undefined;
|
|
47
|
+
agentSkillsDir?: string | undefined;
|
|
48
|
+
adapters: LockfileState["adapters"];
|
|
49
|
+
adapterOverride?: string | undefined;
|
|
50
|
+
syncHistory: SyncHistory;
|
|
51
|
+
legacySync: LockfileState["sync"];
|
|
52
|
+
enabled: boolean;
|
|
53
|
+
now: NowFn;
|
|
54
|
+
changed: boolean;
|
|
55
|
+
mode?: SyncWriteMode | undefined;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Triggers a sync after remove operations across every previously-synced
|
|
59
|
+
* adapter (so that removed skills are dropped from each adapter's view).
|
|
60
|
+
*
|
|
61
|
+
* Each adapter is synced concurrently via `Promise.all` and the results are
|
|
62
|
+
* aggregated into an array so callers can report each adapter's outcome
|
|
63
|
+
* individually. Returns `null` when no sync was performed (no changes or
|
|
64
|
+
* no adapters to sync).
|
|
65
|
+
*/
|
|
66
|
+
export declare function maybeSyncAfterRemove(options: MaybeSyncAfterRemoveOptions, syncFn: SyncFn): Promise<SyncCommandResult[] | null>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-sync orchestration after install / update / remove operations.
|
|
3
|
+
*
|
|
4
|
+
* Decoupled from `install.ts` so the install/update/remove handlers focus on
|
|
5
|
+
* lockfile mutations only. The `syncFn` parameter (typically
|
|
6
|
+
* `syncInstalledSkills` from `install.ts`) is injected to avoid an import
|
|
7
|
+
* cycle.
|
|
8
|
+
*/
|
|
9
|
+
import { DEFAULT_INSTALL_SCOPE } from "./config.js";
|
|
10
|
+
/**
|
|
11
|
+
* Resolves the list of adapter ids that should receive a sync. When an
|
|
12
|
+
* explicit override is provided, only that adapter is targeted. Otherwise,
|
|
13
|
+
* the active adapter (if any) is listed first followed by the remaining
|
|
14
|
+
* detected adapters in workspace order.
|
|
15
|
+
*/
|
|
16
|
+
export function resolveSyncAdapterIds(adapters, adapterOverride) {
|
|
17
|
+
if (adapterOverride) {
|
|
18
|
+
return [adapterOverride];
|
|
19
|
+
}
|
|
20
|
+
const adapterIds = [];
|
|
21
|
+
if (adapters.active) {
|
|
22
|
+
adapterIds.push(adapters.active);
|
|
23
|
+
}
|
|
24
|
+
for (const adapterId of adapters.detected || []) {
|
|
25
|
+
if (!adapterIds.includes(adapterId)) {
|
|
26
|
+
adapterIds.push(adapterId);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return adapterIds;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Triggers a sync after install/update when auto-sync is enabled and at
|
|
33
|
+
* least one skill changed. Returns `null` when no sync was performed.
|
|
34
|
+
*/
|
|
35
|
+
export async function maybeAutoSync(options, syncFn) {
|
|
36
|
+
if (!options.enabled || !options.changed) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
if (resolveSyncAdapterIds(options.adapters, options.adapterOverride).length === 0) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
return syncFn({
|
|
43
|
+
cwd: options.cwd,
|
|
44
|
+
scope: options.scope || DEFAULT_INSTALL_SCOPE,
|
|
45
|
+
...(options.agentSkillsDir ? { agentSkillsDir: options.agentSkillsDir } : {}),
|
|
46
|
+
...(options.adapterOverride ? { adapter: options.adapterOverride } : {}),
|
|
47
|
+
...(options.mode ? { mode: options.mode } : {}),
|
|
48
|
+
now: options.now,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Triggers a sync after remove operations across every previously-synced
|
|
53
|
+
* adapter (so that removed skills are dropped from each adapter's view).
|
|
54
|
+
*
|
|
55
|
+
* Each adapter is synced concurrently via `Promise.all` and the results are
|
|
56
|
+
* aggregated into an array so callers can report each adapter's outcome
|
|
57
|
+
* individually. Returns `null` when no sync was performed (no changes or
|
|
58
|
+
* no adapters to sync).
|
|
59
|
+
*/
|
|
60
|
+
export async function maybeSyncAfterRemove(options, syncFn) {
|
|
61
|
+
if (!options.changed) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const adapters = new Set();
|
|
65
|
+
for (const adapterId of Object.keys(options.syncHistory || {})) {
|
|
66
|
+
adapters.add(adapterId);
|
|
67
|
+
}
|
|
68
|
+
if (options.legacySync?.adapter) {
|
|
69
|
+
adapters.add(options.legacySync.adapter);
|
|
70
|
+
}
|
|
71
|
+
if (options.adapterOverride) {
|
|
72
|
+
adapters.add(options.adapterOverride);
|
|
73
|
+
}
|
|
74
|
+
else if (options.enabled) {
|
|
75
|
+
for (const adapterId of resolveSyncAdapterIds(options.adapters)) {
|
|
76
|
+
adapters.add(adapterId);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (adapters.size === 0) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
const results = await Promise.all([...adapters].map((adapterId) => syncFn({
|
|
83
|
+
cwd: options.cwd,
|
|
84
|
+
scope: options.scope || DEFAULT_INSTALL_SCOPE,
|
|
85
|
+
...(options.agentSkillsDir ? { agentSkillsDir: options.agentSkillsDir } : {}),
|
|
86
|
+
adapter: adapterId,
|
|
87
|
+
...(options.mode ? { mode: options.mode } : {}),
|
|
88
|
+
now: options.now,
|
|
89
|
+
})));
|
|
90
|
+
return results;
|
|
91
|
+
}
|
package/dist/catalog.js
CHANGED
|
@@ -6,6 +6,7 @@ import { normalizeAdapterList } from "./adapters.js";
|
|
|
6
6
|
import { assertSafeRelativePath } from "./fs.js";
|
|
7
7
|
import { fetchJson, fetchOptionalJson, fetchText } from "./http.js";
|
|
8
8
|
import { debug } from "./output.js";
|
|
9
|
+
import { parseSkillFrontmatter } from "./skill.js";
|
|
9
10
|
import { CatalogError } from "./types.js";
|
|
10
11
|
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
11
12
|
/**
|
|
@@ -210,13 +211,14 @@ async function loadCatalogFromTree(source) {
|
|
|
210
211
|
const skillPath = skillFile.path.slice(0, -"/SKILL.md".length);
|
|
211
212
|
const skillId = skillPath.split("/").pop();
|
|
212
213
|
const skillBody = await fetchJsonLikeText(buildRawGitHubUrl(source.repo, source.ref, skillFile.path));
|
|
213
|
-
const metadata =
|
|
214
|
+
const metadata = parseSkillFrontmatter(skillBody);
|
|
214
215
|
return normalizeSkill({
|
|
215
216
|
id: skillId,
|
|
216
217
|
path: skillPath,
|
|
217
218
|
name: metadata.name || skillId,
|
|
218
219
|
description: metadata.description || "",
|
|
219
220
|
files: collectFilesUnderPath(files, skillPath),
|
|
221
|
+
...(metadata.category ? { category: metadata.category } : {}),
|
|
220
222
|
}, source);
|
|
221
223
|
}));
|
|
222
224
|
return {
|
|
@@ -229,34 +231,6 @@ async function loadCatalogFromTree(source) {
|
|
|
229
231
|
async function fetchJsonLikeText(url) {
|
|
230
232
|
return fetchText(url, { headers: { Accept: "text/plain" } });
|
|
231
233
|
}
|
|
232
|
-
function extractSkillMetadata(content) {
|
|
233
|
-
const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
234
|
-
if (!match) {
|
|
235
|
-
return {};
|
|
236
|
-
}
|
|
237
|
-
const frontmatter = match[1];
|
|
238
|
-
if (frontmatter === undefined) {
|
|
239
|
-
return {};
|
|
240
|
-
}
|
|
241
|
-
const name = extractFrontmatterValue(frontmatter, "name");
|
|
242
|
-
const description = extractFrontmatterValue(frontmatter, "description");
|
|
243
|
-
return {
|
|
244
|
-
...(name !== null ? { name } : {}),
|
|
245
|
-
...(description !== null ? { description } : {}),
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
function extractFrontmatterValue(frontmatter, key) {
|
|
249
|
-
const expression = new RegExp(`^${key}:\\s*(.+)$`, "m");
|
|
250
|
-
const match = frontmatter.match(expression);
|
|
251
|
-
if (!match) {
|
|
252
|
-
return null;
|
|
253
|
-
}
|
|
254
|
-
const value = match[1];
|
|
255
|
-
if (value === undefined) {
|
|
256
|
-
return null;
|
|
257
|
-
}
|
|
258
|
-
return value.trim().replace(/^["']|["']$/g, "");
|
|
259
|
-
}
|
|
260
234
|
function collectFilesUnderPath(treeFiles, skillPath) {
|
|
261
235
|
return treeFiles
|
|
262
236
|
.filter((item) => item.type === "blob" && item.path.startsWith(`${skillPath}/`))
|
|
@@ -303,6 +277,7 @@ function normalizeSkill(skill, source) {
|
|
|
303
277
|
const skillPath = skill.path || `${source.skillsDir}/${id}`;
|
|
304
278
|
const files = Array.isArray(skill.files) ? skill.files.map(assertSafeRelativePath) : ["SKILL.md"];
|
|
305
279
|
const uniqueFiles = [...new Set(files)];
|
|
280
|
+
const category = typeof skill.category === "string" && skill.category.trim() ? skill.category.trim() : undefined;
|
|
306
281
|
return {
|
|
307
282
|
id,
|
|
308
283
|
name: skill.name || id,
|
|
@@ -314,6 +289,7 @@ function normalizeSkill(skill, source) {
|
|
|
314
289
|
entry: skill.entry || "SKILL.md",
|
|
315
290
|
path: stripLeadingSlash(skillPath),
|
|
316
291
|
files: uniqueFiles,
|
|
292
|
+
...(category !== undefined ? { category } : {}),
|
|
317
293
|
};
|
|
318
294
|
}
|
|
319
295
|
/**
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ParsedArgs } from "./types.js";
|
|
1
2
|
/**
|
|
2
3
|
* Runs the Skillex CLI entrypoint.
|
|
3
4
|
*
|
|
@@ -6,3 +7,15 @@
|
|
|
6
7
|
*/
|
|
7
8
|
export declare function main(argv: string[]): Promise<void>;
|
|
8
9
|
export declare function resolveCommandRoute(command: string | undefined): string;
|
|
10
|
+
/**
|
|
11
|
+
* Parses argv into a typed `ParsedArgs` shape with strict validation:
|
|
12
|
+
*
|
|
13
|
+
* - Unknown flags raise `UNKNOWN_FLAG` with a "did you mean" suggestion.
|
|
14
|
+
* - Boolean flags (`BOOLEAN_FLAGS`) accept presence-only or `--flag=value` forms.
|
|
15
|
+
* - String flags (`STRING_FLAGS`) require a value via `--flag=value` or
|
|
16
|
+
* `--flag value`. Missing values raise `MISSING_FLAG_VALUE`.
|
|
17
|
+
* - The literal `--` token marks end-of-options; remaining tokens become
|
|
18
|
+
* `positionalAfter` and are forwarded to handlers (used by `run` to pass
|
|
19
|
+
* arguments to the underlying script without flag interpretation).
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseArgs(argv: string[]): ParsedArgs;
|