bwx 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.
package/README.md CHANGED
@@ -54,20 +54,27 @@ bwx doctor
54
54
 
55
55
  | Command | Description |
56
56
  |---------|-------------|
57
- | `bwx status` | Show vault state |
57
+ | `bwx status` | Show vault state and whether reads work right now |
58
58
  | `bwx unlock` | Unlock vault & cache session |
59
59
  | `bwx lock` | Lock vault & clear session |
60
60
  | `bwx sync` | Sync vault from server |
61
61
  | `bwx doctor` | Check setup (bw CLI, email, password, config) |
62
62
 
63
+ `status` reports a `session` alongside the vault state, because `bw` answers for the
64
+ vault and not for bwx: a `locked` vault with a `valid` session reads fine and needs no
65
+ unlock. `valid` means reads work now, `stale` means the cached session was rejected, and
66
+ `none` means the next read unlocks.
67
+
63
68
  ### Read
64
69
 
65
70
  | Command | Description |
66
71
  |---------|-------------|
67
72
  | `bwx get <field> <item>` | Get field (`password`, `username`, `totp`, `notes`, `uri`, `item`) |
68
- | `bwx field <item> <name>` | Get custom field by name |
73
+ | `bwx field <name> <item>` | Get custom field by name (same argument order as `get`) |
69
74
  | `bwx search <query>` | Search items (`--type`, `--folder`, `--limit`, `--full-items`) |
70
- | `bwx list [type]` | List items (`--folder`, `--limit`, `--full-items`) |
75
+ | `bwx list [type]` | List items (`--type`, `--folder`, `--limit`, `--full-items`) |
76
+ | `bwx folders` | List folder names and IDs |
77
+ | `bwx trash` | List items in the trash |
71
78
  | `bwx run --env NAME=<field>:<item> -- <cmd>` | Run a command with secrets in its environment |
72
79
  | `bwx attach list <item>` | List attachments for an item |
73
80
  | `bwx attach get <item> <attachment>` | Download attachment by ID or filename |
@@ -77,6 +84,41 @@ human, `--plain`, and `--json` form, so asking for machine-readable output never
77
84
  what is exposed. `--full-items` is the explicit opt-in to complete item objects, which
78
85
  carry passwords, notes, and custom fields.
79
86
 
87
+ A lookup that finds nothing suggests what you probably meant, and one that matches
88
+ several items lists them, so neither ends in a guessing round:
89
+
90
+ ```
91
+ $ bwx get password "GitHub PAT vforsh old"
92
+ error: No item matching "GitHub PAT vforsh old". Did you mean:
93
+
94
+ 934c1f57-… note GitHub PAT (vforsh)
95
+ 3683fb9a-… note GitHub PAT (yaforsh) [codex-monitor-pr]
96
+ ```
97
+
98
+ ### Listings are capped
99
+
100
+ Listings stop at 50 items by default — an uncapped `bwx list` returns the whole vault,
101
+ and with `--full-items` that is every secret in one call. `--limit 0` opts out. Truncation
102
+ is never silent: it warns on stderr (surviving `--json`) and adds `meta` to the JSON
103
+ envelope.
104
+
105
+ ```bash
106
+ bwx list --limit 0 # everything
107
+ bwx list --json # { "data": [...], "meta": { "total": 491, "shown": 50, "truncated": true } }
108
+ ```
109
+
110
+ ### Folders
111
+
112
+ `--folder` takes a name or an ID on `list`, `search`, `create`, and `edit`; `none` means
113
+ the unfiled bucket. Names are resolved against the vault, so `bwx folders` is only needed
114
+ to see what exists.
115
+
116
+ ```bash
117
+ bwx folders
118
+ bwx list --folder "API Keys"
119
+ bwx edit "Acme" --folder none # unfile
120
+ ```
121
+
80
122
  ### Secrets into a child process
81
123
 
82
124
  `bwx run` resolves secrets and places them only in the child's environment: nothing is
