@massu/core 1.10.8 → 1.12.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.
Files changed (178) hide show
  1. package/CHANGELOG.md +1303 -0
  2. package/dist/adapter.js +6 -4
  3. package/dist/cli.js +517 -397
  4. package/dist/detect/adapters/.bundle-shasums.json +4 -7
  5. package/dist/detect/adapters/rails.js +6 -4
  6. package/dist/detect/adapters/spring.js +6 -4
  7. package/dist/detect/adapters/tree-sitter-loader.js +6 -4
  8. package/dist/hooks/auto-learning-pipeline.js +24 -1
  9. package/dist/hooks/classify-failure.js +32 -3
  10. package/dist/hooks/cost-tracker.js +24 -0
  11. package/dist/hooks/fix-detector.js +97 -1
  12. package/dist/hooks/incident-pipeline.js +30 -1
  13. package/dist/hooks/intent-suggester.js +6 -3
  14. package/dist/hooks/post-edit-context.js +24 -1
  15. package/dist/hooks/post-tool-use.js +66 -18
  16. package/dist/hooks/pre-compact.js +27 -2
  17. package/dist/hooks/pre-delete-check.js +48 -22
  18. package/dist/hooks/pre-tool-use-gate.js +962 -0
  19. package/dist/hooks/quality-event.js +24 -0
  20. package/dist/hooks/rule-enforcement-pipeline.js +24 -1
  21. package/dist/hooks/security-gate.js +29 -16
  22. package/dist/hooks/session-end.js +24 -0
  23. package/dist/hooks/session-start.js +8710 -8460
  24. package/dist/hooks/user-prompt.js +24 -0
  25. package/package.json +15 -18
  26. package/commands/README.md +0 -134
  27. package/src/adapter.ts +0 -121
  28. package/src/adr-generator.ts +0 -292
  29. package/src/analytics.ts +0 -373
  30. package/src/audit-trail.ts +0 -450
  31. package/src/backfill-sessions.ts +0 -182
  32. package/src/changelog-generator.ts +0 -178
  33. package/src/cli.ts +0 -246
  34. package/src/cloud-sync.ts +0 -234
  35. package/src/commands/adapters.ts +0 -824
  36. package/src/commands/changelog.ts +0 -165
  37. package/src/commands/config-check-drift.ts +0 -133
  38. package/src/commands/config-refresh.ts +0 -396
  39. package/src/commands/config-upgrade.ts +0 -127
  40. package/src/commands/doctor.ts +0 -485
  41. package/src/commands/hook-runner.ts +0 -145
  42. package/src/commands/init.ts +0 -1823
  43. package/src/commands/install-commands.ts +0 -692
  44. package/src/commands/install-hooks.ts +0 -27
  45. package/src/commands/permissions.ts +0 -150
  46. package/src/commands/refresh-log.ts +0 -37
  47. package/src/commands/show-template.ts +0 -65
  48. package/src/commands/template-engine.ts +0 -301
  49. package/src/commands/watch.ts +0 -430
  50. package/src/config.ts +0 -765
  51. package/src/cost-tracker.ts +0 -355
  52. package/src/db.ts +0 -370
  53. package/src/dependency-scorer.ts +0 -337
  54. package/src/detect/adapters/aspnet.ts +0 -4
  55. package/src/detect/adapters/discover.ts +0 -469
  56. package/src/detect/adapters/file-sampler.ts +0 -225
  57. package/src/detect/adapters/go-chi.ts +0 -4
  58. package/src/detect/adapters/index.ts +0 -51
  59. package/src/detect/adapters/nextjs-trpc.ts +0 -166
  60. package/src/detect/adapters/parse-guard.ts +0 -133
  61. package/src/detect/adapters/phoenix.ts +0 -4
  62. package/src/detect/adapters/python-django.ts +0 -208
  63. package/src/detect/adapters/python-fastapi.ts +0 -223
  64. package/src/detect/adapters/python-flask.ts +0 -235
  65. package/src/detect/adapters/query-helpers.ts +0 -170
  66. package/src/detect/adapters/rails.ts +0 -4
  67. package/src/detect/adapters/runner.ts +0 -284
  68. package/src/detect/adapters/spring.ts +0 -4
  69. package/src/detect/adapters/swift-swiftui.ts +0 -171
  70. package/src/detect/adapters/tree-sitter-loader.ts +0 -517
  71. package/src/detect/adapters/types.ts +0 -191
  72. package/src/detect/codebase-introspector.ts +0 -210
  73. package/src/detect/domain-inferrer.ts +0 -197
  74. package/src/detect/drift.ts +0 -199
  75. package/src/detect/framework-detector.ts +0 -307
  76. package/src/detect/index.ts +0 -209
  77. package/src/detect/manifest-registry.ts +0 -261
  78. package/src/detect/migrate.ts +0 -362
  79. package/src/detect/monorepo-detector.ts +0 -348
  80. package/src/detect/package-detector.ts +0 -828
  81. package/src/detect/passthrough.ts +0 -108
  82. package/src/detect/regex-fallback.ts +0 -449
  83. package/src/detect/source-dir-detector.ts +0 -297
  84. package/src/detect/vr-command-map.ts +0 -167
  85. package/src/docs-map.json +0 -100
  86. package/src/docs-tools.ts +0 -519
  87. package/src/domains.ts +0 -182
  88. package/src/hooks/auto-learning-pipeline.ts +0 -228
  89. package/src/hooks/classify-failure.ts +0 -259
  90. package/src/hooks/cost-tracker.ts +0 -66
  91. package/src/hooks/fix-detector.ts +0 -186
  92. package/src/hooks/incident-pipeline.ts +0 -190
  93. package/src/hooks/intent-suggester.ts +0 -131
  94. package/src/hooks/post-edit-context.ts +0 -91
  95. package/src/hooks/post-tool-use.ts +0 -413
  96. package/src/hooks/pre-compact.ts +0 -168
  97. package/src/hooks/pre-delete-check.ts +0 -243
  98. package/src/hooks/quality-event.ts +0 -127
  99. package/src/hooks/rule-enforcement-pipeline.ts +0 -159
  100. package/src/hooks/security-gate.ts +0 -153
  101. package/src/hooks/session-end.ts +0 -467
  102. package/src/hooks/session-start.ts +0 -436
  103. package/src/hooks/user-prompt.ts +0 -157
  104. package/src/import-resolver.ts +0 -226
  105. package/src/knowledge-db.ts +0 -176
  106. package/src/knowledge-indexer.ts +0 -708
  107. package/src/knowledge-tools.ts +0 -1414
  108. package/src/lib/fileLock.ts +0 -203
  109. package/src/lib/framework-supports.ts +0 -85
  110. package/src/lib/gitToplevel.ts +0 -22
  111. package/src/lib/hook-registry.ts +0 -43
  112. package/src/lib/installLock.ts +0 -66
  113. package/src/lib/memory-path.ts +0 -49
  114. package/src/lib/pidLiveness.ts +0 -67
  115. package/src/lib/settings-local.ts +0 -110
  116. package/src/lib/sql-table-names.ts +0 -71
  117. package/src/license.ts +0 -547
  118. package/src/lsp/auto-detect.ts +0 -98
  119. package/src/lsp/client.ts +0 -776
  120. package/src/lsp/enrich.ts +0 -127
  121. package/src/lsp/types.ts +0 -221
  122. package/src/memory-db.ts +0 -1614
  123. package/src/memory-file-ingest.ts +0 -128
  124. package/src/memory-tools.ts +0 -424
  125. package/src/middleware-tree.ts +0 -71
  126. package/src/observability-tools.ts +0 -343
  127. package/src/observation-extractor.ts +0 -418
  128. package/src/page-deps.ts +0 -285
  129. package/src/permissions.ts +0 -422
  130. package/src/prompt-analyzer.ts +0 -332
  131. package/src/python/coupling-detector.ts +0 -125
  132. package/src/python/domain-enforcer.ts +0 -84
  133. package/src/python/impact-analyzer.ts +0 -96
  134. package/src/python/import-parser.ts +0 -244
  135. package/src/python/import-resolver.ts +0 -136
  136. package/src/python/migration-indexer.ts +0 -116
  137. package/src/python/migration-parser.ts +0 -332
  138. package/src/python/model-indexer.ts +0 -71
  139. package/src/python/model-parser.ts +0 -279
  140. package/src/python/route-indexer.ts +0 -59
  141. package/src/python/route-parser.ts +0 -317
  142. package/src/python-tools.ts +0 -630
  143. package/src/regression-detector.ts +0 -319
  144. package/src/rules.ts +0 -57
  145. package/src/schema-mapper.ts +0 -232
  146. package/src/security/adapter-origin.ts +0 -130
  147. package/src/security/adapter-verifier.ts +0 -319
  148. package/src/security/atomic-write.ts +0 -164
  149. package/src/security/fetcher.ts +0 -200
  150. package/src/security/install-tracking.ts +0 -319
  151. package/src/security/license-pubkey.generated.ts +0 -30
  152. package/src/security/license-response-verifier.ts +0 -149
  153. package/src/security/local-fingerprint.ts +0 -225
  154. package/src/security/manifest-cache.ts +0 -333
  155. package/src/security/manifest-schema.ts +0 -129
  156. package/src/security/registry-pubkey.generated.ts +0 -35
  157. package/src/security/telemetry.ts +0 -320
  158. package/src/security-scorer.ts +0 -405
  159. package/src/security-utils.ts +0 -133
  160. package/src/sentinel-db.ts +0 -580
  161. package/src/sentinel-scanner.ts +0 -406
  162. package/src/sentinel-tools.ts +0 -513
  163. package/src/sentinel-types.ts +0 -140
  164. package/src/server-dispatch.ts +0 -225
  165. package/src/server.ts +0 -107
  166. package/src/session-archiver.ts +0 -111
  167. package/src/session-state-generator.ts +0 -174
  168. package/src/team-knowledge.ts +0 -407
  169. package/src/tool-db-needs.ts +0 -232
  170. package/src/tools.ts +0 -1206
  171. package/src/transcript-parser.ts +0 -458
  172. package/src/trpc-index.ts +0 -215
  173. package/src/validate-features-runner.ts +0 -107
  174. package/src/validation-engine.ts +0 -358
  175. package/src/watch/daemon.ts +0 -385
  176. package/src/watch/lockfile-detector.ts +0 -65
  177. package/src/watch/paths.ts +0 -279
  178. package/src/watch/state.ts +0 -178
