sublyzer-snapshot 0.3.0 → 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 (71) hide show
  1. package/README.md +114 -193
  2. package/dist/cli.js +83 -38
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/ci.d.ts.map +1 -1
  5. package/dist/commands/ci.js +15 -14
  6. package/dist/commands/ci.js.map +1 -1
  7. package/dist/commands/compare.d.ts +1 -0
  8. package/dist/commands/compare.d.ts.map +1 -1
  9. package/dist/commands/compare.js +14 -8
  10. package/dist/commands/compare.js.map +1 -1
  11. package/dist/commands/doctor.d.ts.map +1 -1
  12. package/dist/commands/doctor.js +47 -45
  13. package/dist/commands/doctor.js.map +1 -1
  14. package/dist/commands/init.d.ts +2 -0
  15. package/dist/commands/init.d.ts.map +1 -1
  16. package/dist/commands/init.js +70 -28
  17. package/dist/commands/init.js.map +1 -1
  18. package/dist/commands/open.d.ts.map +1 -1
  19. package/dist/commands/open.js +6 -1
  20. package/dist/commands/open.js.map +1 -1
  21. package/dist/commands/pull.d.ts.map +1 -1
  22. package/dist/commands/pull.js +4 -1
  23. package/dist/commands/pull.js.map +1 -1
  24. package/dist/commands/report.d.ts +1 -0
  25. package/dist/commands/report.d.ts.map +1 -1
  26. package/dist/commands/report.js +19 -19
  27. package/dist/commands/report.js.map +1 -1
  28. package/dist/commands/run.d.ts +5 -12
  29. package/dist/commands/run.d.ts.map +1 -1
  30. package/dist/commands/run.js +12 -90
  31. package/dist/commands/run.js.map +1 -1
  32. package/dist/commands/scan.d.ts +13 -0
  33. package/dist/commands/scan.d.ts.map +1 -0
  34. package/dist/commands/scan.js +17 -0
  35. package/dist/commands/scan.js.map +1 -0
  36. package/dist/commands/status.d.ts.map +1 -1
  37. package/dist/commands/status.js +31 -22
  38. package/dist/commands/status.js.map +1 -1
  39. package/dist/config.d.ts +17 -5
  40. package/dist/config.d.ts.map +1 -1
  41. package/dist/config.js +27 -4
  42. package/dist/config.js.map +1 -1
  43. package/dist/constants.d.ts +4 -1
  44. package/dist/constants.d.ts.map +1 -1
  45. package/dist/constants.js +3 -1
  46. package/dist/constants.js.map +1 -1
  47. package/dist/detect/scan-target.d.ts +13 -0
  48. package/dist/detect/scan-target.d.ts.map +1 -0
  49. package/dist/detect/scan-target.js +95 -0
  50. package/dist/detect/scan-target.js.map +1 -0
  51. package/dist/report/markdown.d.ts +1 -1
  52. package/dist/report/markdown.d.ts.map +1 -1
  53. package/dist/report/markdown.js +5 -0
  54. package/dist/report/markdown.js.map +1 -1
  55. package/dist/scan/bundle-size.d.ts +12 -0
  56. package/dist/scan/bundle-size.d.ts.map +1 -0
  57. package/dist/scan/bundle-size.js +50 -0
  58. package/dist/scan/bundle-size.js.map +1 -0
  59. package/dist/scan/execute.d.ts +28 -0
  60. package/dist/scan/execute.d.ts.map +1 -0
  61. package/dist/scan/execute.js +135 -0
  62. package/dist/scan/execute.js.map +1 -0
  63. package/dist/scan/history.d.ts +5 -5
  64. package/dist/scan/history.d.ts.map +1 -1
  65. package/dist/scan/history.js +15 -15
  66. package/dist/scan/history.js.map +1 -1
  67. package/dist/scan/snapshot.d.ts +8 -0
  68. package/dist/scan/snapshot.d.ts.map +1 -1
  69. package/dist/scan/snapshot.js +37 -9
  70. package/dist/scan/snapshot.js.map +1 -1
  71. package/package.json +10 -8
package/README.md CHANGED
@@ -2,35 +2,33 @@
2
2
 
3
3
  # Sublyzer Snapshot
4
4
 
5
- <img src="./Sublyzer1.png" alt="Sublyzer" width="96" />
5
+ <img src="./Sublyzer1.png" alt="Sublyzer Snapshot" width="96" />
6
6
 