@@ -114,6 +156,34 @@ The check is cached in `~/.config/bwx/state.json`, so a fresh vault costs no ext
114
156
  | `bwx create` | Create item (`--type`, `--name`, `--username`, `--password-stdin`, `--field-file k=path`) |
115
157
  | `bwx edit <item>` | Edit item (`--name`, `--password-env`, `--add-field-file k=path`, `--rm-field name`) |
116
158
  | `bwx delete <item>` | Delete item (`--permanent`, `--force`) |
159
+ | `bwx restore <item>` | Restore an item from the trash |
160
+ | `bwx generate` | Generate a password or passphrase (local, no vault access) |
161
+
162
+ Login-only flags (`--username`, any `--password-*`, `--uri`) require a login item. A
163
+ secure note has nowhere to put them, so passing them to one is an error rather than an
164
+ exit 0 over a credential that was silently dropped.
165
+
166
+ `bwx delete` requires `--force` whenever it is not talking to a terminal. The confirmation
167
+ prompt cannot render for a script or an agent, so without it `--force` would be decorative
168
+ and every non-interactive delete unguarded. Deletes go to the trash unless `--permanent`,
169
+ and `bwx trash` / `bwx restore` are the way back.
170
+
171
+ ### Generated passwords
172
+
173
+ `--password-generate` on `create` and `edit` sends a new secret straight to the vault. It
174
+ is never printed, never echoed back in `--json`, and never reaches a terminal, a
175
+ transcript, or a log — which is what makes it safe to use from an agent.
176
+
177
+ ```bash
178
+ bwx create --type login --name "Deploy" --username svc --password-generate
179
+ bwx edit "Deploy" --password-generate --generate-length 32
180
+ bwx generate --length 32 # print one instead
181
+ bwx generate --passphrase --words 5
182
+ ```
183
+
184
+ `bwx generate` runs locally and needs no unlocked vault. Flags: `--length <n>`,
185
+ `--no-uppercase`, `--no-lowercase`, `--no-numbers`, `-s/--special`, `--ambiguous`,
186
+ `--passphrase`, `--words <n>`, `--separator <char>`, `--capitalize`, `--include-number`.
117
187
 
118
188
  ### Config
119
189
 
@@ -156,7 +226,7 @@ Supported source flags:
156
226
 
157
227
  | Flag | Effect |
158
228
  |------|--------|
159
- | `--json` | JSON output: `{ "data": ... }` / `{ "error": ... }` |
229
+ | `--json` | JSON output: `{ "data": ... }` / `{ "error": ... }`, plus `meta` where a response describes itself |
160
230
  | `--plain` | Tab-separated, one item per line |
161
231
  | `-q, --quiet` | Suppress logs and warnings |
162
232
  | `-v, --verbose` | Include stack traces |
@@ -191,6 +261,17 @@ Field syntax for `--field`, `--field-file`, `--field-env`, and edit variants:
191
261
  | 8 | Timeout |
192
262
  | 9 | User cancelled |
193
263
 
264
+ ## Upgrading to 0.5.0
265
+
266
+ Breaking changes, all of them cases where the old behavior failed quietly:
267
+
268
+ - `bwx field` takes `<name> <item>`, matching `bwx get`. It was `<item> <name>`.
269
+ - `bwx delete` needs `--force` when not interactive; it used to delete unprompted.
270
+ - `bwx list` and `bwx search` stop at 50 items unless given `--limit`. Pass `--limit 0`
271
+ for the old behavior.
272
+ - `--limit` rejects non-numeric values instead of ignoring them (and returning everything).
273
+ - Login-only flags on a secure note are an error instead of a silent drop.
274
+
194
275
  ## Stack
195
276
 
196
277
  Bun, TypeScript, Commander, Zod, picocolors.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bwx",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "keywords": [
6
6
  "bitwarden",
@@ -12,7 +12,8 @@
12
12
  ],
13
13
  "files": [
14
14
  "bin",
15
- "src"
15
+ "src",
16
+ "skill"
16
17
  ],
