@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
package/src/analytics.ts DELETED
@@ -1,373 +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
-
8
- // ============================================================
9
- // Quality Trend Analytics
10
- // ============================================================
11
-
12
- /** Prefix a base tool name with the configured tool prefix. */
13
- function p(baseName: string): string {
14
- return `${getConfig().toolPrefix}_${baseName}`;
15
- }
16
-
17
- export interface QualityBreakdown {
18
- security: number;
19
- architecture: number;
20
- coupling: number;
21
- tests: number;
22
- rule_compliance: number;
23
- [key: string]: number;
24
- }
25
-
26
- /** Default scoring weights. Can be overridden via config.analytics.quality.weights */
27
- const DEFAULT_WEIGHTS: Record<string, number> = {
28
- bug_found: -5,
29
- vr_failure: -10,
30
- incident: -20,
31
- cr_violation: -3,
32
- vr_pass: 2,
33
- clean_commit: 5,
34
- successful_verification: 3,
35
- };
36
-
37
- /** Default quality categories */
38
- const DEFAULT_CATEGORIES = ['security', 'architecture', 'coupling', 'tests', 'rule_compliance'];
39
-
40
- /**
41
- * Get the scoring weights from config or defaults.
42
- */
43
- function getWeights(): Record<string, number> {
44
- return getConfig().analytics?.quality?.weights ?? DEFAULT_WEIGHTS;
45
- }
46
-
47
- /**
48
- * Get the quality categories from config or defaults.
49
- */
50
- function getCategories(): string[] {
51
- return getConfig().analytics?.quality?.categories ?? DEFAULT_CATEGORIES;
52
- }
53
-
54
- /**
55
- * Calculate a quality score from observations in a session.
56
- * Score starts at 50 and adjusts based on weighted events.
57
- */
58
- export function calculateQualityScore(
59
- db: Database.Database,
60
- sessionId: string
61
- ): { score: number; breakdown: QualityBreakdown } {
62
- const weights = getWeights();
63
- const categories = getCategories();
64
-
65
- const observations = db.prepare(
66
- 'SELECT type, detail FROM observations WHERE session_id = ?'
67
- ).all(sessionId) as Array<{ type: string; detail: string }>;
68
-
69
- let score = 50; // Base score
70
- const breakdown: QualityBreakdown = Object.fromEntries(
71
- categories.map(c => [c, 0])
72
- ) as QualityBreakdown;
73
-
74
- for (const obs of observations) {
75
- const weight = weights[obs.type] ?? 0;
76
- score += weight;
77
-
78
- // Categorize observation
79
- const desc = (obs.detail ?? '').toLowerCase();
80
- for (const category of categories) {
81
- if (desc.includes(category)) {
82
- breakdown[category] += weight;
83
- }
84
- }
85
- }
86
-
87
- return {
88
- score: Math.max(0, Math.min(100, score)),
89
- breakdown,
90
- };
91
- }
92
-
93
- /**
94
- * Store a quality score for a session.
95
- */
96
- export function storeQualityScore(
97
- db: Database.Database,
98
- sessionId: string,
99
- score: number,
100
- breakdown: QualityBreakdown
101
- ): void {
102
- db.prepare(`
103
- INSERT INTO session_quality_scores
104
- (session_id, score, security_score, architecture_score, coupling_score, test_score, rule_compliance_score)
105
- VALUES (?, ?, ?, ?, ?, ?, ?)
106
- `).run(
107
- sessionId, score,
108
- breakdown.security ?? 0,
109
- breakdown.architecture ?? 0,
110
- breakdown.coupling ?? 0,
111
- breakdown.tests ?? 0,
112
- breakdown.rule_compliance ?? 0
113
- );
114
- }
115
-
116
- /**
117
- * Backfill quality scores for sessions that don't have them.
118
- */
119
- export function backfillQualityScores(db: Database.Database): number {
120
- const sessions = db.prepare(`
121
- SELECT DISTINCT s.session_id
122
- FROM sessions s
123
- LEFT JOIN session_quality_scores q ON s.session_id = q.session_id
124
- WHERE q.session_id IS NULL
125
- `).all() as Array<{ session_id: string }>;
126
-
127
- let backfilled = 0;
128
- for (const session of sessions) {
129
- const { score, breakdown } = calculateQualityScore(db, session.session_id);
130
- storeQualityScore(db, session.session_id, score, breakdown);
131
- backfilled++;
132
- }
133
-
134
- return backfilled;
135
- }
136
-
137
- // ============================================================
138
- // MCP Tool Definitions & Handlers
139
- // ============================================================
140
-
141
- export function getAnalyticsToolDefinitions(): ToolDefinition[] {
142
- return [
143
- {
144
- name: p('quality_score'),
145
- description: 'Calculate and store quality score for a session based on observations. Shows breakdown by category.',
146
- inputSchema: {
147
- type: 'object',
148
- properties: {
149
- session_id: { type: 'string', description: 'Session ID to score' },
150
- },
151
- required: ['session_id'],
152
- },
153
- },
154
- {
155
- name: p('quality_trend'),
156
- description: 'Quality trend over recent sessions. Shows score progression and identifies improving/declining areas.',
157
- inputSchema: {
158
- type: 'object',
159
- properties: {
160
- days: { type: 'number', description: 'Number of days to look back (default: 30)' },
161
- },
162
- required: [],
163
- },
164
- },
165
- {
166
- name: p('quality_report'),
167
- description: 'Comprehensive quality report with averages, trends, and recommendations.',
168
- inputSchema: {
169
- type: 'object',
170
- properties: {
171
- days: { type: 'number', description: 'Days to cover (default: 30)' },
172
- },
173
- required: [],
174
- },
175
- },
176
- ];
177
- }
178
-
179
- const ANALYTICS_BASE_NAMES = new Set(['quality_score', 'quality_trend', 'quality_report']);
180
-
181
- export function isAnalyticsTool(name: string): boolean {
182
- const pfx = getConfig().toolPrefix + '_';
183
- const baseName = name.startsWith(pfx) ? name.slice(pfx.length) : name;
184
- return ANALYTICS_BASE_NAMES.has(baseName);
185
- }
186
-
187
- export function handleAnalyticsToolCall(
188
- name: string,
189
- args: Record<string, unknown>,
190
- memoryDb: Database.Database
191
- ): ToolResult {
192
- try {
193
- const pfx = getConfig().toolPrefix + '_';
194
- const baseName = name.startsWith(pfx) ? name.slice(pfx.length) : name;
195
-
196
- switch (baseName) {
197
- case 'quality_score':
198
- return handleQualityScore(args, memoryDb);
199
- case 'quality_trend':
200
- return handleQualityTrend(args, memoryDb);
201
- case 'quality_report':
202
- return handleQualityReport(args, memoryDb);
203
- default:
204
- return text(`Unknown analytics tool: ${name}`);
205
- }
206
- } catch (error) {
207
- return text(`Error in ${name}: ${error instanceof Error ? error.message : String(error)}\n\nUsage: ${p('quality_score')} { session_id: "..." }, ${p('quality_trend')} { days: 30 }`);
208
- }
209
- }
210
-
211
- function handleQualityScore(args: Record<string, unknown>, db: Database.Database): ToolResult {
212
- const sessionId = args.session_id as string;
213
- if (!sessionId) return text(`Usage: ${p('quality_score')} { session_id: "abc123" } - Calculate quality score for a specific session.`);
214
-
215
- const { score, breakdown } = calculateQualityScore(db, sessionId);
216
- storeQualityScore(db, sessionId, score, breakdown);
217
-
218
- const categories = getCategories();
219
- const lines = [
220
- `## Quality Score: ${score}/100`,
221
- '',
222
- '### Breakdown',
223
- '| Category | Impact |',
224
- '|----------|--------|',
225
- ];
226
-
227
- for (const category of categories) {
228
- const impact = breakdown[category] ?? 0;
229
- const indicator = impact > 0 ? '+' : impact < 0 ? '' : ' ';
230
- lines.push(`| ${category} | ${indicator}${impact} |`);
231
- }
232
-
233
- return text(lines.join('\n'));
234
- }
235
-
236
- function handleQualityTrend(args: Record<string, unknown>, db: Database.Database, retried = false): ToolResult {
237
- const days = (args.days as number) ?? 30;
238
-
239
- const scores = db.prepare(`
240
- SELECT session_id, score, security_score, architecture_score, coupling_score, test_score, rule_compliance_score, created_at
241
- FROM session_quality_scores
242
- WHERE created_at >= datetime('now', ?)
243
- ORDER BY created_at ASC
244
- `).all(`-${days} days`) as Array<{
245
- session_id: string;
246
- score: number;
247
- created_at: string;
248
- }>;
249
-
250
- if (scores.length === 0) {
251
- if (!retried) {
252
- const backfilled = backfillQualityScores(db);
253
- if (backfilled > 0) {
254
- return handleQualityTrend(args, db, true);
255
- }
256
- }
257
- return text(`No quality scores found in the last ${days} days. Quality scores are calculated automatically from session observations (bugfixes, VR checks, incidents). Try: ${p('quality_score')} { session_id: "..." } to score a specific session, or try a longer time range with { days: 90 }.`);
258
- }
259
-
260
- const avg = scores.reduce((sum, s) => sum + s.score, 0) / scores.length;
261
- const recent = scores.slice(-5);
262
- const recentAvg = recent.reduce((sum, s) => sum + s.score, 0) / recent.length;
263
-
264
- const trend = recentAvg > avg ? 'IMPROVING' : recentAvg < avg - 5 ? 'DECLINING' : 'STABLE';
265
-
266
- const lines = [
267
- `## Quality Trend (${days} days)`,
268
- `Sessions scored: ${scores.length}`,
269
- `Average: ${avg.toFixed(1)}`,
270
- `Recent (last 5): ${recentAvg.toFixed(1)} [${trend}]`,
271
- '',
272
- '### Recent Scores',
273
- '| Session | Score | Date |',
274
- '|---------|-------|------|',
275
- ];
276
-
277
- for (const s of scores.slice(-10)) {
278
- lines.push(`| ${s.session_id.slice(0, 8)}... | ${s.score} | ${s.created_at} |`);
279
- }
280
-
281
- return text(lines.join('\n'));
282
- }
283
-
284
- function handleQualityReport(args: Record<string, unknown>, db: Database.Database, retried = false): ToolResult {
285
- const days = (args.days as number) ?? 30;
286
-
287
- const scores = db.prepare(`
288
- SELECT score, security_score, architecture_score, coupling_score, test_score, rule_compliance_score
289
- FROM session_quality_scores
290
- WHERE created_at >= datetime('now', ?)
291
- `).all(`-${days} days`) as Array<Record<string, number>>;
292
-
293
- if (scores.length === 0) {
294
- if (!retried) {
295
- const backfilled = backfillQualityScores(db);
296
- if (backfilled > 0) {
297
- return handleQualityReport(args, db, true);
298
- }
299
- }
300
- return text(`No quality data available for the last ${days} days. Quality scores are calculated from session observations (bugfixes, VR checks, incidents). Try: ${p('quality_score')} { session_id: "..." } to score individual sessions first, or try a longer time range with { days: 90 }.`);
301
- }
302
-
303
- const avg = scores.reduce((sum, s) => sum + s.score, 0) / scores.length;
304
- const max = Math.max(...scores.map(s => s.score));
305
- const min = Math.min(...scores.map(s => s.score));
306
-
307
- const categoryColumns: Record<string, string> = {
308
- security: 'security_score',
309
- architecture: 'architecture_score',
310
- coupling: 'coupling_score',
311
- tests: 'test_score',
312
- rule_compliance: 'rule_compliance_score',
313
- };
314
-
315
- const categories = getCategories();
316
- const categoryTotals: Record<string, number> = {};
317
- for (const category of categories) {
318
- categoryTotals[category] = 0;
319
- }
320
-
321
- for (const s of scores) {
322
- for (const category of categories) {
323
- const col = categoryColumns[category];
324
- if (col) {
325
- categoryTotals[category] += s[col] ?? 0;
326
- }
327
- }
328
- }
329
-
330
- const lines = [
331
- `## Quality Report (${days} days)`,
332
- '',
333
- '### Summary',
334
- `| Metric | Value |`,
335
- `|--------|-------|`,
336
- `| Sessions | ${scores.length} |`,
337
- `| Average Score | ${avg.toFixed(1)} |`,
338
- `| Best Session | ${max} |`,
339
- `| Worst Session | ${min} |`,
340
- '',
341
- '### Category Impact (Cumulative)',
342
- '| Category | Total Impact | Avg/Session |',
343
- '|----------|-------------|-------------|',
344
- ];
345
-
346
- for (const category of categories) {
347
- const total = categoryTotals[category];
348
- const avgCat = total / scores.length;
349
- lines.push(`| ${category} | ${total > 0 ? '+' : ''}${total} | ${avgCat > 0 ? '+' : ''}${avgCat.toFixed(1)} |`);
350
- }
351
-
352
- // Recommendations
353
- const worstCategory = categories.reduce((worst, cat) =>
354
- categoryTotals[cat] < categoryTotals[worst] ? cat : worst, categories[0]);
355
-
356
- lines.push('');
357
- lines.push('### Recommendations');
358
- if (avg < 40) {
359
- lines.push('- **Critical**: Average quality below 40. Focus on reducing incidents and VR failures.');
360
- }
361
- if (categoryTotals[worstCategory] < -10) {
362
- lines.push(`- **Focus Area**: ${worstCategory} has the most negative impact (${categoryTotals[worstCategory]}). Prioritize improvements here.`);
363
- }
364
- if (avg >= 70) {
365
- lines.push('- Quality is good. Maintain current practices and focus on consistency.');
366
- }
367
-
368
- return text(lines.join('\n'));
369
- }
370
-
371
- function text(content: string): ToolResult {
372
- return { content: [{ type: 'text', text: content }] };
373
- }