@amdahl/cli 0.3.6 → 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.
Files changed (3) hide show
  1. package/README.md +263 -8
  2. package/dist/amdahl.js +1565 -458
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1,9 +1,12 @@
1
1
  # @amdahl/cli
2
2
 
3
3
  The `amdahl` command line. It signs you in with your console account, checks
4
- that a workspace can optimize, rewrites messages, manages API keys (each one
5
- approved in the console), points MCP clients at Amdahl, and installs skills
6
- from the library at https://amdahl.ai/skills.
4
+ that a workspace can optimize, rewrites messages one at a time or a whole
5
+ list at once, answers questions about your customers, calls and deals, shows
6
+ and adds connected sources, records what happened to a message after you
7
+ sent it, manages API keys (each one approved in the console), points MCP
8
+ clients at Amdahl, and installs skills from the library at
9
+ https://amdahl.ai/skills.
7
10
 
8
11
  ## Using the Claude or ChatGPT app? You do not need this CLI
9
12
 
@@ -40,8 +43,10 @@ Full steps: https://amdahl.ai/connect
40
43
 
41
44
  ## Install
42
45
 
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.
46
+ Needs Node.js 22 or later. Node.js 20 reached end of life in April 2026 and
47
+ gets no security fixes. `amdahl` still starts on it, but npm warns when it
48
+ installs. On a Node older than 20, `amdahl` stops before it does anything,
49
+ names the version it found, and links the Node.js download page.
45
50
 
46
51
  ```sh
47
52
  # Run once, with no install
@@ -203,6 +208,18 @@ If not: quit and reopen Claude Code, type /mcp, pick amdahl, sign in.
203
208
  Want to try it here instead? Run: npx -y amdahl optimize
204
209
  ```
205
210
 
211
+ If you are already signed in here (a saved `amdahl login` for this API host,
212
+ or a key in `--api-key` or the environment), the last line is replaced by
213
+ what to do next in this terminal:
214
+
215
+ ```
216
+ Try it here: amdahl optimize or amdahl search "your question"
217
+ Add data: amdahl connect, see it all: amdahl open
218
+ ```
219
+
220
+ It still reads nothing from the network. An agent's run and a run with no
221
+ terminal print exactly what they did before.
222
+
206
223
  An old copy of the Amdahl skill is brought up to date on the way. A tool it
207
224
  finds that does not point at Amdahl yet brings the menu back.
208
225
  `npx -y amdahl --verbose` also prints the files it wrote, the profile and
@@ -302,6 +319,13 @@ amdahl login # sign in through the browser with your console acc
302
319
  amdahl whoami # the account, workspace and role you are signed in as
303
320
  amdahl status # can this workspace optimize, and what blocks it
304
321
  amdahl optimize draft.md # rewrite one message (or pipe it in with -, or run it bare and paste)
322
+ amdahl optimize --list drafts.csv # rewrite a whole list in one batch
323
+ amdahl search "what objections came up in lost deals?" # ask about your customers
324
+ amdahl connections # what is connected, and whether it is healthy
325
+ amdahl connect hubspot # open the console's add page, to add a CRM or call recorder
326
+ amdahl open # open this workspace in the console
327
+ amdahl feedback <run-id> --evidence did_it_myself --outcome used # what happened to it
328
+ amdahl sent <run-id> --provider gmail --channel email --sent-as returned --provider-send-id "<msg-id@mail>" # it went out
305
329
  amdahl install claude-code # connect an AI client and add the Amdahl skill (also codex, cursor)
306
330
  amdahl skills add <publisher>/<skill> # add a skill from amdahl.ai/skills to this project
