@hasna/skills 0.4.0 → 0.5.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.
Files changed (45) hide show
  1. package/README.md +176 -5
  2. package/bin/index.js +6518 -4950
  3. package/bin/mcp.js +1173 -410
  4. package/bin/migrate.js +148 -40
  5. package/bin/server.js +53 -83
  6. package/bin/worker.js +41 -73
  7. package/dist/admin-contract.d.ts +37 -19
  8. package/dist/admin-contract.js +1 -1
  9. package/dist/cli/cli.test-utils.d.ts +10 -8
  10. package/dist/cli/commands/customer-profile.d.ts +2 -0
  11. package/dist/cli/commands/customer-verification.d.ts +5 -0
  12. package/dist/cli/commands/tool-primitives.d.ts +1 -1
  13. package/dist/cli/commands/workspace-member-mutations.d.ts +2 -0
  14. package/dist/cli/commands/workspace-members.d.ts +2 -0
  15. package/dist/cli/env-assignment.d.ts +9 -0
  16. package/dist/index.d.ts +5 -2
  17. package/dist/index.js +613 -153
  18. package/dist/lib/agent-sync.d.ts +13 -8
  19. package/dist/lib/api-url.d.ts +4 -3
  20. package/dist/lib/app-home.d.ts +0 -1
  21. package/dist/lib/client-types.d.ts +75 -0
  22. package/dist/lib/credential-state.d.ts +12 -0
  23. package/dist/lib/fleet-credentials.d.ts +41 -15
  24. package/dist/lib/home-adoption.d.ts +2 -0
  25. package/dist/lib/home-census.d.ts +3 -1
  26. package/dist/lib/local-opt-in.d.ts +24 -0
  27. package/dist/lib/portable-skills-files.d.ts +6 -2
  28. package/dist/lib/read-access.d.ts +83 -0
  29. package/dist/lib/remote-auth.d.ts +14 -0
  30. package/dist/lib/remote-client.d.ts +34 -5
  31. package/dist/lib/remote-profile.d.ts +26 -0
  32. package/dist/lib/remote-registry.d.ts +7 -3
  33. package/dist/lib/remote-workspace.d.ts +76 -0
  34. package/dist/lib/skillinfo.d.ts +1 -1
  35. package/dist/mcp/helpers.d.ts +22 -0
  36. package/dist/mcp/index.d.ts +16 -0
  37. package/dist/sdk/governance-store.d.ts +1 -0
  38. package/dist/sdk/index.d.ts +5 -2
  39. package/dist/sdk/index.js +1084 -283
  40. package/dist/sdk/outputs.d.ts +0 -11
  41. package/dist/sdk/runs.d.ts +1 -1
  42. package/dist/storage.js +6 -40
  43. package/docs/skill-standard.md +30 -2
  44. package/package.json +6 -4
  45. package/dist/lib/instance-credentials-race.fixture.d.ts +0 -1
package/README.md CHANGED
@@ -124,7 +124,7 @@ The unprefixed `SKILLS_API_KEY` and `SKILLS_API_URL` spellings are still accepte
124
124
  as silent aliases one rung below the canonical names, for one release. Use the
125
125
  `HASNA_`-prefixed names. `SKILL_API_KEY` (singular) is no longer read at all.
126
126
 
127
- **Three outcomes, and no fourth:**
127
+ **Fail closed, and the local run is a deliberate choice:**
128
128
 
129
129
  - a credential resolves → **hosted**, against the configured URL or the gateway.
130
130
  A credential that resolves but cannot produce a usable key — a deliberate
@@ -133,14 +133,39 @@ as silent aliases one rung below the canonical names, for one release. Use the
133
133
  - no credential but a URL is configured → **loud failure**, exit non-zero. There
134
134
  is no local fallback: answering from the bundled corpus while authentication is
135
135
  unconfigured is a false green;
