@amdahl/cli 0.1.4 → 0.2.1

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
@@ -2,7 +2,8 @@
2
2
 
3
3
  The `amdahl` command line. It signs you in with your console account, checks
4
4
  that a workspace can optimize, rewrites messages, manages API keys (each one
5
- approved in the console), and points MCP clients at Amdahl.
5
+ approved in the console), points MCP clients at Amdahl, and installs skills
6
+ from the library at https://amdahl.ai/skills.
6
7
 
7
8
  ## Using the Claude or ChatGPT app? You do not need this CLI
8
9
 
@@ -39,7 +40,8 @@ Full steps: https://amdahl.ai/connect
39
40
 
40
41
  ## Install
41
42
 
42
- Needs Node.js 20 or later.
43
+ Needs Node.js 20 or later. On an older Node, `amdahl` stops before it does
44
+ anything, names the version it found, and links the Node.js download page.
43
45
 
44
46
  ```sh
45
47
  # Run once, with no install
@@ -50,6 +52,10 @@ npm i -g @amdahl/cli
50
52
  amdahl login
51
53
  ```
52
54
 
55
+ `amdahl` is also on npm by its short name, so `npx amdahl ...` is the same as
56
+ `npx @amdahl/cli ...`. That package ([packages/amdahl](packages/amdahl)) has no
57
+ code of its own: it depends on `@amdahl/cli` at the same version and runs it.
58
+
53
59
  To connect an AI client, run `amdahl install claude-code` (or `codex`,
54
60
  `cursor`). It adds the Amdahl MCP server and also installs the Amdahl skill,
55
61
  a map of everything Amdahl can do, so your agent knows which tool fits each
@@ -76,6 +82,7 @@ amdahl whoami # the account, workspace and role you are signed in
76
82
  amdahl status # can this workspace optimize, and what blocks it
77
83
  amdahl optimize draft.md # rewrite one message (or pipe it in with -)
78
84
  amdahl install claude-code # connect an AI client and add the Amdahl skill (also codex, cursor)
85
+ amdahl skills add <publisher>/<skill> # add a skill from amdahl.ai/skills to this project
79
86
  ```
80
87
 
81
88
  No workspace yet? `amdahl login` prints the console link where you can
@@ -98,11 +105,129 @@ amdahl keys list
98
105
  amdahl keys revoke <id|prefix> [--yes] [--no-browser]
99
106
  amdahl workspace list | amdahl workspace use <profile>
100
107
  amdahl install claude-code|codex|cursor [--print] [--force]
108
+ amdahl skills add <publisher>/<skill> [-g] [--agents] [--force] [--site <origin>]
101
109
  ```
102
110
 
103
111
  Global flags: `--profile`, `--api-url`, `--api-key`, `--json`, `--no-color`,
104
112
  `-h`, `-v`. With `--json`, data goes to stdout and messages to stderr.
105
113
 
