@westbayberry/dg 2.1.0 → 2.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.
Files changed (148) hide show
  1. package/README.md +216 -226
  2. package/dist/agents/claude-code.js +123 -0
  3. package/dist/agents/codex.js +65 -0
  4. package/dist/agents/copilot-cli.js +115 -0
  5. package/dist/agents/cursor.js +118 -0
  6. package/dist/agents/gate-posture.js +21 -0
  7. package/dist/agents/gemini.js +107 -0
  8. package/dist/agents/persistence.js +351 -0
  9. package/dist/agents/registry.js +128 -0
  10. package/dist/agents/routing.js +118 -0
  11. package/dist/agents/types.js +1 -0
  12. package/dist/agents/windsurf.js +93 -0
  13. package/dist/api/analyze.js +1 -1
  14. package/dist/audit/detectors.js +0 -11
  15. package/dist/audit/events.js +5 -21
  16. package/dist/audit/rules.js +6 -2
  17. package/dist/audit-ui/AuditApp.js +2 -0
  18. package/dist/audit-ui/components/AuditResultsView.js +55 -92
  19. package/dist/audit-ui/export.js +0 -4
  20. package/dist/audit-ui/format.js +0 -3
  21. package/dist/audit-ui/launch.js +10 -1
  22. package/dist/auth/device-login.js +4 -5
  23. package/dist/auth/login-app.js +7 -7
  24. package/dist/auth/store.js +17 -5
  25. package/dist/bin/dg.js +59 -52
  26. package/dist/commands/agents.js +275 -0
  27. package/dist/commands/audit.js +28 -3
  28. package/dist/commands/config.js +15 -7
  29. package/dist/commands/cooldown.js +389 -0
  30. package/dist/commands/decisions.js +23 -9
  31. package/dist/commands/doctor.js +1 -1
  32. package/dist/commands/help.js +1 -1
  33. package/dist/commands/licenses.js +10 -22
  34. package/dist/commands/logout.js +4 -11
  35. package/dist/commands/router.js +6 -4
  36. package/dist/commands/sbom.js +206 -0
  37. package/dist/commands/scan.js +1 -1
  38. package/dist/commands/service.js +32 -13
  39. package/dist/commands/setup.js +196 -27
  40. package/dist/commands/types.js +1 -0
  41. package/dist/commands/uninstall.js +2 -1
  42. package/dist/commands/update.js +17 -8
  43. package/dist/commands/verify.js +8 -5
  44. package/dist/config/settings.js +49 -48
  45. package/dist/decisions/remember-prompt.js +5 -10
  46. package/dist/export-ui/ExportDialog.js +198 -0
  47. package/dist/install-ui/LiveInstall.js +5 -4
  48. package/dist/install-ui/block-render.js +18 -14
  49. package/dist/install-ui/prep-spinner.js +32 -0
  50. package/dist/launcher/agent-check.js +896 -0
  51. package/dist/launcher/agent-hook-exec.js +70 -0
  52. package/dist/launcher/agent-hook-io.js +39 -0
  53. package/dist/launcher/cargo-cache.js +40 -0
  54. package/dist/launcher/classify.js +44 -7
  55. package/dist/launcher/env.js +105 -26
  56. package/dist/launcher/install-preflight.js +64 -18
  57. package/dist/launcher/live-install.js +29 -8
  58. package/dist/launcher/manifest-screen.js +171 -0
  59. package/dist/launcher/output-redaction.js +10 -4
  60. package/dist/launcher/preflight-prompt.js +18 -4
  61. package/dist/launcher/run.js +178 -103
  62. package/dist/launcher/spawn-invocation.js +21 -0
  63. package/dist/policy/cooldown.js +18 -5
  64. package/dist/policy/evaluate.js +5 -6
  65. package/dist/policy/pypi-name.js +17 -0
  66. package/dist/presentation/mode.js +3 -2
  67. package/dist/presentation/package-page.js +9 -0
  68. package/dist/presentation/theme.js +7 -7
  69. package/dist/project/dgfile.js +146 -6
  70. package/dist/project/override-trust.js +0 -0
  71. package/dist/proxy/auth.js +42 -0
  72. package/dist/proxy/ca.js +29 -9
  73. package/dist/proxy/cooldown-exemptions-file.js +33 -0
  74. package/dist/proxy/enforcement.js +55 -16
  75. package/dist/proxy/metadata-map.js +66 -12
  76. package/dist/proxy/preverified.js +55 -0
  77. package/dist/proxy/server.js +568 -53
  78. package/dist/proxy/worker.js +16 -1
  79. package/dist/publish-set/collect.js +1 -4
  80. package/dist/publish-set/npm.js +8 -5
  81. package/dist/publish-set/pack.js +9 -3
  82. package/dist/runtime/cli.js +0 -4
  83. package/dist/runtime/fatal.js +31 -0
  84. package/dist/runtime/first-run.js +14 -12
  85. package/dist/runtime/node-version.js +43 -6
  86. package/dist/runtime/nudges.js +35 -2
  87. package/dist/sbom/cyclonedx.js +211 -0
  88. package/dist/sbom-ui/SbomApp.js +158 -0
  89. package/dist/sbom-ui/components/SbomHeader.js +32 -0
  90. package/dist/sbom-ui/components/SbomList.js +52 -0
  91. package/dist/sbom-ui/inventory.js +132 -0
  92. package/dist/sbom-ui/launch.js +51 -0
  93. package/dist/sbom-ui/run.js +55 -0
  94. package/dist/sbom-ui/store.js +26 -0
  95. package/dist/scan/collect.js +10 -6
  96. package/dist/scan/command.js +17 -10
  97. package/dist/scan/discovery.js +11 -2
  98. package/dist/scan/render.js +28 -4
  99. package/dist/scan/scanner-report.js +15 -9
  100. package/dist/scan/staged.js +33 -9
  101. package/dist/scan-ui/LegacyApp.js +9 -13
  102. package/dist/scan-ui/alt-screen.js +5 -8
  103. package/dist/scan-ui/components/InteractiveResultsView.js +133 -120
  104. package/dist/scan-ui/components/ProgressBar.js +3 -14
  105. package/dist/scan-ui/components/ProjectSelector.js +1 -1
  106. package/dist/scan-ui/components/ScoreHeader.js +2 -3
  107. package/dist/scan-ui/components/SetupBanner.js +0 -6
  108. package/dist/scan-ui/format-helpers.js +61 -5
  109. package/dist/scan-ui/hooks/useResizeRepaint.js +25 -0
  110. package/dist/scan-ui/hooks/useScan.js +19 -2
  111. package/dist/scan-ui/launch.js +7 -4
  112. package/dist/scan-ui/shims.js +11 -4
  113. package/dist/scripts/detect.js +7 -2
  114. package/dist/scripts/gate.js +1 -1
  115. package/dist/security/sanitize.js +8 -4
  116. package/dist/service/state.js +28 -9
  117. package/dist/service/trust-refresh.js +92 -0
  118. package/dist/service/trust-store.js +5 -9
  119. package/dist/service/worker.js +23 -1
  120. package/dist/setup/activate-shell.js +28 -0
  121. package/dist/setup/git-hook.js +49 -4
  122. package/dist/setup/plan.js +227 -49
  123. package/dist/setup/uninstall-standalone.js +25 -0
  124. package/dist/setup-ui/gate.js +39 -0
  125. package/dist/setup-ui/offer.js +42 -0
  126. package/dist/setup-ui/selector.js +27 -0
  127. package/dist/setup-ui/tasks.js +56 -0
  128. package/dist/setup-ui/wizard.js +233 -0
  129. package/dist/standalone/uninstall.mjs +2123 -0
  130. package/dist/state/cooldown-held.js +66 -0
  131. package/dist/state/index.js +1 -0
  132. package/dist/state/locks.js +4 -2
  133. package/dist/state/store.js +2 -1
  134. package/dist/util/external-tool.js +25 -0
  135. package/dist/util/git.js +10 -3
  136. package/dist/util/report-writer.js +57 -0
  137. package/dist/verify/local.js +242 -44
  138. package/dist/verify/package-check.js +87 -29
  139. package/dist/verify/preflight.js +242 -49
  140. package/dist/verify/render.js +15 -1
  141. package/npm-shrinkwrap.json +2383 -0
  142. package/package.json +7 -8
  143. package/NOTICE +0 -5
  144. package/dist/commands/completion.js +0 -117
  145. package/dist/commands/explain.js +0 -236
  146. package/dist/commands/unavailable.js +0 -11
  147. package/dist/scripts/rebuild.js +0 -28
  148. package/dist/telemetry/events.js +0 -40