136
- - neither **local**. Skills ships its corpus, so running on this machine is a
137
- real mode; it prints one line on stderr saying so.
136
+ - neither a credential nor a URL, and no opt-in **loud failure**, exit
137
+ non-zero. Running on this machine is no longer the silence that follows a
138
+ missing credential: an unconfigured install fails closed, opening no local
139
+ database and emitting no local-fallback event, and the error names the way
140
+ out and where the credential should live. This holds on every data surface:
141
+ `list`, `search`, `categories`, `tags`, `info`, `show`, `docs`, `requires`
142
+ and the bare `skills` listing all exit 1; `skills-mcp` exits 1 at startup
143
+ before answering `initialize` or binding a port, and each MCP data tool
144
+ answers `AUTH_REQUIRED` on its own;
145
+ - the explicit local opt-in → **local**. Skills ships its corpus, so running on
146
+ this machine is a real mode — but it must be asked for:
147
+ `HASNA_SKILLS_LOCAL=1` (alias `SKILLS_LOCAL=1`). It prints one line saying
148
+ "local mode" on stderr. A configured environment always outranks the opt-in:
149
+ with an authority or credential in the environment, `HASNA_SKILLS_LOCAL` is
150
+ ignored and the run goes (or fails) hosted.
138
151
 
139
152
  The retired locations are not read: `auth.json` (in either the app directory or
140
153
  the legacy `~/.skills/`), the old fleet-env and per-machine cloud env folders
141
154
  under `~/.hasna`, and the XDG config directory. `~/.hasna` is a closed namespace
142
155
  of app folders, and `XDG_CONFIG_HOME` is not consulted at all.
143
156
 
157
+ ### Routing environment variables
158
+
159
+ | Variable | Meaning |
160
+ |---|---|
161
+ | `HASNA_SKILLS_API_KEY` | The API key (tier 5 of the ladder). The silent alias `SKILLS_API_KEY` is accepted for one release. |
162
+ | `HASNA_SKILLS_API_URL` | The Skills API origin (HTTPS, or loopback HTTP). The silent alias `SKILLS_API_URL` is accepted for one release. |
163
+ | `HASNA_SKILLS_LOCAL` | Explicit unhosted opt-in: run on this machine against the bundled corpus when no authority is configured. Any non-blank value (`1`). Alias `SKILLS_LOCAL`. Ignored whenever an authority or credential variable IS set. |
164
+ | `HASNA_SKILLS_API_KEY_OVERRIDE` | Deliberate tier-2 key that outranks every store. |
165
+ | `HASNA_SKILLS_API_KEY_REF` | Deliberate tier-2 vault-item pointer (resolved through `@hasna/secrets`). |
166
+ | `HASNA_PROFILE` | Selects an isolated `credentials-<profile>` file (tier 1). |
167
+ | `HASNA_STATION` | The Keychain account for tier 3; falls back to `hostname -s`, then `$USER`. |
168
+
144
169
  ## CLI Commands
145
170
 
146
171
  | Command | Alias | Description |
@@ -151,7 +176,7 @@ of app folders, and `XDG_CONFIG_HOME` is not consulted at all.
151
176
  | `skills unpin <name>` | | Remove a project pin |
152
177
  | `skills pins list` | | List pinned skills |
153
178
  | `skills setup --api-url <url>` | | Point the CLI at a Skills API origin for remote runs |
154
- | `skills setup` | | Show whether an API origin is configured; with none, skills run on this machine |
179
+ | `skills setup` | | Show whether an API origin is configured; with none, running on this machine requires `HASNA_SKILLS_LOCAL=1` |
155
180
  | `skills setup agents` | | Register the Skills MCP server with all supported agents |
156
181
  | `skills list` | `ls` | List available skills (filter with `-c`, `--pinned`, `-t`, `--brief`) |
157
182
  | `skills search <query>` | `s` | Search by name, description, or tags |
@@ -168,6 +193,7 @@ of app folders, and `XDG_CONFIG_HOME` is not consulted at all.
168
193
  | `skills categories` | | List all categories with skill counts |
169
194
  | `skills tags` | | List all unique tags with occurrence counts |
170
195
  | `skills doctor` | | Check env vars, system deps, and pinned skill health |