7
- **Turn any codebase into a live Sublyzer dashboard in 30 seconds.**
7
+ **Local project health scanner for any Node.js codebase.**
8
8
 
9
- Scan routes, dependencies, and vulnerabilities locally get a health score push everything to [Sublyzer](https://sublyzer.com). No SDK required for your first check.
9
+ Works **standalone**no account required. Optionally sync to [Sublyzer](https://sublyzer.com) cloud when you want a live dashboard.
10
10
 
11
11
  <br />
12
12
 
13
- [![npm version](https://img.shields.io/badge/npm-v0.3.0-2563eb?style=for-the-badge&logo=npm&logoColor=white)](https://www.npmjs.com/package/sublyzer-snapshot)
13
+ [![npm version](https://img.shields.io/npm/v/sublyzer-snapshot?style=for-the-badge&logo=npm&logoColor=white&label=npm)](https://www.npmjs.com/package/sublyzer-snapshot)
14
14
  [![License: MIT](https://img.shields.io/badge/License-MIT-22c55e?style=for-the-badge)](LICENSE)
15
15
  [![Node.js](https://img.shields.io/badge/Node.js-18%2B-339933?style=for-the-badge&logo=node.js&logoColor=white)](https://nodejs.org)
16
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-3178C6?style=for-the-badge&logo=typescript&logoColor=white)](https://www.typescriptlang.org)
17
- [![Sublyzer](https://img.shields.io/badge/Powered%20by-Sublyzer-6366f1?style=for-the-badge)](https://sublyzer.com)
18
16
 
19
17
  <br />
20
18
 
21
- [`Quick Start`](#-quick-start) · [`How It Works`](#-how-it-works) · [`Commands`](#-commands) · [`CI/CD`](#-cicd) · [`Docs`](#-environment-variables) · [Sublyzer Dashboard](https://sublyzer.com/dashboard)
19
+ [`Quick Start`](#-quick-start) · [`Standalone vs Cloud`](#-standalone-vs-cloud) · [`Monorepos`](#-monorepos) · [`Commands`](#-commands) · [`Roadmap`](#-roadmap)
22
20
 
23
21
  <br />
24
22
 
25
23
  ```
26
- $ npx sublyzer-snapshot init && npx sublyzer-snapshot run
27
-
28
- Project: my-saas-app
29
- Stack: Next.js (high)
30
- Health: [████████░░] 82/100 grade B
31
- Routes: 14
32
- Vulnerabilities: 2 (critical 0, high 1)
33
- Sent 6 events → dashboard updated
24
+ $ npx sublyzer-snapshot scan
25
+
26
+ Scan root: frontend (auto-selected)
27
+ Stack: Next.js (high)
28
+ Health: [████████░░] 84/100 grade B
29
+ Routes: 42
30
+ Build output: 12.4 MB (.next)
31
+ Saved to .sublyzer/ (local)
34
32
  ```
35
33
 
36
34
  </div>
@@ -39,268 +37,191 @@ Scan routes, dependencies, and vulnerabilities locally — get a health score
39
37
 
40
38
  ## ✨ Introduction
41
39
 
42
- **Sublyzer Snapshot** is an open-source CLI that answers one question every developer asks:
40
+ **Sublyzer Snapshot** is an open-source CLI that scans your repo and answers:
43
41
 
44
- > *"How healthy is my project right now?"*
42
+ > *How healthy is this project right now?*
45
43
 
46
- Instead of wiring up three separate tools (dependency scanner, route inventory, monitoring dashboard), you run **one command**. Snapshot scans your repo locally, computes a **health score (0–100)**, and pushes structured events to your **Sublyzer** integration — where you (or an AI agent like Hermes) can read them back.
44
+ It runs **entirely on your machine**: stack detection, routes, dependency audit, outdated packages, build size, git metadata, and a **0–100 health score**.
47
45
 
48
- Built for **indie hackers**, **startup teams**, and **agencies** who want instant project visibility without a week of DevOps setup.
46
+ **Sublyzer cloud is optional** use it when you want events in a shared dashboard, agents (Hermes), or team visibility. Without cloud, you still get reports, history, CI gates, and compare diffs.
49
47
 
50
48
  ---
51
49
 
52
- ## 🎯 What is it for?
50
+ ## 🔀 Standalone vs Cloud
53
51
 
54
- | Use case | What you get |
55
- |----------|--------------|
56
- | **First-time project check** | Stack detection, route map, CVE summary in ~30s |
57
- | **Pre-deploy sanity check** | Health score + vulnerability gate before shipping |
58
- | **CI/CD pipeline** | `--fail-on high` blocks merges with critical CVEs |
59
- | **Client / team reports** | `report --out HEALTH.md` shareable Markdown |
60
- | **Trend tracking** | `compare` shows what changed since last scan |
61
- | **Sublyzer onboarding** | Data in your dashboard before installing the browser SDK |
52
+ | | **Standalone (default)** | **Cloud (optional)** |
53
+ |---|--------------------------|----------------------|
54
+ | Account | None | [Sublyzer](https://sublyzer.com) integration code |
55
+ | Command | `npx sublyzer-snapshot scan` | `init --code …` then `run --push` |
56
+ | Output | Terminal + `.sublyzer/` history | + Sublyzer dashboard |
57
+ | CI | `scan --fail-on high` | + optional push via secret |
62
58
 
63
- ### Before vs After
64
-
65
- | Before | After (Sublyzer Snapshot) |
66
- |--------|---------------------------|
67
- | Sentry + Dependabot + manual spreadsheet | One CLI, one dashboard |
68
- | 45+ min setup | ~30 seconds |
69
- | No unified health score | 0–100 score with grade A–F |
70
- | Hard to show clients "project status" | Export `report.md` in one command |
71
-
72
- ---
59
+ ```bash
60
+ # Standalone — zero setup
61
+ npx sublyzer-snapshot scan
73
62
 
74
- ## ⚙️ How it works
75
-
76
- ```mermaid
77
- flowchart LR
78
- A[Your repo] --> B[sublyzer-snapshot run]
79
- B --> C[Local scan]
80
- C --> D[Routes · deps · audit · git]
81
- D --> E[Health score 0-100]
82
- E --> F[Sublyzer API]
83
- F --> G[Dashboard]
84
- G --> H[Hermes / agents / team]
85
-
86
- style A fill:#1e293b,stroke:#475569,color:#f8fafc
87
- style G fill:#2563eb,stroke:#1d4ed8,color:#fff
88
- style E fill:#22c55e,stroke:#16a34a,color:#fff
63
+ # Optional cloud link
64
+ npx sublyzer-snapshot init --code YOUR_24_CHAR_CODE -y
65
+ npx sublyzer-snapshot run --push
89
66
  ```
90
67
 
91
- 1. **`init`** — Links your project folder to a Sublyzer integration (24-char code).
92
- 2. **`run`** — Scans locally: framework, routes, `npm audit`, outdated packages, git metadata.
93
- 3. **Score** — Computes health grade from vulnerabilities, routes, env hygiene, and more.
94
- 4. **Push** — Sends events to `POST /data-collection/collect-batch` on Sublyzer.
95
- 5. **Dashboard** — Data appears under your integration; optional SDK for live telemetry later.
96
-
97
- Scan history is stored in `.sublyzer/` (gitignored automatically) so `compare` can show diffs over time.
98
-
99
68
  ---
100
69
 
101
70
  ## 🚀 Quick start
102
71
 
103
- ### Prerequisites
104
-
105
- - **Node.js 18+**
106
- - **npm** (for audit/outdated; optional with `--skip-audit`)
107
- - A [Sublyzer](https://sublyzer.com) account + **integration code** (24 chars, from Dashboard → Integrations)
108
-
109
- ### Install & run
72
+ ### Install (use npx — recommended)
110
73
 
111
74
  ```bash
112
- # 1. Link your project
113
- npx sublyzer-snapshot init
114
-
115
- # 2. Scan and push
116
- npx sublyzer-snapshot run
117
-
118
- # 3. Open dashboard
119
- npx sublyzer-snapshot open
75
+ npx sublyzer-snapshot@latest --version
120
76
  ```
121
77
 
122
- Non-interactive (CI / scripts):
78
+ Do **not** run `npm i sublyzer-snapshot` in random folders — use `npx` to avoid unrelated peer dependency conflicts.
79
+
80
+ ### Scan any project
123
81
 
124
82
  ```bash
125
- export SUBLYZER_INTEGRATION_CODE="YOUR_24_CHAR_CODE"
126
- npx sublyzer-snapshot init -y
127
- npx sublyzer-snapshot run --fail-on high
83
+ cd your-project
84
+ npx sublyzer-snapshot scan
85
+ npx sublyzer-snapshot report --out HEALTH.md
86
+ npx sublyzer-snapshot compare
128
87
  ```
129
88
 
130
- ### Development (this monorepo)
89
+ ### Save preferences (local)
131
90
 
132
91
  ```bash
133
- cd projects/sublyzer-snapshot
134
- npm install && npm run build
135
- node dist/cli.js init --code YOUR_CODE -y
136
- node dist/cli.js run --dry-run
92
+ npx sublyzer-snapshot init --local
93
+ npx sublyzer-snapshot run
137
94
  ```
138
95
 
139
- ---
140
-
141
- ## 🧰 Commands
142
-
143
- | Command | Description |
144
- |---------|-------------|
145
- | `init` | Detect stack, validate code, write `.sublyzer/snapshot.json` |
146
- | `run` | Full scan + push to Sublyzer |
147
- | `report` | Generate Markdown health report (`--out report.md`) |
148
- | `compare` | Diff routes, vulns, and score vs previous scan |
149
- | `ci` | Print or write GitHub Actions workflow |
150
- | `status` | Show linked integration + last scan |
151
- | `doctor` | Verify config, API, integration code, read key |
152
- | `pull` | Fetch data back via public read API (needs `apiReadKey`) |
153
- | `open` | Open integration in browser |
154
-
155
- ### Common flags
96
+ ### Optional cloud sync
156
97
 
157
98
  ```bash
158
- sublyzer-snapshot run --dry-run # Local preview, no push
159
- sublyzer-snapshot run --skip-audit # Faster scan
160
- sublyzer-snapshot run --fail-on high # Exit 1 on high/critical CVEs
161
- sublyzer-snapshot run --json # CI-friendly JSON output
162
-
163
- sublyzer-snapshot report --out HEALTH.md
164
- sublyzer-snapshot compare --rescan
165
- sublyzer-snapshot ci --out .github/workflows/sublyzer-snapshot.yml
99
+ npx sublyzer-snapshot init --code YOUR_24_CHAR_CODE -y
100
+ npx sublyzer-snapshot run --push
101
+ npx sublyzer-snapshot open
166
102
  ```
167
103
 
168
104
  ---
169
105
 
170
- ## 📊 Health score
106
+ ## 📦 Monorepos
171
107
 
172
- Every scan produces a **0–100 score** and letter grade (**A–F**):
108
+ Snapshot **auto-picks** the best package in monorepos (npm/pnpm workspaces, `frontend/`, `backend/`, etc.).
173
109
 
174
- | Factor | Impact |
175
- |--------|--------|
176
- | Critical CVEs | −25 each (max −50) |
177
- | High CVEs | −10 each (max −30) |
178
- | Moderate CVEs | −3 each |
179
- | Web stack with 0 routes detected | −8 |
180
- | Dirty git working tree | −3 |
181
- | Missing `.env.example` | −2 |
182
- | Major outdated packages | −3 each |
183
-
184
- Example terminal output:
110
+ ```bash
111
+ # From repo root — auto-selects e.g. frontend/
112
+ npx sublyzer-snapshot scan
185
113
 
186
- ```
187
- Health: [████████░░] 82/100 grade B
114
+ # Force a subfolder
115
+ npx sublyzer-snapshot scan --path backend
116
+ npx sublyzer-snapshot init --local --path frontend
188
117
  ```
189
118
 
190
- The score is also sent to Sublyzer as a `snapshot_health_score` performance metric.
119
+ On `init`, other scannable packages in the repo are listed as hints.
191
120
 
192
121
  ---
193
122
 
194
- ## 🔍 What gets detected
195
-
196
- | Stack | Signals |
197
- |-------|---------|
198
- | **Next.js** | `next` in package.json, `app/` & `pages/` routes |
199
- | **NestJS** | `@nestjs/core`, decorator routes |
200
- | **Express / Fastify** | Route patterns in source |
201
- | **Remix / Nuxt / SvelteKit** | package.json dependencies |
202
- | **React / Vue / Node** | Fallback detection |
203
- | **Monorepos** | npm & pnpm workspaces |
204
-
205
- Each `run` sends:
206
-
207
- - `custom_event` → `project_snapshot` (stack, routes, git, audit summary)
208
- - `vulnerability` → top npm audit findings
209
- - `performance` → health score + route count
210
-
211
- ---
212
-
213
- ## 🔄 CI/CD
123
+ ## 🧰 Commands
214
124
 
215
- ### Fail the build on vulnerabilities
125
+ | Command | Description |
126
+ |---------|-------------|
127
+ | **`scan`** | Local scan — **no init, no account** |
128
+ | `init` | Save config — `--local` or `--code` for cloud |
129
+ | `run` | Scan + history (pushes in cloud mode or with `--push`) |
130
+ | `report` | Markdown report (`--out file.md`) |
131
+ | `compare` | Diff vs previous scan |
132
+ | `doctor` | Verify Node, scan target, optional cloud |
133
+ | `status` | Config + last scan |
134
+ | `ci` | GitHub Actions template |
135
+ | `pull` / `open` | Cloud only (read API / dashboard) |
136
+
137
+ ### Flags
216
138
 
217
139
  ```bash
218
- sublyzer-snapshot run --fail-on high --json > snapshot-result.json
140
+ npx sublyzer-snapshot scan --path frontend
141
+ npx sublyzer-snapshot scan --fail-on high --json
142
+ npx sublyzer-snapshot run --local # never push
143
+ npx sublyzer-snapshot run --push # force cloud push
144
+ npx sublyzer-snapshot run --skip-audit # faster
219
145
  ```
220
146
 
221
- Levels: `critical` · `high` · `moderate` · `any`
147
+ ---
222
148
 
223
- ### Generate GitHub Actions workflow
149
+ ## 📊 What gets scanned
224
150
 
225
- ```bash
226
- sublyzer-snapshot ci --out .github/workflows/sublyzer-snapshot.yml
227
- ```
151
+ - **Stack** — Next.js, NestJS, Express, Fastify, Remix, Nuxt, SvelteKit, React, Vue
152
+ - **Routes** `app/` / `pages/` or source patterns
153
+ - **Dependencies** + `npm audit` + outdated packages
154
+ - **Build size** — `dist/`, `.next/`, `build/` folders
155
+ - **Git** — branch, commit, dirty state
156
+ - **Health score** — 0–100, grade A–F
228
157
 
229
- Add repository secret: **`SUBLYZER_INTEGRATION_CODE`**
158
+ ---
230
159
 
231
- The generated workflow runs on push, PR, and weekly schedule — uploads JSON + Markdown report as artifacts.
160
+ ## 🔄 CI/CD
232
161
 
233
- ---
162
+ Local-only CI (no secrets):
234
163
 
235
- ## 🤖 Read data back (agents & API)
164
+ ```yaml
165
+ - run: npx sublyzer-snapshot@latest scan --fail-on high --json
166
+ ```
236
167
 
237
- Push uses the **integration code**. To **read** snapshot data programmatically (e.g. Hermes):
168
+ Generate full workflow:
238
169
 
239
170
  ```bash
240
- # Get read key (authenticated)
241
- curl "https://api.sublyzer.com/integrations/{id}/read-access" \
242
- -H "Authorization: Bearer YOUR_JWT"
243
-
244
- # Or via CLI
245
- sublyzer-snapshot pull --read-key YOUR_READ_KEY
171
+ npx sublyzer-snapshot ci --out .github/workflows/sublyzer-snapshot.yml
246
172
  ```
247
173
 
248
- Set `SUBLYZER_READ_KEY` in env or save during `init --read-key ...`.
174
+ Optional cloud push when `SUBLYZER_INTEGRATION_CODE` secret is set.
249
175
 
250
176
  ---
251
177
 
252
178
  ## 🔐 Environment variables
253
179
 
254
- | Variable | Required | Description |
255
- |----------|----------|-------------|
256
- | `SUBLYZER_INTEGRATION_CODE` | For `init` | 24-character integration code |
257
- | `SUBLYZER_READ_KEY` | For `pull` | Public read API key |
258
- | `SUBLYZER_API_URL` | No | Default: `https://api.sublyzer.com` |
259
- | `SUBLYZER_DASHBOARD_URL` | No | Default: `https://sublyzer.com` |
260
-
261
- > **Security:** `.sublyzer/snapshot.json` contains your integration code. It is auto-added to `.gitignore` on `init` — never commit it to public repos.
180
+ | Variable | When |
181
+ |----------|------|
182
+ | `SUBLYZER_INTEGRATION_CODE` | Cloud `init` |
183
+ | `SUBLYZER_READ_KEY` | Cloud `pull` |
184
+ | `SUBLYZER_API_URL` | Custom API (default: `https://api.sublyzer.com`) |
262
185
 
263
186
  ---
264
187
 
265
- ## 📁 Project structure (local)
188
+ ## 📁 Local data
266
189
 
267
190
  ```
268
191
  your-project/
269
- ├── .sublyzer/
270
- ├── snapshot.json # Linked integration config
271
- ├── last-snapshot.json # Latest scan cache
272
- └── history/ # Previous scans (for compare)
273
- ├── src/
274
- └── package.json
192
+ └── .sublyzer/ # gitignored on init
193
+ ├── snapshot.json # config (local or cloud)
194
+ ├── last-snapshot.json
195
+ └── history/ # for compare
275
196
  ```
276
197
 
277
198
  ---
278
199
 
279
200
  ## 🛣️ Roadmap
280
201
 
281
- - [ ] npm publish (`npx sublyzer-snapshot`)
282
- - [ ] `login` flow (no manual code paste)
283
- - [ ] Bundle size analysis
284
- - [ ] PR comment bot (GitHub App)
202
+ - [x] npm publish [sublyzer-snapshot](https://www.npmjs.com/package/sublyzer-snapshot)
203
+ - [x] Standalone `scan` (no account)
204
+ - [x] Local vs cloud modes
205
+ - [x] Monorepo auto-target (`frontend/`, workspaces)
206
+ - [x] Build output size scan
207
+ - [x] Health score + compare + report + CI template
208
+ - [ ] `login` OAuth (no manual code paste)
209
+ - [ ] SARIF export for GitHub Security
210
+ - [ ] PR comment bot
211
+ - [ ] Python / Go stack detection
285
212
 
286
213
  ---
287
214
 
288
215
  ## 📄 License
289
216
 
290
- MIT © [Sublyzer](https://sublyzer.com) — see [LICENSE](./LICENSE).
217
+ MIT — see [LICENSE](./LICENSE).
291
218
 
292
219
  ---
293
220
 
294
221
  <div align="center">
295
222
 
296
- **[Sublyzer](https://sublyzer.com)** · **[Documentation](https://sublyzer.com/docs)** · **[Dashboard](https://sublyzer.com/dashboard)**
297
-
298
- <br />
299
-
300
- If this saved you time, ⭐ star the repo — it helps other devs find it.
301
-
302
- <br />
223
+ **[npm](https://www.npmjs.com/package/sublyzer-snapshot)** · **[Sublyzer Cloud](https://sublyzer.com)** · **[Docs](https://sublyzer.com/docs)**
303
224
 
304
- <sub>Built with TypeScript · Powered by <a href="https://sublyzer.com">Sublyzer</a> observability platform</sub>
225
+ <sub>Standalone by default · Cloud when you need it</sub>
305
226
 
306
227
  </div>
package/dist/cli.js CHANGED
@@ -8,7 +8,8 @@ import { runInit } from './commands/init.js';
8
8
  import { runOpen } from './commands/open.js';
9
9
  import { runPull } from './commands/pull.js';
10
10
  import { runReport } from './commands/report.js';
11
- import { runScan } from './commands/run.js';
11
+ import { runRunCommand } from './commands/run.js';
12
+ import { runScanCommand } from './commands/scan.js';
12
13
  import { runStatus } from './commands/status.js';
13
14
  const program = new Command();
14
15
  function handleError(e) {
@@ -17,24 +18,62 @@ function handleError(e) {
17
18
  process.exit(1);
18
19
  }
19
20
  const FAIL_ON_LEVELS = ['critical', 'high', 'moderate', 'any'];
21
+ function sharedScanOptions(cmd) {
22
+ return cmd
23
+ .option('--path <dir>', 'Scan a subfolder (e.g. frontend, backend)')
24
+ .option('--skip-audit', 'Skip npm audit')
25
+ .option('--skip-outdated', 'Skip npm outdated check')
26
+ .option('--skip-bundle', 'Skip build output size scan')
27
+ .option('--json', 'JSON output')
28
+ .option('--fail-on <level>', 'Exit 1 on vulns: critical|high|moderate|any');
29
+ }
20
30
  program
21
31
  .name('sublyzer-snapshot')
22
- .description('Scan any project and push a health snapshot to Sublyzer')
32
+ .description('Local project health scanner optional Sublyzer cloud sync')
23
33
  .version(SDK_VERSION);
34
+ sharedScanOptions(program
35
+ .command('scan')
36
+ .description('Scan project locally (no account, no init required)')
37
+ .option('--push', 'Push to Sublyzer if cloud config exists'))
38
+ .action(async (opts) => {
39
+ try {
40
+ const failOn = FAIL_ON_LEVELS.includes(opts.failOn) ? opts.failOn : undefined;
41
+ const result = await runScanCommand({
42
+ path: opts.path,
43
+ push: opts.push,
44
+ skipAudit: opts.skipAudit,
45
+ skipOutdated: opts.skipOutdated,
46
+ skipBundle: opts.skipBundle,
47
+ json: opts.json,
48
+ failOn,
49
+ });
50
+ if (opts.json)
51
+ console.log(JSON.stringify(result, null, 2));
52
+ if (result.policyFailed)
53
+ process.exit(1);
54
+ }
55
+ catch (e) {
56
+ handleError(e);
57
+ }
58
+ });
24
59
  program
25
60
  .command('init')
26
- .description('Detect stack and link this project to your Sublyzer integration')
27
- .option('--code <code>', 'Integration code (24 chars); or SUBLYZER_INTEGRATION_CODE')
28
- .option('--read-key <key>', 'Optional apiReadKey for pull; or SUBLYZER_READ_KEY')
29
- .option('--api-url <url>', 'Sublyzer API base URL')
30
- .option('--dashboard-url <url>', 'Dashboard base URL')
31
- .option('-y, --yes', 'Non-interactive when code is provided')
61
+ .description('Save scan preferences local-only or link Sublyzer cloud')
62
+ .option('--local', 'Local mode only (no Sublyzer account)')
63
+ .option('--code <code>', 'Cloud: integration code (24 chars)')
64
+ .option('--read-key <key>', 'Cloud: apiReadKey for pull')
65
+ .option('--path <dir>', 'Preferred scan directory in monorepos')
66
+ .option('--api-url <url>', 'Sublyzer API URL')
67
+ .option('--dashboard-url <url>', 'Dashboard URL')
68
+ .option('-y, --yes', 'Non-interactive')
32
69
  .option('--skip-gitignore', 'Do not update .gitignore')
33
70
  .action(async (opts) => {
34
71
  try {
35
72
  await runInit({
73
+ local: opts.local,
36
74
  code: opts.code,
37
75
  readKey: opts.readKey,
76
+ path: opts.path,
38
77
  apiUrl: opts.apiUrl,
39
78
  dashboardUrl: opts.dashboardUrl,
40
79
  yes: opts.yes,
@@ -45,21 +84,23 @@ program
45
84
  handleError(e);
46
85
  }
47
86
  });
48
- program
87
+ sharedScanOptions(program
49
88
  .command('run')
50
- .description('Scan routes, dependencies, vulnerabilities and push to Sublyzer')
51
- .option('--dry-run', 'Scan locally without sending data')
52
- .option('--skip-audit', 'Skip npm audit (faster)')
53
- .option('--skip-outdated', 'Skip npm outdated check')
54
- .option('--json', 'Output machine-readable JSON (CI friendly)')
55
- .option('--fail-on <level>', 'Exit 1 if vulns at level: critical|high|moderate|any')
89
+ .description('Scan + save history (pushes only in cloud mode or with --push)')
90
+ .option('--dry-run', 'Scan without saving push')
91
+ .option('--push', 'Force push to Sublyzer cloud')
92
+ .option('--local', 'Force local-only (no push)'))
56
93
  .action(async (opts) => {
57
94
  try {
58
95
  const failOn = FAIL_ON_LEVELS.includes(opts.failOn) ? opts.failOn : undefined;
59
- const result = await runScan({
96
+ const result = await runRunCommand({
97
+ path: opts.path,
60
98
  dryRun: opts.dryRun,
99
+ push: opts.push,
100
+ local: opts.local,
61
101
  skipAudit: opts.skipAudit,
62
102
  skipOutdated: opts.skipOutdated,
103
+ skipBundle: opts.skipBundle,
63
104
  json: opts.json,
64
105
  failOn,
65
106
  });
@@ -74,8 +115,8 @@ program
74
115
  });
75
116
  program
76
117
  .command('status')
77
- .description('Show linked integration and last scan summary')
78
- .option('--json', 'Output machine-readable JSON')
118
+ .description('Show config and last scan')
119
+ .option('--json', 'JSON output')
79
120
  .action(async (opts) => {
80
121
  try {
81
122
  const data = await runStatus({ json: opts.json });
@@ -88,8 +129,8 @@ program
88
129
  });
89
130
  program
90
131
  .command('doctor')
91
- .description('Verify config, API connectivity, integration code and read key')
92
- .option('--json', 'Output machine-readable JSON')
132
+ .description('Verify Node, scan target, optional cloud link')
133
+ .option('--json', 'JSON output')
93
134
  .action(async (opts) => {
94
135
  try {
95
136
  const result = await runDoctor({ json: opts.json });
@@ -104,15 +145,17 @@ program
104
145
  });
105
146
  program
106
147
  .command('compare')
107
- .description('Diff current vs previous scan (routes, vulns, health score)')
108
- .option('--json', 'Output machine-readable JSON')
109
- .option('--rescan', 'Run a fresh scan before comparing')
110
- .option('--skip-audit', 'Skip audit when using --rescan')
148
+ .description('Diff vs previous scan')
149
+ .option('--json', 'JSON output')
150
+ .option('--rescan', 'Fresh scan before compare')
151
+ .option('--path <dir>', 'Scan path with --rescan')
152
+ .option('--skip-audit', 'Skip audit with --rescan')
111
153
  .action(async (opts) => {
112
154
  try {
113
155
  const data = await runCompare({
114
156
  json: opts.json,
115
157
  rescan: opts.rescan,
158
+ path: opts.path,
116
159
  skipAudit: opts.skipAudit,
117
160
  });
118
161
  if (opts.json)
@@ -124,16 +167,18 @@ program
124
167
  });
125
168
  program
126
169
  .command('report')
127
- .description('Generate a Markdown health report (stdout or --out file)')
128
- .option('--out <file>', 'Write report to file (e.g. sublyzer-report.md)')
129
- .option('--rescan', 'Run a fresh scan instead of using last cached scan')
130
- .option('--skip-audit', 'Skip audit when using --rescan')
131
- .option('--json', 'Output JSON wrapper with markdown body')
170
+ .description('Markdown health report')
171
+ .option('--out <file>', 'Write to file')
172
+ .option('--rescan', 'Fresh scan')
173
+ .option('--path <dir>', 'Scan path with --rescan')
174
+ .option('--skip-audit', 'Skip audit')
175
+ .option('--json', 'JSON wrapper')
132
176
  .action(async (opts) => {
133
177
  try {
134
178
  const out = await runReport({
135
179
  out: opts.out,
136
180
  rescan: opts.rescan,
181
+ path: opts.path,
137
182
  skipAudit: opts.skipAudit,
138
183
  json: opts.json,
139
184
  });
@@ -146,9 +191,9 @@ program
146
191
  });
147
192
  program
148
193
  .command('ci')
149
- .description('Print or write a GitHub Actions workflow for automated snapshots')
150
- .option('--out <path>', 'Write workflow file (default: .github/workflows/sublyzer-snapshot.yml)')
151
- .option('--print', 'Print template to stdout only')
194
+ .description('GitHub Actions workflow template')
195
+ .option('--out <path>', 'Write workflow file')
196
+ .option('--print', 'Print to stdout')
152
197
  .action(async (opts) => {
153
198
  try {
154
199
  await runCi({ out: opts.out, print: opts.print ?? !opts.out });
@@ -159,12 +204,12 @@ program
159
204
  });
160
205
  program
161
206
  .command('pull')
162
- .description('Fetch integration data from Sublyzer (requires apiReadKey)')
163
- .option('--read-key <key>', 'apiReadKey; or SUBLYZER_READ_KEY in env/config')
207
+ .description('Fetch data from Sublyzer cloud (requires apiReadKey)')
208
+ .option('--read-key <key>', 'apiReadKey')
164
209
  .option('--limit <n>', 'Max events', (v) => parseInt(v, 10))
165
- .option('--window-days <n>', 'Lookback window in days', (v) => parseInt(v, 10))
166
- .option('--include <csv>', 'stats,events,telemetry,performance,sdkStatus,activeErrors')
167
- .option('--json', 'Output raw API JSON only')
210
+ .option('--window-days <n>', 'Lookback days', (v) => parseInt(v, 10))
211
+ .option('--include <csv>', 'API include list')
212
+ .option('--json', 'JSON output')
168
213
  .action(async (opts) => {
169
214
  try {
170
215
  const data = await runPull({
@@ -183,7 +228,7 @@ program
183
228
  });
184
229
  program
185
230
  .command('open')
186
- .description('Open the Sublyzer dashboard for this integration')
231
+ .description('Open Sublyzer dashboard (cloud mode only)')
187
232
  .action(async () => {
188
233
  try {
189
234
  await runOpen();