package/README.md CHANGED
@@ -1,8 +1,14 @@
1
1
  # @westbayberry/dg
2
2
 
3
- Dependency Guardian's command-line scanner. It checks the packages you are
4
- about to install or publish and tells you whether Dependency Guardian
5
- verified them, flagged them, or blocked them before they reach your machine.
3
+ [![npm version](https://img.shields.io/npm/v/@westbayberry/dg)](https://www.npmjs.com/package/@westbayberry/dg)
4
+ [![CI](https://github.com/WestBayBerry/DG_CLI/actions/workflows/ci.yml/badge.svg)](https://github.com/WestBayBerry/DG_CLI/actions/workflows/ci.yml)
5
+ [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue)](./LICENSE)
6
+
7
+ Dependency Guardian is a supply-chain firewall for npm and PyPI. The `dg` CLI
8
+ checks a package against the scanner before its code can run, so a malicious
9
+ install is blocked before its install scripts execute, not flagged afterward.
10
+ It scans published package artifacts from the registry; your source code stays
11
+ on your machine.
6
12
 
7
13
  ## Install
8
14
 
@@ -10,289 +16,273 @@ verified them, flagged them, or blocked them before they reach your machine.
10
16
  npm install -g @westbayberry/dg
11
17
  ```
12
18
 
13
- Requires Node.js >= 22.14. The npm package is zero-footprint on install: it
14
- adds only the `dg` binary and runs no install lifecycle scripts. Persistent
15
- shell/shim changes happen only when you run `dg setup` explicitly.
19
+ Requires Node.js >= 22.14. Installing the package adds only the `dg` binary and
20
+ runs no install lifecycle scripts of its own. The published tarball ships an
21
+ `npm-shrinkwrap.json`, so every install resolves the exact dependency tree the
22
+ release was audited with. Nothing touches your shell until you run `dg setup`.
16
23
 
17
24
  ## Quickstart
18
25
 
19
- Scan the project in the current directory:
26
+ Audit the project in the current directory:
20
27
 
21
28
  ```bash
22
29
  dg scan
23
30
  ```
24
31
 
25
- `dg scan` shows a full-screen results browser in an interactive terminal and
26
- prints plain text or machine output otherwise. Exit code 0 means DG verified
27
- the project, 2 means DG blocked it.
28
-
29
- ## Core concepts
32
+ `dg scan` reads your lockfiles, asks the scanner for a verdict on each
33
+ dependency, and opens a full-screen results browser in an interactive terminal
34
+ (plain text or machine output otherwise). It installs nothing and runs no
35
+ package scripts. Exit code 0 means the project is clean, 2 means a block-level
36
+ package was found.
30
37
 
31
- **Verdicts.** Every result is one of three states: **PASS** (DG verified),
32
- **WARN** (DG flagged, review advised), or **BLOCK** (DG blocked, do not
33
- install). The exit code follows the verdict.
38
+ ## Verdicts
34
39
 
35
- **The server is the verdict source.** For authenticated registry checks and the
36
- install firewall, the Dependency Guardian scanner returns the verdict and the
37
- CLI displays it; the CLI never derives a verdict from a local score. If the
38
- server is unreachable, `dg scan` falls back to local heuristics and marks the
39
- report as such (`scannerUnavailable` in JSON output) rather than failing
40
- silently.
40
+ Every result is one of three states the scanner returns: **PASS** (verified),
41
+ **WARN** (flagged, review advised), or **BLOCK** (refused). The exit code
42
+ follows the verdict.
41
43
 
42
- **The install firewall.** Prefix a package-manager command with `dg`
43
- (`dg npm install left-pad`) to route the install through Dependency Guardian.
44
- The CLI streams the package manager's live output, computes each artifact's
45
- SHA-256, asks the scanner for a verdict, and blocks the bytes before delivery
46
- on a BLOCK verdict. A block exits 2 and prints the override command; a warn
47
- prompts `Proceed? [y/N]` (default No) in a TTY. Passthrough commands (those
48
- that fetch nothing) run without a proxy.
44
+ **The server is the verdict source.** For registry checks and the install
45
+ firewall the scanner returns the verdict and the CLI displays it; the CLI never
46
+ derives a verdict from a local score. If the scanner is unreachable, `dg scan`
47
+ falls back to local heuristics and marks the report (`scannerUnavailable` in
48
+ JSON output) rather than passing silently.
49
49
 
50
- ## Common examples
50
+ ## The install firewall
51
51
 
52
- Scan only staged files in a pre-commit context:
52
+ Prefix any install command with `dg` and the package is checked before its
53
+ bytes reach your package manager:
53
54
 
54
55
  ```bash
55
- dg scan --staged
56
+ dg npm install lodash
57
+ dg pnpm add react
58
+ dg pip install requests
59
+ dg cargo add serde
56
60
  ```
57
61
 
58
- Verify a specific published package against the scanner:
62
+ The prefix starts a local proxy for just that command, hashes each artifact as
63
+ it downloads, asks the scanner for a verdict, and refuses a blocked package
64
+ before delivery. Supported prefixes include npm, npx, pnpm, pnpx, Yarn classic,
65
+ pip, pipx, uv, uvx, and cargo. A block exits 2 and prints the override command;
66
+ a warn prompts `Proceed? [y/N]` (default No) in a terminal.
59
67
 
60
- ```bash
61
- dg verify npm:left-pad@1.3.0
62
- ```
68
+ To protect bare commands too, run `dg setup`. It shows one consent screen
69
+ listing exactly what it writes, then installs reversible, user-local shims so
70
+ plain `npm install` and `pip install` are scanned automatically, plus a native
71
+ hook in each AI coding agent it detects. Undo all of it with `dg uninstall`.
63
72
 
64
- Install a package through the firewall:
73
+ ## Command reference
65
74
 
66
- ```bash
67
- dg npm install is-number@7.0.0
68
- ```
75
+ Run `dg <command> --help` for full flags and examples, or `dg --help-all` for
76
+ the complete list. The exhaustive reference lives at
77
+ [westbayberry.com/docs/cli-reference](https://westbayberry.com/docs/cli-reference).
69
78
 
70
- Check what you are about to publish for leaked secrets and risky lifecycle
71
- scripts, fully locally:
79
+ ### dg scan [path]
72
80
 
73
- ```bash
74
- dg audit . --local
75
- ```
81
+ Audits the project's lockfiles for the path (current directory by default).
82
+ Read-only: it never installs, runs package scripts, or changes setup state.
83
+ Reads `package-lock.json`, `npm-shrinkwrap.json`, `yarn.lock`,
84
+ `pnpm-lock.yaml`, `requirements.txt`, `poetry.lock`, `Pipfile.lock`, and
85
+ `uv.lock`, including nested projects in a monorepo.
76
86
 
77
- Produce CI-friendly machine output:
87
+ - Flags: `--staged`, `--json`, `--sarif`, `--output <file>`, `--no-decisions`.
88
+ - Exit codes: 0 pass, 1 warn (strict mode upgrades warn to 2), 2 block,
89
+ 4 analysis incomplete, 10 nothing to scan. JSON output carries a
90
+ `schemaVersion` field and sets `scannerUnavailable` when the scanner could
91
+ not be reached.
78
92
 
79
- ```bash
80
- dg scan --sarif --output dg-scan.sarif
81
- ```
93
+ ### dg sbom [path]
82
94
 
83
- Protect every commit in a repository:
95
+ Inventories the resolved dependency tree as a CycloneDX 1.5 software bill of
96
+ materials, with purl, license, and integrity hash per component. Reads the same
97
+ lockfiles as `dg scan` plus `Cargo.lock`. In a terminal it streams
98
+ BLOCK/WARN/PASS verdicts for npm and PyPI components when signed in; cargo stays
99
+ inventory only. Piped, with `--output` (alias `-o`), or with `--json` it stays
100
+ offline and prints the raw CycloneDX document.
84
101
 
85
- ```bash
86
- dg guard-commit install
87
- ```
102
+ - Flags: `--output <path>` / `-o`, `--json`, `--reproducible` (byte-stable
103
+ output; honors `SOURCE_DATE_EPOCH`).
88
104
 
89
- ## Command reference
105
+ ### dg verify &lt;registry:package[@version] | path | lockfile&gt;
90
106
 
91
- ### dg scan [path]
107
+ Runs a real scanner check on a published package before you install it. Name
108
+ the registry, `npm:react` or `pypi:requests`, with an optional `@version`
109
+ (latest by default). Works signed out with a free verdict summary (the verdict,
110
+ top reasons, provenance, and the package page link); signing in unlocks the
111
+ full reason list, license info, `--json`, and `--output`. Verifying a local
112
+ path, lockfile, or tarball stays free and offline.
92
113
 
93
- Scans package manifests and supported lockfiles for the project at `path` (the
94
- current directory by default). Reads manifests only; it never runs package
95
- scripts, installs dependencies, loads project-local config, or changes setup
96
- state.
114
+ ### dg licenses [path]
97
115
 
98
- - Flags: `--staged`, `--json`, `--sarif`, `--output <file>`.
99
- - Output: TUI in a rich TTY, text or machine output otherwise.
100
- - Exit codes: 0 PASS, 1 WARN (strict mode upgrades WARN to 2), 2 BLOCK,
101
- 4 analysis incomplete, 10 nothing to scan. Server unreachable falls back to
102
- local heuristics and sets `scannerUnavailable` in JSON.
116
+ SPDX license report for the dependency tree, grouped by risk. Use `--csv` or
117
+ `--markdown` for CI artifacts and `--fail-on <risk>[,<risk>]` to exit non-zero
118
+ when a dependency carries a named license-risk tier.
103
119
 
104
- ### dg verify <spec | path | lockfile>
120
+ ### dg audit [path]
105
121
 
106
- Two paths, one exit-code contract.
122
+ Pre-publish check of a package of your own.
123
+ Inspects exactly the resolved publish set of one package, never the whole repo.
124
+ Basic checks run 100% locally and upload nothing: leaked secrets and keys,
125
+ credential files, source maps, `.git`, terraform state, risky lifecycle
126
+ scripts, and a missing `files` allowlist. On a paid plan, and if your org allows
127
+ it, it also runs a deep behavioral scan: a packed copy is uploaded with no
128
+ lifecycle scripts run, raw bytes are never retained, and only the verdict and
129
+ redacted findings reach your dashboard.
107
130
 
108
- - `dg verify npm:<pkg>` / `dg verify pypi:<pkg>` runs an authenticated scanner
109
- check and nudges you to sign in when logged out. Flags: `--json`, `--output`,
110
- `--verbose`.
111
- - `dg verify <path | lockfile>` runs a local advisory check on a package
112
- directory, workspace, lockfile, or `.tgz`/`.tar.gz`/`.zip`/`.whl` archive.
113
- Archive verification computes SHA-256 and checks archive paths before reading;
114
- it never extracts to disk or runs package code. Flags: `--json`, `--sarif`,
115
- `--output`.
116
- - Exit codes (both paths): 0 PASS, 1 WARN (or scan error on the advisory path),
117
- 2 BLOCK, 4 analysis incomplete (registry path).
131
+ - Flags: `--local` (skip the upload), `--require-deep` (fail when deep can't
132
+ run), `--fail-on warn`.
133
+ - Exit codes: 0 clean, 1 warn (with `--fail-on warn`), 2 block, 3 deep required
134
+ but unavailable, 4 analysis incomplete.
118
135
 
119
- ### dg audit [path]
136
+ ### dg setup
120
137
 
121
- Inspects exactly the resolved publish set of one package, never the whole
122
- repo, for leaked secrets, private keys, source-control and build-artifact
123
- leakage, and suspicious lifecycle-script strings. Basic checks run 100% locally
124
- and upload nothing. On a paid plan, with org policy allowing it and only after
125
- explicit consent, the deep audit uploads a packed copy of an npm package to the
126
- scanner for a behavioral scan: no lifecycle scripts are run, raw bytes are never
127
- retained, and only the verdict plus redacted findings reach your dashboard.
128
-
129
- - Flags: `--json`, `--output`/`-o`, `--local` (skip the deep upload),
130
- `--require-deep` (fail if the deep audit could not run), `--fail-on
131
- <warn | block>`.
132
- - Exit codes: 0 clean (WARN counts as clean under the default `--fail-on
133
- block`), 1 WARN with `--fail-on warn`, 2 BLOCK, 3 deep audit required but
134
- unavailable, 4 analysis incomplete.
138
+ Installs the shell shims so bare `npm install` / `pip install` are scanned, and
139
+ the agent hooks, behind one consent screen. `--print` previews the exact write
140
+ plan and changes nothing; `--yes` applies just the shell shims non-interactively
141
+ (add `--agents` or `--guard-commit` for the others). Everything it writes is
142
+ dg-owned and reversible with `dg uninstall`.
135
143
 
136
- ### dg licenses [path]
144
+ ### dg guard-commit
137
145
 
138
- Reports license metadata from local manifests and supported lockfiles without
139
- calling package managers or loading project-local config.
146
+ Installs a per-repo git pre-commit hook that scans the staged lockfile changes
147
+ and blocks a commit that would add a malicious dependency. It chains any
148
+ existing pre-commit hook and self-checks that git will fire it. Reverse with
149
+ `dg guard-commit off`. `git commit --no-verify` is the override.
140
150
 
141
- - Flags: `--json`, `--csv`, `--output <file>`, `--fail-on <risk[,risk...]>`,
142
- `--deny-license <id>`.
143
- - Exit codes: 0 clean, 1 when a license gate trips.
151
+ ### dg agents
144
152
 
145
- ### Package-manager prefixes (the install firewall)
153
+ Routes AI coding agents' installs through the firewall with a native
154
+ pre-command hook in each agent's own config, so a malicious or unresolvable
155
+ package is blocked before it is fetched. Supported: Claude Code, Codex CLI,
156
+ Cursor, Gemini CLI, GitHub Copilot CLI, and Windsurf. Bare `dg agents` lists
157
+ each agent and whether it is protected; `dg agents on` hooks every detected
158
+ agent (or one, `dg agents on claude-code`); `dg agents --check` verifies the
159
+ wiring; `dg agents off` removes it. `dg hook claude-code` keeps working as an
160
+ alias.
146
161
 
147
- `dg npm`, `dg npx`, `dg pnpm`, `dg pnpx`, `dg yarn`, `dg pip`, `dg pipx`,
148
- `dg uv`, `dg uvx`, `dg cargo` route protected install/fetch commands through
149
- the per-invocation proxy. `dg yarn` claims Yarn classic only; `dg bun`,
150
- `dg conda`, and `dg mamba` and Yarn Berry remain gated and fail visibly before
151
- spawning.
162
+ ### dg decisions &lt;list | revoke&gt;
152
163
 
153
- - Live output and stdin pass through; the install decision renders after the
154
- child exits.
155
- - A BLOCK exits 2 with an override hint. Override with `--dg-force-install`,
156
- which is gated by `policy.allowForceOverride` and audited.
157
- - A WARN prompts `Proceed? [y/N]` (default No) in a TTY.
158
- - Signal deaths exit `128 + n`.
164
+ Warn-level findings you accept at an install prompt or commit guard can be
165
+ remembered in a committable `dg.json` at the project root, so repeat runs only
166
+ surface new problems. A new finding category, a severity escalation, or a block
167
+ always resurfaces; blocks can never be acknowledged away. `dg decisions list`
168
+ shows what the project accepted; `dg decisions revoke` removes an entry.
159
169
 
160
- ### dg guard-commit
170
+ ### dg cooldown [&lt;age&gt; | off]
171
+
172
+ Quarantines new installs of a registry release younger than `cooldown.age`
173
+ (default `24h`), since most malicious releases are caught within their first
174
+ day. Change it with `dg cooldown 7d` or disable it with `dg cooldown off`.
175
+ Lockfile scans are never failed by cooldown; packages you already depend on get
176
+ a display annotation only. `dg cooldown exempt <name>` records a shareable
177
+ exemption in `dg.json`.
178
+
179
+ ### dg config &lt;get | set | unset | list&gt;
161
180
 
162
- Installs a per-repo, reversible git pre-commit hook that scans staged changes.
181
+ Views or edits user-global configuration in `~/.dg/config.json`. The everyday
182
+ key is `policy.mode` (`off`, `warn`, `block` the default, or `strict`), which
183
+ sets how a flagged package is handled. There is no detection-tuning knob;
184
+ `policy.mode` changes only how a flagged package is handled, not what the
185
+ scanner looks for.
163
186
 
164
- - Subcommands/flags: `install`, `off` (`remove`/`uninstall`), `--check`,
165
- `--print`, `--yes`.
166
- - Chains any pre-existing pre-commit hook; `dg guard-commit off` removes only
167
- the dg-owned hook.
187
+ ### dg login / dg logout --yes
168
188
 
169
- ### dg setup / dg uninstall / dg doctor
189
+ `dg login` signs in via your browser and links this machine to your account;
190
+ `dg login --token dg_live_...` authenticates CI without the browser flow.
191
+ Scanning works without an account. `dg logout --yes` removes the saved token
192
+ from this machine (the token stays valid until you revoke it from the
193
+ dashboard).
170
194
 
171
- - `dg setup` (POSIX only; `win32` exits 69) writes dg-owned package-manager
172
- shims under `~/.dg/shims` and a sentinel block in your shell rc, recording
173
- every write in the cleanup registry. `--print` shows the plan without
174
- changing files; `--yes` applies it; `--service` opts into service mode.
175
- - `dg uninstall` reverses dg-owned writes and leaves user content untouched.
176
- Flags: `--yes`, `--keep-config`, `--all`, `--service`. `--all` conflicts
177
- with `--keep-config`.
178
- - `dg doctor` reports local setup health and prints concrete unavailable
179
- status for optional gates (Windows, Python `.pth` hook, git hooks, Bun, Yarn
180
- Berry, conda, mamba) instead of claiming them. `--json` for machine output.
195
+ ### dg doctor
196
+
197
+ Checks runtime, auth, API, policy, setup shims, PATH precedence, real-binary
198
+ resolution, stale state, and service state. Every non-pass line ends with the
199
+ exact fix command. `--json` for machine output.
181
200
 
182
201
  ### dg service
183
202
 
184
- Explicit, reversible service mode: `dg service start | stop | restart | status
185
- | doctor | uninstall`, plus consent-gated `dg service trust install |
186
- uninstall`. Trust records store only public certificate fingerprints, provider,
187
- target, and timestamps, never private keys. `dg service status`/`doctor`
188
- detect stale runtime state and `dg service restart` clears it.
203
+ Service mode runs a persistent local proxy plus a managed certificate authority
204
+ so installs are scanned continuously, for CI runners and private registries
205
+ where a per-command proxy is not enough. A paid feature; run `dg login` first,
206
+ then `dg service start`. Fully reversible with `dg uninstall --service`.
207
+
208
+ ### dg update
189
209
 
190
- ### dg login / dg logout
210
+ Checks whether a newer CLI version is published and prints the exact
211
+ `npm install -g` command to run. It never installs anything and never mutates
212
+ the installed package; you run the printed command yourself. Alias: `dg upgrade`.
191
213
 
192
- `dg login` stores a user-local auth token under the dg config directory and
193
- prints only a redacted preview. `dg logout --yes` removes that auth file
194
- without touching setup state or user config.
214
+ ### dg uninstall
195
215
 
196
- ### dg config
216
+ Removes registered dg-owned setup and service writes, state, cache, sessions,
217
+ shims, and shell-rc sentinel blocks while preserving unrelated user content.
218
+ Run it before `npm uninstall -g @westbayberry/dg`.
197
219
 
198
- `dg config set <key> <value>`, `dg config get <key>`, `dg config list`
199
- (`--json`). Keys: `api.baseUrl`, `org.id`, `policy.mode`,
200
- `policy.trustProjectAllowlists`, `policy.allowForceOverride`,
201
- `policy.scriptHardening`, `telemetry.enabled`, `webhooks.enabled`.
220
+ ## Exit codes
202
221
 
203
- ### dg update / dg upgrade
222
+ Stable, so CI and shells can read them directly:
204
223
 
205
- Check the latest published version when registry metadata is available and
206
- print the exact `npm install -g @westbayberry/dg@<version>` command. They never
207
- run the package manager themselves. `upgrade` is an alias of `update`; `--json`
208
- for machine output.
224
+ | Code | Meaning |
225
+ | --- | --- |
226
+ | `0` | Clean. The requested action is allowed. |
227
+ | `1` | Warn-level result (strict policy upgrades this to `2`). |
228
+ | `2` | Block: a block-level package, a denied install, or an unknown command. |
229
+ | `3` | `dg audit --require-deep` ran but the deep scan was unavailable. |
230
+ | `4` | Analysis incomplete; not a clean pass. |
231
+ | `10` | `dg scan` found nothing to scan. |
232
+ | `64` | A usage error (bad flags or arguments). |
233
+ | `69` | A command unavailable or gated on this platform, or a signed-out `dg verify` asking for `--json`/`--output`. |
234
+ | `70` | Internal tool error (for example, a report file could not be written). |
235
+ | `130` / `143` | Interrupted by SIGINT (Ctrl-C) or SIGTERM. |
236
+
237
+ ## What dg sends to the API
238
+
239
+ The CLI sends no telemetry: no crash reporter and no analytics endpoint. It
240
+ talks to the network only when a command needs it.
241
+
242
+ - **Scanning** sends package names and pinned versions from your lockfiles to
243
+ the scan API, which pulls the actual package artifacts from the public
244
+ registry on its side. Your source never leaves your machine.
245
+ - **The install firewall** asks the server for a verdict on each artifact as it
246
+ downloads: name, version, ecosystem, registry host, download URL, and the
247
+ artifact's SHA-256.
248
+ - **Deep audit** (`dg audit`, opt-in, paid) uploads a packed copy of your own
249
+ package to `/v1/scan-tarball`; raw bytes are never retained.
250
+ - **Version check** queries the public npm registry for `@westbayberry/dg` at
251
+ most once a day in an interactive terminal. Set `CI=1` to skip it.
252
+
253
+ Authenticate CI with `DG_API_KEY` (a `dg_live_*` or `dg_test_*` key;
254
+ `DG_API_TOKEN` is an accepted alias). Full detail:
255
+ [westbayberry.com/docs/telemetry](https://westbayberry.com/docs/telemetry).
256
+
257
+ ## Use in CI
209
258
 
210
- ### dg status / dg explain / dg completion
259
+ ```bash
260
+ npm install -g @westbayberry/dg
261
+ export DG_API_KEY="dg_live_..." # from Settings -> CI keys
262
+ dg scan
263
+ ```
211
264
 
212
- - `dg status` reports auth, API, and prefix-mode state for the current machine.
213
- - `dg explain <id>` returns the explanation and next step for a firewall cause
214
- or local scan finding.
215
- - `dg completion <bash | zsh | fish>` prints a shell completion script.
265
+ A non-zero exit means a block-level finding; wire that into your pipeline's
266
+ pass/fail. The CLI auto-detects CI and switches to non-interactive output; pass
267
+ `--json` or `--sarif` when a parser reads stdout. On GitHub, the
268
+ [GitHub App](https://westbayberry.com/docs#github-app) is the simpler path and
269
+ posts a required PR status check.
216
270
 
217
- ## Configuration
271
+ ## Documentation
218
272
 
219
- Config lives in `~/.dg/config.json` (mode 0600). Set values with `dg config
220
- set`.
273
+ - [Getting started](https://westbayberry.com/docs)
274
+ - [CLI reference](https://westbayberry.com/docs/cli-reference)
275
+ - [Blocking behavior](https://westbayberry.com/docs/blocking)
276
+ - [Integrations](https://westbayberry.com/docs/integrations)
277
+ - [Telemetry](https://westbayberry.com/docs/telemetry)
221
278
 
222
- | Key | Purpose |
223
- | --- | --- |
224
- | `api.baseUrl` | Dependency Guardian API endpoint |
225
- | `org.id` | Organization the CLI reports to |
226
- | `policy.mode` | `off`, `warn`, `block`, or `strict` |
227
- | `policy.trustProjectAllowlists` | Whether project-local allowlists are trusted |
228
- | `policy.allowForceOverride` | Whether `--dg-force-install` is permitted |
229
- | `policy.scriptHardening` | Preserve script-disabling flags; never re-enable scripts |
230
- | `telemetry.enabled` | Telemetry (off by default) |
231
- | `webhooks.enabled` | Webhook outbox delivery |
232
-
233
- Project-local allowlists never suppress an install firewall block unless
234
- user-global or org policy explicitly trusts them.
235
-
236
- Telemetry is disabled by default. When enabled, the CLI appends command and
237
- install-decision events to a local `telemetry.jsonl` file in the dg state
238
- directory, limited to a fixed attribute allowlist. Nothing is transmitted
239
- anywhere; the file never leaves your machine.
240
-
241
- ### Environment variables
242
-
243
- - `DG_API_TOKEN`: auth token; overrides the token stored in the config file.
244
- - `DG_PRIVATE_REGISTRY_HOSTS`: comma-separated hosts whose URL-fallback
245
- artifacts are eligible for private-registry scan upload.
246
- - `DG_SCAN_TARBALL_UPLOAD`: set to `1` to enable private-registry artifact
247
- upload to `/v1/scan-tarball` (requires `DG_API_TOKEN`).
248
- - `DG_SERVICE_TRUST_STORE_BACKEND` / `DG_SERVICE_TRUST_STORE_DIR`: for
249
- CI/Docker, set the backend to `file` and a directory to copy the active
250
- public CA certificate into an image-local trust directory without touching
251
- the host OS trust store.
252
- - `NO_COLOR` / `FORCE_COLOR`: disable or force ANSI color output.
279
+ ## Reporting a vulnerability
253
280
 
254
- ## Exit codes
281
+ Please report security issues privately through GitHub's
282
+ [private vulnerability reporting](https://github.com/WestBayBerry/DG_CLI/security/advisories/new)
283
+ rather than a public issue.
255
284
 
256
- Codes shared by every command:
285
+ ## License
257
286
 
258
- | Code | Meaning |
259
- | --- | --- |
260
- | 0 | PASS / clean |
261
- | 1 | WARN |
262
- | 2 | BLOCK, or a usage error |
263
- | 4 | analysis incomplete |
264
- | 69 | command unavailable or gated on this platform |
265
- | 70 | internal error |
266
- | 128 + n | child killed by signal n (firewall installs) |
267
- | 130 / 143 | interrupted (SIGINT / SIGTERM) |
268
-
269
- Per-command specifics:
270
-
271
- - `dg scan`: strict mode upgrades WARN (1) to BLOCK (2); 10 means nothing to
272
- scan.
273
- - `dg audit`: 3 means a deep audit was required but unavailable; under the
274
- default `--fail-on block`, WARN exits 0.
275
- - `dg verify` (registry path): 4 means analysis incomplete; the advisory path
276
- reports a scan error as 1.
277
-
278
- ## Troubleshooting
279
-
280
- Run `dg doctor` first; it reports Node version, package health, config
281
- readability, shim/PATH state, and which optional gates are unavailable.
282
-
283
- - "Scanner unavailable" / `scannerUnavailable` in JSON: the API was
284
- unreachable and `dg scan` fell back to local heuristics. Check `api.baseUrl`
285
- and network access.
286
- - A safe package was blocked: confirm `policy.mode`; `strict` upgrades WARN to
287
- BLOCK. Use `dg explain <id>` to see the cause.
288
- - An install firewall block you need to override: rerun with
289
- `--dg-force-install`, allowed only when `policy.allowForceOverride` is set;
290
- the override is audited.
291
- - `dg setup` exits 69 on Windows: setup is POSIX-only.
292
- - An older `dg` runs instead of the installed one: run `which -a dg` and fix
293
- PATH precedence.
294
-
295
- ## Links
296
-
297
- - Documentation: https://westbayberry.com/docs
298
- - Pricing: https://westbayberry.com/pricing
287
+ Apache-2.0. See [LICENSE](./LICENSE). Issues and pull requests are welcome at
288
+ [github.com/WestBayBerry/DG_CLI](https://github.com/WestBayBerry/DG_CLI).
@@ -0,0 +1,123 @@
1
+ import { join } from "node:path";
2
+ import { formatScreenedNote } from "../launcher/agent-check.js";
3
+ import { dirExists, mergedJsonHook, LEGACY_AGENT_HOOK_SENTINEL } from "./persistence.js";
4
+ const SIGNATURE = "hook-exec claude-code";
5
+ function configPath(home) {
6
+ return join(home, ".claude", "settings.json");
7
+ }
8
+ function isDgGroup(group) {
9
+ if (typeof group !== "object" || group === null) {
10
+ return false;
11
+ }
12
+ const hooks = group.hooks;
13
+ if (!Array.isArray(hooks)) {
14
+ return false;
15
+ }
16
+ return hooks.some((h) => typeof h === "object" &&
17
+ h !== null &&
18
+ typeof h.command === "string" &&
19
+ h.command.includes(SIGNATURE));
20
+ }
21
+ function dgGroup(dgCommand) {
22
+ return { matcher: "Bash", hooks: [{ type: "command", command: dgCommand, timeout: 60 }] };
23
+ }
24
+ // Idempotently insert the dg PreToolUse group, preserving every other key the
25
+ // user has in their settings (round-trips unknown keys).
26
+ export function insertDgHook(settings, dgCommand) {
27
+ const next = { ...settings };
28
+ const hooks = typeof next.hooks === "object" && next.hooks !== null ? { ...next.hooks } : {};
29
+ const pre = Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : [];
30
+ hooks.PreToolUse = [...pre.filter((g) => !isDgGroup(g)), dgGroup(dgCommand)];
31
+ next.hooks = hooks;
32
+ return next;
33
+ }
34
+ export function removeDgHook(settings) {
35
+ const next = { ...settings };
36
+ if (typeof next.hooks !== "object" || next.hooks === null) {
37
+ return { settings: next, changed: false, empty: Object.keys(next).length === 0 };
38
+ }
39
+ const hooks = { ...next.hooks };
40
+ const pre = Array.isArray(hooks.PreToolUse) ? hooks.PreToolUse : [];
41
+ const withoutDg = pre.filter((g) => !isDgGroup(g));
42
+ const changed = withoutDg.length !== pre.length;
43
+ if (withoutDg.length > 0) {
44
+ hooks.PreToolUse = withoutDg;
45
+ }
46
+ else {
47
+ delete hooks.PreToolUse;
48
+ }
49
+ if (Object.keys(hooks).length > 0) {
50
+ next.hooks = hooks;
51
+ }
52
+ else {
53
+ delete next.hooks;
54
+ }
55
+ return { settings: next, changed, empty: Object.keys(next).length === 0 };
56
+ }
57
+ function hasDgHook(settings) {
58
+ const hooks = settings.hooks?.PreToolUse;
59
+ return Array.isArray(hooks) && hooks.some((g) => isDgGroup(g));
60
+ }
61
+ function parseInput(stdin) {
62
+ try {
63
+ const obj = JSON.parse(stdin);
64
+ const command = obj.tool_input?.command;
65
+ if (typeof command !== "string") {
66
+ return null;
67
+ }
68
+ return typeof obj.cwd === "string" ? { command, cwd: obj.cwd } : { command };
69
+ }
70
+ catch {
71
+ return null;
72
+ }
73
+ }
74
+ // Claude Code PreToolUse contract: emit a decision only to deny/ask; stay
75
+ // silent ({}) on allow so we never override the user's normal permission flow.
76
+ function emitDecision(verdict) {
77
+ if (verdict.decision === "allow") {
78
+ const note = verdict.screened ? formatScreenedNote(verdict.screened) : "";
79
+ if (note) {
80
+ return {
81
+ stdout: JSON.stringify({
82
+ hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext: note },
83
+ }),
84
+ exitCode: 0,
85
+ };
86
+ }
87
+ return { stdout: "{}", exitCode: 0 };
88
+ }
89
+ return {
90
+ stdout: JSON.stringify({
91
+ hookSpecificOutput: {
92
+ hookEventName: "PreToolUse",
93
+ permissionDecision: verdict.decision,
94
+ ...(verdict.reason ? { permissionDecisionReason: verdict.reason } : {}),
95
+ },
96
+ }),
97
+ exitCode: 0,
98
+ };
99
+ }
100
+ export const claudeCodeIntegration = {
101
+ id: "claude-code",
102
+ label: "Claude Code",
103
+ kind: "merged-json",
104
+ maturity: "verified",
105
+ minVersion: null,
106
+ configPath,
107
+ detect: (home) => dirExists(join(home, ".claude")),
108
+ probeHookSupport: (home) => ({
109
+ supported: true,
110
+ detail: dirExists(join(home, ".claude"))
111
+ ? "Claude Code settings directory found"
112
+ : "settings hooks are supported by every Claude Code version; ~/.claude will be created",
113
+ }),
114
+ parseInput,
115
+ emitDecision,
116
+ ...mergedJsonHook({
117
+ checkName: "dg PreToolUse hook",
118
+ legacySentinels: [LEGACY_AGENT_HOOK_SENTINEL],
119
+ insert: insertDgHook,
120
+ remove: removeDgHook,
121
+ isInstalled: hasDgHook,
122
+ }),
123
+ };