196
+ | `skills env-check [name]` | `check-env` | Show required environment variables; `--set KEY=VALUE` updates the project's `.env` |
171
197
  | `skills test [name]` | | Test skill readiness (env, system, npm deps) |
172
198
  | `skills outdated` | | Compare pinned vs registry versions |
173
199
  | `skills auth login --api-key <key>` | | Verify and store a Skills API key |
@@ -186,6 +212,7 @@ of app folders, and `XDG_CONFIG_HOME` is not consulted at all.
186
212
  | `skills create <name>` | | Scaffold a new custom skill directory |
187
213
  | `skills sync --to claude` | | Disabled by design; use `skills mcp --register <agent|all>` |
188
214
  | `skills sync --from claude` | | Disabled by design; agent skill folders are not used |
215
+ | `skills sync [names...] --check --for <agent> --source <path>` | `render` | Read-only drift census for the selected corpus, skills and agent; explicit source overrides `SKILLS_SOURCE`, then the installed cache. Unknown selections or drift exit nonzero. Without selectors, check all existing agent homes. |
189
216
  | `skills sync --station <id>` | | Per-station snapshot mode: snapshot the installed skill homes into `resources/<station>/skills` with a v3 sync-manifest (dry-run by default; `--populate` writes) |
190
217
  | `skills hydrate --station <id>` | | Restore the canonical corpus cache from a reviewed per-station snapshot (dry-run by default; `--apply` writes) |
191
218
  | `skills validate <name>` | | Check a skill's directory structure |
@@ -198,6 +225,30 @@ of app folders, and `XDG_CONFIG_HOME` is not consulted at all.
198
225
  | `skills self-update` | | Update this package to the latest version |
199
226
  | `skills completion <shell>` | | Generate shell completions (bash, zsh, fish) |
200
227
 
228
+ `self-update` asks the same Bun executable for its global bin directory after a
229
+ successful installation. It reports success only when the `skills` command on
230
+ your PATH resolves to that installed command and exits successfully with one
231
+ semantic version. If discovery or verification fails, it exits nonzero and
232
+ explains that installation may already have completed. It does not automatically
233
+ reinstall or change your PATH.
234
+
235
+ ### Local environment assignments
236
+
237
+ `skills env-check --set 'KEY=value'` writes one literal value to the current
238
+ project's `.env`. Keys must match `[A-Za-z_][A-Za-z0-9_]*`. Empty values and `=`
239
+ within a value are supported; output confirms the key and path without printing
240
+ the value. Quote the shell argument when it contains shell metacharacters.
241
+
242
+ The writer preserves unrelated lines, comments, line endings and existing file
243
+ permissions. New files use mode `0600`. Symlinks and special files are rejected.
244
+ Values containing control characters, newlines, all three quote delimiters, an
245
+ odd trailing backslash, a backslash immediately before a final dollar, or a
246
+ backslash together with both a single quote and a backtick are rejected because
247
+ literal Bun dotenv serialization is not supported
248
+ for those cases. Multiline or ambiguous existing assignments also
249
+ require manual editing; refusal leaves the file unchanged. Literal round trips
250
+ are tested with Bun 1.3.14 and 1.4.0.
251
+
201
252
  ### Common Options
202
253
 
203
254
  - `--json` — Output as JSON (pipeable)
@@ -255,6 +306,19 @@ Stable command shapes:
255
306
  served by the API and never ships in this package.
256
307
  - Config and schedules: `config * --json` and `schedule * --json` return
257
308
  machine-readable status objects.
309
+ `schedule run` exits 1 if any item fails, in human and JSON output. JSON
310
+ `results[].attempted` identifies items handed to the local executor; `ran`
311
+ counts those attempts, including failures. Missing skills, routing refusals,
312
+ and unsupported hosted scheduling leave that occurrence due and its history
313
+ unchanged. Local attempts record success/error and advance the schedule.
314
+ If history cannot be saved, the item reports `executionStatus` and
315
+ `historyError`; inspect its effects before retrying. Other due items still run.
316
+ No-due and `--dry-run` remain successful without consuming occurrences.
317
+ With `schedule run --json`, child stdout and stderr stream to stderr while
318
+ stdout contains only the command's JSON result. Human-mode child output is
319
+ unchanged. Programmatic `runSkill` callers can select `stdio: "stderr"` to
320
+ stream both child output channels to stderr without buffering; the existing
321
+ default/inherit and pipe modes retain their behavior.
258
322
  - Storage: `storage status --json` returns local `.skills` paths and optional