114
+ ## Skills
115
+
116
+ `skills add` installs one skill from the library at https://amdahl.ai/skills.
117
+ It needs no sign-in.
118
+
119
+ ```sh
120
+ npx amdahl skills add <publisher>/<skill>
121
+ # the skill's link works too, with or without .md:
122
+ npx amdahl skills add https://amdahl.ai/skills/<publisher>/<skill>
123
+ ```
124
+
125
+ In a terminal, it asks one question. It prints `Checking <publisher>/<skill>
126
+ on amdahl.ai... OK` while it downloads and checks the skill. Then it names
127
+ the skill and the agents it sees in use, and asks where to install it, in a
128
+ menu:
129
+
130
+ ```
131
+ System diagram, by Amdahl (17 files), for Claude Code and Cursor.
132
+ Install it in:
133
+ > This project (~/proj)
134
+ Every project on this computer
135
+ (Use the arrow keys, then Enter)
136
+ ```
137
+
138
+ Up and Down (or `k` and `j`) move the marker; typing `1` or `2` jumps to
139
+ that row. Enter picks, and the menu shrinks to one line, such as
140
+ `Install it in: This project (~/proj)`. The marked row is in the brand
141
+ colour, or bold when colour is off (`--no-color`, `NO_COLOR`, `TERM=dumb`).
142
+ The pick is the go-ahead: it installs, prints the folders it wrote, and
143
+ says to open a new chat. In your home folder it does not ask, and installs
144
+ for every project. Ctrl+C stops it with nothing written (exit 130).
145
+
146
+ It never asks about agents. A `.claude` folder here or in your home folder,
147
+ or `CLAUDECODE` set, means Claude Code (`.claude/skills`). A `.codex` folder
148
+ means Codex, a `.cursor` folder or `CURSOR_AGENT` means Cursor, and an
149
+ `.agents` folder means other agents (all `.agents/skills`). When it sees
150
+ both kinds, or neither, it writes both folders. To choose yourself, use the
151
+ flags below.
152
+
153
+ If a different copy of the skill is already there, it asks once more, in
154
+ the same kind of menu: "Keep your copy" (where the marker starts) or
155
+ "Replace it". With `--force` it replaces it without asking.
156
+
157
+ It never asks when stdin or stdout is not a terminal (an agent, CI, a pipe),
158
+ or with `--json`, `-y`, `-g` or `--agents`, or with `AMDAHL_NO_PROMPT=1`.
159
+ Then the flags say where. Without `--agents`, it uses the folder of the agent
160
+ that ran it, when that agent says so: Claude Code sets `CLAUDECODE`
161
+ (`.claude/skills`), Cursor sets `CURSOR_AGENT` (`.agents/skills`). Codex
162
+ documents no such variable, so a run from Codex gets the default, Claude
163
+ Code. Without flags it says on stderr where the skill went. `-y` (`--yes`)
164
+ adds it to this project without asking. Neither replaces a different copy
165
+ without `--force`.
166
+
167
+ It reads the skill's index,
168
+ `https://amdahl.ai/skills/<publisher>/<skill>/.well-known/agent-skills/index.json`
169
+ ([Agent Skills Discovery](https://schemas.agentskills.io/discovery/0.2.0/schema.json)
170
+ v0.2.0). The index lists one of two kinds of skill:
171
+
172
+ - `type: "skill-md"`: one SKILL.md. `skills add` downloads it.
173
+ - `type: "archive"`: a folder (the SKILL.md plus scripts, references and
174
+ other files), as one `.tar.gz`. `skills add` downloads it and unpacks it
175
+ with its own reader. It never runs `tar`.
176
+
177
+ It refuses the download, and writes nothing, unless all of these are true:
178
+
179
+ - The index points at a file on the same site.
180
+ - The sha256 of the downloaded bytes equals the index's `digest`. This is
181
+ checked before anything in an archive is read. The index and the file come
182
+ from the same site, so this only catches a corrupted or cut-off download.
183
+ It is not a signature and does not show the file is unchanged: that rests
184
+ on HTTPS and on who can publish to amdahl.ai/skills.
185
+ - The SKILL.md front matter parses as YAML, `name` is a string, and
186
+ `description` is a string that is not empty.
187
+ - `name` equals the skill's name in the index.
188
+
189
+ An archive must also be a plain folder. `skills add` refuses it if:
190
+
191
+ - the gzip CRC-32 or length does not match, or bytes follow the gzip data;
192
+ - a tar header has a wrong checksum, is not ustar, or the tar has no end;
193
+ - a path is absolute, holds `..`, `.` or an empty segment, a backslash or a
194
+ control character, or two entries share a path (also when only the
195
+ case differs);
196
+ - an entry is not a file or a folder (a link, a device, a pax header);
197
+ - a file mode is not 0644 or 0755;
198
+ - it has more than 200 files, or more than 4 MB of file contents;
199
+ - SKILL.md is not at its root.
200
+
201
+ Without the questions, the flags say where it writes, and it prints each
202
+ path it wrote:
203
+
204
+ | flag | where |
205
+ |---|---|
206
+ | (none) or `-y` | `./.claude/skills/<name>/`, for Claude Code in this project |
207
+ | `-g`, `--global` | `~/.claude/skills/<name>/`, for every project |
208
+ | `--agents` | also `.agents/skills/<name>/` (in this project, or in `~` with `-g`), which Codex, Cursor and most other agents read |
209
+
210
+ A `skill-md` skill writes `SKILL.md` in that folder, exactly as downloaded,
211
+ through a temp file renamed into place. It refuses, even with `--force`, when
212
+ the skill folder, the `skills` or `.claude`/`.agents` folder above it, or
213
+ `SKILL.md` itself is a symbolic link, so it never writes outside that folder.
214
+ An `archive` skill unpacks into a hidden temp folder beside each target,
215
+ then renames it into place only when every target is complete. If any step
216
+ fails, it removes what it unpacked and puts back any folder it replaced.
217
+
218
+ If a different file or folder with that name is already there, `skills add`
219
+ stops before it writes anything. Add `--force` to replace it. For a folder
220
+ skill, `--force` replaces the whole folder, with any files you added to it.
221
+ A skill that is already identical is left as it is.
222
+
223
+ It sends two requests, both to amdahl.ai: the index, then the file. It
224
+ follows no redirect and sends no telemetry. It waits at most 15 seconds for
225
+ each request. It refuses an index over 256 KB, a SKILL.md over 64 KB (the
226
+ most amdahl.ai accepts) and an archive over 4 MB. Each refusal writes
227
+ nothing. `--site <origin>` sends both requests to another origin, for
228
+ example a local copy of the site (`--site http://localhost:6782`). The
229
+ origin must be `https`; `http` is allowed only for localhost.
230
+
106
231
  ## Targeting staging
107
232
 
108
233
  ```sh
@@ -141,7 +266,7 @@ code shown in the terminal; then the key is printed once and stored nowhere.
141
266
  | code | meaning |
142
267
  |---|---|
143
268
  | 0 | ok |
144
- | 1 | general (`login_timeout`, `state_mismatch`, optimize `ok: false`) |
269
+ | 1 | general (`login_timeout`, `state_mismatch`, optimize `ok: false`, a `skills add` refusal) |
145
270
  | 2 | usage |
146
271
  | 3 | not signed in (401, `invalid_grant`, no credential) |
147
272
  | 4 | forbidden (403, `oauth_required`, `cli_client_required`, `not_admin`) |
@@ -149,6 +274,7 @@ code shown in the terminal; then the key is printed once and stored nowhere.
149
274
  | 6 | rate limited or quota (429) |
150
275
  | 7 | approval denied, expired or locked out |
151
276
  | 8 | network or 5xx |
277
+ | 130 | stopped with Ctrl+C at a question (nothing was written) |
152
278
 
153
279
  ## Development
154
280
 
@@ -160,7 +286,7 @@ pnpm install
160
286
  pnpm build # dist/amdahl.js, one file, no runtime deps
161
287
  pnpm test # vitest, against a local mock server
162
288
  pnpm type-check
163
- pnpm smoke:pack # pack, install offline, run --version
289
+ pnpm smoke:pack # pack both packages, install offline, run --help and --version
164
290
  pnpm sync:skill # refresh skill/SKILL.md from docs.amdahl.ai (and skill/shipped.json), then commit both
165
291
  ```
166
292
 
@@ -171,9 +297,10 @@ See [CONTRIBUTING.md](CONTRIBUTING.md). Report security issues as described in
171
297
 
172
298
  `.github/workflows/publish.yml` publishes to npm. To release:
173
299
 
174
- 1. In a PR, bump the version in `package.json` and `src/version.ts` together.
175
- The workflow refuses a version that does not match both, or one that is
176
- already on npm.
300
+ 1. In a PR, bump the version in `package.json` and `src/version.ts` together,
301
+ and in `packages/amdahl/package.json` both its `version` and its
302
+ `@amdahl/cli` dependency. The workflow refuses a version that does not
303
+ match the first two, or one that is already on npm; a test checks the third.
177
304
  2. After it merges, push a tag `cli-v<version>` (for example `cli-v0.2.0`). A
178
305
  version with a `-` (for example `0.2.0-rc.1`) goes to the `next` dist-tag;
179
306
  anything else goes to `latest`.
@@ -189,3 +316,26 @@ the `npm` environment, so no long-lived npm token is needed. Every release
189
316
  carries a signed [provenance attestation](https://docs.npmjs.com/generating-provenance-statements)
190
317
  that links the package on npm to the commit and workflow run that built it.
191
318
  Check it with `npm audit signatures` or on the package page on npmjs.com.
319
+
320
+ The workflow publishes `amdahl` (the short name) right after `@amdahl/cli`,
321
+ from the same run, only when the repository variable `NPM_PUBLISH_AMDAHL` is
322
+ `true`. Amdahl already holds the `amdahl` name on npm (a 0.0.1 placeholder
323
+ with no bin), so trusted publishing can be set up for it now. Before you push
324
+ the tag:
325
+
326
+ 1. On npmjs.com, `amdahl`, Settings, add a trusted publisher: GitHub Actions,
327
+ `amdahlco/amdahl-cli`, workflow `publish.yml`, environment `npm`.
328
+ 2. Set the repository variable `NPM_PUBLISH_AMDAHL` to `true`.
329
+
330
+ Then each `cli-v<version>` tag publishes both packages from CI, both with
331
+ provenance. Each publish step skips a version already on npm, so if the
332
+ `amdahl` step fails, use **Re-run failed jobs** on that run. (Re-running every
333
+ job stops at the check that the version is not on npm yet.)
334
+
335
+ Only if CI cannot publish `amdahl`, publish it by hand as a fallback: download
336
+ the `tarball` artifact from the run (it holds `amdahl-<version>.tgz`, already
337
+ checked), then, as an owner of the `amdahl` npm org with 2FA, run
338
+ `npm publish amdahl-<version>.tgz --access public --provenance=false`.
339
+ `publishConfig` asks for provenance, which a laptop cannot give, so without
340
+ `--provenance=false` the publish is expected to fail. That version has no
341
+ provenance.
package/bin/amdahl.js ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ // bin/amdahl.js
3
+ //
4
+ // The `amdahl` command. It checks the Node.js version before it loads the
5
+ // CLI: dist/amdahl.js is built for Node 20, and an older Node fails to parse
6
+ // it or crashes in it, while npx only warns about `engines`. So this file
7
+ // uses no syntax newer than Node 14 (no `??=`, no top-level await, no class
8
+ // fields), and an old Node can run it far enough to say what to do.
9
+
10
+ var version = process.versions.node
11
+ var major = parseInt(version.split('.')[0], 10)
12
+
13
+ if (!(major >= 20)) {
14
+ process.stderr.write(
15
+ 'amdahl needs Node.js 20 or later. This computer has Node.js ' +
16
+ version +
17
+ '.\nInstall a newer one from https://nodejs.org/en/download, then run the same command again.\n'
18
+ )
19
+ process.exitCode = 1
20
+ } else {
21
+ import('../dist/amdahl.js')
22
+ }