fullstack-critic 1.0.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.
@@ -0,0 +1,45 @@
1
+ # Project Profile
2
+
3
+ <!-- Written by the AI on first REVIEW. Updated when architecture changes are confirmed. -->
4
+ <!-- Never store credentials, tokens, API keys, PII, passwords, or raw log data here. -->
5
+
6
+ ## System
7
+
8
+ - **Name:**
9
+ - **Purpose:**
10
+ - **Language:**
11
+ - **Framework:**
12
+ - **Database:**
13
+ - **Cache:**
14
+ - **Queue:**
15
+ - **Auth mechanism:**
16
+ - **Hosting / runtime:**
17
+ - **CI/CD:**
18
+
19
+ ## Entry points
20
+
21
+ <!--
22
+ Key files confirmed during the discovery layer.
23
+ - src/index.ts — HTTP server
24
+ - src/routes.ts — route registration
25
+ -->
26
+
27
+ ## Architecture summary
28
+
29
+ <!--
30
+ Short description of the request lifecycle, major services, and data flow.
31
+ Written after discovery layer. Updated only when evidence contradicts it.
32
+ -->
33
+
34
+ ## Known constraints
35
+
36
+ <!--
37
+ Hard limits discovered during review:
38
+ DB connection pool size, queue concurrency cap, external API rate limits, vendor SLAs.
39
+ -->
40
+
41
+ ## Scale context
42
+
43
+ - **Stated target:**
44
+ - **Load test evidence:** none / see REVIEW_HISTORY.md entry YYYY-MM-DD
45
+ - **Observed ceiling (if known):**
@@ -0,0 +1,24 @@
1
+ # Review History
2
+
3
+ <!-- One entry per completed session. Written at session end. -->
4
+ <!-- Never store credentials, tokens, API keys, PII, passwords, or raw log data here. -->
5
+
6
+ ## Entry format
7
+
8
+ ```
9
+ ### YYYY-MM-DD — Session title
10
+
11
+ - **Mode:** REVIEW | FIX | OPTIMIZE | CLEAN | VERIFY | FULL
12
+ - **Layers loaded:** list
13
+ - **Files read:** count
14
+ - **Findings:** BLOCKER: 0 CRITICAL: 0 HIGH: 0 MEDIUM: 0 LOW: 0 INFO: 0
15
+ - **Top finding:** One-line description of most severe confirmed issue
16
+ - **Fixed this session:** yes / no — list changed files if yes
17
+ - **Status:** open | in-progress | resolved
18
+ - **Commit / PR:** hash or link if resolved
19
+ - **Load test run:** yes / no — brief result if yes
20
+ ```
21
+
22
+ ---
23
+
24
+ <!-- AI writes entries below this line -->
@@ -0,0 +1,40 @@
1
+ # Run State
2
+
3
+ <!-- Written at session start. Updated when layer switches. Reset each new session. -->
4
+ <!-- Never store credentials, tokens, API keys, PII, passwords, or raw log data here. -->
5
+
6
+ ## Current session
7
+
8
+ - **Started:** YYYY-MM-DD HH:MM UTC
9
+ - **Mode:** REVIEW | FIX | OPTIMIZE | CLEAN | VERIFY | FULL
10
+ - **Focus:** (from PROJECT_REVIEW.md or direct user request)
11
+ - **Active layer:**
12
+ - **Files currently loaded:**
13
+ <!--
14
+ List here — cleared on layer switch
15
+ - src/routes/checkout.ts
16
+ - src/middleware/auth.ts
17
+ -->
18
+
19
+ ## Layer history this session
20
+
21
+ <!--
22
+ One line per completed layer — keeps a breadcrumb of what was already covered.
23
+ api — checkout route and payment middleware — 3 findings staged
24
+ backend — BookingService.reserve() — 1 finding staged
25
+ -->
26
+
27
+ ## Staged findings
28
+
29
+ <!--
30
+ Temporary. Move to final report before ending session.
31
+ SEVERITY | path/to/file:line | short description
32
+ CRITICAL | src/routes/checkout.ts:84 | payment amount trusted from client body
33
+ -->
34
+
35
+ ## Pending layer switches
36
+
37
+ <!--
38
+ Layers still required by evidence. Clear when loaded or ruled out.
39
+ database — required: N+1 suspected at src/services/product.ts:112
40
+ -->
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "fullstack-critic",
3
+ "version": "1.0.0",
4
+ "description": "A free, universal principal-engineer critic. Attach it to any project or run it as a background watcher over any in-progress workflow. Reviews 100% of resources — code, packages, and dependencies — across 12 dimensions and emits an evidence-based report with a fix / optimize / delete / add action plan.",
5
+ "keywords": [
6
+ "code-review",
7
+ "critic",
8
+ "static-analysis",
9
+ "watcher",
10
+ "security",
11
+ "dependencies",
12
+ "quality",
13
+ "ci",
14
+ "ai"
15
+ ],
16
+ "license": "MIT",
17
+ "author": "amansingh79033-ship-it",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/amansingh79033-ship-it/fullstack-critic.git"
21
+ },
22
+ "bin": {
23
+ "fullstack-critic": "bin/fullstack-critic.js",
24
+ "critic": "bin/fullstack-critic.js"
25
+ },
26
+ "main": "src/index.js",
27
+ "files": [
28
+ "bin",
29
+ "src",
30
+ "tests",
31
+ "CRITIC.md",
32
+ "AGENTS.md",
33
+ "CLAUDE.md",
34
+ "GEMINI.md",
35
+ "PROJECT_REVIEW.md",
36
+ "agent",
37
+ "layers",
38
+ "memory",
39
+ "prompts",
40
+ "docs",
41
+ "LICENSE"
42
+ ],
43
+ "engines": {
44
+ "node": ">=18"
45
+ },
46
+ "scripts": {
47
+ "review": "node bin/fullstack-critic.js review",
48
+ "watch": "node bin/fullstack-critic.js watch",
49
+ "audit": "node bin/fullstack-critic.js audit",
50
+ "test": "node --test tests/*.test.js",
51
+ "lint": "node -c bin/fullstack-critic.js && node --check src/cli.js",
52
+ "selftest": "node bin/fullstack-critic.js review . --json .critic-review.json"
53
+ },
54
+ "dependencies": {}
55
+ }
@@ -0,0 +1,299 @@
1
+ # Composio Agent Prompt — Upgrade fullstack-critic to v2
2
+
3
+ Paste this entire file into a Composio workspace agent.
4
+ The agent will read the current repo state, apply all v2 upgrades, validate,
5
+ and push to GitHub in a single commit — autonomously, without breaking any
6
+ running session or workflow attached to the repo.
7
+
8
+ ---
9
+
10
+ ## Your identity
11
+
12
+ You are a senior full-stack engineering agent in a Composio workspace.
13
+ Work through every phase below in order. Report clearly after each phase.
14
+ Do not ask for confirmation between phases unless a step is explicitly marked CONFIRM.
15
+ Do not skip validation. Do not commit if validation fails.
16
+
17
+ ## Repository
18
+
19
+ `amansingh79033-ship-it/fullstack-critic`
20
+
21
+ ---
22
+
23
+ ## Phase 1 — Read current state (no writes)
24
+
25
+ 1. `GITHUB_GET_REPOSITORY` → confirm access, note default branch and last commit SHA.
26
+ 2. `GITHUB_LIST_REPO_CONTENTS` on root → record every file and folder present.
27
+ 3. `GITHUB_GET_FILE_CONTENTS` for each of these files (capture SHA of each):
28
+ - `CRITIC.md`
29
+ - `CLAUDE.md`
30
+ - `GEMINI.md`
31
+ - `AGENTS.md`
32
+ - `README.md`
33
+ - `agent/fullstack-critic-agent.md`
34
+ - `layers/manifest.md`
35
+ - `prompts/composio-upgrade-agent.md` (if exists)
36
+ - `memory/templates/PROJECT_PROFILE.md` (if exists)
37
+ - `memory/templates/DECISIONS.md` (if exists)
38
+ - `memory/templates/REVIEW_HISTORY.md` (if exists)
39
+ - `memory/templates/RUN_STATE.md` (if exists)
40
+ 4. List what exists, what is missing, and what has content vs stubs.
41
+ 5. **Do not write anything yet.**
42
+
43
+ ---
44
+
45
+ ## Phase 2 — Apply upgrades
46
+
47
+ For every file below, use `GITHUB_GET_FILE_CONTENTS` to get the current SHA
48
+ (even if already read in Phase 1 — SHA may have changed).
49
+ Then use `GITHUB_CREATE_OR_UPDATE_FILE_CONTENTS` with that SHA to write.
50
+ For new files (not in Phase 1 listing), omit the SHA parameter.
51
+
52
+ ### 2.1 — CRITIC.md
53
+
54
+ Replace with the universal v2 rubric. The file must contain:
55
+ - A title: `Full-Stack Critic — Universal Engineering Standard`
56
+ - A Modes table with 6 rows: REVIEW, FIX, OPTIMIZE, CLEAN, VERIFY, FULL
57
+ - A "12 Dimensions" section with all 12 headings numbered 1–12:
58
+ 1. Correctness and data integrity
59
+ 2. Security
60
+ 3. Architecture and design
61
+ 4. API design
62
+ 5. Backend and services
63
+ 6. Database and persistence
64
+ 7. Frontend and UI
65
+ 8. Performance and scalability
66
+ 9. Code quality and maintainability
67
+ 10. Testing and observability
68
+ 11. Deployment and infrastructure
69
+ 12. Enterprise-grade practices
70
+ - A "Non-negotiable rules" section
71
+ - A "Report format" section with:
72
+ - Executive Summary table (12 rows + overall)
73
+ - Findings block using `### SEVERITY — Title` format (no pipe/bracket syntax)
74
+ - Fix Summary table
75
+ - Scalability Assessment
76
+ - Prioritised Action Plan table
77
+ - Final Verdict (4 choices)
78
+
79
+ Do not include the broken `[BLOCKER|CRITICAL|HIGH|MEDIUM|LOW|INFO]` heading format.
80
+ Use `### SEVERITY — Title` (em dash, not pipe) for finding headings.
81
+
82
+ ### 2.2 — CLAUDE.md
83
+
84
+ Replace with content covering:
85
+ - Read order: CRITIC.md → PROJECT_REVIEW.md → AGENTS.md
86
+ - Zero-impact attach instructions (submodule + clone commands)
87
+ - Claude Code tool discipline: Glob before read, Grep before file open,
88
+ Read with line ranges, never load build/vendor dirs
89
+ - TodoWrite per finding, TodoRead before layer switch
90
+ - Layer discipline: one layer at a time, 15-file cap, summarise before switch
91
+ - FIX mode: smallest diff, state verification step, record in Fix Summary
92
+ - Memory bootstrap from `memory/templates/`
93
+
94
+ ### 2.3 — GEMINI.md
95
+
96
+ Replace with content covering:
97
+ - Read order: CRITIC.md → PROJECT_REVIEW.md → AGENTS.md
98
+ - Zero-impact attach instructions (submodule + clone commands + `@` usage)
99
+ - Gemini CLI tool discipline: `@` references for layer files,
100
+ never read directories, use grep/find/rg first
101
+ - Load priority when context constrained: Routes → Middleware → Schema → Service → Repository → Tests
102
+ - Layer discipline: one layer, evidence required for second, scratchpad before switch
103
+ - FIX mode: minimal unified diff, state exact verification command
104
+ - Memory bootstrap from `memory/templates/`
105
+
106
+ ### 2.4 — AGENTS.md
107
+
108
+ Replace with content covering:
109
+ - What this tool is (one paragraph — free, universal, any AI, no setup)
110
+ - Zero-impact attach instructions (submodule, clone, or copy minimum files)
111
+ - Modes table with 6 rows and example commands per mode
112
+ - Quick start section with copy-paste prompts for: generic, Claude Code, Gemini CLI, Composio
113
+ - 8 rules that apply to every AI and every mode
114
+
115
+ ### 2.5 — agent/fullstack-critic-agent.md
116
+
117
+ Update (preserve existing structure, apply these additions):
118
+ - Add zero-impact attach instructions at the top
119
+ - Add the 6-mode table from CRITIC.md
120
+ - Update workflow to `READ → SELECT → IMPORT → WORK → STAGE → SUMMARISE → DISCARD → EXPORT → UPDATE`
121
+ - Add validation step in COMPOSIO_REMOTE_WORKBENCH before every write
122
+ - Add example requests for REVIEW, FIX, and FULL modes
123
+ - Preserve all existing Composio tool names exactly as they are
124
+
125
+ ### 2.6 — layers/manifest.md
126
+
127
+ Update the layer table to add these rows if not already present:
128
+ - `api-client` — how a frontend or service calls an API
129
+ - `auth` — authentication, authorisation, sessions, tokens
130
+ - `architecture` — system design, module boundaries, dependency graph
131
+ - `transactional` — orders, payments, reservations, inventory, state machines
132
+
133
+ Update the dependency rules table to include:
134
+ - `transactional` → `database`
135
+ - `transactional` → `cache-queue`
136
+ - `auth` → `api`
137
+ - `auth` → `database`
138
+
139
+ ### 2.7 — layers/transactional-domain.md (new file)
140
+
141
+ Create this file. Content must include:
142
+ - Purpose statement (universal — e-commerce, SaaS, ticketing, booking, fintech)
143
+ - Chunks to load table (7 rows)
144
+ - Checks section with subsections: Idempotency, Atomicity and race conditions,
145
+ Payment flow, State machine integrity, Inventory and capacity,
146
+ Cancellation and refunds, Notifications and side effects
147
+ - Extra report sections: Transactional Integrity Review, Payment Flow Review,
148
+ Inventory and Capacity Review, State Machine Review
149
+
150
+ ### 2.8 — PROJECT_REVIEW.md (new file at repo root)
151
+
152
+ Create universal template. Fields required:
153
+ - Project name and purpose (comment explaining it)
154
+ - Tech stack table (Language, Framework, Database, Cache, Queue, Auth, Hosting, CI/CD, Monitoring)
155
+ - Entry points (commented example)
156
+ - Review mode (comment: REVIEW | FIX | OPTIMIZE | CLEAN | VERIFY | FULL)
157
+ - Review focus (comment with specific and general examples)
158
+ - Scale requirement (comment: write "unknown" not guesses)
159
+ - Known risks (commented examples)
160
+ - Out of scope (commented examples)
161
+ - Prior incidents (commented example)
162
+ - Load test status (comment explaining the AI will not claim targets without this)
163
+
164
+ ### 2.9 — memory/templates/ (4 files — create if missing, do not overwrite if content exists)
165
+
166
+ Check each file. If it exists and has content beyond the header, skip it.
167
+ If it is missing or empty, create it with:
168
+
169
+ **PROJECT_PROFILE.md** — System fields, Entry points, Architecture summary, Known constraints, Scale context
170
+ **DECISIONS.md** — Entry format block, divider, "AI writes below" comment
171
+ **REVIEW_HISTORY.md** — Entry format block, divider, "AI writes below" comment
172
+ **RUN_STATE.md** — Current session fields, Layer history section, Staged findings section, Pending layer switches section
173
+
174
+ ### 2.10 — prompts/composio-upgrade-agent.md
175
+
176
+ Replace with the current contents of this file (this prompt itself, updated).
177
+ This ensures the upgrade prompt is always in sync with the repo.
178
+
179
+ ---
180
+
181
+ ## Phase 3 — Validate (in COMPOSIO_REMOTE_WORKBENCH)
182
+
183
+ Run these checks in Python before committing. Fail loudly on any check that fails.
184
+
185
+ ```python
186
+ import re
187
+
188
+ files_to_check = {
189
+ "CRITIC.md": [
190
+ "Full-Stack Critic",
191
+ "## Modes",
192
+ "REVIEW", "FIX", "OPTIMIZE", "CLEAN", "VERIFY", "FULL",
193
+ "1. Correctness",
194
+ "2. Security",
195
+ "3. Architecture",
196
+ "4. API design",
197
+ "5. Backend",
198
+ "6. Database",
199
+ "7. Frontend",
200
+ "8. Performance",
201
+ "9. Code quality",
202
+ "10. Testing",
203
+ "11. Deployment",
204
+ "12. Enterprise",
205
+ "SEVERITY — Title", # correct heading format
206
+ "Final Verdict",
207
+ ],
208
+ "AGENTS.md": [
209
+ "zero impact", "running sessions",
210
+ "REVIEW", "FIX", "FULL",
211
+ "8 rules",
212
+ ],
213
+ "layers/manifest.md": [
214
+ "transactional", "api-client", "auth", "architecture",
215
+ ],
216
+ "layers/transactional-domain.md": [
217
+ "Idempotency", "Atomicity", "Payment flow",
218
+ "State machine", "Inventory", "Cancellation",
219
+ ],
220
+ }
221
+
222
+ forbidden_terms = ["ItaliaRail", "rail booking", "booking-domain", "[BLOCKER|CRITICAL"]
223
+
224
+ all_passed = True
225
+ for filename, required_strings in files_to_check.items():
226
+ content = open(filename).read()
227
+ for term in required_strings:
228
+ if term.lower() not in content.lower():
229
+ print(f"FAIL: '{term}' not found in {filename}")
230
+ all_passed = False
231
+ for bad in forbidden_terms:
232
+ if bad in content:
233
+ print(f"FAIL: forbidden term '{bad}' found in {filename}")
234
+ all_passed = False
235
+
236
+ if all_passed:
237
+ print("ALL VALIDATION CHECKS PASSED — safe to commit")
238
+ else:
239
+ print("VALIDATION FAILED — do not commit")
240
+ ```
241
+
242
+ If any check fails: fix the specific file, re-validate, then proceed.
243
+ Do not commit until all checks pass.
244
+
245
+ ---
246
+
247
+ ## Phase 4 — Commit
248
+
249
+ Single commit to `main` with this exact message:
250
+
251
+ ```
252
+ v2: universal holistic critic — 12 dimensions, 6 modes, fix-capable, all AI runtimes
253
+
254
+ Changes:
255
+ - CRITIC.md: 12 universal dimensions, 6 modes (REVIEW/FIX/OPTIMIZE/CLEAN/VERIFY/FULL), fixed heading format
256
+ - CLAUDE.md: Claude Code tool discipline, zero-impact attach, memory bootstrap
257
+ - GEMINI.md: Gemini CLI @ reference discipline, zero-impact attach, load priority
258
+ - AGENTS.md: universal entry point, 8 rules, quick start for all AI runtimes
259
+ - agent/fullstack-critic-agent.md: updated workflow, validation step, example requests
260
+ - layers/manifest.md: api-client, auth, architecture, transactional layers added
261
+ - layers/transactional-domain.md: new universal domain layer (payments/orders/inventory)
262
+ - PROJECT_REVIEW.md: universal project context template
263
+ - memory/templates/: all four session memory templates bootstrapped
264
+ - prompts/composio-upgrade-agent.md: self-updating upgrade prompt
265
+ ```
266
+
267
+ ---
268
+
269
+ ## Phase 5 — Final report
270
+
271
+ After commit, output:
272
+
273
+ ```
274
+ UPGRADE COMPLETE
275
+ ================
276
+ Commit SHA: <sha>
277
+ Branch: main
278
+
279
+ Files updated:
280
+ - CRITIC.md — 12 dimensions, 6 modes, fixed headings
281
+ - CLAUDE.md — Claude Code discipline + zero-impact attach
282
+ - GEMINI.md — Gemini CLI discipline + zero-impact attach
283
+ - AGENTS.md — universal entry point for all AI runtimes
284
+ - agent/fullstack-critic-agent.md — updated Composio contract
285
+ - layers/manifest.md — 4 new layers added
286
+ - layers/transactional-domain.md — NEW
287
+ - PROJECT_REVIEW.md — NEW
288
+ - memory/templates/PROJECT_PROFILE.md — NEW or preserved
289
+ - memory/templates/DECISIONS.md — NEW or preserved
290
+ - memory/templates/REVIEW_HISTORY.md — NEW or preserved
291
+ - memory/templates/RUN_STATE.md — NEW or preserved
292
+ - prompts/composio-upgrade-agent.md — updated
293
+
294
+ Files skipped (if any):
295
+ - <file> — reason
296
+
297
+ Validation: ALL CHECKS PASSED
298
+ Ready for public use: YES
299
+ ```
@@ -0,0 +1,228 @@
1
+ 'use strict';
2
+ /**
3
+ * Analyzer: the engine that produces a finding set + coverage proof for a project.
4
+ * Guarantees "100% of resources" by walking every non-ignored, non-binary file,
5
+ * parsing every dependency manifest, and explicitly reporting what (and why) was skipped.
6
+ */
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ const { walk, readText, exists } = require('./util');
10
+ const { LINE_RULES } = require('./rules');
11
+ const { analyzeDeps } = require('./deps');
12
+
13
+ const SEVERITY_RANK = { BLOCKER: 0, CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3, INFO: 4 };
14
+ const CAP_PER_RULE = 12; // cap per (rule,file) to avoid flooding; extra collapsed into a summary finding
15
+
16
+ const DIMENSIONS = [
17
+ 'Correctness', 'Security', 'Architecture', 'API design', 'Backend', 'Database',
18
+ 'Frontend', 'Performance', 'Code quality', 'Testing', 'Infrastructure', 'Enterprise practices',
19
+ ];
20
+
21
+ function lineOf(text, index) {
22
+ let line = 1;
23
+ for (let i = 0; i < index; i++) if (text.charCodeAt(i) === 10) line++;
24
+ return line;
25
+ }
26
+
27
+ function snippet(lineText) {
28
+ const t = lineText.replace(/\s+/g, ' ').trim();
29
+ return t.length > 160 ? t.slice(0, 157) + '...' : t;
30
+ }
31
+
32
+ function ruleApplies(rule, ext) {
33
+ if (!rule.ext) return true;
34
+ return rule.ext.includes(ext.toLowerCase());
35
+ }
36
+
37
+ function scanFile(file) {
38
+ const findings = [];
39
+ const text = readText(file.abs);
40
+ if (text == null) return { findings, lines: 0, scannable: false };
41
+ const lines = text.split(/\r?\n/);
42
+ // Whole-file suppression: a `critic-ignore-file` marker in the header (for rule-definition files).
43
+ if (/critic-ignore-file/.test(lines.slice(0, 2).join('\n'))) return { findings, lines: lines.length, scannable: true };
44
+ const perRule = {};
45
+
46
+ for (const rule of LINE_RULES) {
47
+ if (!ruleApplies(rule, file.ext)) continue;
48
+ perRule[rule.id] = 0;
49
+ for (let i = 0; i < lines.length; i++) {
50
+ const line = lines[i];
51
+ if (!rule.test.test(line)) continue;
52
+ if (/critic-ignore/.test(line)) continue; // inline suppression: `// critic-ignore` (like eslint-disable-line)
53
+ if (rule.skipIf && rule.skipIf(line)) continue;
54
+ perRule[rule.id]++;
55
+ if (perRule[rule.id] > CAP_PER_RULE) continue;
56
+ findings.push({
57
+ ruleId: rule.id,
58
+ severity: rule.severity,
59
+ category: rule.category,
60
+ dimension: rule.dimension,
61
+ title: rule.title,
62
+ file: file.rel,
63
+ line: i + 1,
64
+ problem: rule.problem,
65
+ evidence: snippet(line),
66
+ impact: rule.impact,
67
+ fix: rule.fix,
68
+ verify: rule.verify,
69
+ confirmed: true,
70
+ });
71
+ }
72
+ }
73
+ // Collapse overflow into one summary finding per rule.
74
+ for (const [id, count] of Object.entries(perRule)) {
75
+ if (count > CAP_PER_RULE) {
76
+ const rule = LINE_RULES.find((r) => r.id === id);
77
+ findings.push({
78
+ ruleId: id + ':overflow', severity: rule.severity, category: rule.category,
79
+ dimension: rule.dimension, title: `${rule.title} (and ${count - CAP_PER_RULE} more)`,
80
+ file: file.rel, line: 1, problem: rule.problem, evidence: `${count} matches in this file`,
81
+ impact: rule.impact, fix: rule.fix, verify: rule.verify, confirmed: true,
82
+ });
83
+ }
84
+ }
85
+ // Long-file signal.
86
+ if (lines.length > 500) {
87
+ findings.push({
88
+ ruleId: 'long-file', severity: 'LOW', category: 'optimize', dimension: 'Architecture',
89
+ title: `Large file (${lines.length} lines)`, file: file.rel, line: 1,
90
+ problem: 'Files this large usually hold more than one responsibility.',
91
+ evidence: `${lines.length} lines`, impact: 'Harder to test, review, and reason about.',
92
+ fix: 'Split along responsibility boundaries.', verify: 'each module has one reason to change', confirmed: true,
93
+ });
94
+ }
95
+ return { findings, lines: lines.length, scannable: true };
96
+ }
97
+
98
+ function structuralFindings(root, files, ctx) {
99
+ const F = [];
100
+ const rels = new Set(files.map((f) => f.rel));
101
+ const hasPkg = rels.has('package.json');
102
+ const isPython = files.some((f) => f.ext === '.py');
103
+ const isGo = rels.has('go.mod');
104
+ const isRust = rels.has('Cargo.toml');
105
+
106
+ // Committed .env (real, not the .example) — secrets dimension.
107
+ for (const f of files) {
108
+ if (/(^|\/)\.env(\.|$)/.test(f.rel) && !/(example|sample|template|dist|local\.example)/.test(f.rel)) {
109
+ F.push({
110
+ ruleId: 'env-committed', severity: 'HIGH', category: 'fix', dimension: 'Security',
111
+ title: 'Environment file checked into the tree', file: f.rel, line: 1,
112
+ problem: '.env-style files frequently hold live secrets and are committed by accident.',
113
+ evidence: f.rel, impact: 'Credential exposure via repository access.',
114
+ fix: 'Add to .gitignore, remove from history, move config to a secret manager.',
115
+ verify: 'file no longer tracked; secrets rotated.', confirmed: 'suspected',
116
+ });
117
+ }
118
+ }
119
+
120
+ // Test presence (any mainstream language).
121
+ const testPat = /(^|\/)(tests?|__tests__|spec)\//i;
122
+ const testFilePat = /(\.test\.|\.spec\.|_test\.(go|py)|test_.*\.py|Tests\.cs|Test[A-Za-z0-9]+\.java)$/i;
123
+ const hasTests = files.some((f) => testPat.test(f.rel) || testFilePat.test(f.rel));
124
+ const codeFiles = files.filter((f) => ['.js', '.ts', '.tsx', '.jsx', '.py', '.go', '.rb', '.java', '.rs', '.php', '.cs'].includes(f.ext));
125
+ if (codeFiles.length >= 5 && !hasTests) {
126
+ F.push({
127
+ ruleId: 'no-tests', severity: 'HIGH', category: 'add', dimension: 'Testing',
128
+ title: 'No test files detected anywhere in the project', file: '.', line: 1,
129
+ problem: `${codeFiles.length} source files exist but no unit/integration/e2e tests were found.`,
130
+ evidence: 'no tests/, spec/, *.test.*, *_test.go, test_*.py', impact: 'Every change is shipped unverified; regressions go live.',
131
+ fix: 'Add a runner and cover the highest-risk paths first (auth, payments, data mutations).',
132
+ verify: hasPkg ? 'npm test' : (isGo ? 'go test ./...' : (isRust ? 'cargo test' : (isPython ? 'pytest' : 'the suite runs green'))), confirmed: true,
133
+ });
134
+ }
135
+
136
+ // CI presence.
137
+ const hasCI = files.some((f) => f.rel.startsWith('.github/workflows/') || /(^|\/)(\.gitlab-ci\.yml|Jenkinsfile|\.circleci\/|azure-pipelines\.yml|\.travis\.yml|bitbucket-pipelines\.yml)$/.test(f.rel));
138
+ if (!hasCI) {
139
+ F.push({
140
+ ruleId: 'no-ci', severity: 'MEDIUM', category: 'add', dimension: 'Infrastructure',
141
+ title: 'No CI pipeline found', file: '.', line: 1,
142
+ problem: 'No lint/test/build/security-scan automation gate was detected.',
143
+ evidence: 'no .github/workflows, .gitlab-ci.yml, Jenkinsfile, etc.', impact: 'Quality depends on manual discipline; bad deploys are not blocked.',
144
+ fix: 'Add a pipeline that runs lint, type-check, tests, and a dependency audit and blocks on failure.',
145
+ verify: 'a failing test blocks the pipeline.', confirmed: true,
146
+ });
147
+ }
148
+
149
+ // Config discipline: any committed env-ish secret filenames.
150
+ // Documentation / onboarding.
151
+ const hasReadme = files.some((f) => /(^|\/)README\.(md|rst|txt)$/i.test(f.rel));
152
+ if (!hasReadme && files.length > 3) {
153
+ F.push({
154
+ ruleId: 'no-readme', severity: 'LOW', category: 'add', dimension: 'Enterprise practices',
155
+ title: 'No README found', file: '.', line: 1, problem: 'No onboarding or run instructions.',
156
+ evidence: 'README.* absent', impact: 'Slower onboarding; undocumented setup.',
157
+ fix: 'Add purpose, stack, setup, run, and test instructions.', verify: 'a new contributor can run the app from README alone.', confirmed: true,
158
+ });
159
+ }
160
+ return F;
161
+ }
162
+
163
+ function summarizeDimensions(all) {
164
+ const touched = new Set(all.map((f) => f.dimension));
165
+ return DIMENSIONS.map((d) => ({ dimension: d, touched: touched.has(d) }));
166
+ }
167
+
168
+ function analyze(root, opts = {}) {
169
+ const started = Date.now();
170
+ const { files, skipped } = walk(root);
171
+ const findings = [];
172
+ let totalLines = 0;
173
+ let scanned = 0;
174
+ const byExt = {};
175
+
176
+ if (!opts.skipCode) {
177
+ for (const f of files) {
178
+ byExt[f.ext || '(none)'] = (byExt[f.ext || '(none)'] || 0) + 1;
179
+ const r = scanFile(f);
180
+ totalLines += r.lines;
181
+ if (r.scannable) scanned++;
182
+ findings.push(...r.findings);
183
+ }
184
+ }
185
+
186
+ // Dependency + package-manifest analysis (100% of ecosystems present).
187
+ const dep = analyzeDeps(root);
188
+ findings.push(...dep.findings);
189
+
190
+ // Project-wide structural checks.
191
+ findings.push(...structuralFindings(root, files, { hasPkg: exists(path.join(root, 'package.json')) }));
192
+
193
+ findings.sort((a, b) =>
194
+ (SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity]) ||
195
+ String(a.file).localeCompare(String(b.file)) || (a.line - b.line));
196
+
197
+ const counts = { BLOCKER: 0, CRITICAL: 0, HIGH: 0, MEDIUM: 0, LOW: 0, INFO: 0 };
198
+ const cats = { fix: 0, optimize: 0, delete: 0, add: 0 };
199
+ for (const f of findings) { counts[f.severity] = (counts[f.severity] || 0) + 1; cats[f.category] = (cats[f.category] || 0) + 1; }
200
+
201
+ return {
202
+ meta: {
203
+ root: path.resolve(root),
204
+ started,
205
+ durationMs: Date.now() - started,
206
+ cliVersion: readVersion(),
207
+ },
208
+ coverage: {
209
+ filesScanned: scanned,
210
+ filesSkipped: skipped.length,
211
+ totalLines,
212
+ byExt,
213
+ skipped: skipped.slice(0, 50),
214
+ dimensions: summarizeDimensions(findings),
215
+ },
216
+ dependencies: dep.ecosystems,
217
+ counts,
218
+ categoryCounts: cats,
219
+ findings,
220
+ };
221
+ }
222
+
223
+ function readVersion() {
224
+ try { return JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')).version; }
225
+ catch { return '0.0.0'; }
226
+ }
227
+
228
+ module.exports = { analyze, DIMENSIONS, SEVERITY_RANK };