@hivecommons/promptargs 0.7.0 → 0.9.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 (81) hide show
  1. package/README.md +106 -20
  2. package/dist/autodetect.js +39 -12
  3. package/dist/autodetect.js.map +1 -1
  4. package/dist/cli.js +78 -147
  5. package/dist/cli.js.map +1 -1
  6. package/dist/commands.d.ts +4 -0
  7. package/dist/commands.js +148 -0
  8. package/dist/commands.js.map +1 -0
  9. package/dist/flags.d.ts +20 -0
  10. package/dist/flags.js +101 -0
  11. package/dist/flags.js.map +1 -0
  12. package/dist/loader.d.ts +2 -0
  13. package/dist/loader.js +84 -11
  14. package/dist/loader.js.map +1 -1
  15. package/dist/log.d.ts +19 -0
  16. package/dist/log.js +41 -0
  17. package/dist/log.js.map +1 -0
  18. package/dist/parser.js +39 -9
  19. package/dist/parser.js.map +1 -1
  20. package/dist/resolver.d.ts +3 -1
  21. package/dist/resolver.js +105 -34
  22. package/dist/resolver.js.map +1 -1
  23. package/dist/status.d.ts +1 -1
  24. package/dist/status.js +5 -4
  25. package/dist/status.js.map +1 -1
  26. package/dist/ui.d.ts +3 -0
  27. package/dist/ui.html +21 -6
  28. package/dist/ui.js +89 -12
  29. package/dist/ui.js.map +1 -1
  30. package/package.json +13 -4
  31. package/dist/autodetect.test.d.ts +0 -1
  32. package/dist/autodetect.test.js +0 -207
  33. package/dist/autodetect.test.js.map +0 -1
  34. package/dist/cli.test.d.ts +0 -1
  35. package/dist/cli.test.js +0 -310
  36. package/dist/cli.test.js.map +0 -1
  37. package/dist/index.test.d.ts +0 -1
  38. package/dist/index.test.js +0 -59
  39. package/dist/index.test.js.map +0 -1
  40. package/dist/iterate.test.d.ts +0 -1
  41. package/dist/iterate.test.js +0 -20
  42. package/dist/iterate.test.js.map +0 -1
  43. package/dist/loader.test.d.ts +0 -1
  44. package/dist/loader.test.js +0 -65
  45. package/dist/loader.test.js.map +0 -1
  46. package/dist/parser.test.d.ts +0 -1
  47. package/dist/parser.test.js +0 -93
  48. package/dist/parser.test.js.map +0 -1
  49. package/dist/resolver.test.d.ts +0 -1
  50. package/dist/resolver.test.js +0 -184
  51. package/dist/resolver.test.js.map +0 -1
  52. package/dist/sanitize.test.d.ts +0 -1
  53. package/dist/sanitize.test.js +0 -38
  54. package/dist/sanitize.test.js.map +0 -1
  55. package/dist/status.test.d.ts +0 -1
  56. package/dist/status.test.js +0 -33
  57. package/dist/status.test.js.map +0 -1
  58. package/dist/ui-bootstrap.test.d.ts +0 -1
  59. package/dist/ui-bootstrap.test.js +0 -479
  60. package/dist/ui-bootstrap.test.js.map +0 -1
  61. package/dist/ui-builder.test.d.ts +0 -1
  62. package/dist/ui-builder.test.js +0 -272
  63. package/dist/ui-builder.test.js.map +0 -1
  64. package/dist/ui-command-quoting.test.d.ts +0 -1
  65. package/dist/ui-command-quoting.test.js +0 -95
  66. package/dist/ui-command-quoting.test.js.map +0 -1
  67. package/dist/ui-harness.d.ts +0 -20
  68. package/dist/ui-harness.js +0 -40
  69. package/dist/ui-harness.js.map +0 -1
  70. package/dist/ui-harness.test.d.ts +0 -1
  71. package/dist/ui-harness.test.js +0 -30
  72. package/dist/ui-harness.test.js.map +0 -1
  73. package/dist/ui-inline.test.d.ts +0 -1
  74. package/dist/ui-inline.test.js +0 -90
  75. package/dist/ui-inline.test.js.map +0 -1
  76. package/dist/ui-interactions.test.d.ts +0 -1
  77. package/dist/ui-interactions.test.js +0 -489
  78. package/dist/ui-interactions.test.js.map +0 -1
  79. package/dist/ui.test.d.ts +0 -1
  80. package/dist/ui.test.js +0 -283
  81. package/dist/ui.test.js.map +0 -1