17
18
  "bin": {
18
19
  "bwx": "bin/bwx"
@@ -0,0 +1,196 @@
1
+ ---
2
+ name: bwx
3
+ description: Bitwarden Extended CLI — retrieve, create, edit, delete vault items with auto-unlock. Use when the agent needs passwords, API keys, TOTP codes, custom fields, or vault management. Triggers on mentions of Bitwarden, bw, bwx, vault, password, secrets, TOTP, save key/token.
4
+ ---
5
+
6
+ # bwx
7
+
8
+ Extended Bitwarden CLI. Wraps `bw` with auto-unlock, session caching, and structured output.
9
+
10
+ ## Quick start
11
+
12
+ ```bash
13
+ bwx get password "GitHub" # retrieve a password (auto-unlocks)
14
+ bwx get totp "AWS (user@example.com)" # TOTP code
15
+ bwx get username "GitHub" # username
16
+ bwx get notes "Deploy key" # secure note content
17
+ bwx get uri "GitHub" # first URI
18
+ bwx get item "GitHub" # full item JSON
19
+ bwx get "API Key" "Acme" # custom field by name
20
+ bwx field "API Key" "Acme" # custom-field-only lookup, same arg order
21
+
22
+ bwx search github # fuzzy search, human table
23
+ bwx search github --json # id/type/name/username only — no secrets
24
+ bwx search github --type login --limit 5
25
+
26
+ # hand a secret to a child process instead of printing it
27
+ bwx run --env KEENETIC_PASS=password:'http://my.keenetic.net' -- expect ./recovery.exp
28
+
29
+ # generate straight into the vault — the value never enters your context
30
+ bwx create --type login --name "Deploy" --username svc --password-generate
31
+
32
+ bwx attach list "Apple Developer" # list item attachments
33
+ bwx attach get "Apple Developer" AuthKey.p8
34
+ bwx attach get "Apple Developer" AuthKey.p8 --output ./secrets/
35
+
36
+ bwx create --name "API Key" --notes "sk_live_abc123" --field account=acme
37
+ printf '%s' "$TOKEN" | bwx create --type login --name "GitHub" --username user --password-stdin --uri https://github.com
38
+ bwx create --name "Acme" --field-file '!API Key=./token.txt' --field-env account=ACME_ACCOUNT
39
+ echo "secret" | bwx create --name "Piped Note"
40
+ ```
41
+
42
+ ## Commands
43
+
44
+ ### `bwx get <field> <item>`
45
+
46
+ Built-in fields: `password | username | totp | notes | uri | item`.
47
+ Anything else is looked up as a custom field. Lists available fields on miss.
48
+
49
+ A miss suggests near matches; an ambiguous name lists candidates with IDs. Read the
50
+ suggestions instead of retrying variations — the answer is usually in them.
51
+
52
+ ### `bwx field <name> <item>`
53
+
54
+ Custom fields only, so an item whose custom field is named `password` is still reachable.
55
+ Takes its arguments in the same order as `get` (this changed in 0.5.0).
56
+
57
+ ### `bwx search <query>` / `bwx list [type]`
58
+
59
+ Flags: `--type login|note|card|identity`, `--folder <name|id>`, `--limit <n>`, `--full-items`
60
+
61
+ Human, `--plain`, and `--json` output all carry the same reduced projection — `id`, `type`,
62
+ `name`, `username` — so listings never leak passwords, notes, or custom fields. `--full-items`
63
+ opts into complete item objects and **does** include secrets; avoid it in logged sessions.
64
+
65
+ **Listings stop at 50 items** unless you pass `--limit` (`--limit 0` for all). Truncation is
66
+ reported on stderr and in `meta` on the JSON envelope — check it before concluding a listing
67
+ was complete. `--limit` rejects non-numeric values rather than ignoring them.
68
+
69
+ ### `bwx folders`
70
+
71
+ Lists folder names and IDs. `--folder` accepts either on `list`, `search`, `create`, and
72
+ `edit`; `none` selects unfiled items.
73
+
74
+ ### `bwx run --env NAME=<field>:<item> -- <command>`
75
+
76
+ Resolves secrets and injects them into the child's environment only — not stdout, not argv.
77
+ This is the preferred way to give a subprocess a credential.
78
+
79
+ ```bash
80
+ bwx run --env DB_PASS=password:'Prod DB' -- ./migrate.sh
81
+ bwx run --env TOKEN='API Key:Acme' --env USER=username:Acme -- ./deploy.sh
82
+ ```
83
+
84
+ - `--env` is repeatable; the field resolves as in `bwx get` (built-in, else custom field).
85
+ - Only the first `:` splits field from item, so `password:http://my.router` works.
86
+ - Put `--` before the command when it takes its own flags.
87
+ - Child stdio is inherited; bwx exits with the child's status (`128 + signal` if killed).
88
+ - `-v` logs injected variable names only, never values.
89
+ - Several variables from one item cost a single vault read, so group them here rather
90
+ than issuing separate `bwx get` calls.
91
+
92
+ ### `bwx attach list <item>`
93
+
94
+ Lists attachments for an item. Aliases: `bwx attachment`, `bwx attachments`; `list` has `ls`.
95
+
96
+ ### `bwx attach get <item> <attachment>`
97
+
98
+ Downloads an attachment by ID or filename. Defaults to saving as the attachment filename in the current directory. Use `--output <path>` to choose a file or directory.
99
+
100
+ ### `bwx create [options]`
101
+
102
+ Flags: `--type login|note`, `--name`, `--notes`, `--notes-file`, `--username`, `--password`, `--password-stdin`, `--password-file`, `--password-env`, `--password-generate`, `--generate-length <n>`, `--uri` (repeatable), `--field k=v` (repeatable), `--field-file k=path`, `--field-env k=ENV`, `--folder <name|id>`, `--favorite`, `--from-json`
103
+
104
+ `--username`, any `--password-*`, and `--uri` require `--type login`; a note cannot hold
105
+ them and passing them to one is now an error rather than a silent drop.
106
+
107
+ Field syntax: `k=v` (text), `!k=v` (hidden), `bool:k=v` (boolean). The same key syntax works for `--field-file` and `--field-env`, where the value is a path or environment variable name. Stdin is read as `--notes` when piped; prefer `--password-stdin` for login passwords.
108
+
109
+ ### `bwx edit <item> [options]`
110
+
111
+ Fetch → patch → push.
112
+ ```bash
113
+ bwx edit "API Key" --add-field region=us-east-1
114
+ printf '%s' "$TOKEN" | bwx edit "GitHub" --password-stdin --uri https://github.com
115
+ bwx edit "Note" --rm-field old-key
116
+ ```
117
+ Flags: `--name`, `--notes`, `--notes-file`, `--username`, `--password`, `--password-stdin`, `--password-file`, `--password-env`, `--password-generate`, `--generate-length <n>`, `--uri` (replaces all), `--add-field k=v`, `--add-field-file k=path`, `--add-field-env k=ENV`, `--rm-field name`, `--folder <name|id>` (`none` unfiles), `--favorite/--no-favorite`, `--from-json`
118
+
119
+ ### `bwx delete <item>`
120
+
121
+ ```bash
122
+ bwx delete "Test Note" --force # --force is REQUIRED when not a TTY
123
+ bwx delete "Test Note" --permanent --force # skip the trash
124
+ bwx trash # what is recoverable
125
+ bwx restore "Test Note" # undo a non-permanent delete
126
+ ```
127
+
128
+ The prompt only exists for a human at a terminal, so an agent must pass `--force`
129
+ deliberately. Deletes are recoverable through `bwx restore` unless `--permanent`.
130
+
131
+ ### `bwx generate`
132
+
133
+ ```bash
134
+ bwx generate --length 32
135
+ bwx generate --passphrase --words 5 --separator -
136
+ ```
137
+
138
+ Local, no vault access, no unlock. Flags: `--length <n>`, `--no-uppercase`,
139
+ `--no-lowercase`, `--no-numbers`, `-s/--special`, `--ambiguous`, `--passphrase`,
140
+ `--words <n>`, `--separator <char>`, `--capitalize`, `--include-number`.
141
+
142
+ Prefer `--password-generate` on `create`/`edit` over generating and passing the value:
143
+ it goes to the vault without ever entering your context, and `--json` returns
144
+ `***GENERATED***` in place of the password.
145
+
146
+ ### Other
147
+
148
+ | Command | Description |
149
+ |---------|-------------|
150
+ | `bwx status` | Vault state + session usability (no auto-unlock) |
151
+ | `bwx sync` | Force vault sync |
152
+ | `bwx unlock` / `bwx lock` | Manual session control |
153
+ | `bwx folders` | Folder names and IDs |
154
+ | `bwx trash` / `bwx restore <item>` | Inspect and undo deletes |
155
+ | `bwx config list` / `bwx cfg ls` | Show config path + redacted content |
156
+ | `bwx config master-password` | Set/update master password in Keychain |
157
+
158
+ `bwx status` reports `session: valid | stale | none` separately from the vault state.
159
+ **A `locked` vault with a `valid` session reads fine — do not run `bwx unlock` on it.**
160
+ Only `stale`/`none` mean the next read pays for an unlock.
161
+
162
+ ## Global flags
163
+
164
+ | Flag | Effect |
165
+ |------|--------|
166
+ | `--json` | `{ "data": ... }` on success, `{ "error": { "message", "exitCode" } }` on failure |
167
+ | `--plain` | Stable parseable lines on stdout |
168
+ | `-q` | Suppress logs and warnings |
169
+ | `-v` | Verbose (stack traces on error) |
170
+ | `--timeout <duration>` | Deadline per `bw` call (default `1m`, `0` disables) → exit 8 |
171
+ | `--sync-if-older-than <duration>` | Sync first when the vault last synced longer ago |
172
+
173
+ Durations: `250ms`, `30s`, `15m`, `2h`, `1d`; a bare number is seconds.
174
+
175
+ ## Vault freshness
176
+
177
+ Reads come from the local vault copy. Reads warn on stderr when the last sync is over 24h
178
+ old — a rotated secret can otherwise look valid or missing. Pass
179
+ `--sync-if-older-than 15m` to sync first instead, or run `bwx sync`. Warnings reach stderr
180
+ even under `--json`; `-q` silences them.
181
+
182
+ ## Safety rules
183
+
184
+ - Never use `--full-items` (or `get item` for discovery) in logged sessions.
185
+ - Prefer `bwx run --env ...` over `export VAR="$(bwx get ...)"`; secrets never touch stdout or argv.
186
+ - Prefer `--password-generate` over generating a secret yourself and passing it in.
187
+ - Request the narrowest field, resolving an exact item ID first.
188
+ - Use `-q` for command substitution so unlock logs cannot contaminate merged output.
189
+ - On writes, prefer `--password-stdin`, `--password-env`, `--field-file`, `--field-env`.
190
+ - Never echo, log, checksum, or commit retrieved values; `unset` temporary variables.
191
+
192
+ ## Auth
193
+
194
+ - Master password: macOS Keychain (account `bitwarden`, service `bitwarden-master`)
195
+ - Session cached to `~/.config/bwx/session` (mode 600), given to `bw` via `BW_SESSION`
196
+ - Freshness snapshot cached to `~/.config/bwx/state.json`
@@ -98,7 +98,8 @@ export interface PatchOptions {
98
98
  uris?: string[];
99
99
  addFields?: ParsedField[];
100
100
  rmFields?: string[];
101
- folderId?: string;
101
+ /** `null` unfiles the item; `undefined` leaves its folder alone. */
102
+ folderId?: string | null;
102
103
  favorite?: boolean;
103
104
  }
104
105