skannr 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/cli.js +86 -0
  2. package/dist/cli.js.map +1 -1
  3. package/dist/guard/call-context.d.ts +14 -0
  4. package/dist/guard/call-context.d.ts.map +1 -0
  5. package/dist/guard/call-context.js +120 -0
  6. package/dist/guard/call-context.js.map +1 -0
  7. package/dist/guard/fix-applier.d.ts +38 -0
  8. package/dist/guard/fix-applier.d.ts.map +1 -0
  9. package/dist/guard/fix-applier.js +181 -0
  10. package/dist/guard/fix-applier.js.map +1 -0
  11. package/dist/guard/hook-installer.d.ts +19 -0
  12. package/dist/guard/hook-installer.d.ts.map +1 -0
  13. package/dist/guard/hook-installer.js +119 -0
  14. package/dist/guard/hook-installer.js.map +1 -0
  15. package/dist/guard/index.d.ts +34 -0
  16. package/dist/guard/index.d.ts.map +1 -0
  17. package/dist/guard/index.js +152 -0
  18. package/dist/guard/index.js.map +1 -0
  19. package/dist/guard/provider.d.ts +12 -0
  20. package/dist/guard/provider.d.ts.map +1 -0
  21. package/dist/guard/provider.js +211 -0
  22. package/dist/guard/provider.js.map +1 -0
  23. package/dist/guard/review-runner.d.ts +21 -0
  24. package/dist/guard/review-runner.d.ts.map +1 -0
  25. package/dist/guard/review-runner.js +85 -0
  26. package/dist/guard/review-runner.js.map +1 -0
  27. package/dist/guard/rules-loader.d.ts +10 -0
  28. package/dist/guard/rules-loader.d.ts.map +1 -0
  29. package/dist/guard/rules-loader.js +156 -0
  30. package/dist/guard/rules-loader.js.map +1 -0
  31. package/dist/guard/schema.d.ts +182 -0
  32. package/dist/guard/schema.d.ts.map +1 -0
  33. package/dist/guard/schema.js +53 -0
  34. package/dist/guard/schema.js.map +1 -0
  35. package/dist/guard/symbol-diff.d.ts +18 -0
  36. package/dist/guard/symbol-diff.d.ts.map +1 -0
  37. package/dist/guard/symbol-diff.js +243 -0
  38. package/dist/guard/symbol-diff.js.map +1 -0
  39. package/dist/guard/types.d.ts +62 -0
  40. package/dist/guard/types.d.ts.map +1 -0
  41. package/dist/guard/types.js +6 -0
  42. package/dist/guard/types.js.map +1 -0
  43. package/dist/index.d.ts +1 -1
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +1 -2
  46. package/dist/index.js.map +1 -1
  47. package/dist/mcp-server.d.ts.map +1 -1
  48. package/dist/mcp-server.js +44 -0
  49. package/dist/mcp-server.js.map +1 -1
  50. package/dist/scanner.d.ts +0 -1
  51. package/dist/scanner.d.ts.map +1 -1
  52. package/dist/scanner.js +0 -7
  53. package/dist/scanner.js.map +1 -1
  54. package/package.json +3 -2
  55. package/src/cli.ts +101 -0
  56. package/src/guard/call-context.ts +101 -0
  57. package/src/guard/fix-applier.ts +172 -0
  58. package/src/guard/hook-installer.ts +96 -0
  59. package/src/guard/index.ts +134 -0
  60. package/src/guard/provider.ts +209 -0
  61. package/src/guard/review-runner.ts +110 -0
  62. package/src/guard/rules-loader.ts +131 -0
  63. package/src/guard/schema.ts +59 -0
  64. package/src/guard/symbol-diff.ts +227 -0
  65. package/src/guard/types.ts +68 -0
  66. package/src/index.ts +2 -2
  67. package/src/mcp-server.ts +49 -0
  68. package/src/scanner.ts +0 -7
  69. package/gemini-extension/GEMINI.md +0 -26
  70. package/gemini-extension/gemini-extension.json +0 -12
  71. package/gemini-extension/package.json +0 -14
  72. package/gemini-extension/src/server.ts +0 -69
  73. package/gemini-extension/tsconfig.json +0 -14
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ /**
3
+ * Review runner — orchestrates the full guard pipeline:
4
+ * build symbol diffs → enrich with call context → batch → call LLM → validate.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.runGuardReview = runGuardReview;
8
+ const rules_loader_1 = require("./rules-loader");
9
+ const symbol_diff_1 = require("./symbol-diff");
10
+ const call_context_1 = require("./call-context");
11
+ const provider_1 = require("./provider");
12
+ /** Maximum symbols to include in a single LLM call. */
13
+ const BATCH_SIZE = 15;
14
+ /**
15
+ * Run the full guard review pipeline.
16
+ * Returns a GuardResult with violations, or throws with exit code 2 errors.
17
+ */
18
+ async function runGuardReview(options) {
19
+ const { root, diffContent, diffOnly = false } = options;
20
+ const started = Date.now();
21
+ // 1. Load rules
22
+ const rules = (0, rules_loader_1.loadRules)(root);
23
+ // 2. Load provider config
24
+ const config = (0, rules_loader_1.loadGuardConfig)(root);
25
+ // 3. Build symbol-level diffs
26
+ let units;
27
+ if (diffContent) {
28
+ units = (0, symbol_diff_1.buildSymbolDiffsFromDiff)(root, diffContent);
29
+ }
30
+ else {
31
+ units = (0, symbol_diff_1.buildSymbolDiffs)(root);
32
+ }
33
+ if (units.length === 0) {
34
+ return {
35
+ response: {
36
+ violations: [],
37
+ status: 'pass',
38
+ summary: 'No staged changes with identifiable symbols to review.',
39
+ },
40
+ rulesUsed: rules,
41
+ symbolsReviewed: 0,
42
+ durationMs: Date.now() - started,
43
+ };
44
+ }
45
+ // 4. Enrich with call context (unless --diff-only)
46
+ if (!diffOnly) {
47
+ units = (0, call_context_1.enrichWithCallContext)(units, root);
48
+ }
49
+ // 5. Batch and review
50
+ const allViolations = [];
51
+ const batches = batchUnits(units, BATCH_SIZE);
52
+ // Run batches concurrently (up to 3 in parallel)
53
+ const CONCURRENCY = 3;
54
+ for (let i = 0; i < batches.length; i += CONCURRENCY) {
55
+ const chunk = batches.slice(i, i + CONCURRENCY);
56
+ const results = await Promise.all(chunk.map((batch) => (0, provider_1.runLlmReview)(config, rules, batch)));
57
+ for (const result of results) {
58
+ allViolations.push(...result.violations);
59
+ }
60
+ }
61
+ // 6. Determine status
62
+ const hasHighConfidenceFail = allViolations.some((v) => v.confidence >= 0.7 && (v.severity === 'high' || v.severity === 'critical'));
63
+ const response = {
64
+ violations: allViolations,
65
+ status: hasHighConfidenceFail ? 'fail' : 'pass',
66
+ summary: allViolations.length === 0
67
+ ? `All ${units.length} symbol(s) pass review.`
68
+ : `${allViolations.length} violation(s) found across ${units.length} symbol(s).`,
69
+ };
70
+ return {
71
+ response,
72
+ rulesUsed: rules,
73
+ symbolsReviewed: units.length,
74
+ durationMs: Date.now() - started,
75
+ };
76
+ }
77
+ /** Split units into batches of the given size. */
78
+ function batchUnits(units, size) {
79
+ const batches = [];
80
+ for (let i = 0; i < units.length; i += size) {
81
+ batches.push(units.slice(i, i + size));
82
+ }
83
+ return batches;
84
+ }
85
+ //# sourceMappingURL=review-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-runner.js","sourceRoot":"","sources":["../../src/guard/review-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Load and validate the rules file (.skannr/rules.json or .skannr/rules.yaml).
3
+ * Returns validated rules or throws with a clear error message.
4
+ */
5
+ import type { GuardRule, GuardConfig } from './types';
6
+ /** Load and validate rules from the project root. Falls back to defaults if no file exists. */
7
+ export declare function loadRules(root: string): GuardRule[];
8
+ /** Load guard provider configuration from .skannr/guard.json or env vars. */
9
+ export declare function loadGuardConfig(root: string): GuardConfig;
10
+ //# sourceMappingURL=rules-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules-loader.d.ts","sourceRoot":"","sources":["../../src/guard/rules-loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAwDtD,+FAA+F;AAC/F,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,CAwBnD;AAED,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CA2BzD"}
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ /**
3
+ * Load and validate the rules file (.skannr/rules.json or .skannr/rules.yaml).
4
+ * Returns validated rules or throws with a clear error message.
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.loadRules = loadRules;
41
+ exports.loadGuardConfig = loadGuardConfig;
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
44
+ const schema_1 = require("./schema");
45
+ const schema_2 = require("./schema");
46
+ /** Default locations to search for the rules file. */
47
+ const RULES_PATHS = [
48
+ '.skannr/rules.json',
49
+ '.skannr/rules.yaml',
50
+ '.skannr/rules.yml',
51
+ ];
52
+ /** Built-in default rules used when no .skannr/rules.json exists. */
53
+ const DEFAULT_RULES = [
54
+ {
55
+ id: 'no-any-type',
56
+ description: 'Do not use the `any` type in TypeScript; use explicit types or `unknown`.',
57
+ severity: 'high',
58
+ fixable: true,
59
+ category: 'type-safety',
60
+ },
61
+ {
62
+ id: 'no-console-log',
63
+ description: 'Remove console.log statements before committing; use a proper logger in production code.',
64
+ severity: 'medium',
65
+ fixable: true,
66
+ category: 'code-quality',
67
+ },
68
+ {
69
+ id: 'error-handling',
70
+ description: 'Async functions that can throw should have error handling (try/catch or .catch()).',
71
+ severity: 'high',
72
+ fixable: false,
73
+ category: 'reliability',
74
+ },
75
+ {
76
+ id: 'no-hardcoded-secrets',
77
+ description: 'Do not hardcode API keys, passwords, tokens, or connection strings. Use environment variables.',
78
+ severity: 'critical',
79
+ fixable: false,
80
+ category: 'security',
81
+ },
82
+ {
83
+ id: 'function-complexity',
84
+ description: 'Functions should not exceed ~40 lines. Extract helper functions for complex logic.',
85
+ severity: 'medium',
86
+ fixable: false,
87
+ category: 'maintainability',
88
+ },
89
+ {
90
+ id: 'unused-imports',
91
+ description: 'Remove unused imports.',
92
+ severity: 'low',
93
+ fixable: true,
94
+ category: 'code-quality',
95
+ },
96
+ ];
97
+ /** Load and validate rules from the project root. Falls back to defaults if no file exists. */
98
+ function loadRules(root) {
99
+ const rulesPath = findRulesFile(root);
100
+ if (!rulesPath) {
101
+ return DEFAULT_RULES;
102
+ }
103
+ const raw = fs.readFileSync(rulesPath, 'utf-8');
104
+ let parsed;
105
+ try {
106
+ parsed = JSON.parse(raw);
107
+ }
108
+ catch {
109
+ throw new Error(`Failed to parse rules file as JSON: ${rulesPath}`);
110
+ }
111
+ const result = schema_1.RulesFileSchema.safeParse(parsed);
112
+ if (!result.success) {
113
+ const errors = result.error.issues
114
+ .map((i) => ` - ${i.path.join('.')}: ${i.message}`)
115
+ .join('\n');
116
+ throw new Error(`Invalid rules file (${rulesPath}):\n${errors}`);
117
+ }
118
+ return result.data.rules;
119
+ }
120
+ /** Load guard provider configuration from .skannr/guard.json or env vars. */
121
+ function loadGuardConfig(root) {
122
+ const configPath = path.join(root, '.skannr', 'guard.json');
123
+ let rawConfig = {};
124
+ if (fs.existsSync(configPath)) {
125
+ try {
126
+ rawConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
127
+ }
128
+ catch {
129
+ // Fall through to defaults + env
130
+ }
131
+ }
132
+ // Env vars override file config
133
+ const provider = (process.env.SKANNR_GUARD_PROVIDER || rawConfig.provider || 'gemini');
134
+ const model = (process.env.SKANNR_GUARD_MODEL || rawConfig.model || 'gemini-2.0-flash-exp');
135
+ const apiKey = process.env.SKANNR_GUARD_API_KEY
136
+ || process.env.GEMINI_API_KEY
137
+ || process.env.OPENAI_API_KEY
138
+ || rawConfig.apiKey;
139
+ const baseUrl = process.env.OPENAI_BASE_URL || rawConfig.baseUrl;
140
+ const result = schema_2.GuardConfigSchema.safeParse({ provider, model, apiKey, baseUrl });
141
+ if (!result.success) {
142
+ throw new Error(`Invalid guard config: ${result.error.issues.map((i) => i.message).join(', ')}`);
143
+ }
144
+ return result.data;
145
+ }
146
+ /** Find the first existing rules file in the project. */
147
+ function findRulesFile(root) {
148
+ for (const rel of RULES_PATHS) {
149
+ const full = path.join(root, rel);
150
+ if (fs.existsSync(full)) {
151
+ return full;
152
+ }
153
+ }
154
+ return null;
155
+ }
156
+ //# sourceMappingURL=rules-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules-loader.js","sourceRoot":"","sources":["../../src/guard/rules-loader.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DH,8BAwBC;AAGD,0CA2BC;AAlHD,uCAAyB;AACzB,2CAA6B;AAC7B,qCAA2C;AAE3C,qCAA6C;AAE7C,sDAAsD;AACtD,MAAM,WAAW,GAAG;IAClB,oBAAoB;IACpB,oBAAoB;IACpB,mBAAmB;CACpB,CAAC;AAEF,qEAAqE;AACrE,MAAM,aAAa,GAAgB;IACjC;QACE,EAAE,EAAE,aAAa;QACjB,WAAW,EAAE,2EAA2E;QACxF,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE,aAAa;KACxB;IACD;QACE,EAAE,EAAE,gBAAgB;QACpB,WAAW,EAAE,0FAA0F;QACvG,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE,cAAc;KACzB;IACD;QACE,EAAE,EAAE,gBAAgB;QACpB,WAAW,EAAE,oFAAoF;QACjG,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,KAAK;QACd,QAAQ,EAAE,aAAa;KACxB;IACD;QACE,EAAE,EAAE,sBAAsB;QAC1B,WAAW,EAAE,gGAAgG;QAC7G,QAAQ,EAAE,UAAU;QACpB,OAAO,EAAE,KAAK;QACd,QAAQ,EAAE,UAAU;KACrB;IACD;QACE,EAAE,EAAE,qBAAqB;QACzB,WAAW,EAAE,oFAAoF;QACjG,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,KAAK;QACd,QAAQ,EAAE,iBAAiB;KAC5B;IACD;QACE,EAAE,EAAE,gBAAgB;QACpB,WAAW,EAAE,wBAAwB;QACrC,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE,cAAc;KACzB;CACF,CAAC;AAEF,+FAA+F;AAC/F,SAAgB,SAAS,CAAC,IAAY;IACpC,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAChD,IAAI,MAAe,CAAC;IAEpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,uCAAuC,SAAS,EAAE,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,MAAM,GAAG,wBAAe,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACjD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM;aAC/B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;aACnD,IAAI,CAAC,IAAI,CAAC,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,uBAAuB,SAAS,OAAO,MAAM,EAAE,CAAC,CAAC;IACnE,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;AAC3B,CAAC;AAED,6EAA6E;AAC7E,SAAgB,eAAe,CAAC,IAAY;IAC1C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;IAE5D,IAAI,SAAS,GAA4B,EAAE,CAAC;IAC5C,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;QAC/D,CAAC;QAAC,MAAM,CAAC;YACP,iCAAiC;QACnC,CAAC;IACH,CAAC;IAED,gCAAgC;IAChC,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,SAAS,CAAC,QAAQ,IAAI,QAAQ,CAAW,CAAC;IACjG,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,SAAS,CAAC,KAAK,IAAI,sBAAsB,CAAW,CAAC;IACtG,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB;WAC1C,OAAO,CAAC,GAAG,CAAC,cAAc;WAC1B,OAAO,CAAC,GAAG,CAAC,cAAc;WACzB,SAAS,CAAC,MAA6B,CAAC;IAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAK,SAAS,CAAC,OAA8B,CAAC;IAEzF,MAAM,MAAM,GAAG,0BAAiB,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;IACjF,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,yBAAyB,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACnG,CAAC;IAED,OAAO,MAAM,CAAC,IAAmB,CAAC;AACpC,CAAC;AAED,yDAAyD;AACzD,SAAS,aAAa,CAAC,IAAY;IACjC,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAClC,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Zod schemas for rules file validation and LLM response contract enforcement.
3
+ * These ensure we never parse free text — all LLM output is validated against
4
+ * a strict schema before being used.
5
+ */
6
+ import { z } from 'zod';
7
+ export declare const SeveritySchema: z.ZodEnum<["low", "medium", "high", "critical"]>;
8
+ export declare const RuleSchema: z.ZodObject<{
9
+ id: z.ZodString;
10
+ description: z.ZodString;
11
+ severity: z.ZodEnum<["low", "medium", "high", "critical"]>;
12
+ fixable: z.ZodBoolean;
13
+ category: z.ZodString;
14
+ }, "strip", z.ZodTypeAny, {
15
+ id: string;
16
+ description: string;
17
+ severity: "low" | "high" | "critical" | "medium";
18
+ fixable: boolean;
19
+ category: string;
20
+ }, {
21
+ id: string;
22
+ description: string;
23
+ severity: "low" | "high" | "critical" | "medium";
24
+ fixable: boolean;
25
+ category: string;
26
+ }>;
27
+ export declare const RulesFileSchema: z.ZodObject<{
28
+ rules: z.ZodArray<z.ZodObject<{
29
+ id: z.ZodString;
30
+ description: z.ZodString;
31
+ severity: z.ZodEnum<["low", "medium", "high", "critical"]>;
32
+ fixable: z.ZodBoolean;
33
+ category: z.ZodString;
34
+ }, "strip", z.ZodTypeAny, {
35
+ id: string;
36
+ description: string;
37
+ severity: "low" | "high" | "critical" | "medium";
38
+ fixable: boolean;
39
+ category: string;
40
+ }, {
41
+ id: string;
42
+ description: string;
43
+ severity: "low" | "high" | "critical" | "medium";
44
+ fixable: boolean;
45
+ category: string;
46
+ }>, "many">;
47
+ }, "strip", z.ZodTypeAny, {
48
+ rules: {
49
+ id: string;
50
+ description: string;
51
+ severity: "low" | "high" | "critical" | "medium";
52
+ fixable: boolean;
53
+ category: string;
54
+ }[];
55
+ }, {
56
+ rules: {
57
+ id: string;
58
+ description: string;
59
+ severity: "low" | "high" | "critical" | "medium";
60
+ fixable: boolean;
61
+ category: string;
62
+ }[];
63
+ }>;
64
+ export declare const ViolationSchema: z.ZodObject<{
65
+ rule_id: z.ZodString;
66
+ file: z.ZodString;
67
+ symbol: z.ZodString;
68
+ line_start: z.ZodNumber;
69
+ line_end: z.ZodNumber;
70
+ severity: z.ZodEnum<["low", "medium", "high", "critical"]>;
71
+ confidence: z.ZodNumber;
72
+ fixable: z.ZodBoolean;
73
+ message: z.ZodString;
74
+ suggested_fix: z.ZodOptional<z.ZodString>;
75
+ }, "strip", z.ZodTypeAny, {
76
+ symbol: string;
77
+ message: string;
78
+ file: string;
79
+ severity: "low" | "high" | "critical" | "medium";
80
+ fixable: boolean;
81
+ rule_id: string;
82
+ line_start: number;
83
+ line_end: number;
84
+ confidence: number;
85
+ suggested_fix?: string | undefined;
86
+ }, {
87
+ symbol: string;
88
+ message: string;
89
+ file: string;
90
+ severity: "low" | "high" | "critical" | "medium";
91
+ fixable: boolean;
92
+ rule_id: string;
93
+ line_start: number;
94
+ line_end: number;
95
+ confidence: number;
96
+ suggested_fix?: string | undefined;
97
+ }>;
98
+ export declare const ReviewResponseSchema: z.ZodObject<{
99
+ violations: z.ZodArray<z.ZodObject<{
100
+ rule_id: z.ZodString;
101
+ file: z.ZodString;
102
+ symbol: z.ZodString;
103
+ line_start: z.ZodNumber;
104
+ line_end: z.ZodNumber;
105
+ severity: z.ZodEnum<["low", "medium", "high", "critical"]>;
106
+ confidence: z.ZodNumber;
107
+ fixable: z.ZodBoolean;
108
+ message: z.ZodString;
109
+ suggested_fix: z.ZodOptional<z.ZodString>;
110
+ }, "strip", z.ZodTypeAny, {
111
+ symbol: string;
112
+ message: string;
113
+ file: string;
114
+ severity: "low" | "high" | "critical" | "medium";
115
+ fixable: boolean;
116
+ rule_id: string;
117
+ line_start: number;
118
+ line_end: number;
119
+ confidence: number;
120
+ suggested_fix?: string | undefined;
121
+ }, {
122
+ symbol: string;
123
+ message: string;
124
+ file: string;
125
+ severity: "low" | "high" | "critical" | "medium";
126
+ fixable: boolean;
127
+ rule_id: string;
128
+ line_start: number;
129
+ line_end: number;
130
+ confidence: number;
131
+ suggested_fix?: string | undefined;
132
+ }>, "many">;
133
+ status: z.ZodEnum<["pass", "fail"]>;
134
+ summary: z.ZodString;
135
+ }, "strip", z.ZodTypeAny, {
136
+ status: "pass" | "fail";
137
+ violations: {
138
+ symbol: string;
139
+ message: string;
140
+ file: string;
141
+ severity: "low" | "high" | "critical" | "medium";
142
+ fixable: boolean;
143
+ rule_id: string;
144
+ line_start: number;
145
+ line_end: number;
146
+ confidence: number;
147
+ suggested_fix?: string | undefined;
148
+ }[];
149
+ summary: string;
150
+ }, {
151
+ status: "pass" | "fail";
152
+ violations: {
153
+ symbol: string;
154
+ message: string;
155
+ file: string;
156
+ severity: "low" | "high" | "critical" | "medium";
157
+ fixable: boolean;
158
+ rule_id: string;
159
+ line_start: number;
160
+ line_end: number;
161
+ confidence: number;
162
+ suggested_fix?: string | undefined;
163
+ }[];
164
+ summary: string;
165
+ }>;
166
+ export declare const GuardConfigSchema: z.ZodObject<{
167
+ provider: z.ZodDefault<z.ZodEnum<["gemini", "openai"]>>;
168
+ model: z.ZodDefault<z.ZodString>;
169
+ apiKey: z.ZodOptional<z.ZodString>;
170
+ baseUrl: z.ZodOptional<z.ZodString>;
171
+ }, "strip", z.ZodTypeAny, {
172
+ model: string;
173
+ provider: "gemini" | "openai";
174
+ apiKey?: string | undefined;
175
+ baseUrl?: string | undefined;
176
+ }, {
177
+ apiKey?: string | undefined;
178
+ model?: string | undefined;
179
+ provider?: "gemini" | "openai" | undefined;
180
+ baseUrl?: string | undefined;
181
+ }>;
182
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/guard/schema.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,cAAc,kDAAgD,CAAC;AAE5E,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;EAMrB,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAE1B,CAAC;AAMH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAW1B,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAI/B,CAAC;AAMH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;EAK5B,CAAC"}
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ /**
3
+ * Zod schemas for rules file validation and LLM response contract enforcement.
4
+ * These ensure we never parse free text — all LLM output is validated against
5
+ * a strict schema before being used.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.GuardConfigSchema = exports.ReviewResponseSchema = exports.ViolationSchema = exports.RulesFileSchema = exports.RuleSchema = exports.SeveritySchema = void 0;
9
+ const zod_1 = require("zod");
10
+ // ---------------------------------------------------------------------------
11
+ // Rules file schema
12
+ // ---------------------------------------------------------------------------
13
+ exports.SeveritySchema = zod_1.z.enum(['low', 'medium', 'high', 'critical']);
14
+ exports.RuleSchema = zod_1.z.object({
15
+ id: zod_1.z.string().min(1),
16
+ description: zod_1.z.string().min(1),
17
+ severity: exports.SeveritySchema,
18
+ fixable: zod_1.z.boolean(),
19
+ category: zod_1.z.string().min(1),
20
+ });
21
+ exports.RulesFileSchema = zod_1.z.object({
22
+ rules: zod_1.z.array(exports.RuleSchema).min(1),
23
+ });
24
+ // ---------------------------------------------------------------------------
25
+ // Violation / LLM response schema
26
+ // ---------------------------------------------------------------------------
27
+ exports.ViolationSchema = zod_1.z.object({
28
+ rule_id: zod_1.z.string().min(1),
29
+ file: zod_1.z.string().min(1),
30
+ symbol: zod_1.z.string(),
31
+ line_start: zod_1.z.number().int().nonnegative(),
32
+ line_end: zod_1.z.number().int().nonnegative(),
33
+ severity: exports.SeveritySchema,
34
+ confidence: zod_1.z.number().min(0).max(1),
35
+ fixable: zod_1.z.boolean(),
36
+ message: zod_1.z.string().min(1),
37
+ suggested_fix: zod_1.z.string().optional(),
38
+ });
39
+ exports.ReviewResponseSchema = zod_1.z.object({
40
+ violations: zod_1.z.array(exports.ViolationSchema),
41
+ status: zod_1.z.enum(['pass', 'fail']),
42
+ summary: zod_1.z.string().min(1),
43
+ });
44
+ // ---------------------------------------------------------------------------
45
+ // Guard config schema (optional .skannr/guard.json)
46
+ // ---------------------------------------------------------------------------
47
+ exports.GuardConfigSchema = zod_1.z.object({
48
+ provider: zod_1.z.enum(['gemini', 'openai']).default('gemini'),
49
+ model: zod_1.z.string().default('gemini-2.0-flash-exp'),
50
+ apiKey: zod_1.z.string().optional(),
51
+ baseUrl: zod_1.z.string().optional(),
52
+ });
53
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/guard/schema.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEH,6BAAwB;AAExB,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAEjE,QAAA,cAAc,GAAG,OAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;AAE/D,QAAA,UAAU,GAAG,OAAC,CAAC,MAAM,CAAC;IACjC,EAAE,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACrB,WAAW,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,QAAQ,EAAE,sBAAc;IACxB,OAAO,EAAE,OAAC,CAAC,OAAO,EAAE;IACpB,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAC5B,CAAC,CAAC;AAEU,QAAA,eAAe,GAAG,OAAC,CAAC,MAAM,CAAC;IACtC,KAAK,EAAE,OAAC,CAAC,KAAK,CAAC,kBAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;CAClC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,kCAAkC;AAClC,8EAA8E;AAEjE,QAAA,eAAe,GAAG,OAAC,CAAC,MAAM,CAAC;IACtC,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1B,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE;IAClB,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IAC1C,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IACxC,QAAQ,EAAE,sBAAc;IACxB,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACpC,OAAO,EAAE,OAAC,CAAC,OAAO,EAAE;IACpB,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1B,aAAa,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CACrC,CAAC,CAAC;AAEU,QAAA,oBAAoB,GAAG,OAAC,CAAC,MAAM,CAAC;IAC3C,UAAU,EAAE,OAAC,CAAC,KAAK,CAAC,uBAAe,CAAC;IACpC,MAAM,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CAC3B,CAAC,CAAC;AAEH,8EAA8E;AAC9E,oDAAoD;AACpD,8EAA8E;AAEjE,QAAA,iBAAiB,GAAG,OAAC,CAAC,MAAM,CAAC;IACxC,QAAQ,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;IACxD,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,sBAAsB,CAAC;IACjD,MAAM,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC7B,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC/B,CAAC,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Build symbol-level diff units from staged git changes.
3
+ * Uses Skannr's existing parser to identify which symbols changed,
4
+ * instead of working with raw file diffs.
5
+ */
6
+ import type { SymbolDiffUnit } from './types';
7
+ /** Get the list of staged files (paths relative to root). */
8
+ export declare function getStagedFiles(root: string): string[];
9
+ /**
10
+ * Build symbol-level diff units for all staged files in the project.
11
+ * This is the primary input to the review runner.
12
+ */
13
+ export declare function buildSymbolDiffs(root: string): SymbolDiffUnit[];
14
+ /**
15
+ * Build symbol diffs from a PR diff string (for --pr-mode).
16
+ */
17
+ export declare function buildSymbolDiffsFromDiff(root: string, diffContent: string): SymbolDiffUnit[];
18
+ //# sourceMappingURL=symbol-diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"symbol-diff.d.ts","sourceRoot":"","sources":["../../src/guard/symbol-diff.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAQH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,6DAA6D;AAC7D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAUrD;AAmFD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,EAAE,CAgE/D;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,cAAc,EAAE,CA6C5F"}