307
331
  ```
@@ -340,6 +364,18 @@ amdahl auth token
340
364
  amdahl status
341
365
  amdahl optimize [<file>... | -] [--channel email|linkedin] [--evidence off|workspace]
342
366
  [--on-unsupported flag|remove] [--tries] [--context <file.json>]
367
+ amdahl optimize --list <file.csv|file.jsonl> [--no-wait] [--batch <id>] [--force] [the optimize flags]
368
+ amdahl search "<question>" [--research] [--limit <n>]
369
+ amdahl connections [--all]
370
+ amdahl connect [<connector>] [--no-browser]
371
+ amdahl open [--no-browser]
372
+ amdahl feedback <run-id> --evidence did_it_myself|reported_to_me|inferred
373
+ [--outcome used|used_with_edits|not_used|unsure] [--sent-text <file>|-] [--comment <text>]
374
+ amdahl sent <run-id> --provider <provider> --channel <channel> --sent-as returned|edited
375
+ (--provider-send-id <id> | --recipient <email|url> | --recipient-key-hash <sha256>)
376
+ [--sent-at <time>] [--provider-campaign-id <id>] [--sequence-step <n>]
377
+ [--provider-variant-id <id>] [--provider-lead-id <id>] [--thread-id <id>]
378
+ [--sent-text <file>|-]
343
379
  amdahl keys create --name <name> [--preset read-only|agent|internal|admin] [--expires 30d|90d|365d] [--no-browser]
344
380
  amdahl keys list
345
381
  amdahl keys revoke <id|prefix> [--yes] [--no-browser]
@@ -353,6 +389,198 @@ Global flags: `--profile`, `--api-url`, `--api-key`, `--json`, `--no-color`,
353
389
  stderr. An unknown option prints `Unknown option --foo.` and `Run \`amdahl
354
390
  --help\`.` (exit 2).
355
391
 