package/README.md CHANGED
@@ -28,6 +28,8 @@ Works as a **CLI** and as a **skill** in Claude Code, Copilot, Goose, and Bob.
28
28
 
29
29
  ## Install
30
30
 
31
+ Requires Node.js 22 or newer.
32
+
31
33
  ### CLI
32
34
 
33
35
  ```bash
@@ -40,9 +42,29 @@ Or just run it without installing:
40
42
  npx @hivecommons/promptargs help
41
43
  ```
42
44
 
43
- ### As a Slash Command / Skill
45
+ ### As an Agent Skill
46
+
47
+ promptargs ships an [Agent Skill](https://agentskills.io/specification) at [`skills/promptargs/SKILL.md`](skills/promptargs/SKILL.md), loaded by GitHub Copilot (CLI, cloud agent, VS Code agent mode) and Claude Code.
48
+
49
+ **With the GitHub CLI** (2.90.0 or later):
50
+ ```bash
51
+ gh skill install hivecommons/promptargs promptargs
52
+ ```
53
+
54
+ **Manually** (copy the `skills/promptargs/` directory):
55
+ ```bash
56
+ # Copilot
57
+ mkdir -p ~/.copilot/skills
58
+ cp -r skills/promptargs ~/.copilot/skills/
59
+
60
+ # Claude Code
61
+ mkdir -p ~/.claude/skills
62
+ cp -r skills/promptargs ~/.claude/skills/
63
+ ```
64
+
65
+ Use `.github/skills/` or `.claude/skills/` inside a project instead to share it with your team.
44
66
 
45
- This repo ships skill files for multiple AI coding tools. Install the one(s) you use:
67
+ ### As a Slash Command (other tools)
46
68
 
47
69
  **Claude Code:**
48
70
  ```bash
@@ -65,8 +87,6 @@ curl -o ~/.bob/skills/promptargs.md \
65
87
  https://raw.githubusercontent.com/hivecommons/promptargs/main/.bob/skills/promptargs.md
66
88
  ```
67
89
 
68
- **Copilot:** Automatically detected from `.github/copilot-instructions.md` when you clone this repo.
69
-
70
90
  ---
71
91
 
72
92
  ## Quick Start (3 steps)
@@ -122,7 +142,11 @@ A blank looks like this: `{{name}}`
122
142
 
123
143
  A blank with a default looks like this: `{{name=hello}}` (promptargs extension)
124
144
 
125
- Padding inside the braces is fine too — `{{ name }}` and `{{ name=hello }}` mean the same thing.
145
+ Padding inside the braces is fine too — `{{ name }}` and `{{ name=hello }}` mean the same thing, and so does `{{ name = hello }}` (padding around `=` is not part of the default).
146
+
147
+ Names may use letters, digits, `_` and `-` (`{{file-name}}`). Dotted names such as `{{repo.owner}}` are rejected with an error — use `{{repo_owner}}`; the switch names `no-interactive`, `cross`, `json`, `status` and `help` are also rejected, as they are reserved for CLI switches. Likewise, the template file names `help`, `env`, `ui`, `init`, `list` and `show` are reserved for the subcommands of the same name; such a template is listed as unreachable by `promptargs list` (and `show` warns), because the subcommand always wins.
148
+
149
+ A default may appear on any occurrence of a blank (`{{name}} … {{name=hello}}`); if two occurrences give different defaults, the first one wins.
126
150
 
127
151
  That's the basics. Mustache comments (`{{! ignore me}}`) work as documented and are safe to use.
128
152
 
@@ -204,6 +228,7 @@ Status lines showing iteration progress go to stderr:
204
228
  |--------|-------------|
205
229
  | `--file=a.go,b.go,c.go` | Comma-separated list |
206
230
  | `--file="src/*.go"` | Glob pattern (expands to matching files) |
231
+ | `--file="src/**/*.go"` | Recursive glob (`**` matches any depth) |
207
232
  | `--file=@list.txt` | Reads one value per line from a file |
208
233
 
209
234
  ### Multiple arrays: zip vs cross-product
@@ -222,6 +247,34 @@ promptargs "Review {{file}} for {{focus}}" \
222
247
  ✅ [3/3]: file=db.go focus=correctness
223
248
  ```
224
249
 
250
+ When arrays have different lengths, the number of runs is the length of the longest array, and shorter arrays repeat (cycle around):
251
+
252
+ ```bash
253
+ promptargs "Review {{file}} for {{focus}}" \
254
+ --file=api.go,auth.go,db.go \
255
+ --focus=security
256
+ ```
257
+
258
+ ```
259
+ ✅ [1/3]: file=api.go focus=security
260
+ ✅ [2/3]: file=auth.go focus=security
261
+ ✅ [3/3]: file=db.go focus=security
262
+ ```
263
+
264
+ Or with two unequal arrays:
265
+
266
+ ```bash
267
+ promptargs "Review {{file}} for {{focus}}" \
268
+ --file=a.go,b.go,c.go \
269
+ --focus=x,y
270
+ ```
271
+
272
+ ```
273
+ ✅ [1/3]: file=a.go focus=x
274
+ ✅ [2/3]: file=b.go focus=y
275
+ ✅ [3/3]: file=c.go focus=x
276
+ ```
277
+
225
278
  **Cross-product mode (`--cross`):** every combination of every value:
226
279
 
227
280
  ```bash
@@ -245,12 +298,6 @@ promptargs "Review {{file}} for {{focus}}" \
245
298
 
246
299
  3 files × 3 focuses = 9 runs. Use `--cross` when you want the full matrix.
247
300
 
248
- By default, array iterations run one at a time and print as they finish. Add `--parallel` to run every iteration at once and print all results together:
249
-
250
- ```bash
251
- promptargs review --file=api.go,auth.go,db.go --parallel
252
- ```
253
-
254
301
  ---
255
302
 
256
303
  ## Where Templates Live
@@ -262,6 +309,8 @@ promptargs review --file=api.go,auth.go,db.go --parallel
262
309
 
263
310
  Project templates override personal ones with the same name.
264
311
 
312
+ Symlinked `*.md` entries are followed only when their real target is inside the same `.prompts/` directory (a sub-directory is fine); a symlink whose target escapes the directory is listed with an error and refused when run, so a cloned repo cannot make `promptargs` read files elsewhere on your machine. Symlinks to directories and dangling symlinks are skipped. The project `.prompts/` entry itself must be a real directory: if it is a symlink, it is refused with a diagnostic on stderr (`loader_dir_symlink` under `PROMPTARGS_LOG=json`) and no project templates are loaded (`~/.prompts/` is yours and may be a symlink).
313
+
265
314
  ---
266
315
 
267
316
  ## Auto-Magic Variables ✨
@@ -301,7 +350,7 @@ In the **Builder UI** (`promptargs ui`), click "Show terminal env" to see all av
301
350
 
302
351
  Common useful env vars: `NODE_ENV`, `AWS_REGION`, `CI`, `USER`, `EDITOR`, `PATH`, `SHELL`.
303
352
 
304
- Credential-looking names are intentionally **excluded** from auto-detection: anything containing `TOKEN`, `SECRET`, `PASSWORD`/`PASSWD`/`PASSPHRASE`, `CREDENTIAL`, `API_KEY`/`ACCESS_KEY`/`PRIVATE_KEY`, `BEARER`, `COOKIE`, `WEBHOOK`, `JWT`, `HMAC`, `OAUTH`, `CONNECTION_STRING`, or the `_`-delimited words `AUTH`, `KEY`/`KEYS`, `PWD`, `PAT`, `PASS`, `DSN` (so `GITHUB_TOKEN`, `SIGNING_KEY`, `MYSQL_PWD`, `GITHUB_PAT` and `SLACK_WEBHOOK_URL` are all refused, while `PATH`, `PWD`, `KEYBOARD` and `AUTHOR` still work). Credential-**shaped** values are excluded whatever the name: URLs with a password in them (`postgres://user:pass@host/db`), PEM blocks, JWTs, and well-known token prefixes (`ghp_`, `github_pat_`, `glpat-`, `sk-`, `xoxb-`, `AKIA…`, `npm_…`, …). A repo-controlled template can therefore never expand `{{GITHUB_TOKEN}}` or `{{DATABASE_URL}}` into a secret, even though an explicit `--flag=value` can still pass any value you choose. This also applies to the Builder UI's "Show terminal env" list.
353
+ Credential-looking names are intentionally **excluded** from auto-detection: anything containing `TOKEN`, `SECRET`, `PASSWORD`/`PASSWD`/`PASSPHRASE`, `CREDENTIAL`/`CREDENTIALS`/`CREDS`, `API_KEY`/`ACCESS_KEY`/`PRIVATE_KEY`, `BEARER`, `COOKIE`, `WEBHOOK`, `JWT`, `HMAC`, `OAUTH`, `CONNECTION_STRING`, or the `_`-delimited words `AUTH`, `KEY`/`KEYS`, `PWD`, `PAT`, `PASS`, `DSN` (so `GITHUB_TOKEN`, `SIGNING_KEY`, `MYSQL_PWD`, `GITHUB_PAT` and `SLACK_WEBHOOK_URL` are all refused, while `PATH`, `PWD`, `KEYBOARD` and `AUTHOR` still work). Credential-**shaped** values are excluded whatever the name: URLs with a password in them (`postgres://user:pass@host/db`), PEM blocks, JWTs, and well-known token prefixes (`ghp_`, `github_pat_`, `glpat-`, `sk-`, `xoxb-`, `AKIA…`, `npm_…`, `hf_`, `pypi-`, `dop_v1_`, `lin_api_`, `shpat_`, `SG.`, `dapi`, `glc_`, `ya29.`, …). A repo-controlled template can therefore never expand `{{GITHUB_TOKEN}}` or `{{DATABASE_URL}}` into a secret, even though an explicit `--flag=value` can still pass any value you choose. This also applies to the Builder UI's "Show terminal env" list.
305
354
 
306
355
  ### Priority
307
356
 
@@ -350,7 +399,7 @@ Don't want to memorize flags? Open the visual builder:
350
399
  promptargs ui
351
400
  ```
352
401
 
353
- Opens a browser at `http://localhost:3700` with:
402
+ Opens a browser at `http://localhost:3700/?t=<token>` — the token is random per run and printed at startup — with:
354
403
 
355
404
  - **Preset examples** — 8 clickable templates (review, explain, fix, test, migrate, security, docs, refactor)
356
405
  - **Template editor** — type your template, variables auto-populate below
@@ -373,12 +422,37 @@ promptargs ui --port=4000
373
422
 
374
423
  promptargs does not collect or export telemetry. There is no telemetry SDK, metrics endpoint, or observability backend configured anywhere in this package — nothing you run ever phones home.
375
424
 
376
- All runtime diagnostics are human-readable console output (CLI status/errors, UI startup/port messages). The **Builder UI** (`promptargs ui`) binds to loopback only (`127.0.0.1`) and rejects requests with a non-local `Host` header, so the page — including the auto-detected Git and terminal environment values it embeds (see [Auto-Magic Variables](#auto-magic-variables-)) — is never reachable from other machines on your network.
425
+ All runtime diagnostics are human-readable console output (CLI status/errors, UI startup/port messages). The **Builder UI** (`promptargs ui`) binds to loopback only (`127.0.0.1`), rejects requests with a non-local `Host` header, and only serves the page to a client presenting the random per-run token in the URL it prints at startup, so the page — including the auto-detected Git and terminal environment values it embeds (see [Auto-Magic Variables](#auto-magic-variables-)) — is never reachable from other machines on your network, nor from other accounts sharing the same machine's loopback interface.
426
+
427
+ **Opt-in structured diagnostics:** set `PROMPTARGS_LOG=json` to make diagnostics (CLI errors and iteration progress, UI Host and token rejections, asset failures, unreadable or symlinked prompts directories, and one line per UI request) print as JSON lines on stderr with a fixed field set: `event`, plus `status`, `duration_ms`, `error_class` (`usage`, `not_found`, `io`, `port_in_use`, `unknown`), `count`, `current`, `total` where relevant. Template contents, argument values, paths and error messages are never logged. When unset, output is unchanged. Nothing is sent over the network.
377
428
 
378
429
  If observability is ever added in the future, it will be explicit opt-in, bounded to local diagnostics, and will exclude environment values, template/prompt content, file paths, diffs, and user identifiers. No network exporter will be added unless you explicitly configure a backend yourself.
379
430
 
380
431
  ---
381
432
 
433
+ ## Use as a Library
434
+
435
+ The package also exports its building blocks as an ES module (Node.js 22+). Install it first with `npm install @hivecommons/promptargs`.
436
+
437
+ ```js
438
+ import { parseVars, expand } from '@hivecommons/promptargs';
439
+
440
+ const template = 'Review {{file}} for {{focus=correctness}} issues.';
441
+
442
+ parseVars(template);
443
+ // [{ name: 'file', ... }, { name: 'focus', defaultValue: 'correctness', ... }]
444
+
445
+ expand(template, { file: 'src/app.ts', focus: 'security' });
446
+ // 'Review src/app.ts for security issues.'
447
+
448
+ expand(template, { file: 'src/app.ts' });
449
+ // 'Review src/app.ts for correctness issues.' (defaults fill missing values)
450
+ ```
451
+
452
+ Variables with no value and no default are left as `{{name}}` in the output. Other exports: `autodetect`, `autodetectAll`, `AUTODETECT_VARS`, `loadTemplates`, `findTemplate`, `resolve` and `renderStatus`.
453
+
454
+ ---
455
+
382
456
  ## Write Your Own Template
383
457
 
384
458
  Create a file in `.prompts/` with any name ending in `.md`:
@@ -415,13 +489,13 @@ promptargs review --file=src/main.ts --no-interactive | claude -p "do this revie
415
489
  # Goose
416
490
  promptargs review --file=src/main.ts --no-interactive | goose run
417
491
 
418
- # Copilot
419
- promptargs review --file=src/main.ts --no-interactive | gh copilot explain
492
+ # Copilot CLI (needs the standalone `copilot` CLI, installed and signed in)
493
+ copilot -p "$(promptargs review --file=src/main.ts --no-interactive)"
420
494
  ```
421
495
 
422
496
  ### As a skill inside your AI tool
423
497
 
424
- If you installed the skill (see [Install](#as-a-skill)), use it directly inside your session:
498
+ If you installed the skill (see [Install](#as-an-agent-skill)), use it directly inside your session:
425
499
 
426
500
  **Claude Code:**
427
501
  ```
@@ -430,7 +504,7 @@ If you installed the skill (see [Install](#as-a-skill)), use it directly inside
430
504
 
431
505
  **Goose / Bob:** The skill is automatically available after install — ask your agent to "use promptargs" or run a template.
432
506
 
433
- **Copilot:** The instructions in `.github/copilot-instructions.md` teach Copilot about promptargs when you're in a repo that has it.
507
+ **Copilot:** Installed skills load automatically; ask Copilot to use promptargs.
434
508
 
435
509
  ---
436
510
 
@@ -448,16 +522,28 @@ If you installed the skill (see [Install](#as-a-skill)), use it directly inside
448
522
  | `promptargs env` | Show auto-detected variable values |
449
523
  | `--no-interactive` | Skip questions, use defaults only |
450
524
  | `--cross` | Cross-product mode (all combinations) |
451
- | `--parallel` | Run array iterations in parallel |
452
525
  | `--json` | Output as JSON |
453
526
  | `--status` | Output status line only |
454
527
  | `... \| claude -p "do this"` | Pipe to Claude |
455
528
  | `... \| goose run` | Pipe to Goose |
456
529
 
530
+ The switch names `no-interactive`, `cross`, `json`, `status` and `help` are reserved and cannot be used as template variable names. Switches take no value: `--json=false` is rejected with a usage error. A flag that names no blank in the template (e.g. a typo like `--fiel=x`) is also an error. So is a blank left unfilled under `--no-interactive` or piped stdin: `promptargs` exits 1 with `Missing value for {{name}}: ...` rather than printing the raw placeholder.
531
+
532
+ ---
533
+
534
+ ## Contributing & Community
535
+
536
+ promptargs is part of [Hive Commons](https://hivecommons.dev), a family of tools for working with AI agents.
537
+
538
+ - Want to contribute? See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, checks, and pull request guidelines.
539
+ - Found a bug or have an idea? Open an [issue](https://github.com/hivecommons/promptargs/issues).
540
+ - Reporting a security vulnerability? Follow [SECURITY.md](SECURITY.md) to report it privately, not through public issues.
541
+ - Please follow the [Hive Commons Code of Conduct](https://github.com/hivecommons/.github/blob/main/CODE_OF_CONDUCT.md).
542
+
457
543
  ---
458
544
 
459
545
  ## License
460
546
 
461
547
  Apache-2.0
462
548
 
463
- Pirate flag icon: [Twemoji](https://twemoji.twitter.com/), [CC-BY 4.0](https://creativecommons.org/licenses/by/4.0/).
549
+ Pirate flag icon: [Twemoji](https://github.com/twitter/twemoji), [CC-BY 4.0](https://creativecommons.org/licenses/by/4.0/).
@@ -2,9 +2,20 @@
2
2
  * Auto-detect variable values from the current environment.
3
3
  */
4
4
  import { execSync } from 'node:child_process';
5
+ // gh is the only network call on the UI startup path; bound it so a stalled gh
6
+ // (hung proxy, expired auth) cannot hang startup.
7
+ const GH_TIMEOUT_MS = 5000;
8
+ // execSync's default 1 MiB maxBuffer makes `git diff` on a large staged change
9
+ // throw ENOBUFS, which the catch below turns into a silent "nothing detected"
10
+ // (#281). Raise it so large diffs are returned rather than dropped.
11
+ const GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
5
12
  function git(cmd) {
6
13
  try {
7
- const out = execSync(`git ${cmd}`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
14
+ const out = execSync(`git ${cmd}`, {
15
+ encoding: 'utf8',
16
+ stdio: ['pipe', 'pipe', 'pipe'],
17
+ maxBuffer: GIT_MAX_BUFFER_BYTES,
18
+ }).trim();
8
19
  // Success with empty output (detached HEAD for `branch --show-current`,
9
20
  // clean tree for `diff`) is "nothing detected", not an empty value —
10
21
  // otherwise it would override the template default and skip the prompt.
@@ -36,6 +47,7 @@ const DETECTORS = {
36
47
  const out = execSync('gh pr view --json number --jq .number', {
37
48
  encoding: 'utf8',
38
49
  stdio: ['pipe', 'pipe', 'pipe'],
50
+ timeout: GH_TIMEOUT_MS,
39
51
  }).trim();
40
52
  return out || undefined;
41
53
  }
@@ -44,7 +56,7 @@ const DETECTORS = {
44
56
  }
45
57
  },
46
58
  user: () => git('config user.name'),
47
- date: () => new Date().toISOString().split('T')[0],
59
+ date: () => new Date().toLocaleDateString('sv-SE'),
48
60
  };
49
61
  export const AUTODETECT_VARS = Object.keys(DETECTORS);
50
62
  // Env var names that look like credentials. The implicit process.env fallback
@@ -55,23 +67,27 @@ export const AUTODETECT_VARS = Object.keys(DETECTORS);
55
67
  // cwd) and AUTHOR stay detectable while GITHUB_PAT, SIGNING_KEY, MYSQL_PWD and
56
68
  // AUTH_HEADER do not.
57
69
  const SENSITIVE_ENV_PATTERN = new RegExp([
58
- 'TOKEN', 'SECRET', 'PASSWORD', 'PASSWD', 'PASSPHRASE', 'CREDENTIAL',
70
+ 'TOKEN', 'SECRET', 'PASSWORD', 'PASSWD', 'PASSPHRASE', 'CREDENTIALS?',
59
71
  'API_?KEY', 'ACCESS_KEY', 'PRIVATE_KEY', 'BEARER', 'COOKIE', 'WEBHOOK',
60
- 'JWT', 'HMAC', 'OAUTH', 'CONN(?:ECTION)?_?STR(?:ING)?',
72
+ 'JWT', 'HMAC', 'OAUTH', 'AUTHORI[SZ]ATION', 'CONN(?:ECTION)?_?STR(?:ING)?',
61
73
  '(?:^|_)AUTH(?:_|$)', '(?:^|_)KEYS?(?:_|$)', '_PWD(?:_|$)',
62
- '(?:^|_)PAT(?:_|$)', '(?:^|_)PASS(?:_|$)', '(?:^|_)DSN(?:_|$)',
74
+ '(?:^|_)PAT(?:_|$)', '(?:^|_)PASS(?:_|$)', '(?:^|_)DSN(?:_|$)', '(?:^|_)CREDS(?:_|$)',
63
75
  ].join('|'), 'i');
64
76
  export function isSensitiveEnvName(name) {
65
77
  return SENSITIVE_ENV_PATTERN.test(name);
66
78
  }
67
79
  // Values that are credentials regardless of what the variable is called:
68
80
  // a URL carrying a password in its userinfo (DATABASE_URL, REDIS_URL,
69
- // MONGO_URI, SENTRY_DSN), PEM blocks, JWTs, and the fixed prefixes vendors
70
- // stamp on their tokens so scanners can recognise them.
81
+ // MONGO_URI, SENTRY_DSN), PEM blocks, JWTs, HTTP Authorization header values
82
+ // (Bearer/Basic), and the fixed prefixes vendors stamp on their tokens so
83
+ // scanners can recognise them (GitHub, GitLab, OpenAI, Stripe, Slack, AWS,
84
+ // Google, npm, Vault, Hugging Face, PyPI, DigitalOcean, Linear, Shopify,
85
+ // SendGrid, Databricks, Grafana).
71
86
  const SENSITIVE_ENV_VALUE_PATTERN = new RegExp([
72
87
  '^[a-z][a-z0-9+.-]*://[^/\\s:@]*:[^/\\s@]+@',
73
88
  '-----BEGIN ',
74
89
  '^eyJ[\\w-]+\\.eyJ[\\w-]+\\.',
90
+ '^(?:Bearer|Basic)\\s+\\S+',
75
91
  '^(?:gh[pousr]_|github_pat_)[A-Za-z0-9_]{20,}',
76
92
  '^glpat-[\\w-]{20,}',
77
93
  '^sk-[A-Za-z0-9_-]{20,}',
@@ -81,22 +97,33 @@ const SENSITIVE_ENV_VALUE_PATTERN = new RegExp([
81
97
  '^AIza[\\w-]{35}$',
82
98
  '^npm_[A-Za-z0-9]{36}$',
83
99
  '^hvs\\.[\\w-]{20,}',
100
+ '^hf_[A-Za-z0-9]{30,}',
101
+ '^pypi-AgEI[\\w-]{20,}',
102
+ '^dop_v1_[a-f0-9]{60,}',
103
+ '^lin_api_[A-Za-z0-9]{20,}',
104
+ '^shp(?:at|ss|ca|pa)_[a-f0-9]{30,}',
105
+ '^SG\\.[\\w-]{20,}\\.[\\w-]{20,}',
106
+ '^dapi[a-f0-9]{30,}',
107
+ '^glc_[A-Za-z0-9+/=]{30,}',
108
+ '^ya29\\.[\\w-]{20,}',
84
109
  ].join('|'), 'i');
85
110
  export function isSensitiveEnvValue(value) {
86
111
  return SENSITIVE_ENV_VALUE_PATTERN.test(value);
87
112
  }
88
113
  export function autodetect(varName) {
89
- const detector = DETECTORS[varName];
90
- if (detector)
91
- return detector();
114
+ // Own-property check: {{constructor}} / {{toString}} must not resolve to
115
+ // Object.prototype members.
116
+ if (Object.hasOwn(DETECTORS, varName))
117
+ return DETECTORS[varName]();
92
118
  // Fall back to terminal environment variables — but never implicitly expand
93
119
  // credential-looking names ({{GITHUB_TOKEN}} in a repo template must not
94
120
  // exfiltrate secrets into the generated prompt) or credential-shaped values
95
121
  // under innocuous names ({{DATABASE_URL}}). Explicit --flags still work.
96
- if (isSensitiveEnvName(varName))
122
+ if (isSensitiveEnvName(varName) || !Object.hasOwn(process.env, varName))
97
123
  return undefined;
98
124
  const value = process.env[varName];
99
- if (value === undefined || isSensitiveEnvValue(value))
125
+ // Treat empty env var as not detected, matching --FOO= flags and git detectors (see #91).
126
+ if (value === undefined || value === '' || isSensitiveEnvValue(value))
100
127
  return undefined;
101
128
  return value;
102
129
  }
@@ -1 +1 @@
1
- {"version":3,"file":"autodetect.js","sourceRoot":"","sources":["../src/autodetect.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"autodetect.js","sourceRoot":"","sources":["../src/autodetect.ts"],"names":[],"mappings":"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"}