@kb-labs/review-entry 2.14.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/README.md ADDED
@@ -0,0 +1,293 @@
1
+ # @kb-labs/review-cli
2
+
3
+ CLI commands for AI Review plugin.
4
+
5
+ ## Commands
6
+
7
+ ### `review:run`
8
+
9
+ Run code review analysis.
10
+
11
+ ```bash
12
+ # Heuristic mode (CI, fast, no LLM)
13
+ kb review run
14
+
15
+ # Full mode (heuristic + LLM)
16
+ kb review run --mode=full
17
+
18
+ # LLM-only mode (deep analysis)
19
+ kb review run --mode=llm
20
+
21
+ # Scope options
22
+ kb review run --scope=all # Entire codebase
23
+ kb review run --scope=changed # Changed files vs main (default)
24
+ kb review run --scope=staged # Staged files only
25
+
26
+ # Use preset
27
+ kb review run --preset=typescript-strict
28
+
29
+ # Custom file patterns
30
+ kb review run --files="src/**/*.ts" --files="src/**/*.tsx"
31
+
32
+ # JSON output
33
+ kb review run --json
34
+ ```
35
+
36
+ ## Review Modes
37
+
38
+ ### heuristic (CI Mode)
39
+
40
+ Fast, deterministic analysis using only heuristic engines:
41
+ - ESLint (TypeScript/JavaScript)
42
+ - Ruff (Python) - coming soon
43
+ - golangci-lint (Go) - coming soon
44
+ - Clippy (Rust) - coming soon
45
+
46
+ **Use cases:**
47
+ - CI/CD pipelines
48
+ - Pre-commit hooks
49
+ - Quick local checks
50
+
51
+ **Exit code:**
52
+ - 0: No blocker or high severity issues
53
+ - 1: Has blocker or high severity issues
54
+
55
+ ### full (Local Mode)
56
+
57
+ Comprehensive analysis with heuristic + LLM:
58
+ - All heuristic engines
59
+ - LLM analyzers for complex issues
60
+ - Cached LLM results
61
+
62
+ **Use cases:**
63
+ - Local development
64
+ - Pre-PR review
65
+ - Weekly codebase audits
66
+
67
+ **Exit code:**
68
+ - 0: No blocker issues
69
+ - 1: Has blocker issues
70
+
71
+ ### llm (Deep Analysis Mode)
72
+
73
+ LLM-only analysis for semantic issues:
74
+ - Naming conventions
75
+ - Architecture patterns
76
+ - Logic bugs
77
+ - Code smells
78
+
79
+ **Use cases:**
80
+ - Architecture reviews
81
+ - Complex refactoring
82
+ - Semantic bug detection
83
+
84
+ **Exit code:**
85
+ - 0: No blocker issues
86
+ - 1: Has blocker issues
87
+
88
+ ## Scope Options
89
+
90
+ ### all
91
+
92
+ Analyze entire codebase:
93
+ ```bash
94
+ kb review run --scope=all
95
+ ```
96
+
97
+ ### changed
98
+
99
+ Analyze files changed vs main branch (git diff):
100
+ ```bash
101
+ kb review run --scope=changed # default
102
+ ```
103
+
104
+ ### staged
105
+
106
+ Analyze only staged files (git diff --staged):
107
+ ```bash
108
+ kb review run --scope=staged
109
+ ```
110
+
111
+ ## Presets
112
+
113
+ Use predefined rule sets:
114
+
115
+ ```bash
116
+ # TypeScript strict preset
117
+ kb review run --preset=typescript-strict
118
+
119
+ # Python production preset
120
+ kb review run --preset=python-production
121
+
122
+ # Security-focused preset
123
+ kb review run --preset=security-all
124
+ ```
125
+
126
+ ## Output Formats
127
+
128
+ ### Text (default)
129
+
130
+ Human-readable output with colors:
131
+ ```
132
+ Running heuristic analysis...
133
+ ✔ Found 3 issue(s)
134
+
135
+ ┌─ Code Review ─────────────────┐
136
+ │ Summary │
137
+ │ Files: 5 │
138
+ │ Findings: 3 │
139
+ │ High: 1 │
140
+ │ Medium: 2 │
141
+ │ Engines: eslint │
142
+ │ │
143
+ │ Findings │
144
+ │ [HIGH] src/index.ts:10 - ... │
145
+ │ [MEDIUM] src/utils.ts:5 - ... │
146
+ │ [MEDIUM] src/api.ts:20 - ... │
147
+ └───────────────────────────────┘
148
+ ```
149
+
150
+ ### JSON
151
+
152
+ Machine-readable output:
153
+ ```bash
154
+ kb review run --json
155
+ ```
156
+
157
+ ```json
158
+ {
159
+ "findings": [
160
+ {
161
+ "id": "eslint:src/index.ts:no-unused-vars:10:5",
162
+ "ruleId": "no-unused-vars",
163
+ "type": "code-quality",
164
+ "severity": "medium",
165
+ "confidence": "certain",
166
+ "file": "src/index.ts",
167
+ "line": 10,
168
+ "message": "'foo' is defined but never used.",
169
+ "engine": "eslint",
170
+ "source": "heuristic"
171
+ }
172
+ ],
173
+ "metadata": {
174
+ "mode": "heuristic",
175
+ "scope": "changed",
176
+ "analyzedFiles": 5,
177
+ "duration": 1234,
178
+ "engines": ["eslint"],
179
+ "timestamp": "2025-01-21T12:00:00Z"
180
+ }
181
+ }
182
+ ```
183
+
184
+ ## Examples
185
+
186
+ ### CI Pipeline
187
+
188
+ ```yaml
189
+ # .github/workflows/review.yml
190
+ - name: Code Review
191
+ run: |
192
+ kb review run --mode=heuristic --scope=changed --json > review.json
193
+
194
+ # Fail if issues found
195
+ if [ $? -ne 0 ]; then
196
+ echo "Code review failed"
197
+ exit 1
198
+ fi
199
+ ```
200
+
201
+ ### Pre-commit Hook
202
+
203
+ ```bash
204
+ #!/bin/bash
205
+ # .git/hooks/pre-commit
206
+
207
+ kb review run --mode=heuristic --scope=staged
208
+
209
+ if [ $? -ne 0 ]; then
210
+ echo "Fix issues before committing"
211
+ exit 1
212
+ fi
213
+ ```
214
+
215
+ ### Local Development
216
+
217
+ ```bash
218
+ # Quick check
219
+ kb review run
220
+
221
+ # Comprehensive review before PR
222
+ kb review run --mode=full --scope=all
223
+
224
+ # Deep analysis for refactoring
225
+ kb review run --mode=llm --files="src/core/**/*.ts"
226
+ ```
227
+
228
+ ## Configuration
229
+
230
+ Configure via `.kb/kb.config.json`:
231
+
232
+ ```json
233
+ {
234
+ "plugins": {
235
+ "@kb-labs/review": {
236
+ "eslintConfig": ".eslintrc.json",
237
+ "defaultMode": "heuristic",
238
+ "defaultScope": "changed"
239
+ }
240
+ }
241
+ }
242
+ ```
243
+
244
+ ## Platform Integration
245
+
246
+ The CLI uses KB Labs platform composables:
247
+
248
+ ```typescript
249
+ import { useLLM, useCache, useAnalytics, useLoader, useConfig } from '@kb-labs/sdk';
250
+
251
+ // Access LLM (if configured)
252
+ const llm = useLLM({ tier: 'medium' });
253
+
254
+ // Access cache
255
+ const cache = useCache();
256
+
257
+ // Show progress
258
+ const loader = useLoader('Analyzing...');
259
+ loader.start();
260
+ ```
261
+
262
+ ## Exit Codes
263
+
264
+ - **0**: Success (no critical issues)
265
+ - **1**: Issues found (blockers or high severity in heuristic mode)
266
+ - **1**: Error during execution
267
+
268
+ ## Architecture
269
+
270
+ ```
271
+ review:run
272
+
273
+ ├─ ReviewOrchestrator
274
+ │ ├─ runHeuristicAnalysis()
275
+ │ │ ├─ ESLint adapter
276
+ │ │ ├─ Ruff adapter (TODO)
277
+ │ │ └─ deduplicateFindings()
278
+ │ │
279
+ │ ├─ runFullAnalysis()
280
+ │ │ ├─ heuristic + LLM
281
+ │ │ └─ deduplicateFindings()
282
+ │ │
283
+ │ └─ runLLMAnalysis()
284
+ │ └─ LLM-only (TODO)
285
+
286
+ └─ Output (text/json)
287
+ ```
288
+
289
+ ## Related Packages
290
+
291
+ - **@kb-labs/review-contracts** - Type definitions
292
+ - **@kb-labs/review-heuristic** - Heuristic engines (ESLint, etc.)
293
+ - **@kb-labs/review-core** - Orchestration logic
@@ -0,0 +1,49 @@
1
+ import * as _kb_labs_shared_command_kit from '@kb-labs/shared-command-kit';
2
+
3
+ /**
4
+ * Command flags definitions for AI Review plugin
5
+ */
6
+ /**
7
+ * Flags for review:run command
8
+ */
9
+ declare const runFlags: _kb_labs_shared_command_kit.FlagSchemaWithInfer<{
10
+ mode: {
11
+ type: "string";
12
+ description: string;
13
+ choices: string[];
14
+ default: string;
15
+ };
16
+ scope: {
17
+ type: "string";
18
+ description: string;
19
+ choices: string[];
20
+ default: string;
21
+ };
22
+ repos: {
23
+ type: "array";
24
+ description: string;
25
+ };
26
+ task: {
27
+ type: "string";
28
+ description: string;
29
+ };
30
+ preset: {
31
+ type: "string";
32
+ description: string;
33
+ };
34
+ files: {
35
+ type: "array";
36
+ description: string;
37
+ };
38
+ eslintConfig: {
39
+ type: "string";
40
+ description: string;
41
+ };
42
+ json: {
43
+ type: "boolean";
44
+ description: string;
45
+ default: false;
46
+ };
47
+ }>;
48
+
49
+ export { runFlags };
@@ -0,0 +1,46 @@
1
+ import { defineFlags } from '@kb-labs/sdk';
2
+
3
+ // src/commands/flags.ts
4
+ var runFlags = defineFlags({
5
+ mode: {
6
+ type: "string",
7
+ description: "Review mode: heuristic (CI), full (local), llm (deep)",
8
+ choices: ["heuristic", "full", "llm"],
9
+ default: "heuristic"
10
+ },
11
+ scope: {
12
+ type: "string",
13
+ description: "File scope: all, changed (vs main), staged",
14
+ choices: ["all", "changed", "staged"],
15
+ default: "changed"
16
+ },
17
+ repos: {
18
+ type: "array",
19
+ description: "Repository scope (submodule names to include, e.g., --repos kb-labs-core kb-labs-cli)"
20
+ },
21
+ task: {
22
+ type: "string",
23
+ description: "Task context - describe what the changes are trying to achieve"
24
+ },
25
+ preset: {
26
+ type: "string",
27
+ description: "Preset ID (e.g., typescript-strict)"
28
+ },
29
+ files: {
30
+ type: "array",
31
+ description: "File patterns to analyze"
32
+ },
33
+ eslintConfig: {
34
+ type: "string",
35
+ description: "ESLint config file path"
36
+ },
37
+ json: {
38
+ type: "boolean",
39
+ description: "Output JSON",
40
+ default: false
41
+ }
42
+ });
43
+
44
+ export { runFlags };
45
+ //# sourceMappingURL=flags.js.map
46
+ //# sourceMappingURL=flags.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/commands/flags.ts"],"names":[],"mappings":";;;AASO,IAAM,WAAW,WAAA,CAAY;AAAA,EAClC,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,QAAA;AAAA,IACN,WAAA,EAAa,uDAAA;AAAA,IACb,OAAA,EAAS,CAAC,WAAA,EAAa,MAAA,EAAQ,KAAK,CAAA;AAAA,IACpC,OAAA,EAAS;AAAA,GACX;AAAA,EACA,KAAA,EAAO;AAAA,IACL,IAAA,EAAM,QAAA;AAAA,IACN,WAAA,EAAa,4CAAA;AAAA,IACb,OAAA,EAAS,CAAC,KAAA,EAAO,SAAA,EAAW,QAAQ,CAAA;AAAA,IACpC,OAAA,EAAS;AAAA,GACX;AAAA,EACA,KAAA,EAAO;AAAA,IACL,IAAA,EAAM,OAAA;AAAA,IACN,WAAA,EAAa;AAAA,GACf;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,QAAA;AAAA,IACN,WAAA,EAAa;AAAA,GACf;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,QAAA;AAAA,IACN,WAAA,EAAa;AAAA,GACf;AAAA,EACA,KAAA,EAAO;AAAA,IACL,IAAA,EAAM,OAAA;AAAA,IACN,WAAA,EAAa;AAAA,GACf;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,IAAA,EAAM,QAAA;AAAA,IACN,WAAA,EAAa;AAAA,GACf;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,SAAA;AAAA,IACN,WAAA,EAAa,aAAA;AAAA,IACb,OAAA,EAAS;AAAA;AAEb,CAAC","file":"flags.js","sourcesContent":["/**\n * Command flags definitions for AI Review plugin\n */\n\nimport { defineFlags } from '@kb-labs/sdk';\n\n/**\n * Flags for review:run command\n */\nexport const runFlags = defineFlags({\n mode: {\n type: 'string',\n description: 'Review mode: heuristic (CI), full (local), llm (deep)',\n choices: ['heuristic', 'full', 'llm'],\n default: 'heuristic',\n },\n scope: {\n type: 'string',\n description: 'File scope: all, changed (vs main), staged',\n choices: ['all', 'changed', 'staged'],\n default: 'changed',\n },\n repos: {\n type: 'array',\n description: 'Repository scope (submodule names to include, e.g., --repos kb-labs-core kb-labs-cli)',\n },\n task: {\n type: 'string',\n description: 'Task context - describe what the changes are trying to achieve',\n },\n preset: {\n type: 'string',\n description: 'Preset ID (e.g., typescript-strict)',\n },\n files: {\n type: 'array',\n description: 'File patterns to analyze',\n },\n eslintConfig: {\n type: 'string',\n description: 'ESLint config file path',\n },\n json: {\n type: 'boolean',\n description: 'Output JSON',\n default: false,\n },\n});\n"]}
@@ -0,0 +1,46 @@
1
+ import * as _kb_labs_shared_command_kit from '@kb-labs/shared-command-kit';
2
+ import { CLIInput } from '@kb-labs/sdk';
3
+ import { ReviewMode } from '@kb-labs/review-contracts';
4
+
5
+ /**
6
+ * Flags for review:run command
7
+ */
8
+ type RunFlags = {
9
+ mode?: ReviewMode;
10
+ scope?: 'all' | 'changed' | 'staged';
11
+ repos?: string[];
12
+ task?: string;
13
+ preset?: string;
14
+ files?: string[];
15
+ eslintConfig?: string;
16
+ cwd?: string;
17
+ json?: boolean;
18
+ agent?: boolean;
19
+ };
20
+ /**
21
+ * Agent-friendly review report.
22
+ * Simplified format for automated workflows.
23
+ */
24
+ interface AgentReviewReport {
25
+ /** Can the agent proceed (commit/merge)? */
26
+ passed: boolean;
27
+ /** Issues that must be fixed before proceeding */
28
+ issues: Array<{
29
+ file: string;
30
+ line: number;
31
+ problem: string;
32
+ fix: string;
33
+ severity: 'blocker' | 'high' | 'medium' | 'low' | 'info';
34
+ /** Rule ID (e.g., "rule:security/no-eval" or "llm-lite:security") */
35
+ ruleId: string;
36
+ /** Source: "rule" (matched project rule) or "llm" (ad-hoc finding) */
37
+ source: 'rule' | 'llm';
38
+ /** Confidence score (0.0-1.0) from verification engine */
39
+ confidence: number;
40
+ }>;
41
+ /** One-line summary for logs */
42
+ summary: string;
43
+ }
44
+ declare const _default: _kb_labs_shared_command_kit.CommandHandlerV3<unknown, CLIInput<RunFlags>, AgentReviewReport>;
45
+
46
+ export { _default as default };
@@ -0,0 +1,334 @@
1
+ import { defineCommand, useConfig, useLoader } from '@kb-labs/sdk';
2
+ import { resolveGitScope, getReposWithChanges, runReview } from '@kb-labs/review-core';
3
+ import * as path from 'path';
4
+ import { realpath } from 'fs/promises';
5
+
6
+ // src/commands/run.ts
7
+ var SEVERITY_ORDER = {
8
+ blocker: 0,
9
+ high: 1,
10
+ medium: 2,
11
+ low: 3,
12
+ info: 4
13
+ };
14
+ var SECURITY_IGNORE_PATTERNS = [
15
+ "**/node_modules/**",
16
+ "**/dist/**",
17
+ "**/build/**",
18
+ "**/.git/**",
19
+ "**/.env",
20
+ "**/.env.*",
21
+ "**/.ssh/**",
22
+ "**/credentials/**",
23
+ "**/password/**",
24
+ "**/*.pem",
25
+ "**/*.key",
26
+ "**/*.secret"
27
+ ];
28
+ var SECURITY_REGEX_PATTERNS = [
29
+ /node_modules/,
30
+ /\.git\//,
31
+ /\.env$/,
32
+ /\.env\./,
33
+ /\.ssh/,
34
+ /\/etc\//,
35
+ /\/usr\//,
36
+ /\/var\//,
37
+ /credentials/i,
38
+ /password/i,
39
+ /\.pem$/,
40
+ /\.key$/,
41
+ /\.secret$/
42
+ ];
43
+ async function isPathWithinCwd(filePath, cwd) {
44
+ try {
45
+ const resolvedPath = path.resolve(cwd, filePath);
46
+ const resolvedCwd = path.resolve(cwd);
47
+ const realPath = await realpath(resolvedPath).catch(() => resolvedPath);
48
+ const realCwd = await realpath(resolvedCwd).catch(() => resolvedCwd);
49
+ const relative2 = path.relative(realCwd, realPath);
50
+ return relative2 !== "" && !relative2.startsWith("..") && !path.isAbsolute(relative2);
51
+ } catch {
52
+ return false;
53
+ }
54
+ }
55
+ async function filterSecurePaths(filePaths, cwd) {
56
+ const results = await Promise.all(
57
+ filePaths.map(async (filePath) => {
58
+ if (!await isPathWithinCwd(filePath, cwd)) {
59
+ return null;
60
+ }
61
+ if (SECURITY_REGEX_PATTERNS.some((pattern) => pattern.test(filePath))) {
62
+ return null;
63
+ }
64
+ return filePath;
65
+ })
66
+ );
67
+ return results.filter((p) => p !== null);
68
+ }
69
+ function toAgentReport(result) {
70
+ const blockingIssues = result.findings.filter(
71
+ (f) => f.severity === "blocker" || f.severity === "high"
72
+ );
73
+ const passed = blockingIssues.length === 0;
74
+ const sortedFindings = [...result.findings].sort((a, b) => {
75
+ const aOrder = SEVERITY_ORDER[a.severity] ?? 5;
76
+ const bOrder = SEVERITY_ORDER[b.severity] ?? 5;
77
+ return aOrder - bOrder;
78
+ });
79
+ const issues = sortedFindings.map((f) => ({
80
+ file: f.file,
81
+ line: f.line,
82
+ problem: f.message,
83
+ fix: f.suggestion ?? "Review and fix manually",
84
+ severity: f.severity,
85
+ ruleId: f.ruleId ?? "unknown",
86
+ source: f.ruleId?.startsWith("rule:") ? "rule" : "llm",
87
+ // Map categorical confidence to numeric score (default 0.5 for undefined/unknown)
88
+ confidence: f.confidence === "certain" ? 1 : f.confidence === "likely" ? 0.8 : f.confidence === "heuristic" ? 0.6 : 0.5
89
+ }));
90
+ const mediumCount = result.findings.filter((f) => f.severity === "medium").length;
91
+ const lowCount = result.findings.filter((f) => f.severity === "low").length;
92
+ const infoCount = result.findings.filter((f) => f.severity === "info").length;
93
+ const nonBlockingCount = mediumCount + lowCount + infoCount;
94
+ const summary = passed ? nonBlockingCount > 0 ? `Review passed. ${nonBlockingCount} non-blocking issue(s) found (${mediumCount} medium, ${lowCount} low, ${infoCount} info).` : "Review passed. No issues found." : `Review failed. ${blockingIssues.length} blocking issue(s) must be fixed.`;
95
+ return { passed, issues, summary };
96
+ }
97
+ var run_default = defineCommand({
98
+ id: "review:run",
99
+ description: "Run code review analysis",
100
+ handler: {
101
+ // eslint-disable-next-line sonarjs/cognitive-complexity -- Main orchestration handler: coordinates input modes (repos/files/scope), git resolution, security filtering, output formats (agent/json/human), and severity-based exit codes
102
+ async execute(ctx, input) {
103
+ const startTime = Date.now();
104
+ const flags = input.flags;
105
+ const mode = flags.mode ?? "heuristic";
106
+ const scope = flags.scope ?? "changed";
107
+ const cwd = flags.cwd ?? ctx.cwd ?? process.cwd();
108
+ const fileConfig = await useConfig();
109
+ const loader = useLoader(`Running ${mode} analysis...`);
110
+ loader.start();
111
+ try {
112
+ let files = [];
113
+ let repoScope;
114
+ const reposInput = flags.repos ? Array.isArray(flags.repos) ? flags.repos : [flags.repos] : void 0;
115
+ if (reposInput && reposInput.length > 0) {
116
+ repoScope = reposInput;
117
+ const scopedFiles = await resolveGitScope({
118
+ cwd,
119
+ repos: repoScope,
120
+ includeStaged: scope === "staged" || scope === "changed",
121
+ includeUnstaged: scope === "changed",
122
+ // Include untracked (new) files when explicitly scoping to repos
123
+ includeUntracked: true
124
+ });
125
+ files = scopedFiles.files;
126
+ } else if (flags.files) {
127
+ const filePaths = Array.isArray(flags.files) ? flags.files : [flags.files];
128
+ const safeFilePaths = await filterSecurePaths(filePaths, cwd);
129
+ const fileReadResults = await Promise.allSettled(
130
+ safeFilePaths.map(async (filePath) => ({
131
+ path: filePath,
132
+ content: await ctx.runtime.fs.readFile(filePath, "utf-8")
133
+ }))
134
+ );
135
+ files = fileReadResults.filter((r) => r.status === "fulfilled").map((r) => r.value);
136
+ } else {
137
+ let filePaths = [];
138
+ switch (scope) {
139
+ case "all":
140
+ const [tsFiles, tsxFiles, jsFiles, jsxFiles] = await Promise.all([
141
+ ctx.runtime.fs.glob("**/*.ts", { cwd, ignore: [...SECURITY_IGNORE_PATTERNS] }),
142
+ ctx.runtime.fs.glob("**/*.tsx", { cwd, ignore: [...SECURITY_IGNORE_PATTERNS] }),
143
+ ctx.runtime.fs.glob("**/*.js", { cwd, ignore: [...SECURITY_IGNORE_PATTERNS] }),
144
+ ctx.runtime.fs.glob("**/*.jsx", { cwd, ignore: [...SECURITY_IGNORE_PATTERNS] })
145
+ ]);
146
+ filePaths = [...tsFiles, ...tsxFiles, ...jsFiles, ...jsxFiles];
147
+ break;
148
+ case "staged":
149
+ const reposWithChanges = await getReposWithChanges(cwd);
150
+ if (reposWithChanges.length > 0) {
151
+ const scopedFiles = await resolveGitScope({
152
+ cwd,
153
+ repos: reposWithChanges,
154
+ includeStaged: true,
155
+ includeUnstaged: false,
156
+ includeUntracked: false
157
+ });
158
+ files = scopedFiles.files;
159
+ repoScope = reposWithChanges;
160
+ } else {
161
+ filePaths = await ctx.runtime.fs.glob("**/*.{ts,tsx,js,jsx}", {
162
+ cwd,
163
+ ignore: [...SECURITY_IGNORE_PATTERNS]
164
+ });
165
+ }
166
+ break;
167
+ case "changed":
168
+ default:
169
+ const changedRepos = await getReposWithChanges(cwd);
170
+ if (changedRepos.length > 0) {
171
+ const scopedFiles = await resolveGitScope({
172
+ cwd,
173
+ repos: changedRepos,
174
+ includeStaged: true,
175
+ includeUnstaged: true,
176
+ includeUntracked: false
177
+ });
178
+ files = scopedFiles.files;
179
+ repoScope = changedRepos;
180
+ } else {
181
+ filePaths = await ctx.runtime.fs.glob("**/*.{ts,tsx,js,jsx}", {
182
+ cwd,
183
+ ignore: [...SECURITY_IGNORE_PATTERNS]
184
+ });
185
+ }
186
+ break;
187
+ }
188
+ if (files.length === 0 && filePaths.length > 0) {
189
+ const safeFilePaths = await filterSecurePaths(filePaths, cwd);
190
+ const fileReadResults = await Promise.allSettled(
191
+ safeFilePaths.map(async (filePath) => ({
192
+ path: filePath,
193
+ content: await ctx.runtime.fs.readFile(filePath, "utf-8")
194
+ }))
195
+ );
196
+ files = fileReadResults.filter((r) => r.status === "fulfilled").map((r) => r.value);
197
+ }
198
+ }
199
+ const result = await runReview({
200
+ files,
201
+ mode,
202
+ presetId: flags.preset ?? "default",
203
+ cwd,
204
+ taskContext: flags.task,
205
+ repoScope,
206
+ config: {
207
+ eslintConfig: flags.eslintConfig ?? fileConfig?.eslintConfig
208
+ }
209
+ });
210
+ loader.succeed(`Found ${result.findings.length} issue(s)`);
211
+ if (flags.json) {
212
+ ctx.ui?.json?.(result);
213
+ } else {
214
+ const sections = [];
215
+ const counts = {
216
+ blocker: result.findings.filter((f) => f.severity === "blocker").length,
217
+ high: result.findings.filter((f) => f.severity === "high").length,
218
+ medium: result.findings.filter((f) => f.severity === "medium").length,
219
+ low: result.findings.filter((f) => f.severity === "low").length,
220
+ info: result.findings.filter((f) => f.severity === "info").length
221
+ };
222
+ const summaryItems = [
223
+ `Files: ${result.metadata.analyzedFiles}`,
224
+ `Findings: ${result.findings.length}`
225
+ ];
226
+ if (flags.task) {
227
+ summaryItems.push(`Task: ${flags.task}`);
228
+ }
229
+ if (repoScope && repoScope.length > 0) {
230
+ summaryItems.push(`Repos: ${repoScope.join(", ")}`);
231
+ }
232
+ if (counts.blocker > 0) {
233
+ summaryItems.push(`Blocker: ${counts.blocker}`);
234
+ }
235
+ if (counts.high > 0) {
236
+ summaryItems.push(`High: ${counts.high}`);
237
+ }
238
+ if (counts.medium > 0) {
239
+ summaryItems.push(`Medium: ${counts.medium}`);
240
+ }
241
+ if (counts.low > 0) {
242
+ summaryItems.push(`Low: ${counts.low}`);
243
+ }
244
+ if (counts.info > 0) {
245
+ summaryItems.push(`Info: ${counts.info}`);
246
+ }
247
+ summaryItems.push(`Engines: ${result.metadata.engines.join(", ")}`);
248
+ const incr = result.metadata.incremental;
249
+ if (incr) {
250
+ if (incr.cachedFiles > 0) {
251
+ summaryItems.push(`Cached: ${incr.cachedFiles} file(s) skipped`);
252
+ }
253
+ if (incr.newFindings > 0 || incr.knownFindings > 0 || incr.cachedFindings > 0) {
254
+ const parts = [];
255
+ if (incr.newFindings > 0) {
256
+ parts.push(`${incr.newFindings} new`);
257
+ }
258
+ if (incr.knownFindings > 0) {
259
+ parts.push(`${incr.knownFindings} known`);
260
+ }
261
+ if (incr.cachedFindings > 0) {
262
+ parts.push(`${incr.cachedFindings} cached`);
263
+ }
264
+ summaryItems.push(`Breakdown: ${parts.join(", ")}`);
265
+ }
266
+ }
267
+ sections.push({
268
+ header: "Summary",
269
+ items: summaryItems
270
+ });
271
+ if (result.findings.length > 0) {
272
+ const sortedFindings = [...result.findings].sort((a, b) => {
273
+ const aOrder = SEVERITY_ORDER[a.severity] ?? 5;
274
+ const bOrder = SEVERITY_ORDER[b.severity] ?? 5;
275
+ return aOrder - bOrder;
276
+ });
277
+ const topFindings = sortedFindings.slice(0, 10);
278
+ const findingsItems = topFindings.map((f) => {
279
+ const loc = `${f.file}:${f.line}`;
280
+ const severity = f.severity.toUpperCase();
281
+ return `[${severity}] ${loc} - ${f.message}`;
282
+ });
283
+ if (result.findings.length > 10) {
284
+ findingsItems.push(`... and ${result.findings.length - 10} more`);
285
+ }
286
+ sections.push({
287
+ header: "Findings",
288
+ items: findingsItems
289
+ });
290
+ }
291
+ const timing = Date.now() - startTime;
292
+ if (result.findings.length === 0) {
293
+ ctx.ui?.success?.("No issues found", {
294
+ title: "Code Review",
295
+ sections,
296
+ timing
297
+ });
298
+ } else {
299
+ ctx.ui?.warn?.(`Found ${result.findings.length} issue(s)`, {
300
+ title: "Code Review",
301
+ sections,
302
+ timing
303
+ });
304
+ }
305
+ }
306
+ const blockerCount = result.findings.filter((f) => f.severity === "blocker").length;
307
+ const highCount = result.findings.filter((f) => f.severity === "high").length;
308
+ const agentReport = toAgentReport(result);
309
+ if (blockerCount > 0) {
310
+ return { exitCode: 1, result: agentReport };
311
+ }
312
+ if (mode === "heuristic" && highCount > 0) {
313
+ return { exitCode: 1, result: agentReport };
314
+ }
315
+ return { exitCode: 0, result: agentReport };
316
+ } catch (error) {
317
+ const message = error instanceof Error ? error.message : String(error);
318
+ loader.fail(`Review failed: ${message}`);
319
+ ctx.ui?.error?.(message);
320
+ ctx.platform.logger?.error?.("review:run failed", error instanceof Error ? error : new Error(message));
321
+ const errorReport = {
322
+ passed: false,
323
+ issues: [],
324
+ summary: `Review error: ${message}`
325
+ };
326
+ return { exitCode: 1, result: errorReport };
327
+ }
328
+ }
329
+ }
330
+ });
331
+
332
+ export { run_default as default };
333
+ //# sourceMappingURL=run.js.map
334
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/commands/run.ts"],"names":["relative"],"mappings":"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","file":"run.js","sourcesContent":["/**\n * review:run command\n * Run code review analysis\n */\n\nimport {\n defineCommand,\n useLoader,\n useConfig,\n type PluginContextV3,\n type CLIInput,\n type CommandResult,\n} from '@kb-labs/sdk';\nimport type { ReviewMode, ReviewResult, InputFile } from '@kb-labs/review-contracts';\nimport { runReview, resolveGitScope, getReposWithChanges } from '@kb-labs/review-core';\nimport * as path from 'node:path';\nimport { realpath } from 'node:fs/promises';\n\n/**\n * Flags for review:run command\n */\ntype RunFlags = {\n mode?: ReviewMode;\n scope?: 'all' | 'changed' | 'staged';\n repos?: string[];\n task?: string;\n preset?: string;\n files?: string[];\n eslintConfig?: string;\n cwd?: string;\n json?: boolean;\n agent?: boolean;\n};\n\n/**\n * Agent-friendly review report.\n * Simplified format for automated workflows.\n */\ninterface AgentReviewReport {\n /** Can the agent proceed (commit/merge)? */\n passed: boolean;\n /** Issues that must be fixed before proceeding */\n issues: Array<{\n file: string;\n line: number;\n problem: string;\n fix: string;\n severity: 'blocker' | 'high' | 'medium' | 'low' | 'info';\n /** Rule ID (e.g., \"rule:security/no-eval\" or \"llm-lite:security\") */\n ruleId: string;\n /** Source: \"rule\" (matched project rule) or \"llm\" (ad-hoc finding) */\n source: 'rule' | 'llm';\n /** Confidence score (0.0-1.0) from verification engine */\n confidence: number;\n }>;\n /** One-line summary for logs */\n summary: string;\n}\n\n/**\n * Severity order for sorting (lower = more critical)\n */\nconst SEVERITY_ORDER: Record<string, number> = {\n blocker: 0,\n high: 1,\n medium: 2,\n low: 3,\n info: 4,\n};\n\n/**\n * Security-sensitive patterns for file filtering.\n * Shared across all file filtering operations for consistency.\n */\nconst SECURITY_IGNORE_PATTERNS = [\n '**/node_modules/**',\n '**/dist/**',\n '**/build/**',\n '**/.git/**',\n '**/.env',\n '**/.env.*',\n '**/.ssh/**',\n '**/credentials/**',\n '**/password/**',\n '**/*.pem',\n '**/*.key',\n '**/*.secret',\n] as const;\n\n/**\n * Regex patterns for additional security filtering.\n * Used as a second layer of defense after glob ignore.\n */\nconst SECURITY_REGEX_PATTERNS = [\n /node_modules/,\n /\\.git\\//,\n /\\.env$/,\n /\\.env\\./,\n /\\.ssh/,\n /\\/etc\\//,\n /\\/usr\\//,\n /\\/var\\//,\n /credentials/i,\n /password/i,\n /\\.pem$/,\n /\\.key$/,\n /\\.secret$/,\n] as const;\n\n/**\n * Validate that a file path is within the allowed directory.\n * Prevents path traversal attacks including symlink bypass.\n * Uses fs.realpath() to resolve symlinks and path.relative() for cross-platform safety.\n */\nasync function isPathWithinCwd(filePath: string, cwd: string): Promise<boolean> {\n try {\n const resolvedPath = path.resolve(cwd, filePath);\n const resolvedCwd = path.resolve(cwd);\n\n // Resolve symlinks to prevent symlink bypass attacks\n const realPath = await realpath(resolvedPath).catch(() => resolvedPath);\n const realCwd = await realpath(resolvedCwd).catch(() => resolvedCwd);\n\n const relative = path.relative(realCwd, realPath);\n // Path is within cwd if relative path exists, doesn't start with '..', and isn't absolute\n return relative !== '' && !relative.startsWith('..') && !path.isAbsolute(relative);\n } catch {\n // If path doesn't exist or can't be resolved, reject it\n return false;\n }\n}\n\n/**\n * Filter file paths for security concerns.\n */\nasync function filterSecurePaths(filePaths: string[], cwd: string): Promise<string[]> {\n const results = await Promise.all(\n filePaths.map(async filePath => {\n // Path traversal check (including symlink resolution)\n if (!await isPathWithinCwd(filePath, cwd)) {\n return null;\n }\n // Security pattern check\n if (SECURITY_REGEX_PATTERNS.some(pattern => pattern.test(filePath))) {\n return null;\n }\n return filePath;\n })\n );\n return results.filter((p): p is string => p !== null);\n}\n\n/**\n * Transform ReviewResult to agent-friendly format\n */\nfunction toAgentReport(result: ReviewResult): AgentReviewReport {\n // Blocking issues = blocker and high severity only\n // Medium/low/info are acceptable and don't block the review\n const blockingIssues = result.findings.filter(\n f => f.severity === 'blocker' || f.severity === 'high'\n );\n\n // Pass when no blocking issues (medium and below are acceptable)\n const passed = blockingIssues.length === 0;\n\n // Sort by severity: blocker > high > medium > low > info\n const sortedFindings = [...result.findings].sort((a, b) => {\n const aOrder = SEVERITY_ORDER[a.severity] ?? 5;\n const bOrder = SEVERITY_ORDER[b.severity] ?? 5;\n return aOrder - bOrder;\n });\n\n const issues = sortedFindings.map(f => ({\n file: f.file,\n line: f.line,\n problem: f.message,\n fix: f.suggestion ?? 'Review and fix manually',\n severity: f.severity as 'blocker' | 'high' | 'medium' | 'low' | 'info',\n ruleId: f.ruleId ?? 'unknown',\n source: (f.ruleId?.startsWith('rule:') ? 'rule' : 'llm') as 'rule' | 'llm',\n // Map categorical confidence to numeric score (default 0.5 for undefined/unknown)\n confidence: f.confidence === 'certain' ? 1.0\n : f.confidence === 'likely' ? 0.8\n : f.confidence === 'heuristic' ? 0.6\n : 0.5,\n }));\n\n // Count non-blocking issues for summary\n const mediumCount = result.findings.filter(f => f.severity === 'medium').length;\n const lowCount = result.findings.filter(f => f.severity === 'low').length;\n const infoCount = result.findings.filter(f => f.severity === 'info').length;\n const nonBlockingCount = mediumCount + lowCount + infoCount;\n\n const summary = passed\n ? nonBlockingCount > 0\n ? `Review passed. ${nonBlockingCount} non-blocking issue(s) found (${mediumCount} medium, ${lowCount} low, ${infoCount} info).`\n : 'Review passed. No issues found.'\n : `Review failed. ${blockingIssues.length} blocking issue(s) must be fixed.`;\n\n return { passed, issues, summary };\n}\n\nexport default defineCommand<unknown, CLIInput<RunFlags>, AgentReviewReport>({\n id: 'review:run',\n description: 'Run code review analysis',\n\n handler: {\n // eslint-disable-next-line sonarjs/cognitive-complexity -- Main orchestration handler: coordinates input modes (repos/files/scope), git resolution, security filtering, output formats (agent/json/human), and severity-based exit codes\n async execute(ctx: PluginContextV3, input: CLIInput<RunFlags>): Promise<CommandResult<AgentReviewReport>> {\n const startTime = Date.now();\n\n // Extract flags from input\n const flags = input.flags;\n\n // Parse input\n const mode = flags.mode ?? 'heuristic';\n const scope = flags.scope ?? 'changed';\n\n // Use --cwd flag if provided, otherwise use current directory where command was run\n const cwd = flags.cwd ?? ctx.cwd ?? process.cwd();\n\n // Load config\n const fileConfig = await useConfig<{ eslintConfig?: string }>();\n\n // Show progress\n const loader = useLoader(`Running ${mode} analysis...`);\n loader.start();\n\n try {\n // Collect files based on scope\n let files: InputFile[] = [];\n let repoScope: string[] | undefined;\n\n // If --repos flag is provided, use git-scope resolver\n // Normalize repos to array (may come as string from CLI)\n const reposInput = flags.repos\n ? (Array.isArray(flags.repos) ? flags.repos : [flags.repos])\n : undefined;\n\n if (reposInput && reposInput.length > 0) {\n repoScope = reposInput;\n\n const scopedFiles = await resolveGitScope({\n cwd,\n repos: repoScope,\n includeStaged: scope === 'staged' || scope === 'changed',\n includeUnstaged: scope === 'changed',\n // Include untracked (new) files when explicitly scoping to repos\n includeUntracked: true,\n });\n\n files = scopedFiles.files;\n } else if (flags.files) {\n // Use explicitly provided files (with security validation)\n const filePaths = Array.isArray(flags.files) ? flags.files : [flags.files];\n const safeFilePaths = await filterSecurePaths(filePaths, cwd);\n\n const fileReadResults = await Promise.allSettled(\n safeFilePaths.map(async (filePath) => ({\n path: filePath,\n content: await ctx.runtime.fs.readFile(filePath, 'utf-8'),\n }))\n );\n\n files = fileReadResults\n .filter((r): r is PromiseFulfilledResult<InputFile> => r.status === 'fulfilled')\n .map(r => r.value);\n } else {\n // Resolve files based on file scope (all/changed/staged)\n let filePaths: string[] = [];\n\n switch (scope) {\n case 'all':\n // All TypeScript/JavaScript files\n // Run multiple globs in parallel since ctx.runtime.fs.glob might not support brace expansion\n const [tsFiles, tsxFiles, jsFiles, jsxFiles] = await Promise.all([\n ctx.runtime.fs.glob('**/*.ts', { cwd, ignore: [...SECURITY_IGNORE_PATTERNS] }),\n ctx.runtime.fs.glob('**/*.tsx', { cwd, ignore: [...SECURITY_IGNORE_PATTERNS] }),\n ctx.runtime.fs.glob('**/*.js', { cwd, ignore: [...SECURITY_IGNORE_PATTERNS] }),\n ctx.runtime.fs.glob('**/*.jsx', { cwd, ignore: [...SECURITY_IGNORE_PATTERNS] }),\n ]);\n filePaths = [...tsFiles, ...tsxFiles, ...jsFiles, ...jsxFiles];\n break;\n\n case 'staged':\n // Git staged files - try to detect repos with changes\n const reposWithChanges = await getReposWithChanges(cwd);\n if (reposWithChanges.length > 0) {\n const scopedFiles = await resolveGitScope({\n cwd,\n repos: reposWithChanges,\n includeStaged: true,\n includeUnstaged: false,\n includeUntracked: false,\n });\n files = scopedFiles.files;\n repoScope = reposWithChanges;\n } else {\n // Fallback to glob\n filePaths = await ctx.runtime.fs.glob('**/*.{ts,tsx,js,jsx}', {\n cwd,\n ignore: [...SECURITY_IGNORE_PATTERNS],\n });\n }\n break;\n\n case 'changed':\n default:\n // Changed files - try to detect repos with changes\n const changedRepos = await getReposWithChanges(cwd);\n if (changedRepos.length > 0) {\n const scopedFiles = await resolveGitScope({\n cwd,\n repos: changedRepos,\n includeStaged: true,\n includeUnstaged: true,\n includeUntracked: false,\n });\n files = scopedFiles.files;\n repoScope = changedRepos;\n } else {\n // Fallback to glob\n filePaths = await ctx.runtime.fs.glob('**/*.{ts,tsx,js,jsx}', {\n cwd,\n ignore: [...SECURITY_IGNORE_PATTERNS],\n });\n }\n break;\n }\n\n // If we used glob fallback, read file contents\n if (files.length === 0 && filePaths.length > 0) {\n // Apply security filtering (path traversal + pattern matching)\n const safeFilePaths = await filterSecurePaths(filePaths, cwd);\n\n // Read files with individual error handling\n const fileReadResults = await Promise.allSettled(\n safeFilePaths.map(async (filePath) => ({\n path: filePath,\n content: await ctx.runtime.fs.readFile(filePath, 'utf-8'),\n }))\n );\n\n // Collect successful reads, skip failed ones\n files = fileReadResults\n .filter((r): r is PromiseFulfilledResult<InputFile> => r.status === 'fulfilled')\n .map(r => r.value);\n }\n }\n\n // Run review\n const result = await runReview({\n files,\n mode,\n presetId: flags.preset ?? 'default',\n cwd,\n taskContext: flags.task,\n repoScope,\n config: {\n eslintConfig: flags.eslintConfig ?? fileConfig?.eslintConfig,\n },\n });\n\n loader.succeed(`Found ${result.findings.length} issue(s)`);\n\n // Output results\n if (flags.json) {\n ctx.ui?.json?.(result);\n } else {\n // Build sections\n const sections: Array<{ header?: string; items: string[] }> = [];\n\n // Summary section\n const counts = {\n blocker: result.findings.filter((f) => f.severity === 'blocker').length,\n high: result.findings.filter((f) => f.severity === 'high').length,\n medium: result.findings.filter((f) => f.severity === 'medium').length,\n low: result.findings.filter((f) => f.severity === 'low').length,\n info: result.findings.filter((f) => f.severity === 'info').length,\n };\n\n const summaryItems: string[] = [\n `Files: ${result.metadata.analyzedFiles}`,\n `Findings: ${result.findings.length}`,\n ];\n\n // Show task context if provided\n if (flags.task) {\n summaryItems.push(`Task: ${flags.task}`);\n }\n\n // Show repo scope if used\n if (repoScope && repoScope.length > 0) {\n summaryItems.push(`Repos: ${repoScope.join(', ')}`);\n }\n\n if (counts.blocker > 0) {\n summaryItems.push(`Blocker: ${counts.blocker}`);\n }\n if (counts.high > 0) {\n summaryItems.push(`High: ${counts.high}`);\n }\n if (counts.medium > 0) {\n summaryItems.push(`Medium: ${counts.medium}`);\n }\n if (counts.low > 0) {\n summaryItems.push(`Low: ${counts.low}`);\n }\n if (counts.info > 0) {\n summaryItems.push(`Info: ${counts.info}`);\n }\n\n summaryItems.push(`Engines: ${result.metadata.engines.join(', ')}`);\n\n // Show incremental stats if available (LLM modes with caching)\n const incr = result.metadata.incremental;\n if (incr) {\n if (incr.cachedFiles > 0) {\n summaryItems.push(`Cached: ${incr.cachedFiles} file(s) skipped`);\n }\n if (incr.newFindings > 0 || incr.knownFindings > 0 || incr.cachedFindings > 0) {\n const parts: string[] = [];\n if (incr.newFindings > 0) {\n parts.push(`${incr.newFindings} new`);\n }\n if (incr.knownFindings > 0) {\n parts.push(`${incr.knownFindings} known`);\n }\n if (incr.cachedFindings > 0) {\n parts.push(`${incr.cachedFindings} cached`);\n }\n summaryItems.push(`Breakdown: ${parts.join(', ')}`);\n }\n }\n\n sections.push({\n header: 'Summary',\n items: summaryItems,\n });\n\n // Findings section (top 10, sorted by severity)\n if (result.findings.length > 0) {\n // Sort by severity: blocker > high > medium > low > info\n const sortedFindings = [...result.findings].sort((a, b) => {\n const aOrder = SEVERITY_ORDER[a.severity] ?? 5;\n const bOrder = SEVERITY_ORDER[b.severity] ?? 5;\n return aOrder - bOrder;\n });\n\n const topFindings = sortedFindings.slice(0, 10);\n const findingsItems = topFindings.map((f) => {\n const loc = `${f.file}:${f.line}`;\n const severity = f.severity.toUpperCase();\n return `[${severity}] ${loc} - ${f.message}`;\n });\n\n if (result.findings.length > 10) {\n findingsItems.push(`... and ${result.findings.length - 10} more`);\n }\n\n sections.push({\n header: 'Findings',\n items: findingsItems,\n });\n }\n\n const timing = Date.now() - startTime;\n\n if (result.findings.length === 0) {\n ctx.ui?.success?.('No issues found', {\n title: 'Code Review',\n sections,\n timing,\n });\n } else {\n ctx.ui?.warn?.(`Found ${result.findings.length} issue(s)`, {\n title: 'Code Review',\n sections,\n timing,\n });\n }\n }\n\n // Exit code based on severity\n const blockerCount = result.findings.filter((f) => f.severity === 'blocker').length;\n const highCount = result.findings.filter((f) => f.severity === 'high').length;\n const agentReport = toAgentReport(result);\n\n if (blockerCount > 0) {\n return { exitCode: 1, result: agentReport };\n }\n\n if (mode === 'heuristic' && highCount > 0) {\n // In CI mode (heuristic), fail on high severity\n return { exitCode: 1, result: agentReport };\n }\n\n return { exitCode: 0, result: agentReport };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n loader.fail(`Review failed: ${message}`);\n ctx.ui?.error?.(message);\n ctx.platform.logger?.error?.('review:run failed', error instanceof Error ? error : new Error(message));\n const errorReport: AgentReviewReport = {\n passed: false,\n issues: [],\n summary: `Review error: ${message}`,\n };\n return { exitCode: 1, result: errorReport };\n }\n },\n },\n});\n"]}
@@ -0,0 +1,19 @@
1
+ export { default as manifest } from './manifest.js';
2
+ import { ReviewFinding, ReviewResult } from '@kb-labs/review-contracts';
3
+ import '@kb-labs/perm-presets';
4
+
5
+ /**
6
+ * @module @kb-labs/review-cli/formatters
7
+ * Output formatters for code review results.
8
+ */
9
+
10
+ /**
11
+ * Format findings as text.
12
+ */
13
+ declare function formatFindings(findings: ReviewFinding[]): string;
14
+ /**
15
+ * Format summary statistics.
16
+ */
17
+ declare function formatSummary(result: ReviewResult): string;
18
+
19
+ export { formatFindings, formatSummary };
package/dist/index.js ADDED
@@ -0,0 +1,217 @@
1
+ import { defineFlags, combinePermissions, gitWorkflowPreset, kbPlatformPreset, defineCommandFlags } from '@kb-labs/sdk';
2
+ import chalk from 'chalk';
3
+
4
+ // src/manifest.ts
5
+ var runFlags = defineFlags({
6
+ mode: {
7
+ type: "string",
8
+ description: "Review mode: heuristic (CI), full (local), llm (deep)",
9
+ choices: ["heuristic", "full", "llm"],
10
+ default: "heuristic"
11
+ },
12
+ scope: {
13
+ type: "string",
14
+ description: "File scope: all, changed (vs main), staged",
15
+ choices: ["all", "changed", "staged"],
16
+ default: "changed"
17
+ },
18
+ repos: {
19
+ type: "array",
20
+ description: "Repository scope (submodule names to include, e.g., --repos kb-labs-core kb-labs-cli)"
21
+ },
22
+ task: {
23
+ type: "string",
24
+ description: "Task context - describe what the changes are trying to achieve"
25
+ },
26
+ preset: {
27
+ type: "string",
28
+ description: "Preset ID (e.g., typescript-strict)"
29
+ },
30
+ files: {
31
+ type: "array",
32
+ description: "File patterns to analyze"
33
+ },
34
+ eslintConfig: {
35
+ type: "string",
36
+ description: "ESLint config file path"
37
+ },
38
+ json: {
39
+ type: "boolean",
40
+ description: "Output JSON",
41
+ default: false
42
+ }
43
+ });
44
+
45
+ // src/manifest.ts
46
+ var pluginPermissions = combinePermissions().with(gitWorkflowPreset).with(kbPlatformPreset).withFs({
47
+ mode: "read",
48
+ allow: ["**/*"]
49
+ }).withPlatform({
50
+ llm: true,
51
+ // LLM for full/llm modes
52
+ cache: ["review:"],
53
+ // Cache namespace prefix
54
+ analytics: true
55
+ // Track review events
56
+ }).withQuotas({
57
+ timeoutMs: 6e5,
58
+ // 10 min for deep analysis
59
+ memoryMb: 512
60
+ }).build();
61
+ var manifest = {
62
+ schema: "kb.plugin/3",
63
+ id: "@kb-labs/review",
64
+ version: "0.1.0",
65
+ display: {
66
+ name: "AI Review",
67
+ description: "AI-powered code review with heuristic engines (ESLint, Ruff, etc.) and LLM analyzers.",
68
+ tags: ["code-review", "linting", "ai", "quality"]
69
+ },
70
+ // Configuration section in kb.config.json
71
+ configSection: "review",
72
+ // Platform requirements
73
+ platform: {
74
+ requires: ["storage", "cache"],
75
+ optional: ["llm", "analytics", "logger"]
76
+ },
77
+ // CLI commands
78
+ cli: {
79
+ commands: [
80
+ {
81
+ id: "review:run",
82
+ group: "review",
83
+ describe: "Run code review analysis",
84
+ longDescription: "Analyzes code using heuristic engines (ESLint, Ruff, etc.) and optionally LLM analyzers. Supports three modes: heuristic (CI, fast), full (local, comprehensive), llm (deep analysis).",
85
+ handler: "./commands/run.js#default",
86
+ handlerPath: "./commands/run.js",
87
+ flags: defineCommandFlags(runFlags.schema),
88
+ examples: [
89
+ "kb review run",
90
+ "kb review run --mode=full",
91
+ "kb review run --scope=staged",
92
+ "kb review run --preset=typescript-strict",
93
+ 'kb review run --files="src/**/*.ts"',
94
+ "kb review run --json"
95
+ ]
96
+ }
97
+ ]
98
+ },
99
+ // Permissions
100
+ permissions: pluginPermissions
101
+ };
102
+ var manifest_default = manifest;
103
+ function formatFindings(findings) {
104
+ if (findings.length === 0) {
105
+ return chalk.green("\u2713 No issues found!");
106
+ }
107
+ const lines = [];
108
+ const byFile = /* @__PURE__ */ new Map();
109
+ for (const finding of findings) {
110
+ if (!byFile.has(finding.file)) {
111
+ byFile.set(finding.file, []);
112
+ }
113
+ byFile.get(finding.file).push(finding);
114
+ }
115
+ for (const [file, fileFindings] of byFile) {
116
+ lines.push("");
117
+ lines.push(chalk.bold.white(file));
118
+ for (const finding of fileFindings) {
119
+ const severity = formatSeverity(finding.severity);
120
+ const location = chalk.dim(`${finding.line}:${finding.column ?? 0}`);
121
+ const message = finding.message;
122
+ const sourceTag = formatSource(finding.source, finding.ruleId);
123
+ lines.push(` ${location} ${severity} ${message} ${sourceTag}`);
124
+ if (finding.fix && finding.automated) {
125
+ lines.push(chalk.dim(" \u26A1 Auto-fixable"));
126
+ }
127
+ }
128
+ }
129
+ return lines.join("\n");
130
+ }
131
+ function formatSource(source, ruleId) {
132
+ if (ruleId.startsWith("rule:")) {
133
+ const projectRuleId = ruleId.slice(5);
134
+ return chalk.green(`[rule:${projectRuleId}]`);
135
+ } else if (ruleId.startsWith("llm-lite:")) {
136
+ const category = ruleId.slice(9);
137
+ return chalk.dim(`[llm:${category}]`);
138
+ } else {
139
+ return chalk.dim(`[${ruleId}]`);
140
+ }
141
+ }
142
+ function formatSeverity(severity) {
143
+ switch (severity) {
144
+ case "blocker":
145
+ return chalk.bgRed.white(" BLOCKER ");
146
+ case "high":
147
+ return chalk.red("error");
148
+ case "medium":
149
+ return chalk.yellow("warning");
150
+ case "low":
151
+ return chalk.blue("info");
152
+ case "info":
153
+ return chalk.gray("hint");
154
+ default:
155
+ return severity;
156
+ }
157
+ }
158
+ function formatSummary(result) {
159
+ const lines = [];
160
+ lines.push("");
161
+ lines.push(chalk.bold("Summary"));
162
+ lines.push("\u2500".repeat(50));
163
+ const counts = {
164
+ blocker: 0,
165
+ high: 0,
166
+ medium: 0,
167
+ low: 0,
168
+ info: 0
169
+ };
170
+ const sourceCounts = {
171
+ rule: 0,
172
+ llm: 0
173
+ };
174
+ for (const finding of result.findings) {
175
+ counts[finding.severity]++;
176
+ if (finding.ruleId.startsWith("rule:")) {
177
+ sourceCounts.rule++;
178
+ } else {
179
+ sourceCounts.llm++;
180
+ }
181
+ }
182
+ if (counts.blocker > 0) {
183
+ lines.push(chalk.red(` Blocker: ${counts.blocker}`));
184
+ }
185
+ if (counts.high > 0) {
186
+ lines.push(chalk.red(` High: ${counts.high}`));
187
+ }
188
+ if (counts.medium > 0) {
189
+ lines.push(chalk.yellow(` Medium: ${counts.medium}`));
190
+ }
191
+ if (counts.low > 0) {
192
+ lines.push(chalk.blue(` Low: ${counts.low}`));
193
+ }
194
+ if (counts.info > 0) {
195
+ lines.push(chalk.gray(` Info: ${counts.info}`));
196
+ }
197
+ lines.push("");
198
+ lines.push(` Total: ${result.findings.length}`);
199
+ lines.push(` Files: ${result.metadata.analyzedFiles}`);
200
+ lines.push(` Time: ${result.metadata.durationMs}ms`);
201
+ lines.push(` Engines: ${result.metadata.engines.join(", ")}`);
202
+ if (result.findings.length > 0) {
203
+ lines.push("");
204
+ lines.push(chalk.bold(" Finding Sources:"));
205
+ if (sourceCounts.rule > 0) {
206
+ lines.push(chalk.green(` From rules: ${sourceCounts.rule}`));
207
+ }
208
+ if (sourceCounts.llm > 0) {
209
+ lines.push(chalk.dim(` LLM ad-hoc: ${sourceCounts.llm}`));
210
+ }
211
+ }
212
+ return lines.join("\n");
213
+ }
214
+
215
+ export { formatFindings, formatSummary, manifest_default as manifest };
216
+ //# sourceMappingURL=index.js.map
217
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/flags.ts","../src/manifest.ts","../src/formatters/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Command flags definitions for AI Review plugin\n */\n\nimport { defineFlags } from '@kb-labs/sdk';\n\n/**\n * Flags for review:run command\n */\nexport const runFlags = defineFlags({\n mode: {\n type: 'string',\n description: 'Review mode: heuristic (CI), full (local), llm (deep)',\n choices: ['heuristic', 'full', 'llm'],\n default: 'heuristic',\n },\n scope: {\n type: 'string',\n description: 'File scope: all, changed (vs main), staged',\n choices: ['all', 'changed', 'staged'],\n default: 'changed',\n },\n repos: {\n type: 'array',\n description: 'Repository scope (submodule names to include, e.g., --repos kb-labs-core kb-labs-cli)',\n },\n task: {\n type: 'string',\n description: 'Task context - describe what the changes are trying to achieve',\n },\n preset: {\n type: 'string',\n description: 'Preset ID (e.g., typescript-strict)',\n },\n files: {\n type: 'array',\n description: 'File patterns to analyze',\n },\n eslintConfig: {\n type: 'string',\n description: 'ESLint config file path',\n },\n json: {\n type: 'boolean',\n description: 'Output JSON',\n default: false,\n },\n});\n","/**\n * KB Labs AI Review Plugin - Manifest V3\n */\n\nimport {\n defineCommandFlags,\n combinePermissions,\n gitWorkflowPreset,\n kbPlatformPreset,\n} from '@kb-labs/sdk';\nimport { runFlags } from './commands/flags.js';\n\n/**\n * Build permissions:\n * - gitWorkflow: HOME, USER, GIT_* for git operations\n * - kbPlatform: KB_* env vars and .kb/ directory\n * - Custom: file read access, LLM, cache, analytics\n */\nconst pluginPermissions = combinePermissions()\n .with(gitWorkflowPreset)\n .with(kbPlatformPreset)\n .withFs({\n mode: 'read',\n allow: ['**/*'],\n })\n .withPlatform({\n llm: true, // LLM for full/llm modes\n cache: ['review:'], // Cache namespace prefix\n analytics: true, // Track review events\n })\n .withQuotas({\n timeoutMs: 600000, // 10 min for deep analysis\n memoryMb: 512,\n })\n .build();\n\nexport const manifest = {\n schema: 'kb.plugin/3',\n id: '@kb-labs/review',\n version: '0.1.0',\n\n display: {\n name: 'AI Review',\n description: 'AI-powered code review with heuristic engines (ESLint, Ruff, etc.) and LLM analyzers.',\n tags: ['code-review', 'linting', 'ai', 'quality'],\n },\n\n // Configuration section in kb.config.json\n configSection: 'review',\n\n // Platform requirements\n platform: {\n requires: ['storage', 'cache'],\n optional: ['llm', 'analytics', 'logger'],\n },\n\n // CLI commands\n cli: {\n commands: [\n {\n id: 'review:run',\n group: 'review',\n describe: 'Run code review analysis',\n longDescription:\n 'Analyzes code using heuristic engines (ESLint, Ruff, etc.) and optionally LLM analyzers. ' +\n 'Supports three modes: heuristic (CI, fast), full (local, comprehensive), llm (deep analysis).',\n\n handler: './commands/run.js#default',\n handlerPath: './commands/run.js',\n\n flags: defineCommandFlags(runFlags.schema),\n\n examples: [\n 'kb review run',\n 'kb review run --mode=full',\n 'kb review run --scope=staged',\n 'kb review run --preset=typescript-strict',\n 'kb review run --files=\"src/**/*.ts\"',\n 'kb review run --json',\n ],\n },\n ],\n },\n\n // Permissions\n permissions: pluginPermissions,\n};\n\nexport default manifest;\n","/**\n * @module @kb-labs/review-cli/formatters\n * Output formatters for code review results.\n */\n\nimport type { ReviewFinding, ReviewResult } from '@kb-labs/review-contracts';\nimport chalk from 'chalk';\n\n/**\n * Format findings as text.\n */\nexport function formatFindings(findings: ReviewFinding[]): string {\n if (findings.length === 0) {\n return chalk.green('✓ No issues found!');\n }\n\n const lines: string[] = [];\n\n // Group by severity\n const byFile = new Map<string, ReviewFinding[]>();\n\n for (const finding of findings) {\n if (!byFile.has(finding.file)) {\n byFile.set(finding.file, []);\n }\n byFile.get(finding.file)!.push(finding);\n }\n\n // Format by file\n for (const [file, fileFindings] of byFile) {\n lines.push('');\n lines.push(chalk.bold.white(file));\n\n for (const finding of fileFindings) {\n const severity = formatSeverity(finding.severity);\n const location = chalk.dim(`${finding.line}:${finding.column ?? 0}`);\n const message = finding.message;\n const sourceTag = formatSource(finding.source, finding.ruleId);\n\n lines.push(` ${location} ${severity} ${message} ${sourceTag}`);\n\n // Show fix hint if available\n if (finding.fix && finding.automated) {\n lines.push(chalk.dim(' ⚡ Auto-fixable'));\n }\n }\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Format source tag (rule vs llm).\n * Shows whether finding came from a project rule or LLM ad-hoc analysis.\n */\nfunction formatSource(source: string, ruleId: string): string {\n // Parse ruleId format: \"rule:category/name\" or \"llm-lite:category\"\n if (ruleId.startsWith('rule:')) {\n // Project rule - show rule ID with green highlight\n const projectRuleId = ruleId.slice(5); // Remove \"rule:\" prefix\n return chalk.green(`[rule:${projectRuleId}]`);\n } else if (ruleId.startsWith('llm-lite:')) {\n // LLM ad-hoc finding - show category with dim styling\n const category = ruleId.slice(9); // Remove \"llm-lite:\" prefix\n return chalk.dim(`[llm:${category}]`);\n } else {\n // Legacy format - just show the ruleId\n return chalk.dim(`[${ruleId}]`);\n }\n}\n\n/**\n * Format severity with color.\n */\nfunction formatSeverity(severity: string): string {\n switch (severity) {\n case 'blocker':\n return chalk.bgRed.white(' BLOCKER ');\n case 'high':\n return chalk.red('error');\n case 'medium':\n return chalk.yellow('warning');\n case 'low':\n return chalk.blue('info');\n case 'info':\n return chalk.gray('hint');\n default:\n return severity;\n }\n}\n\n/**\n * Format summary statistics.\n */\nexport function formatSummary(result: ReviewResult): string {\n const lines: string[] = [];\n\n lines.push('');\n lines.push(chalk.bold('Summary'));\n lines.push('─'.repeat(50));\n\n // Count by severity\n const counts = {\n blocker: 0,\n high: 0,\n medium: 0,\n low: 0,\n info: 0,\n };\n\n // Count by source (rule vs llm)\n const sourceCounts = {\n rule: 0,\n llm: 0,\n };\n\n for (const finding of result.findings) {\n counts[finding.severity]++;\n\n // Count by source based on ruleId format\n if (finding.ruleId.startsWith('rule:')) {\n sourceCounts.rule++;\n } else {\n sourceCounts.llm++;\n }\n }\n\n // Format counts\n if (counts.blocker > 0) {\n lines.push(chalk.red(` Blocker: ${counts.blocker}`));\n }\n if (counts.high > 0) {\n lines.push(chalk.red(` High: ${counts.high}`));\n }\n if (counts.medium > 0) {\n lines.push(chalk.yellow(` Medium: ${counts.medium}`));\n }\n if (counts.low > 0) {\n lines.push(chalk.blue(` Low: ${counts.low}`));\n }\n if (counts.info > 0) {\n lines.push(chalk.gray(` Info: ${counts.info}`));\n }\n\n lines.push('');\n lines.push(` Total: ${result.findings.length}`);\n lines.push(` Files: ${result.metadata.analyzedFiles}`);\n lines.push(` Time: ${result.metadata.durationMs}ms`);\n lines.push(` Engines: ${result.metadata.engines.join(', ')}`);\n\n // Show source breakdown if there are findings\n if (result.findings.length > 0) {\n lines.push('');\n lines.push(chalk.bold(' Finding Sources:'));\n if (sourceCounts.rule > 0) {\n lines.push(chalk.green(` From rules: ${sourceCounts.rule}`));\n }\n if (sourceCounts.llm > 0) {\n lines.push(chalk.dim(` LLM ad-hoc: ${sourceCounts.llm}`));\n }\n }\n\n return lines.join('\\n');\n}\n"]}
@@ -0,0 +1,43 @@
1
+ import * as _kb_labs_perm_presets from '@kb-labs/perm-presets';
2
+
3
+ /**
4
+ * KB Labs AI Review Plugin - Manifest V3
5
+ */
6
+ declare const manifest: {
7
+ schema: string;
8
+ id: string;
9
+ version: string;
10
+ display: {
11
+ name: string;
12
+ description: string;
13
+ tags: string[];
14
+ };
15
+ configSection: string;
16
+ platform: {
17
+ requires: string[];
18
+ optional: string[];
19
+ };
20
+ cli: {
21
+ commands: {
22
+ id: string;
23
+ group: string;
24
+ describe: string;
25
+ longDescription: string;
26
+ handler: string;
27
+ handlerPath: string;
28
+ flags: {
29
+ name: string;
30
+ type: "string" | "boolean" | "number" | "array";
31
+ alias?: string;
32
+ default?: unknown;
33
+ description?: string;
34
+ choices?: string[];
35
+ required?: boolean;
36
+ }[];
37
+ examples: string[];
38
+ }[];
39
+ };
40
+ permissions: _kb_labs_perm_presets.RuntimePermissionSpec;
41
+ };
42
+
43
+ export { manifest as default, manifest };
@@ -0,0 +1,105 @@
1
+ import { defineFlags, combinePermissions, gitWorkflowPreset, kbPlatformPreset, defineCommandFlags } from '@kb-labs/sdk';
2
+
3
+ // src/manifest.ts
4
+ var runFlags = defineFlags({
5
+ mode: {
6
+ type: "string",
7
+ description: "Review mode: heuristic (CI), full (local), llm (deep)",
8
+ choices: ["heuristic", "full", "llm"],
9
+ default: "heuristic"
10
+ },
11
+ scope: {
12
+ type: "string",
13
+ description: "File scope: all, changed (vs main), staged",
14
+ choices: ["all", "changed", "staged"],
15
+ default: "changed"
16
+ },
17
+ repos: {
18
+ type: "array",
19
+ description: "Repository scope (submodule names to include, e.g., --repos kb-labs-core kb-labs-cli)"
20
+ },
21
+ task: {
22
+ type: "string",
23
+ description: "Task context - describe what the changes are trying to achieve"
24
+ },
25
+ preset: {
26
+ type: "string",
27
+ description: "Preset ID (e.g., typescript-strict)"
28
+ },
29
+ files: {
30
+ type: "array",
31
+ description: "File patterns to analyze"
32
+ },
33
+ eslintConfig: {
34
+ type: "string",
35
+ description: "ESLint config file path"
36
+ },
37
+ json: {
38
+ type: "boolean",
39
+ description: "Output JSON",
40
+ default: false
41
+ }
42
+ });
43
+
44
+ // src/manifest.ts
45
+ var pluginPermissions = combinePermissions().with(gitWorkflowPreset).with(kbPlatformPreset).withFs({
46
+ mode: "read",
47
+ allow: ["**/*"]
48
+ }).withPlatform({
49
+ llm: true,
50
+ // LLM for full/llm modes
51
+ cache: ["review:"],
52
+ // Cache namespace prefix
53
+ analytics: true
54
+ // Track review events
55
+ }).withQuotas({
56
+ timeoutMs: 6e5,
57
+ // 10 min for deep analysis
58
+ memoryMb: 512
59
+ }).build();
60
+ var manifest = {
61
+ schema: "kb.plugin/3",
62
+ id: "@kb-labs/review",
63
+ version: "0.1.0",
64
+ display: {
65
+ name: "AI Review",
66
+ description: "AI-powered code review with heuristic engines (ESLint, Ruff, etc.) and LLM analyzers.",
67
+ tags: ["code-review", "linting", "ai", "quality"]
68
+ },
69
+ // Configuration section in kb.config.json
70
+ configSection: "review",
71
+ // Platform requirements
72
+ platform: {
73
+ requires: ["storage", "cache"],
74
+ optional: ["llm", "analytics", "logger"]
75
+ },
76
+ // CLI commands
77
+ cli: {
78
+ commands: [
79
+ {
80
+ id: "review:run",
81
+ group: "review",
82
+ describe: "Run code review analysis",
83
+ longDescription: "Analyzes code using heuristic engines (ESLint, Ruff, etc.) and optionally LLM analyzers. Supports three modes: heuristic (CI, fast), full (local, comprehensive), llm (deep analysis).",
84
+ handler: "./commands/run.js#default",
85
+ handlerPath: "./commands/run.js",
86
+ flags: defineCommandFlags(runFlags.schema),
87
+ examples: [
88
+ "kb review run",
89
+ "kb review run --mode=full",
90
+ "kb review run --scope=staged",
91
+ "kb review run --preset=typescript-strict",
92
+ 'kb review run --files="src/**/*.ts"',
93
+ "kb review run --json"
94
+ ]
95
+ }
96
+ ]
97
+ },
98
+ // Permissions
99
+ permissions: pluginPermissions
100
+ };
101
+ var manifest_default = manifest;
102
+
103
+ export { manifest_default as default, manifest };
104
+ //# sourceMappingURL=manifest.js.map
105
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/flags.ts","../src/manifest.ts"],"names":[],"mappings":";;;AASO,IAAM,WAAW,WAAA,CAAY;AAAA,EAClC,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,QAAA;AAAA,IACN,WAAA,EAAa,uDAAA;AAAA,IACb,OAAA,EAAS,CAAC,WAAA,EAAa,MAAA,EAAQ,KAAK,CAAA;AAAA,IACpC,OAAA,EAAS;AAAA,GACX;AAAA,EACA,KAAA,EAAO;AAAA,IACL,IAAA,EAAM,QAAA;AAAA,IACN,WAAA,EAAa,4CAAA;AAAA,IACb,OAAA,EAAS,CAAC,KAAA,EAAO,SAAA,EAAW,QAAQ,CAAA;AAAA,IACpC,OAAA,EAAS;AAAA,GACX;AAAA,EACA,KAAA,EAAO;AAAA,IACL,IAAA,EAAM,OAAA;AAAA,IACN,WAAA,EAAa;AAAA,GACf;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,QAAA;AAAA,IACN,WAAA,EAAa;AAAA,GACf;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,QAAA;AAAA,IACN,WAAA,EAAa;AAAA,GACf;AAAA,EACA,KAAA,EAAO;AAAA,IACL,IAAA,EAAM,OAAA;AAAA,IACN,WAAA,EAAa;AAAA,GACf;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,IAAA,EAAM,QAAA;AAAA,IACN,WAAA,EAAa;AAAA,GACf;AAAA,EACA,IAAA,EAAM;AAAA,IACJ,IAAA,EAAM,SAAA;AAAA,IACN,WAAA,EAAa,aAAA;AAAA,IACb,OAAA,EAAS;AAAA;AAEb,CAAC,CAAA;;;AC7BD,IAAM,iBAAA,GAAoB,oBAAmB,CAC1C,IAAA,CAAK,iBAAiB,CAAA,CACtB,IAAA,CAAK,gBAAgB,CAAA,CACrB,MAAA,CAAO;AAAA,EACN,IAAA,EAAM,MAAA;AAAA,EACN,KAAA,EAAO,CAAC,MAAM;AAChB,CAAC,EACA,YAAA,CAAa;AAAA,EACZ,GAAA,EAAK,IAAA;AAAA;AAAA,EACL,KAAA,EAAO,CAAC,SAAS,CAAA;AAAA;AAAA,EACjB,SAAA,EAAW;AAAA;AACb,CAAC,EACA,UAAA,CAAW;AAAA,EACV,SAAA,EAAW,GAAA;AAAA;AAAA,EACX,QAAA,EAAU;AACZ,CAAC,EACA,KAAA,EAAM;AAEF,IAAM,QAAA,GAAW;AAAA,EACtB,MAAA,EAAQ,aAAA;AAAA,EACR,EAAA,EAAI,iBAAA;AAAA,EACJ,OAAA,EAAS,OAAA;AAAA,EAET,OAAA,EAAS;AAAA,IACP,IAAA,EAAM,WAAA;AAAA,IACN,WAAA,EAAa,uFAAA;AAAA,IACb,IAAA,EAAM,CAAC,aAAA,EAAe,SAAA,EAAW,MAAM,SAAS;AAAA,GAClD;AAAA;AAAA,EAGA,aAAA,EAAe,QAAA;AAAA;AAAA,EAGf,QAAA,EAAU;AAAA,IACR,QAAA,EAAU,CAAC,SAAA,EAAW,OAAO,CAAA;AAAA,IAC7B,QAAA,EAAU,CAAC,KAAA,EAAO,WAAA,EAAa,QAAQ;AAAA,GACzC;AAAA;AAAA,EAGA,GAAA,EAAK;AAAA,IACH,QAAA,EAAU;AAAA,MACR;AAAA,QACE,EAAA,EAAI,YAAA;AAAA,QACJ,KAAA,EAAO,QAAA;AAAA,QACP,QAAA,EAAU,0BAAA;AAAA,QACV,eAAA,EACE,wLAAA;AAAA,QAGF,OAAA,EAAS,2BAAA;AAAA,QACT,WAAA,EAAa,mBAAA;AAAA,QAEb,KAAA,EAAO,kBAAA,CAAmB,QAAA,CAAS,MAAM,CAAA;AAAA,QAEzC,QAAA,EAAU;AAAA,UACR,eAAA;AAAA,UACA,2BAAA;AAAA,UACA,8BAAA;AAAA,UACA,0CAAA;AAAA,UACA,qCAAA;AAAA,UACA;AAAA;AACF;AACF;AACF,GACF;AAAA;AAAA,EAGA,WAAA,EAAa;AACf;AAEA,IAAO,gBAAA,GAAQ","file":"manifest.js","sourcesContent":["/**\n * Command flags definitions for AI Review plugin\n */\n\nimport { defineFlags } from '@kb-labs/sdk';\n\n/**\n * Flags for review:run command\n */\nexport const runFlags = defineFlags({\n mode: {\n type: 'string',\n description: 'Review mode: heuristic (CI), full (local), llm (deep)',\n choices: ['heuristic', 'full', 'llm'],\n default: 'heuristic',\n },\n scope: {\n type: 'string',\n description: 'File scope: all, changed (vs main), staged',\n choices: ['all', 'changed', 'staged'],\n default: 'changed',\n },\n repos: {\n type: 'array',\n description: 'Repository scope (submodule names to include, e.g., --repos kb-labs-core kb-labs-cli)',\n },\n task: {\n type: 'string',\n description: 'Task context - describe what the changes are trying to achieve',\n },\n preset: {\n type: 'string',\n description: 'Preset ID (e.g., typescript-strict)',\n },\n files: {\n type: 'array',\n description: 'File patterns to analyze',\n },\n eslintConfig: {\n type: 'string',\n description: 'ESLint config file path',\n },\n json: {\n type: 'boolean',\n description: 'Output JSON',\n default: false,\n },\n});\n","/**\n * KB Labs AI Review Plugin - Manifest V3\n */\n\nimport {\n defineCommandFlags,\n combinePermissions,\n gitWorkflowPreset,\n kbPlatformPreset,\n} from '@kb-labs/sdk';\nimport { runFlags } from './commands/flags.js';\n\n/**\n * Build permissions:\n * - gitWorkflow: HOME, USER, GIT_* for git operations\n * - kbPlatform: KB_* env vars and .kb/ directory\n * - Custom: file read access, LLM, cache, analytics\n */\nconst pluginPermissions = combinePermissions()\n .with(gitWorkflowPreset)\n .with(kbPlatformPreset)\n .withFs({\n mode: 'read',\n allow: ['**/*'],\n })\n .withPlatform({\n llm: true, // LLM for full/llm modes\n cache: ['review:'], // Cache namespace prefix\n analytics: true, // Track review events\n })\n .withQuotas({\n timeoutMs: 600000, // 10 min for deep analysis\n memoryMb: 512,\n })\n .build();\n\nexport const manifest = {\n schema: 'kb.plugin/3',\n id: '@kb-labs/review',\n version: '0.1.0',\n\n display: {\n name: 'AI Review',\n description: 'AI-powered code review with heuristic engines (ESLint, Ruff, etc.) and LLM analyzers.',\n tags: ['code-review', 'linting', 'ai', 'quality'],\n },\n\n // Configuration section in kb.config.json\n configSection: 'review',\n\n // Platform requirements\n platform: {\n requires: ['storage', 'cache'],\n optional: ['llm', 'analytics', 'logger'],\n },\n\n // CLI commands\n cli: {\n commands: [\n {\n id: 'review:run',\n group: 'review',\n describe: 'Run code review analysis',\n longDescription:\n 'Analyzes code using heuristic engines (ESLint, Ruff, etc.) and optionally LLM analyzers. ' +\n 'Supports three modes: heuristic (CI, fast), full (local, comprehensive), llm (deep analysis).',\n\n handler: './commands/run.js#default',\n handlerPath: './commands/run.js',\n\n flags: defineCommandFlags(runFlags.schema),\n\n examples: [\n 'kb review run',\n 'kb review run --mode=full',\n 'kb review run --scope=staged',\n 'kb review run --preset=typescript-strict',\n 'kb review run --files=\"src/**/*.ts\"',\n 'kb review run --json',\n ],\n },\n ],\n },\n\n // Permissions\n permissions: pluginPermissions,\n};\n\nexport default manifest;\n"]}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@kb-labs/review-entry",
3
+ "version": "2.14.0",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "import": "./dist/index.js",
8
+ "types": "./dist/index.d.ts"
9
+ }
10
+ },
11
+ "kb": {
12
+ "manifest": "./dist/manifest.js"
13
+ },
14
+ "dependencies": {
15
+ "chalk": "^5.3.0",
16
+ "@kb-labs/review-core": "2.14.0",
17
+ "@kb-labs/sdk": "1.6.6",
18
+ "@kb-labs/review-contracts": "2.14.0",
19
+ "@kb-labs/shared-cli-ui": "2.14.0"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^24.3.3",
23
+ "tsup": "^8.5.0",
24
+ "typescript": "^5.6.3",
25
+ "rimraf": "^6.0.1",
26
+ "vitest": "^3.2.4",
27
+ "@kb-labs/devkit": "2.14.0"
28
+ },
29
+ "engines": {
30
+ "node": ">=20.0.0",
31
+ "pnpm": ">=9.0.0"
32
+ },
33
+ "files": [
34
+ "dist"
35
+ ],
36
+ "scripts": {
37
+ "build": "tsup",
38
+ "clean": "rimraf dist",
39
+ "dev": "tsup --config tsup.config.ts --watch",
40
+ "lint": "eslint src --ext .ts",
41
+ "lint:fix": "eslint . --fix",
42
+ "type-check": "tsc --noEmit",
43
+ "test": "vitest run --passWithNoTests",
44
+ "test:watch": "vitest"
45
+ }
46
+ }