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.
- package/AGENTS.md +92 -0
- package/CLAUDE.md +59 -0
- package/CRITIC.md +230 -0
- package/GEMINI.md +58 -0
- package/LICENSE +21 -0
- package/PROJECT_REVIEW.md +92 -0
- package/README.md +227 -0
- package/agent/fullstack-critic-agent.md +125 -0
- package/bin/fullstack-critic.js +4 -0
- package/docs/GETTING_STARTED.md +145 -0
- package/layers/manifest.md +68 -0
- package/layers/transactional-domain.md +88 -0
- package/memory/templates/DECISIONS.md +20 -0
- package/memory/templates/PROJECT_PROFILE.md +45 -0
- package/memory/templates/REVIEW_HISTORY.md +24 -0
- package/memory/templates/RUN_STATE.md +40 -0
- package/package.json +55 -0
- package/prompts/composio-upgrade-agent.md +299 -0
- package/src/analyzer.js +228 -0
- package/src/cli.js +163 -0
- package/src/deps.js +200 -0
- package/src/index.js +18 -0
- package/src/init.js +80 -0
- package/src/report.js +171 -0
- package/src/rules.js +182 -0
- package/src/util.js +99 -0
- package/src/watcher.js +127 -0
- package/tests/critic.test.js +95 -0
|
@@ -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
|
+
```
|
package/src/analyzer.js
ADDED
|
@@ -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 };
|