@hasna/skills 0.2.0 → 0.3.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
@@ -19,11 +19,12 @@ Requires [Bun](https://bun.sh/) 1.0+.
19
19
  # Browse skills interactively
20
20
  skills
21
21
 
22
- # Point the CLI at a Skills API server for server-owned (premium) skill runs
23
- skills setup --api-url https://skills.example.com
24
- skills auth login --api-key "$SKILLS_API_KEY"
22
+ # Sign in. With a credential and no URL, the CLI talks to the fleet gateway;
23
+ # point it at your own instance first if you run one.
24
+ skills setup --api-url https://skills.example.com # only for your own instance
25
+ skills auth login --api-key "$HASNA_SKILLS_API_KEY"
25
26
 
26
- # With no API URL configured, skills simply run on this machine
27
+ # With no credential and no URL, skills simply run on this machine
27
28
  skills list
28
29
 
29
30
  # Optionally pin a skill preference in this project
@@ -54,21 +55,16 @@ create local run metadata, and then expose status and artifact commands. They
54
55
  do not fall back to bundled local execution when auth is missing or the server
55
56
  runtime is unavailable.
56
57
 
57
- Routing is config-driven and local is the default: a run is sent to the API
58
- only when an origin is configured (`apiUrl` or `$SKILLS_API_URL`), a credential
59
- is present (`SKILLS_API_KEY` or the auth store), and the skill carries the
60
- server-owned marker. Every other skill runs on this machine, whether or not an
61
- API is configured. No skill in the OSS catalog is server-owned today; the
58
+ Routing is credential-driven and local is the default: a run is sent to the API
59
+ only when a credential resolves (see **Credentials** below) and the skill carries
60
+ the server-owned marker. Every other skill runs on this machine, whether or not
61
+ a credential exists. No skill in the OSS catalog is server-owned today; the
62
62
  marker arrives with skills synced from a Skills API deployment. A server-owned
63
- skill run without the origin or the credential fails closed with an error
64
- naming the missing setup — it never silently runs locally.
65
-
66
- Use `SKILLS_API_KEY` or `skills auth login --api-key` for server-side
67
- execution:
63
+ skill run without a credential fails closed with an error naming the missing
64
+ setup — it never silently runs locally.
68
65
 
69
66
  ```bash
70
- skills setup --api-url https://skills.example.com
71
- skills auth login --api-key "$SKILLS_API_KEY"
67
+ skills auth login --api-key "$HASNA_SKILLS_API_KEY"
72
68
  skills run <server-owned-skill> --brief "minimal geometric owl mark"
73
69
  skills runs status <run-id>
74
70
  skills exports download <run-id>
@@ -78,11 +74,73 @@ Browser/device-code and email-code login commands are retained for compatible
78
74
  deployments. A Skills deployment can bootstrap with a provisioned API key via
79
75
  `skills auth login --api-key`.
80
76
 
81
- `SKILLS_API_KEY` is the Skills API credential. It is not a provider
77
+ `HASNA_SKILLS_API_KEY` is the Skills API credential. It is not a provider
82
78
  credential. Provider keys such as `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, or
83
79
  `GEMINI_API_KEY` remain supported only for free/local OSS skills whose
84
80
  requirements explicitly document local provider use.
85
81
 
82
+ ## Credentials
83
+
84
+ The credential and the service address are resolved by the shared client in
85
+ [`@hasna/contracts`](https://www.npmjs.com/package/@hasna/contracts), the same
86
+ ladder every Hasna CLI uses. Nothing here is resolved twice, and this package
87
+ keeps no credential store of its own.
88
+
89
+ **The credential, in precedence order, resolved fresh on every call:**
90
+
91
+ | # | Tier | Where |
92
+ |---|------|-------|
93
+ | 1 | Explicit argument | `--api-key`, `--profile` |
94
+ | 2 | Deliberate env pointer | `HASNA_SKILLS_API_KEY_OVERRIDE`, `HASNA_PROFILE`, `HASNA_SKILLS_API_KEY_REF` (a secrets-vault item key, never a value) |
95
+ | 3 | macOS Keychain | generic-password item `hasna.credentials.skills.api-key`, account `$HASNA_STATION`, else `hostname -s`, else `$USER` |
96
+ | 4 | Disk | `~/.hasna/skills/config/credentials` (mode 0400/0600; `HASNA_HOME` and `HASNA_CONFIG_HOME` relocate it; XDG is never consulted) |
97
+ | 5 | Environment | `HASNA_SKILLS_API_KEY` — a legitimate tier, and deliberately *below* disk |
98
+
99
+ Tier 5 sits below disk on purpose. A wrapper that injects `HASNA_SKILLS_API_KEY`
100
+ into one child process re-reads its store every time and cannot go stale; a shell
101
+ `export` can, and after a key rotation the file on disk is the correct one.
102
+
103
+ `skills auth login` writes tier 4. A tier an operator set on purpose (1 and 2)
104
+ never falls through to another identity: if it cannot be honoured, the command
105
+ fails rather than acting as a different principal.
106
+
107
+ `HASNA_SKILLS_API_KEY_REF` names a *vault item*, not a key, so it resolves in two
108
+ steps: the item is fetched through the `@hasna/secrets` SDK on each call, which
109
+ means a rotated item is picked up without a restart — and which means the SDK has
110
+ to be installed in the process. Every way that fetch can fail (SDK absent, vault
111
+ unreachable, item missing or empty) is terminal and exits non-zero; a pointer
112
+ never falls through to another tier, and never to the local corpus.
113
+
114
+ **The service address, in the same shape:**
115
+
116
+ `HASNA_SKILLS_API_URL` → the Keychain item `hasna.credentials.skills.api-url` →
117
+ `~/.hasna/skills/config/credentials` → the fleet gateway
118
+ `https://api.hasna.com/skills`. The gateway default applies only once a
119
+ credential has resolved, so an install with no credential names no host at all.
120
+ `skills setup --api-url <origin>` writes the credentials file; the address is
121
+ per-user, never per-project.
122
+
123
+ The unprefixed `SKILLS_API_KEY` and `SKILLS_API_URL` spellings are still accepted
124
+ as silent aliases one rung below the canonical names, for one release. Use the
125
+ `HASNA_`-prefixed names. `SKILL_API_KEY` (singular) is no longer read at all.
126
+
127
+ **Three outcomes, and no fourth:**
128
+
129
+ - a credential resolves → **hosted**, against the configured URL or the gateway.
130
+ A credential that resolves but cannot produce a usable key — a deliberate
131
+ selection that cannot be honoured, a vault pointer whose item is missing, any
132
+ tier that yields a blank value — is a **loud failure** too, never a demotion;
133
+ - no credential but a URL is configured → **loud failure**, exit non-zero. There
134
+ is no local fallback: answering from the bundled corpus while authentication is
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.
138
+
139
+ The retired locations are not read: `auth.json` (in either the app directory or
140
+ the legacy `~/.skills/`), the old fleet-env and per-machine cloud env folders
141
+ under `~/.hasna`, and the XDG config directory. `~/.hasna` is a closed namespace
142
+ of app folders, and `XDG_CONFIG_HOME` is not consulted at all.
143
+
86
144
  ## CLI Commands
87
145
 
88
146
  | Command | Alias | Description |
@@ -122,7 +180,7 @@ requirements explicitly document local provider use.
122
180
  | `skills export` | | Export pinned skills as JSON |
123
181
  | `skills import <file>` | | Pin skills from a JSON export |
124
182
  | `skills config set <key> <value>` | | Set default agent, scope, output format, or API origin |
125
- | `skills config unset <key>` | | Remove a configuration value (`skills config unset apiUrl` returns to running on this machine) |
183
+ | `skills config unset <key>` | | Remove a configuration value (`skills config unset apiUrl` clears the stored service address and returns to running on this machine) |
126
184
  | `skills new <name>` | `scaffold` | Scaffold a portable skill under `~/.hasna/skills/installed/<name>` |
127
185
  | `skills port <path>` | `add` | Import an existing skill folder into the portable standard |
128
186
  | `skills create <name>` | | Scaffold a new custom skill directory |
@@ -146,7 +204,7 @@ requirements explicitly document local provider use.
146
204
  - `--brief` — One-line format
147
205
  - `--limit <n>` — Cap human rows where supported; use `--limit all` or `--limit 0` for every row
148
206
  - `--cursor <n>` — Continue human-output pagination from a numeric offset
149
- - `--remote` — Read browse/search data from `SKILLS_API_URL` or `config apiUrl`
207
+ - `--remote` — Read browse/search data from the configured Skills instance (see **Credentials**)
150
208
  - `--dry-run` — Preview without applying changes
151
209
  - `--verbose` — Debug logging globally; richer human discovery rows where supported
152
210
  - `--no-color` — Disable ANSI colors
@@ -206,15 +264,16 @@ Stable command shapes:
206
264
  ## Remote Registry
207
265
 
208
266
  The npm package ships no bundled skill corpus. Discovery reads the local corpus
209
- cache (`~/.hasna/skills/installed`, filled by `skills pull`) and, when an API
210
- base URL is set, the server's registry. This is not a mode you select: pointing
211
- browse/search commands at a server's registry is one fact, an API base URL being
212
- set. To set it:
267
+ cache (`~/.hasna/skills/installed`, filled by `skills pull`) and, when a
268
+ credential resolves, the server's registry. This is not a mode you select:
269
+ whether browse/search commands read a server's registry is one fact, whether a
270
+ credential resolves (see [Credentials](#credentials)). To point at your own
271
+ instance:
213
272
 
214
273
  ```bash
215
- export SKILLS_API_URL=https://your-server.example
216
- # or persist it:
217
- skills config set apiUrl https://your-server.example
274
+ export HASNA_SKILLS_API_URL=https://your-server.example
275
+ # or persist it in the credentials file the shared ladder reads:
276
+ skills setup --api-url https://your-server.example
218
277
  # and to stop using it:
219
278
  skills config unset apiUrl
220
279
 
@@ -228,8 +287,9 @@ If the URL is an origin such as `https://your-server.example`, the CLI requests
228
287
  `/api/v1/skills`. If it already ends in `/api` or `/api/v1`, the CLI appends
229
288
  `/skills`.
230
289
 
231
- Authenticated registry listing and premium server-side execution use
232
- `SKILLS_API_KEY` or the credential saved by `skills auth login --api-key`.
290
+ Authenticated registry listing and premium server-side execution use whichever
291
+ credential the ladder resolves most often the one saved by
292
+ `skills auth login --api-key`.
233
293
 
234
294
  The typed `RemoteSkillsClient` also exposes pin, tag, and cursor-based
235
295
  incremental-sync methods (`listPins`/`pin`/`unpin`, `listTags`/`skillsByTag`,
@@ -307,8 +367,8 @@ skills mcp --register all # Register with all supported agents
307
367
  ## Skills API
308
368
 
309
369
  ```bash
310
- skills setup --api-url https://skills.example.com
311
- skills auth login --api-key "$SKILLS_API_KEY"
370
+ skills setup --api-url https://skills.example.com # only for your own instance
371
+ skills auth login --api-key "$HASNA_SKILLS_API_KEY"
312
372
  skills billing status
313
373
  ```
314
374
 
@@ -324,11 +384,15 @@ different product: the schema, the organization scoping, and the run lifecycle a
324
384
  identical either way.
325
385
 
326
386
  ```bash
327
- skills-server # SQLite at ~/.hasna/skills/server.db
328
- HASNA_SKILLS_DATABASE_URL=<path>/server.db skills-server
329
- HASNA_SKILLS_DATABASE_URL=postgres://user:CHANGEME@host/skills skills-server
387
+ skills-serve # SQLite at ~/.hasna/skills/server.db
388
+ HASNA_SKILLS_DATABASE_URL=<path>/server.db skills-serve
389
+ HASNA_SKILLS_DATABASE_URL=postgres://user:CHANGEME@host/skills skills-serve
330
390
  ```
331
391
 
392
+ > **Bin naming:** the canonical server bin is `skills-serve`; `skills-server` remains
393
+ > installed as a deprecated alias for one release (same entrypoint). `skills-worker`
394
+ > and `skills-migrate` are additional documented surfaces.
395
+
332
396
  | `HASNA_SKILLS_DATABASE_URL` | Backend | Survives restart |
333
397
  | --- | --- | --- |
334
398
  | *(unset)* | SQLite at `<data dir>/server.db` | yes |
@@ -368,8 +432,8 @@ SQLite file, so it stays usable as a deploy gate.
368
432
 
369
433
  `HASNA_SKILLS_DATABASE_URL` and `DATABASE_URL` are server-only. CLI, MCP, and SDK
370
434
  clients never read them and never open a database connection: a client reaches
371
- the cloud only through `SKILLS_API_URL` plus an API key (the `apiKey` stored by
372
- `skills auth`, or `SKILLS_API_KEY`). The one exception is the repo-native storage
435
+ the cloud only through the resolved API URL plus the resolved API key (see
436
+ [Credentials](#credentials)). The one exception is the repo-native storage
373
437
  sync under [Storage Boundary](#storage-boundary), an operator tool that
374
438
  intentionally reads the same variables.
375
439
 
@@ -387,7 +451,7 @@ Three things the server will not do:
387
451
  against the same file — nothing more. A container without a persistent volume gets a
388
452
  database in its own ephemeral layer, and two replicas each get their *own* database
389
453
  rather than sharing one. Multi-replica and container deployments want Postgres; both
390
- `skills-server` and `skills-worker` print the database they opened on startup, so a
454
+ `skills-serve` and `skills-worker` print the database they opened on startup, so a
391
455
  split-brain SQLite setup shows up as two different paths in the logs.
392
456
 
393
457
  <a id="storage-boundary"></a>