392
+ ## Search
393
+
394
+ ```sh
395
+ amdahl search "What objections came up in lost deals last quarter?"
396
+ ```
397
+
398
+ `search` asks one question about the workspace's customers, calls and deals
399
+ (`POST /search/query`, with `answer: true`). It prints a short answer, with
400
+ each citation said in plain words, and the rows it came from under `From:`,
401
+ at most five. With no written answer it prints up to ten rows. Each row is
402
+ one line (its title, who and when) and up to three lines of its text, in 80
403
+ columns. `--limit <n>` asks for at most `n` rows, and `--json` prints the
404
+ server's whole answer, every row included.
405
+
406
+ Research is something search starts, never a command of its own. Search
407
+ hands a statistical or long question to a research job by itself, and
408
+ `--research` asks it to (`route: "research"`). The CLI then waits for the
409
+ job (`GET /research/:id`, 45 seconds a read, up to 15 minutes) and prints its
410
+ cited answer, then what the answer rests on under `Details:` (its
411
+ `content_blocks`, in the order the job presented them), then up to three
412
+ questions to ask next.
413
+
414
+ Each table prints as a plain ASCII table that fits 80 columns: a long cell
415
+ wraps (a column is narrowed to its longest word first, so an id or a timestamp
416
+ stays whole), a cell past four lines is cut with `...`, a word wider than its
417
+ column is split with a `-`, and a table with more columns than 80 columns hold
418
+ prints each row as `label: value` lines, so no column is dropped. A table
419
+ prints at most 25 rows and counts the rest. Text, callout, citation, metric and
420
+ theme blocks print as plain text (an `<unverified>` figure is marked
421
+ `(unverified)`). A chart prints one line that says so, and so does any block
422
+ type this version does not know: both point to `--json`, where the whole block
423
+ is. Starting research needs
424
+ `conversations:write` and reading it back `conversations:read`. A CLI
425
+ sign-in has neither, so the server runs the search instead and the CLI adds
426
+ one line:
427
+
428
+ ```
429
+ A deeper answer needs a Customer agent key: amdahl keys create --name research --preset agent
430
+ ```
431
+
432
+ A workspace with nothing connected answers with `setup_needed`: the CLI
433
+ prints its message, which carries the console link, then
434
+ `Connect your CRM or call recorder: amdahl connect`. With no rows, it prints
435
+ the server's one line on how fresh the data is, or `Nothing matched.`
436
+
437
+ ## Connections
438
+
439
+ ```sh
440
+ amdahl connections
441
+ ```
442
+
443
+ ```
444
+ HubSpot crm healthy synced 2026-11-11
445
+ Gong (Sales calls) calls needs sign-in never synced
446
+ Sign in to it again: https://console.amdahl.ai/acme/connections/c1
447
+ ```
448
+
449
+ `connections` lists what the workspace has connected (`GET /connections`),
450
+ each with its health in words and its last sync. Under the list, one line
451
+ says where to fix what needs attention: the one connection's console page,
452
+ or the Connections page when there are several. `--all` also lists
453
+ disconnected and never-synced sources. With nothing connected it says so and
454
+ names `amdahl connect`.
455
+
456
+ `amdahl connect hubspot` checks the connector against the catalog
457
+ (`GET /connections/catalog`; a type or a name, in any case), then opens the
458
+ console's add page,
459
+ `<console>/<workspace>/settings/connections?add=1&connector=hubspot`. The
460
+ `connector` only fills the search box on that page: you pick HubSpot there and
461
+ sign in to it. With no connector it opens the add page with an empty search
462
+ box. The CLI never creates a connection itself. An unknown connector exits 2
463
+ and lists the ones you can connect; one that is not offered yet exits 1 and
464
+ says it is coming soon. It opens the browser only for a person at a terminal:
465
+ stdin and stdout are terminals, `AMDAHL_NO_PROMPT=1` is not set, no agent runs
466
+ it (`CLAUDECODE`, `CURSOR_AGENT`), it is not CI and there is no `--json`. So
467
+ `amdahl connect hubspot | cat` opens nothing. Everywhere else, with
468
+ `--no-browser`, or when the browser does not open, it prints the link on
469
+ stdout instead (under `--json`, as `url` with `opened: false`).
470
+
471
+ `amdahl open` opens this workspace in the console (the console home when you
472
+ are not signed in), on the same terms. `--no-browser` prints the link.
473
+
474
+ ## Optimize a list
475
+
476
+ ```sh
477
+ amdahl optimize --list drafts.csv
478
+ ```
479
+
480
+ ```
481
+ Queued 40 messages as batch 7c1e....
482
+ Ctrl+C stops waiting, not the batch. To pick it up later: amdahl optimize --list drafts.csv --batch 7c1e...
483
+ 12 of 40 done, about 3 min left
484
+ 40 of 40 messages optimized.
485
+ Results: drafts.optimized.csv
486
+ ```
487
+
488
+ `--list` sends a whole list as one batch (`POST /messages/optimize/batch`),
489
+ which the server runs on spare capacity and never refuses as busy, then
490
+ reads it back (`GET /messages/optimize/batches/:id`) until it completes. A
491
+ file given without `--list` is still one message, as before.
492
+
493
+ The list is a CSV file with a header row, or a JSONL file (`.jsonl` or
494
+ `.ndjson`) with one batch item, a JSON object, per line. A CSV has a
495
+ `message` column and may have `item_id`, `channel`, `evidence`,
496
+ `on_unsupported`, `facts` and `rules`; facts and rules take one per line
497
+ inside the cell. Any other column is refused by name before anything is
498
+ sent, so a misspelled column is never dropped without a word (use JSONL for
499
+ any other field a single optimize takes). The optimize flags (`--channel`,
500
+ `--evidence`, `--on-unsupported`, `--tries`, `--context`) apply to every
501
+ item that does not set its own; a row's facts and a `--context` file's rules
502
+ and voice examples are both sent.
503
+
504
+ The whole file is read and checked first. A header the CLI does not read, or a
505
+ missing `message` column, says which headers it expects. Progress goes to
506
+ stderr, in place at a terminal. When the batch completes, the results go next
507
+ to the list: `drafts.csv` gets `drafts.optimized.csv` (every input column,
508
+ then `optimized_message`, `status`, `what_changed`, `run_id` and `error`), and
509
+ `x.jsonl` gets `x.optimized.jsonl` (one batch item per line, its `result`
510
+ what a single optimize returns). With `--json`, the batch and every item go
511
+ to stdout and no file is written. A failed item exits 1. A refused list
512
+ names the line of the first item the server refused.
513
+
514
+ A spreadsheet runs a cell that starts with `=`, `+`, `-` or `@` as a formula,
515
+ so in the results CSV such a cell (and one that starts with a tab or a
516
+ carriage return) starts with a `'`. The CLI does not replace a results file
517
+ that already exists: it exits 2 before it queues anything, and `--force`
518
+ replaces the file.
519
+
520
+ `--no-wait` queues the batch, prints its id and the command that picks it up,
521
+ and exits. `--batch <id>` picks up a batch queued earlier; with `--list`, the
522
+ results go next to that list and the list must have as many messages as the
523
+ batch has items (exit 2 otherwise), otherwise they go to `<id>.optimized.jsonl`
524
+ here. `--no-wait` with `--batch` exits 2: it only queues a new list.
525
+
526
+ ## After you send
527
+
528
+ ```sh
529
+ amdahl feedback <run-id> --evidence did_it_myself --outcome used_with_edits --sent-text sent.txt
530
+ amdahl sent <run-id> --provider gmail --channel email --sent-as edited --provider-send-id "<msg-id@mail.gmail.com>"
531
+ ```
532
+
533
+ `feedback` says what happened to the message one optimize run returned
534
+ (`POST /messages/optimize/runs/:id/feedback`). `--evidence` is required:
535
+ `did_it_myself`, `reported_to_me` or `inferred`. Send at least one of
536
+ `--outcome` (`used`, `used_with_edits`, `not_used`, `unsure`), `--comment`
537
+ (the sender's own words) and `--sent-text` (a file with the text as sent, or
538
+ `-` for stdin). Amdahl learns the sender's preferences from an edit, unless
539
+ the evidence is `inferred`. Each call stores one event, so running it twice
540
+ stores two.
541
+
542
+ `sent` is for the system that sent the message: it reports that it went out
543
+ (`POST /messages/report_send`). A report from a CLI sign-in is stored as
544
+ `reported_via: cli`. The flags are the report's fields: `--provider`
545
+ (`lemlist`, `instantly`, `outreach`, `salesloft`, `hubspot`, `salesforce`,
546
+ `gmail`, `heyreach`, `apollo`, `linkedin_manual`, `other`), `--channel`
547
+ (`email`, `linkedin_invite`, `linkedin_message`, `linkedin_inmail`),
548
+ `--sent-as` (`returned` or `edited`) and `--sent-at` (a time with a time
549
+ zone; the current time when left out), then the optional ids.
550
+
551
+ The server stores a report only when it can tell which send it is, so one of
552
+ `--provider-send-id`, `--recipient` (an email address, or a LinkedIn profile
553
+ URL for a LinkedIn channel) or `--recipient-key-hash` (a SHA-256) is required.
554
+ A campaign, a lead and a step are not enough. Without one, the CLI exits 2
555
+ before it sends anything. The id is the provider's own: for gmail, the
556
+ `Message-ID` header with its angle brackets; never an email address (give that
557
+ as `--recipient`; Amdahl stores only a hash of it). The command reports one
558
+ send, so give one run id. To report many at once, call
559
+ `POST /messages/report_send`, which takes up to 100 items. Give `--recipient`
560
+ or `--recipient-key-hash`, not both: the server refuses the pair as
561
+ `invalid_recipient`, so the CLI exits 2 first. `--sent-at` needs an uppercase
562
+ `T` and `Z`, as the server does. The CLI checks each value against the
563
+ server's limits first, and a mistake is a usage error that names the flag. It
564
+ counts a text's length as the server does, in UTF-16 units, so an emoji counts
565
+ as two. A send the server did not store prints why and exits 1.
566
+
567
+ ## Next steps
568
+
569
+ Human output names the next step where there is one, in at most two extra
570
+ lines; `--json` never carries them.
571
+
572
+ - `amdahl optimize` on a workspace with no data connected ends with
573
+ `Connect calls or a CRM so Amdahl checks claims against them: amdahl connect`.
574
+ The CLI asks `GET /setup/status` beside the first rewrite, so it adds no
575
+ wait, and says nothing when it cannot tell. Only a person at a terminal
576
+ gets the line and the request (the same test as `optimize` with no file): a
577
+ pipe, an agent, CI, `AMDAHL_NO_PROMPT=1` and `--json` make exactly the
578
+ requests they made before.
579
+ - `amdahl status` gives each blocked item its fix: no data, `amdahl connect`;
580
+ a connection that needs attention, its console page; a used-up quota, the
581
+ day it resets; a role too low, ask a workspace admin.
582
+ - Bare `amdahl`, signed in, with every tool set up: what to try here.
583
+
356
584
  ## Skills