@@ -1,107 +0,0 @@
1
- #!/usr/bin/env npx tsx
2
- // Copyright (c) 2026 Massu. All rights reserved.
3
- // Licensed under BSL 1.1 - see LICENSE file for details.
4
-
5
- // ============================================================
6
- // Standalone feature validation runner
7
- // Called by scripts/validate-features.sh
8
- // Directly imports sentinel-db.ts (no MCP protocol needed)
9
- // ============================================================
10
-
11
- import Database from 'better-sqlite3';
12
- import { resolve } from 'path';
13
- import { existsSync } from 'fs';
14
- import { getProjectRoot, getResolvedPaths } from './config.ts';
15
- import { t } from './lib/sql-table-names.ts';
16
-
17
- const PROJECT_ROOT = getProjectRoot();
18
-
19
- function main(): void {
20
- const dbPath = getResolvedPaths().dataDbPath;
21
-
22
- if (!existsSync(dbPath)) {
23
- console.log('Sentinel: No data DB found - skipping feature validation (run sync first)');
24
- process.exit(0);
25
- }
26
-
27
- let db: Database.Database;
28
- try {
29
- db = new Database(dbPath, { readonly: true });
30
- db.pragma('journal_mode = WAL');
31
- } catch (error) {
32
- console.log('Sentinel: Could not open data DB - skipping feature validation');
33
- process.exit(0);
34
- }
35
-
36
- try {
37
- // Check if sentinel tables exist
38
- const tableExists = db.prepare(
39
- "SELECT name FROM sqlite_master WHERE type='table' AND name=`${t('sentinel')}`"
40
- ).get();
41
-
42
- if (!tableExists) {
43
- console.log('Sentinel: Feature registry not initialized - skipping (run sync first)');
44
- process.exit(0);
45
- }
46
-
47
- // Count active features
48
- const totalActive = db.prepare(
49
- `SELECT COUNT(*) as count FROM ${t('sentinel')} WHERE status = 'active'`
50
- ).get() as { count: number };
51
-
52
- if (totalActive.count === 0) {
53
- console.log('Sentinel: No active features registered - skipping validation');
54
- process.exit(0);
55
- }
56
-
57
- // Check for orphaned features (active features with missing primary component files)
58
- const orphaned = db.prepare(`
59
- SELECT s.feature_key, s.title, s.priority, c.component_file
60
- FROM ${t('sentinel')} s
61
- JOIN ${t('sentinel_components')} c ON c.feature_id = s.id AND c.is_primary = 1
62
- WHERE s.status = 'active'
63
- ORDER BY s.priority DESC, s.domain, s.feature_key
64
- `).all() as { feature_key: string; title: string; priority: string; component_file: string }[];
65
-
66
- const missingFeatures: { feature_key: string; title: string; priority: string; missing_file: string }[] = [];
67
-
68
- for (const row of orphaned) {
69
- const absPath = resolve(PROJECT_ROOT, row.component_file);
70
- if (!existsSync(absPath)) {
71
- missingFeatures.push({
72
- feature_key: row.feature_key,
73
- title: row.title,
74
- priority: row.priority,
75
- missing_file: row.component_file,
76
- });
77
- }
78
- }
79
-
80
- console.log(`Sentinel: ${totalActive.count} active features, checking primary components...`);
81
-
82
- if (missingFeatures.length === 0) {
83
- console.log('Sentinel: All active features have living primary components. PASS');
84
- process.exit(0);
85
- } else {
86
- console.error(`Sentinel: ${missingFeatures.length} features have MISSING primary components:`);
87
- for (const f of missingFeatures) {
88
- console.error(` [${f.priority}] ${f.feature_key}: ${f.title}`);
89
- console.error(` Missing: ${f.missing_file}`);
90
- }
91
-
92
- const criticalCount = missingFeatures.filter(f => f.priority === 'critical').length;
93
- if (criticalCount > 0) {
94
- console.error(`\nFAIL: ${criticalCount} CRITICAL features are orphaned. Fix before committing.`);
95
- process.exit(1);
96
- } else {
97
- console.warn(`\nWARN: ${missingFeatures.length} features are orphaned (non-critical). Consider updating registry.`);
98
- // Non-critical orphans are warnings, not blockers
99
- process.exit(0);
100
- }
101
- }
102
- } finally {
103
- db.close();
104
- }
105
- }
106
-
107
- main();
@@ -1,358 +0,0 @@
1
- // Copyright (c) 2026 Massu. All rights reserved.
2
- // Licensed under BSL 1.1 - see LICENSE file for details.
3
-
4
- import type Database from 'better-sqlite3';
5
- import type { ToolDefinition, ToolResult } from './tools.ts';
6
- import { getConfig } from './config.ts';
7
- import { resolveImportPath } from './import-resolver.ts';
8
- import { existsSync, readFileSync } from 'fs';
9
- import { ensureWithinRoot, globToSafeRegex, safeRegex } from './security-utils.ts';
10
-
11
- // ============================================================
12
- // AI Output Validation Engine
13
- // ============================================================
14
-
15
- /** Prefix a base tool name with the configured tool prefix. */
16
- function p(baseName: string): string {
17
- return `${getConfig().toolPrefix}_${baseName}`;
18
- }
19
-
20
- export interface ValidationCheck {
21
- name: string;
22
- severity: 'info' | 'warning' | 'error' | 'critical';
23
- message: string;
24
- line?: number;
25
- file?: string;
26
- }
27
-
28
- /**
29
- * Get validation checks from config, or defaults.
30
- * Derives pattern checks from the project's configured rules.
31
- */
32
- function getValidationChecks(): Record<string, boolean> {
33
- return getConfig().governance?.validation?.checks ?? {
34
- rule_compliance: true,
35
- import_existence: true,
36
- naming_conventions: true,
37
- };
38
- }
39
-
40
- /**
41
- * Get custom validation patterns from config.
42
- */
43
- function getCustomPatterns(): Array<{ pattern: string; severity: string; message: string }> {
44
- return getConfig().governance?.validation?.custom_patterns ?? [];
45
- }
46
-
47
- /**
48
- * Validate a file against configured rules and custom patterns.
49
- */
50
- export function validateFile(
51
- filePath: string,
52
- projectRoot: string
53
- ): ValidationCheck[] {
54
- const checks: ValidationCheck[] = [];
55
- const config = getConfig();
56
- const activeChecks = getValidationChecks();
57
- const customPatterns = getCustomPatterns();
58
-
59
- let absPath: string;
60
- try {
61
- absPath = ensureWithinRoot(filePath, projectRoot);
62
- } catch {
63
- checks.push({
64
- name: 'path_traversal',
65
- severity: 'critical',
66
- message: `Path traversal blocked: ${filePath}`,
67
- file: filePath,
68
- });
69
- return checks;
70
- }
71
- if (!existsSync(absPath)) {
72
- checks.push({
73
- name: 'file_exists',
74
- severity: 'error',
75
- message: `File not found: ${filePath}`,
76
- file: filePath,
77
- });
78
- return checks;
79
- }
80
-
81
- const source = readFileSync(absPath, 'utf-8');
82
- const lines = source.split('\n');
83
-
84
- // Rule compliance check - uses project rules
85
- if (activeChecks.rule_compliance !== false) {
86
- // Check rules from config
87
- for (const ruleSet of config.rules) {
88
- const rulePattern = globToSafeRegex(ruleSet.pattern);
89
- if (rulePattern.test(filePath)) {
90
- for (const rule of ruleSet.rules) {
91
- // Rules are human-readable; we can't automatically enforce all of them,
92
- // but we can check for patterns that indicate violations
93
- checks.push({
94
- name: 'rule_applicable',
95
- severity: 'info',
96
- message: `Rule applies: ${rule}`,
97
- file: filePath,
98
- });
99
- }
100
- }
101
- }
102
- }
103
-
104
- // Import existence check
105
- if (activeChecks.import_existence !== false) {
106
- for (let i = 0; i < lines.length; i++) {
107
- const line = lines[i];
108
- const importMatch = line.match(/^\s*import\s+.*from\s+['"]([^'"]+)['"]/);
109
- if (importMatch) {
110
- const specifier = importMatch[1];
111
- // Only check relative imports
112
- if (specifier.startsWith('.') || specifier.startsWith('@/')) {
113
- const resolved = resolveImportPath(specifier, filePath);
114
- if (!resolved) {
115
- checks.push({
116
- name: 'import_hallucination',
117
- severity: 'error',
118
- message: `Import target does not exist: ${specifier}`,
119
- line: i + 1,
120
- file: filePath,
121
- });
122
- }
123
- }
124
- }
125
- }
126
- }
127
-
128
- // Custom patterns check - uses safeRegex to prevent ReDoS from config
129
- for (const customPattern of customPatterns) {
130
- const regex = safeRegex(customPattern.pattern);
131
- if (!regex) {
132
- checks.push({
133
- name: 'config_warning',
134
- severity: 'warning',
135
- message: `Custom pattern rejected (invalid or unsafe regex): ${customPattern.pattern.slice(0, 50)}`,
136
- file: filePath,
137
- });
138
- continue;
139
- }
140
- for (let i = 0; i < lines.length; i++) {
141
- if (regex.test(lines[i])) {
142
- checks.push({
143
- name: 'custom_pattern',
144
- severity: customPattern.severity as 'info' | 'warning' | 'error' | 'critical',
145
- message: customPattern.message,
146
- line: i + 1,
147
- file: filePath,
148
- });
149
- }
150
- }
151
- }
152
-
153
- // DB access pattern check (if configured)
154
- if (config.dbAccessPattern) {
155
- const wrongPattern = config.dbAccessPattern === 'ctx.db.{table}'
156
- ? /ctx\.prisma\./
157
- : null;
158
- if (wrongPattern) {
159
- for (let i = 0; i < lines.length; i++) {
160
- if (wrongPattern.test(lines[i])) {
161
- checks.push({
162
- name: 'db_access_pattern',
163
- severity: 'error',
164
- message: `Wrong DB access pattern. Use ${config.dbAccessPattern}`,
165
- line: i + 1,
166
- file: filePath,
167
- });
168
- }
169
- }
170
- }
171
- }
172
-
173
- return checks;
174
- }
175
-
176
- /**
177
- * Store validation results.
178
- */
179
- export function storeValidationResult(
180
- db: Database.Database,
181
- filePath: string,
182
- checks: ValidationCheck[],
183
- sessionId?: string,
184
- validationType = 'file_validation'
185
- ): void {
186
- const errors = checks.filter(c => c.severity === 'error' || c.severity === 'critical');
187
- const warnings = checks.filter(c => c.severity === 'warning');
188
- const passed = errors.length === 0;
189
- const rulesViolated = [...errors, ...warnings].map(c => c.name).join(', ');
190
-
191
- db.prepare(`
192
- INSERT INTO validation_results (session_id, file_path, validation_type, passed, details, rules_violated)
193
- VALUES (?, ?, ?, ?, ?, ?)
194
- `).run(
195
- sessionId ?? null, filePath, validationType,
196
- passed ? 1 : 0, JSON.stringify(checks), rulesViolated || null
197
- );
198
- }
199
-
200
- // ============================================================
201
- // MCP Tool Definitions & Handlers
202
- // ============================================================
203
-
204
- export function getValidationToolDefinitions(): ToolDefinition[] {
205
- return [
206
- {
207
- name: p('validation_check'),
208
- description: 'Validate a file against project rules and custom patterns. Checks import existence, rule compliance, DB access patterns, and custom patterns.',
209
- inputSchema: {
210
- type: 'object',
211
- properties: {
212
- file: { type: 'string', description: 'File path relative to project root' },
213
- session_id: { type: 'string', description: 'Session ID for tracking (optional)' },
214
- },
215
- required: ['file'],
216
- },
217
- },
218
- {
219
- name: p('validation_report'),
220
- description: 'Validation summary across recent sessions. Shows error/warning trends and most-violated rules.',
221
- inputSchema: {
222
- type: 'object',
223
- properties: {
224
- days: { type: 'number', description: 'Days to look back (default: 7)' },
225
- },
226
- required: [],
227
- },
228
- },
229
- ];
230
- }
231
-
232
- const VALIDATION_BASE_NAMES = new Set(['validation_check', 'validation_report']);
233
-
234
- export function isValidationTool(name: string): boolean {
235
- const pfx = getConfig().toolPrefix + '_';
236
- const baseName = name.startsWith(pfx) ? name.slice(pfx.length) : name;
237
- return VALIDATION_BASE_NAMES.has(baseName);
238
- }
239
-
240
- export function handleValidationToolCall(
241
- name: string,
242
- args: Record<string, unknown>,
243
- memoryDb: Database.Database
244
- ): ToolResult {
245
- try {
246
- const pfx = getConfig().toolPrefix + '_';
247
- const baseName = name.startsWith(pfx) ? name.slice(pfx.length) : name;
248
-
249
- switch (baseName) {
250
- case 'validation_check':
251
- return handleValidateFile(args, memoryDb);
252
- case 'validation_report':
253
- return handleValidationReport(args, memoryDb);
254
- default:
255
- return text(`Unknown validation tool: ${name}`);
256
- }
257
- } catch (error) {
258
- return text(`Error in ${name}: ${error instanceof Error ? error.message : String(error)}\n\nUsage: ${p('validation_check')} { file: "src/path/file.ts" }, ${p('validation_report')} { days: 7 }`);
259
- }
260
- }
261
-
262
- function handleValidateFile(args: Record<string, unknown>, db: Database.Database): ToolResult {
263
- const file = args.file as string;
264
- if (!file) return text(`Usage: ${p('validation_check')} { file: "src/path/file.ts" } - Validate a file against project rules and custom patterns.`);
265
-
266
- const config = getConfig();
267
- const checks = validateFile(file, config.project.root);
268
-
269
- // Store results
270
- storeValidationResult(db, file, checks, args.session_id as string | undefined);
271
-
272
- if (checks.length === 0) {
273
- return text(`## Validation: ${file}\n\nNo issues found. File passes all configured validation checks.`);
274
- }
275
-
276
- const errors = checks.filter(c => c.severity === 'error' || c.severity === 'critical');
277
- const warnings = checks.filter(c => c.severity === 'warning');
278
- const info = checks.filter(c => c.severity === 'info');
279
-
280
- const lines = [
281
- `## Validation: ${file}`,
282
- `Errors: ${errors.length} | Warnings: ${warnings.length} | Info: ${info.length}`,
283
- '',
284
- ];
285
-
286
- if (errors.length > 0) {
287
- lines.push('### Errors');
288
- for (const check of errors) {
289
- const loc = check.line ? `:${check.line}` : '';
290
- lines.push(`- [${check.severity.toUpperCase()}] ${check.name}${loc}: ${check.message}`);
291
- }
292
- lines.push('');
293
- }
294
-
295
- if (warnings.length > 0) {
296
- lines.push('### Warnings');
297
- for (const check of warnings) {
298
- const loc = check.line ? `:${check.line}` : '';
299
- lines.push(`- [WARN] ${check.name}${loc}: ${check.message}`);
300
- }
301
- lines.push('');
302
- }
303
-
304
- if (info.length > 0) {
305
- lines.push('### Info');
306
- for (const check of info) {
307
- lines.push(`- ${check.name}: ${check.message}`);
308
- }
309
- }
310
-
311
- return text(lines.join('\n'));
312
- }
313
-
314
- function handleValidationReport(args: Record<string, unknown>, db: Database.Database): ToolResult {
315
- const days = (args.days as number) ?? 7;
316
-
317
- const results = db.prepare(`
318
- SELECT file_path, passed, rules_violated, created_at
319
- FROM validation_results
320
- WHERE created_at >= datetime('now', ?)
321
- ORDER BY created_at DESC
322
- `).all(`-${days} days`) as Array<Record<string, unknown>>;
323
-
324
- if (results.length === 0) {
325
- return text(`No validation results found in the last ${days} days. Run ${p('validation_check')} { file: "src/path/to/file.ts" } on specific files to generate validation data, or try a longer time range with { days: 90 }.`);
326
- }
327
-
328
- const passedCount = results.filter(r => r.passed === 1).length;
329
- const failedCount = results.filter(r => r.passed === 0).length;
330
-
331
- const lines = [
332
- `## Validation Report (${days} days)`,
333
- `Files validated: ${results.length}`,
334
- `Passed: ${passedCount}`,
335
- `Failed: ${failedCount}`,
336
- '',
337
- '### Failed Validations',
338
- '| File | Rules Violated | Date |',
339
- '|------|----------------|------|',
340
- ];
341
-
342
- const failedResults = results.filter(r => r.passed === 0);
343
- for (const r of failedResults.slice(0, 30)) {
344
- const filename = (r.file_path as string).split('/').pop();
345
- const rules = (r.rules_violated as string) ?? '-';
346
- lines.push(`| ${filename} | ${rules.slice(0, 60)} | ${(r.created_at as string).slice(0, 10)} |`);
347
- }
348
-
349
- if (failedResults.length > 30) {
350
- lines.push(`... and ${failedResults.length - 30} more`);
351
- }
352
-
353
- return text(lines.join('\n'));
354
- }
355
-
356
- function text(content: string): ToolResult {
357
- return { content: [{ type: 'text', text: content }] };
358
- }