@codacy/gate-cli 0.2.0 → 0.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.
- package/bin/gate.js +132 -1
- package/data/skills/gate-analyze/SKILL.md +214 -0
- package/data/skills/gate-feedback/SKILL.md +12 -0
- package/data/skills/gate-setup/SKILL.md +402 -0
- package/data/skills/gate-setup/patterns-reference.yaml +586 -0
- package/data/skills/gate-setup/standard-template.yaml +141 -0
- package/data/skills/gate-status/SKILL.md +45 -0
- package/package.json +2 -1
package/bin/gate.js
CHANGED
|
@@ -12093,8 +12093,138 @@ async function runReview(opts, globals) {
|
|
|
12093
12093
|
process.exit(0);
|
|
12094
12094
|
}
|
|
12095
12095
|
|
|
12096
|
+
// src/commands/init.ts
|
|
12097
|
+
var import_node_fs9 = require("node:fs");
|
|
12098
|
+
var import_promises8 = require("node:fs/promises");
|
|
12099
|
+
var import_node_path6 = require("node:path");
|
|
12100
|
+
var import_node_child_process5 = require("node:child_process");
|
|
12101
|
+
function resolveDataDir() {
|
|
12102
|
+
const candidates = [
|
|
12103
|
+
(0, import_node_path6.join)(__dirname, "..", "data"),
|
|
12104
|
+
// installed: node_modules/@codacy/gate-cli/data
|
|
12105
|
+
(0, import_node_path6.join)(__dirname, "..", "..", "data"),
|
|
12106
|
+
// edge case: nested resolution
|
|
12107
|
+
(0, import_node_path6.join)(process.cwd(), "cli", "data")
|
|
12108
|
+
// local dev: running from repo root
|
|
12109
|
+
];
|
|
12110
|
+
for (const candidate of candidates) {
|
|
12111
|
+
if ((0, import_node_fs9.existsSync)((0, import_node_path6.join)(candidate, "skills"))) {
|
|
12112
|
+
return candidate;
|
|
12113
|
+
}
|
|
12114
|
+
}
|
|
12115
|
+
throw new Error(
|
|
12116
|
+
"Could not find GATE.md skill data. Reinstall: npm install -g @codacy/gate-cli"
|
|
12117
|
+
);
|
|
12118
|
+
}
|
|
12119
|
+
async function copyDir(src, dest) {
|
|
12120
|
+
await (0, import_promises8.mkdir)(dest, { recursive: true });
|
|
12121
|
+
await (0, import_promises8.cp)(src, dest, { recursive: true, force: true });
|
|
12122
|
+
}
|
|
12123
|
+
function registerInitCommand(program2) {
|
|
12124
|
+
program2.command("init").description("Initialize GATE.md in the current project").option("--force", "Overwrite existing skills and hooks").action(async (opts) => {
|
|
12125
|
+
const force = opts.force ?? false;
|
|
12126
|
+
const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
|
|
12127
|
+
const isProject = projectMarkers.some((m) => (0, import_node_fs9.existsSync)(m));
|
|
12128
|
+
if (!isProject) {
|
|
12129
|
+
printError("No project detected in the current directory.");
|
|
12130
|
+
printInfo('Run "gate init" from your project root.');
|
|
12131
|
+
process.exit(1);
|
|
12132
|
+
}
|
|
12133
|
+
console.log("");
|
|
12134
|
+
printInfo("Initializing GATE.md in this project...");
|
|
12135
|
+
console.log("");
|
|
12136
|
+
printInfo("Checking prerequisites...");
|
|
12137
|
+
const nodeVersion = process.version;
|
|
12138
|
+
const nodeMajor = parseInt(nodeVersion.slice(1), 10);
|
|
12139
|
+
if (nodeMajor < 20) {
|
|
12140
|
+
printError(`Node.js 20+ required (found ${nodeVersion}). Update from https://nodejs.org`);
|
|
12141
|
+
process.exit(1);
|
|
12142
|
+
}
|
|
12143
|
+
printInfo(` Node.js ${nodeVersion} \u2713`);
|
|
12144
|
+
try {
|
|
12145
|
+
const gitVersion = (0, import_node_child_process5.execSync)("git --version", { encoding: "utf-8" }).trim();
|
|
12146
|
+
printInfo(` ${gitVersion} \u2713`);
|
|
12147
|
+
} catch {
|
|
12148
|
+
printError("git is required but not installed. Install from https://git-scm.com");
|
|
12149
|
+
process.exit(1);
|
|
12150
|
+
}
|
|
12151
|
+
try {
|
|
12152
|
+
(0, import_node_child_process5.execSync)("which claude", { encoding: "utf-8" });
|
|
12153
|
+
printInfo(" Claude Code \u2713");
|
|
12154
|
+
} catch {
|
|
12155
|
+
printWarn(" Claude Code not found \u2014 hooks will be configured but need Claude Code to run.");
|
|
12156
|
+
}
|
|
12157
|
+
try {
|
|
12158
|
+
(0, import_node_child_process5.execSync)("which codacy-analysis", { encoding: "utf-8" });
|
|
12159
|
+
printInfo(" @codacy/analysis-cli \u2713");
|
|
12160
|
+
} catch {
|
|
12161
|
+
printWarn(" @codacy/analysis-cli not found \u2014 static analysis will be unavailable.");
|
|
12162
|
+
printWarn(" Install: npm install -g @codacy/analysis-cli");
|
|
12163
|
+
}
|
|
12164
|
+
console.log("");
|
|
12165
|
+
printInfo("Installing skills...");
|
|
12166
|
+
const dataDir = resolveDataDir();
|
|
12167
|
+
const skillsSource = (0, import_node_path6.join)(dataDir, "skills");
|
|
12168
|
+
const skillsDest = ".claude/skills";
|
|
12169
|
+
const skills = ["gate-setup", "gate-analyze", "gate-status", "gate-feedback"];
|
|
12170
|
+
let skillsInstalled = 0;
|
|
12171
|
+
for (const skill of skills) {
|
|
12172
|
+
const src = (0, import_node_path6.join)(skillsSource, skill);
|
|
12173
|
+
const dest = (0, import_node_path6.join)(skillsDest, skill);
|
|
12174
|
+
if (!(0, import_node_fs9.existsSync)(src)) {
|
|
12175
|
+
printWarn(` Skill data not found: ${skill}`);
|
|
12176
|
+
continue;
|
|
12177
|
+
}
|
|
12178
|
+
if ((0, import_node_fs9.existsSync)(dest) && !force) {
|
|
12179
|
+
const srcSkill = (0, import_node_path6.join)(src, "SKILL.md");
|
|
12180
|
+
const destSkill = (0, import_node_path6.join)(dest, "SKILL.md");
|
|
12181
|
+
if ((0, import_node_fs9.existsSync)(destSkill)) {
|
|
12182
|
+
try {
|
|
12183
|
+
const srcContent = await (0, import_promises8.readFile)(srcSkill, "utf-8");
|
|
12184
|
+
const destContent = await (0, import_promises8.readFile)(destSkill, "utf-8");
|
|
12185
|
+
if (srcContent === destContent) {
|
|
12186
|
+
skillsInstalled++;
|
|
12187
|
+
continue;
|
|
12188
|
+
}
|
|
12189
|
+
} catch {
|
|
12190
|
+
}
|
|
12191
|
+
}
|
|
12192
|
+
}
|
|
12193
|
+
await copyDir(src, dest);
|
|
12194
|
+
skillsInstalled++;
|
|
12195
|
+
}
|
|
12196
|
+
printInfo(` ${skillsInstalled}/${skills.length} skills installed to .claude/skills/ \u2713`);
|
|
12197
|
+
printInfo("Wiring Claude Code hooks...");
|
|
12198
|
+
const settings = await readSettings();
|
|
12199
|
+
const hookResult = installGateHooks(settings, force);
|
|
12200
|
+
if (hookResult.ok) {
|
|
12201
|
+
await writeSettings(hookResult.data);
|
|
12202
|
+
printInfo(" Stop hook: gate analyze \u2713");
|
|
12203
|
+
printInfo(" Intent hook: gate intent capture \u2713");
|
|
12204
|
+
} else {
|
|
12205
|
+
printWarn(` ${hookResult.error}`);
|
|
12206
|
+
printInfo(' Run "gate hooks install --force" to overwrite.');
|
|
12207
|
+
}
|
|
12208
|
+
await (0, import_promises8.mkdir)(GATE_DIR, { recursive: true });
|
|
12209
|
+
const globalGateDir = (0, import_node_path6.join)(process.env.HOME ?? "", ".gate");
|
|
12210
|
+
await (0, import_promises8.mkdir)(globalGateDir, { recursive: true });
|
|
12211
|
+
console.log("");
|
|
12212
|
+
printInfo("GATE.md initialized!");
|
|
12213
|
+
console.log("");
|
|
12214
|
+
console.log(" Installed:");
|
|
12215
|
+
console.log(" .claude/skills/gate-setup/ \u2014 project configuration");
|
|
12216
|
+
console.log(" .claude/skills/gate-analyze/ \u2014 on-demand analysis");
|
|
12217
|
+
console.log(" .claude/skills/gate-status/ \u2014 project health");
|
|
12218
|
+
console.log(" .claude/skills/gate-feedback/ \u2014 beta feedback");
|
|
12219
|
+
console.log(" .claude/settings.json \u2014 hooks (gate analyze + intent capture)");
|
|
12220
|
+
console.log("");
|
|
12221
|
+
console.log(" Next step: open this project in Claude Code and run /gate-setup");
|
|
12222
|
+
console.log("");
|
|
12223
|
+
});
|
|
12224
|
+
}
|
|
12225
|
+
|
|
12096
12226
|
// src/cli.ts
|
|
12097
|
-
program.name("gate").description("CLI for GATE.md quality gate service").version("0.
|
|
12227
|
+
program.name("gate").description("CLI for GATE.md quality gate service").version("0.3.0").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr");
|
|
12098
12228
|
registerAuthCommands(program);
|
|
12099
12229
|
registerHooksCommands(program);
|
|
12100
12230
|
registerIntentCommands(program);
|
|
@@ -12104,4 +12234,5 @@ registerStatusCommand(program);
|
|
|
12104
12234
|
registerFeedbackCommand(program);
|
|
12105
12235
|
registerAnalyzeCommand(program);
|
|
12106
12236
|
registerReviewCommand(program);
|
|
12237
|
+
registerInitCommand(program);
|
|
12107
12238
|
program.parse();
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# /gate-analyze — Run GATE.md analysis on demand
|
|
2
|
+
|
|
3
|
+
You are running an on-demand GATE.md analysis. This is like a "second opinion" — the user (or you) can invoke it at any point to get a quality and security review with rich context.
|
|
4
|
+
|
|
5
|
+
**Usage:**
|
|
6
|
+
- `/gate-analyze` — Curate files, write intent, select specs autonomously (deep review)
|
|
7
|
+
- `/gate-analyze src/api/routes.ts src/db.ts` — Analyze specific files (quick check)
|
|
8
|
+
- `/gate-analyze src/api/` — Analyze a directory and related files
|
|
9
|
+
- `/gate-analyze --full` — Sample the full codebase (20 most recently modified files)
|
|
10
|
+
- `/gate-analyze --intent "Implementing OAuth2 PKCE flow per spec/AUTH.md"` — User provides intent
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Step 1: Check configuration
|
|
15
|
+
|
|
16
|
+
1. Verify `.gate/standard.yaml` exists. If not: "Run `/gate-setup` first."
|
|
17
|
+
2. Verify `gate` CLI is available: `which gate`. If not: "Re-run the GATE.md installer: `curl -fsSL https://raw.githubusercontent.com/codacy/gatemd/main/install.sh | bash`"
|
|
18
|
+
3. Run `gate auth verify` to check token is valid. If it fails: "GATE.md not configured. Run `/gate-setup` first."
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Step 2: Determine files to analyze
|
|
23
|
+
|
|
24
|
+
### Quick mode (specific files or `--full`)
|
|
25
|
+
|
|
26
|
+
**Specific files** (paths or directories provided):
|
|
27
|
+
- Use the provided file paths directly. If a directory is given, include all analyzable files under it.
|
|
28
|
+
- Verify each file exists.
|
|
29
|
+
|
|
30
|
+
**`--full` flag** (full codebase sample):
|
|
31
|
+
```bash
|
|
32
|
+
FILES=$(git ls-files | grep -E '\.(ts|tsx|js|jsx|mjs|cjs|py|go|java|kt|rb|rs|c|cpp|h|hpp|cs|php)$')
|
|
33
|
+
```
|
|
34
|
+
If more than 20 files, select the 20 most recently modified:
|
|
35
|
+
```bash
|
|
36
|
+
echo "$FILES" | xargs ls -t | head -20
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
In quick mode, skip Steps 3 and 4 — go straight to Step 5.
|
|
40
|
+
|
|
41
|
+
### Deep mode (no arguments, or directory hints)
|
|
42
|
+
|
|
43
|
+
When no specific files are given, curate a comprehensive review scope:
|
|
44
|
+
|
|
45
|
+
#### 2a. Start with changed files
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
CHANGED=$(git diff --name-only HEAD 2>/dev/null; git diff --name-only --cached 2>/dev/null; git ls-files --others --exclude-standard 2>/dev/null)
|
|
49
|
+
CHANGED_CODE=$(echo "$CHANGED" | sort -u | grep -E '\.(ts|tsx|js|jsx|mjs|cjs|py|go|java|kt|rb|rs|c|cpp|h|hpp|cs|php)$')
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Also check if the agent just committed (clean tree but recent HEAD):
|
|
53
|
+
```bash
|
|
54
|
+
# If working tree is clean, check what the last commit changed
|
|
55
|
+
git diff --name-only HEAD~1..HEAD 2>/dev/null
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
If the user provided directory hints (e.g., `/gate-analyze src/api/`), also include all files under those directories.
|
|
59
|
+
|
|
60
|
+
#### 2b. Add related files
|
|
61
|
+
|
|
62
|
+
For each changed file, identify and add:
|
|
63
|
+
|
|
64
|
+
1. **Direct dependencies** — files that the changed file imports. Read the import/require statements and resolve the paths.
|
|
65
|
+
2. **Reverse dependencies** — files that import the changed file. Grep for the module name across the codebase.
|
|
66
|
+
3. **Type/interface files** — shared type definitions, interfaces, or schemas referenced by the changes.
|
|
67
|
+
4. **Test files** — corresponding test files for changed modules (e.g., `foo.test.ts` for `foo.ts`, `test_foo.py` for `foo.py`).
|
|
68
|
+
|
|
69
|
+
#### 2c. Prioritize and cap
|
|
70
|
+
|
|
71
|
+
Rank files by relevance:
|
|
72
|
+
1. Changed files (always included first)
|
|
73
|
+
2. Direct dependencies that define interfaces/types being used
|
|
74
|
+
3. Test files for changed code
|
|
75
|
+
4. Reverse dependencies (consumers that might break)
|
|
76
|
+
|
|
77
|
+
**Cap at 20 files total.** If you need to cut, drop reverse dependencies first, then tests.
|
|
78
|
+
|
|
79
|
+
#### 2d. Track changed vs context
|
|
80
|
+
|
|
81
|
+
Keep two lists:
|
|
82
|
+
- **`changed_files`** — the files that were actually modified (git dirty or recently committed)
|
|
83
|
+
- **`all_files`** — all files to include in the analysis (changed + context)
|
|
84
|
+
|
|
85
|
+
If no analyzable files found: "No analyzable files found."
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Step 3: Write the intent description
|
|
90
|
+
|
|
91
|
+
Synthesize the conversation context into a clear intent description. This is NOT the raw user prompt — it's your understanding of what the work is trying to achieve.
|
|
92
|
+
|
|
93
|
+
**Template:**
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
I am [adding/modifying/fixing/refactoring] [feature/component name].
|
|
97
|
+
|
|
98
|
+
Goal: [What the user asked for, or the problem being solved.]
|
|
99
|
+
|
|
100
|
+
Approach: [Key architectural decisions. What patterns are being used?]
|
|
101
|
+
|
|
102
|
+
Trade-offs: [Anything you chose deliberately that could be questioned.]
|
|
103
|
+
|
|
104
|
+
Review focus: [Specific concerns you want the reviewer to evaluate.]
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Guidelines:**
|
|
108
|
+
- Cap at 2000 characters
|
|
109
|
+
- Be specific — "adding JWT auth with RS256 to /api/users" not "adding authentication"
|
|
110
|
+
- Include trade-offs and areas of uncertainty — the reviewer will focus there
|
|
111
|
+
- If the user provided `--intent`, use it as the base and augment with your understanding
|
|
112
|
+
- Skip this step entirely in quick mode (specific files or --full)
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Step 4: Select relevant specs
|
|
117
|
+
|
|
118
|
+
Identify spec/documentation files that provide context:
|
|
119
|
+
|
|
120
|
+
- Specs referenced in the user's request
|
|
121
|
+
- Specs that define interfaces or APIs being implemented
|
|
122
|
+
- Architecture docs relevant to the area being changed
|
|
123
|
+
|
|
124
|
+
**Read each spec file and prepare for inclusion.** Limits: max 10 files, 10KB per file, 30KB total.
|
|
125
|
+
|
|
126
|
+
Common spec locations to check:
|
|
127
|
+
- `spec/*.md`, `docs/*.md`
|
|
128
|
+
- `CLAUDE.md`, `AGENTS.md`, `CONTRIBUTING.md`
|
|
129
|
+
|
|
130
|
+
If no specs are relevant, skip this step.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## Step 5: Run the analysis
|
|
135
|
+
|
|
136
|
+
Use the `gate review` command with all the context you gathered:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
gate review \
|
|
140
|
+
--files "src/auth/pkce.ts,src/auth/callback.ts,src/auth/types.ts,src/middleware/auth.ts" \
|
|
141
|
+
--changed "src/auth/pkce.ts,src/auth/callback.ts" \
|
|
142
|
+
--intent "I am adding OAuth2 PKCE authentication flow..." \
|
|
143
|
+
--specs "spec/AUTH.md,CLAUDE.md"
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Arguments:**
|
|
147
|
+
- `--files` (required): Comma-separated list of ALL files to include (changed + context)
|
|
148
|
+
- `--changed` (optional): Comma-separated list of actually-modified files (subset of --files). If omitted, all files are treated as changed.
|
|
149
|
+
- `--intent` (optional): Your synthesized intent description. Quote it.
|
|
150
|
+
- `--specs` (optional): Comma-separated list of spec file paths to include.
|
|
151
|
+
|
|
152
|
+
The CLI handles static analysis, file reading with size limits, payload construction, and the API call.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Step 6: Display results
|
|
157
|
+
|
|
158
|
+
Parse the JSON response and format clearly:
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
=== GATE.md Analysis ===
|
|
162
|
+
Gate Decision: WARN
|
|
163
|
+
Quality: 7.5/10 | Security: 8.0/10 | Overall: 7.8/10
|
|
164
|
+
Trend: stable
|
|
165
|
+
|
|
166
|
+
--- Assessment ---
|
|
167
|
+
[narrative from assessment.narrative]
|
|
168
|
+
|
|
169
|
+
--- Findings (2) ---
|
|
170
|
+
|
|
171
|
+
[HIGH] Service Role Client for Non-Admin Operations
|
|
172
|
+
File: supabase/functions/webhook/index.ts:25
|
|
173
|
+
Pattern: access-control-checks (CWE-639)
|
|
174
|
+
Fix: Limit SELECT fields to only what's needed for the notification.
|
|
175
|
+
|
|
176
|
+
[MEDIUM] File Complexity Exceeds Threshold
|
|
177
|
+
File: supabase/functions/webhook/index.ts:1
|
|
178
|
+
Pattern: comprehensibility
|
|
179
|
+
Fix: Refactor into separate handler files per event type.
|
|
180
|
+
|
|
181
|
+
--- Intent Alignment ---
|
|
182
|
+
Score: 9/10 (aligned)
|
|
183
|
+
Goal: "Add webhook handlers for Stripe events"
|
|
184
|
+
Implementation matches intent. No gaps detected.
|
|
185
|
+
|
|
186
|
+
--- Quality Dimensions ---
|
|
187
|
+
Comprehensibility: 6.5/10 — webhook file is too large
|
|
188
|
+
Modularity: 7.0/10 — good separation except webhook handler
|
|
189
|
+
Type Safety: 9.0/10 — strict TypeScript throughout
|
|
190
|
+
Test Adequacy: 5.0/10 — missing webhook handler tests
|
|
191
|
+
|
|
192
|
+
--- Pending Items ---
|
|
193
|
+
1. [HIGH] Add tests for webhook event handlers
|
|
194
|
+
2. [MEDIUM] Refactor webhook into handler modules
|
|
195
|
+
|
|
196
|
+
View full report: https://gatemd.lovable.app/runs/run-20260327-...?token=gateview_...
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Step 7: Offer to fix
|
|
202
|
+
|
|
203
|
+
- **If FAIL or findings exist**: "The analysis found N issues. Would you like me to fix them?" If yes, apply fixes from findings, then re-run (iteration 2 max). If iteration 2 still fails, report remaining issues for human review.
|
|
204
|
+
- **If WARN**: "Non-blocking issues found. Would you like me to address them?"
|
|
205
|
+
- **If PASS**: "Code meets the Standard. No issues found."
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Important notes
|
|
210
|
+
|
|
211
|
+
- This does NOT replace the automatic stop hook. The hook still fires on every agent stop.
|
|
212
|
+
- Runs are tagged with `trigger: "review"` in the database.
|
|
213
|
+
- Size limits: 20 files max, 50KB per file, 200KB total code, 2000 char intent, 30KB specs.
|
|
214
|
+
- If the service is unreachable, static analysis still runs locally.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# /gate-feedback — Send feedback to the GATE.md team
|
|
2
|
+
|
|
3
|
+
If the user provided a message (e.g., `/gate-feedback the analysis is too slow`), use their **exact words** as the message. Do NOT rephrase, expand, add context, or editorialize. Send exactly what they typed, nothing more.
|
|
4
|
+
|
|
5
|
+
If they just typed `/gate-feedback` with no message, ask: "What feedback would you like to share?"
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
gate feedback "<their exact message>"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
On success: "Thanks, feedback sent!"
|
|
12
|
+
On error: "Couldn't send feedback right now. Your message: [repeat it so it's not lost]."
|