259
323
  repo-native remote readiness; `storage sync-plan --json` returns a no-network
260
324
  snapshot plan.
@@ -314,6 +378,16 @@ skills show my-skill
314
378
  skills port ./existing-skill
315
379
  ```
316
380
 
381
+ New and imported local skill names use hyphens: `MyHTTPTool` becomes
382
+ `my-http-tool`, and `my_tool.v2` becomes `my-tool-v2`. Existing installed names
383
+ and declared command names remain readable without renaming. Already joined
384
+ lowercase words are not split automatically; supply `--name my-skill` when importing.
385
+ An import updates name declarations in its copied files; original source files stay unchanged.
386
+
387
+ Single-folder `port` / `add` reports validation after importing. If validation
388
+ fails, both human and JSON output exit with status 1; the imported folder remains
389
+ available for correction. Check `valid` and `issues` in JSON before using it.
390
+
317
391
  The scaffold includes `SKILL.md`, `skill.json`, `AGENTS.md`, `package.json`,
318
392
  `tsconfig.json`, and `src/index.ts`. `AGENTS.md` is written for coding agents:
319
393
  after `skills new my-skill`, an agent can open that file, implement the skill,
@@ -600,7 +674,7 @@ skills/ # Public skill contracts and local OSS skills
600
674
  |---|---|---|
601
675
  | Catalog skills | 86 | `SKILLS.length` (`src/lib/registry-data/`) |
602
676
  | Categories | 17 | `CATEGORIES` (`src/lib/registry-types.ts`) |
603
- | MCP tools | 56 | `tools/list` against a live `buildServer()` |
677
+ | MCP tools | 61 | `tools/list` against a live `buildServer()` |
604
678
 
605
679
  Every number in this table is re-derived from the source tree on each test run by
606
680
  `src/lib/readme-derived-counts.test.ts`, so a drifted figure fails a test rather
@@ -718,3 +792,100 @@ hand.
718
792
  ## License
719
793
 
720
794
  Apache-2.0 — see [LICENSE](LICENSE)
795
+
796
+ ### Account and workspace names on a compatible server
797
+
798
+ `skills account update --display-name "Ana" --email you@example.com` and
799
+ `skills workspace update --name "Studio" --email you@example.com` request a
800
+ fresh verification email and prompt for the code without showing its digits.
801
+ The current API selection still determines the server. These commands require
802
+ that server to support the additive customer-name routes; they do not enable
803
+ membership, billing or operator administration.
804
+
805
+ For automation, first request a fresh code through `skills auth login --email you@example.com --json`,
806
+ then pipe the code from your secure input source to the same command with
807
+ `--code-stdin --json`. Do not put verification codes in command arguments or
808
+ shell history. JSON/noninteractive updates require `--code-stdin`. Cancelling
809
+ an interactive prompt exits with status130 before verification.
810
+
811
+ `RemoteSkillsAuthClient.updateProfile(email, code, { displayName })` and
812
+ `updateCurrentWorkspace(email, code, { name })` use a fresh session without
813
+ replacing saved API keys or profile selection. `RemoteSkillsClient` exposes the
814
+ same name operations for an explicitly supplied interactive session. Servers
815
+ retain the final permission checks; ordinary API keys may be refused. MCP tools
816
+ `update_account_profile` and `update_workspace_name` accept a name, email and
817
+ fresh verification code, and return only the safe updated projection.
818
+
819
+ ## Admin user-list authority
820
+
821
+ The `./admin-contract` user-list response retains a global identity whose default
822
+ workspace membership is absent or revoked. Its required `role` is `null` in that
823
+ case, and its `organizationId` remains the default workspace pointer. Null grants
824
+ no workspace authority. Consumers must handle it explicitly; an organization
825
+ filter lists active members of that workspace. Role assignment still requires a
826
+ concrete role and targets the default membership; a missing default membership
827
+ does not select a different workspace automatically. Active organization rosters
828
+ and role-mutation responses continue to require non-null roles.
829
+
830
+ ## Current workspace roster on a compatible server
831
+
832
+ `skills workspace members --email you@example.com` requests fresh email
833
+ verification and reads one page of the current workspace roster. The selected
834
+ server requires a current owner/admin session; API keys and support
835
+ impersonation do not grant roster access. It never replaces saved credentials
836
+ or changes the selected profile.
837
+
838
+ For noninteractive use, request a code with the existing auth flow, then supply
839
+ it on stdin (never as a command argument):
840
+
841
+ ```sh
842
+ skills workspace members --email you@example.com --code-stdin --limit 25 --json
843
+ ```
844
+
845
+ JSON includes `organizationId`, `members`, and required `nextCursor` (null on the
846
+ last page). Pass a returned cursor unchanged with `--cursor` to read the next
847
+ page; limits are 1–100 with server default 50. Timestamps retain the server's
848
+ microsecond strings. Human output also includes a continuation cursor when one
849
+ exists. Empty pages are distinct from denied, unsupported or malformed responses,
850
+ which fail the command.
851
+
852
+ SDK callers with an authorized customer session can use
853
+ `RemoteSkillsClient.listWorkspaceMembers({ limit, cursor })`. For fresh email
854
+ verification, use `RemoteSkillsAuthClient.listWorkspaceMembers(email, code,
855
+ options)`. The MCP tool `list_workspace_members` takes `email`, `code`, and
856
+ optional `limit`/`cursor` and calls the same fresh-auth client.
857
+
858
+ ## Change a current workspace membership
859
+
860
+ On a compatible server, use the exact `membershipId` and role from the roster:
861
+
862
+ ```sh
863
+ skills workspace member role <membership-id> --role viewer --expected-role member --email you@example.com --code-stdin --json
864
+ skills workspace member remove <membership-id> --expected-role viewer --email you@example.com --code-stdin --json
865
+ ```
866
+
867
+ Both commands use fresh verification and leave saved credentials and profiles
868
+ unchanged. Without `--code-stdin`, an interactive terminal requests and prompts
869
+ for a new code. JSON and noninteractive calls require a previously requested
870
+ code on stdin. The server enforces current owner/admin policy. Owners can change
871
+ roles while retaining an owner; admins can manage another member/viewer within
872
+ those two roles. Self-removal is unavailable.
873
+
874
+ `RemoteSkillsClient.setWorkspaceMemberRole(membershipId, { role, expectedRole })`
875
+ and `.removeWorkspaceMember(membershipId, { expectedRole })` accept an explicit
876
+ customer session. The corresponding `RemoteSkillsAuthClient` methods take
877
+ `email, code` before those arguments and obtain an ephemeral session. MCP tools
878
+ `set_workspace_member_role` and `remove_workspace_member` use the same client,
879
+ with required `membershipId`, `expectedRole`, `email`, `code`, and `role` for a
880
+ role change. API keys and support impersonation do not grant this authority.
881
+
882
+ A role result contains `{ organizationId, member, changed }`; removal contains
883
+ `{ organizationId, membershipId, removed: true, alreadyRemoved }`. Exact server
884
+ timestamps remain strings. An authorized already-applied role returns
885
+ `changed: false`; retrying removal of the same tombstone returns
886
+ `alreadyRemoved: true`. A later replacement membership has a different ID.
887
+ Stale expected-role conflicts require a roster refresh before another action.
888
+ The client never refreshes, changes the precondition, or retries automatically.
889
+ Known refusals expose a fixed message and code through `RemoteWorkspaceMemberError`;
890
+ unsupported routes remain errors. Invitations, workspace switching and leaving
891
+ your own workspace are separate capabilities.