357
585
 
358
586
  `skills add` installs one skill from the library at https://amdahl.ai/skills.
@@ -503,6 +731,33 @@ nothing. `--site <origin>` sends both requests to another origin, for
503
731
  example a local copy of the site (`--site http://localhost:6782`). The
504
732
  origin must be `https`; `http` is allowed only for localhost.
505
733
 
734
+ ## Feedback link
735
+
736
+ Without `--json`, `amdahl optimize` ends each run with its run id:
737
+
738
+ ```
739
+ Run: 3f2c8a1e-...
740
+ ```
741
+
742
+ Under `--json` the run id is in `result.run_id` and nothing extra prints.
743
+
744
+ Amdahl staff can opt in to a feedback link after the run id. The link opens the
745
+ run's internal page in the Amdahl console, which only staff can see. It is off
746
+ by default. To turn it on:
747
+
748
+ - Set `"feedbackLink": true` at the top level of the config file
749
+ (`${XDG_CONFIG_HOME:-~/.config}/amdahl/config.json`, or
750
+ `%APPDATA%\amdahl\config.json` on Windows).
751
+ - Or set `AMDAHL_FEEDBACK_LINK=1` for one command or shell.
752
+ `AMDAHL_FEEDBACK_LINK=0` turns it off even when the config turns it on.
753
+
754
+ The link uses the console for your API host (see
755
+ [Targeting staging](#targeting-staging)).
756
+
757
+ Every request to the Amdahl API sends the headers
758
+ `X-Amdahl-Client: cli/<version>` and `User-Agent: amdahl-cli/<version>`, so
759
+ Amdahl can tell which runs came from the CLI. The headers are only labels. It never changes what a credential can do.
760
+
506
761
  ## Targeting staging
507
762
 
508
763
  ```sh
@@ -542,10 +797,10 @@ code shown in the terminal; then the key is printed once and stored nowhere.
542
797
  | code | meaning |
543
798
  |---|---|
544
799
  | 0 | ok |
545
- | 1 | general (`login_timeout`, `state_mismatch`, optimize `ok: false`, a `skills add` refusal, `invalid_config`, bare `amdahl` with no terminal that found no client) |
800
+ | 1 | general (`login_timeout`, `state_mismatch`, optimize `ok: false`, a failed item in `optimize --list`, a send `sent` could not store, research that failed or is still running, a `skills add` refusal, `invalid_config`, bare `amdahl` with no terminal that found no client) |
546
801
  | 2 | usage (also bare `amdahl` in CI, with `AMDAHL_NO_PROMPT=1` or with `--json`) |
547
802
  | 3 | not signed in (401, `invalid_grant`, no credential) |
548
- | 4 | forbidden (403, `oauth_required`, `cli_client_required`, `not_admin`) |
803
+ | 4 | forbidden (403, `oauth_required`, `cli_client_required`, `not_admin`, `missing_scope`; also research that started but this credential cannot read) |
549
804
  | 5 | no workspace |
550
805
  | 6 | rate limited or quota (429) |
551
806
  | 7 | approval denied, expired or locked out |
@@ -555,7 +810,7 @@ code shown in the terminal; then the key is printed once and stored nowhere.
555
810
 
556
811
  ## Development
557
812
 
558
- Needs Node.js 20 or later and pnpm 9 (`corepack enable` picks up the version
813
+ Needs Node.js 22 or later and pnpm 9 (`corepack enable` picks up the version
559
814
  pinned in `package.json`).
560
815
 
561
816
  ```sh