@massu/core 1.10.8 → 1.12.1

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 +1334 -0
  2. package/dist/adapter.js +6 -4
  3. package/dist/cli.js +581 -448
  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 +67 -19
  16. package/dist/hooks/pre-compact.js +28 -3
  17. package/dist/hooks/pre-delete-check.js +53 -27
  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 +31 -6
  23. package/dist/hooks/session-start.js +8709 -8459
  24. package/dist/hooks/user-prompt.js +25 -1
  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,186 +0,0 @@
1
- #!/usr/bin/env node
2
- // Copyright (c) 2026 Massu. All rights reserved.
3
- // Licensed under BSL 1.1 - see LICENSE file for details.
4
-
5
- // ============================================================
6
- // PostToolUse Hook: Fix Detector
7
- // Detects when a bug fix is being applied during a session by
8
- // analyzing git diffs of edited files. Sets session-level state
9
- // so the auto-learning pipeline can trigger at session end.
10
- //
11
- // Part of the Auto-Learning Pipeline:
12
- // [Fix Detected] → Incident Report → Rule → Enforcement
13
- //
14
- // Must complete in <1000ms.
15
- // ============================================================
16
-
17
- import { execFileSync } from 'child_process';
18
- import { existsSync, appendFileSync, mkdirSync, readFileSync } from 'fs';
19
- import { tmpdir } from 'os';
20
- import { join } from 'path';
21
- import { getProjectRoot, getConfig } from '../config.ts';
22
-
23
- interface HookInput {
24
- session_id: string;
25
- tool_name: string;
26
- tool_input: { file_path?: string };
27
- }
28
-
29
- interface FixSignal {
30
- file: string;
31
- signals: string[];
32
- timestamp: string;
33
- }
34
-
35
- // Fix detection heuristics — each returns true if the pattern matches
36
- const FIX_HEURISTICS: Array<{ name: string; test: (diff: string) => boolean }> = [
37
- {
38
- name: 'removed_broken_code',
39
- test: (diff) => /^-.*\b(bug|broken|wrong|incorrect|typo|crash|error|fail|miss|stale)\b/m.test(diff),
40
- },
41
- {
42
- name: 'added_error_handling',
43
- test: (diff) => {
44
- const added = (diff.match(/^\+.*(try|except|catch|guard|if.*nil|if.*None|validate|assert|raise|throw)/gm) || []).length;
45
- return added > 2;
46
- },
47
- },
48
- {
49
- name: 'method_name_correction',
50
- test: (diff) => {
51
- const removed = diff.match(/^-.*\.([a-z_]+)\(/m);
52
- const added = diff.match(/^\+.*\.([a-z_]+)\(/m);
53
- return !!(removed && added && removed[1] !== added[1]);
54
- },
55
- },
56
- {
57
- name: 'auth_fix',
58
- test: (diff) => /^\+.*(token|auth|header|X-Service|Bearer|credential)/im.test(diff),
59
- },
60
- {
61
- name: 'nil_handling_fix',
62
- test: (diff) => /^\+.*(= nil|= None|\.isNil|is None|!= nil|is not None|guard let|if let|optional)/m.test(diff) && /^-/m.test(diff),
63
- },
64
- {
65
- name: 'concurrency_fix',
66
- test: (diff) => /^\+.*(timeout|semaphore|lock|mutex|throttle|rate.limit|max_conn)/im.test(diff),
67
- },
68
- {
69
- name: 'async_pattern_fix',
70
- test: (diff) => /^\+.*(@MainActor|async with|asyncio\.timeout|\.await)/.test(diff) && /^-/m.test(diff),
71
- },
72
- {
73
- name: 'added_missing_import',
74
- test: (diff) => /^\+.*(import|from.*import|require)/.test(diff) && !/^-.*(import|from.*import|require)/m.test(diff),
75
- },
76
- ];
77
-
78
- function getSessionFlagPath(sessionId: string): string {
79
- const dir = join(tmpdir(), 'massu-auto-learning');
80
- if (!existsSync(dir)) {
81
- mkdirSync(dir, { recursive: true });
82
- }
83
- return join(dir, `fixes-${sessionId.slice(0, 12)}.jsonl`);
84
- }
85
-
86
- async function main(): Promise<void> {
87
- try {
88
- const input = await readStdin();
89
- const hookInput = JSON.parse(input) as HookInput;
90
- const filePath = hookInput.tool_input?.file_path;
91
-
92
- if (!filePath || !existsSync(filePath)) {
93
- process.exit(0);
94
- return;
95
- }
96
-
97
- // Only check code files
98
- if (!/\.(py|swift|ts|tsx|js|jsx|rs|go|rb|sh)$/.test(filePath)) {
99
- process.exit(0);
100
- return;
101
- }
102
-
103
- // Skip incident/memory/doc files (those ARE pipeline output)
104
- const config = getConfig();
105
- const incidentDir = config.autoLearning?.incidentDir ?? 'docs/incidents';
106
- const memoryDir = config.autoLearning?.memoryDir ?? 'memory';
107
- if (filePath.includes(incidentDir) || filePath.includes(memoryDir) || filePath.includes('MEMORY.md')) {
108
- process.exit(0);
109
- return;
110
- }
111
-
112
- // Check if auto-learning is enabled
113
- if (config.autoLearning?.enabled === false || config.autoLearning?.fixDetection?.enabled === false) {
114
- process.exit(0);
115
- return;
116
- }
117
-
118
- // Get git diff for this file
119
- const root = getProjectRoot();
120
- let diff = '';
121
- try {
122
- diff = execFileSync('git', ['diff', '--', filePath], { cwd: root, timeout: 3000, encoding: 'utf-8' });
123
- if (!diff) {
124
- diff = execFileSync('git', ['diff', 'HEAD', '--', filePath], { cwd: root, timeout: 3000, encoding: 'utf-8' });
125
- }
126
- } catch {
127
- process.exit(0);
128
- return;
129
- }
130
-
131
- if (!diff) {
132
- process.exit(0);
133
- return;
134
- }
135
-
136
- // Run fix detection heuristics
137
- const enabledSignals = new Set(config.autoLearning?.fixDetection?.signals ?? FIX_HEURISTICS.map(h => h.name));
138
- const detected: string[] = [];
139
- for (const heuristic of FIX_HEURISTICS) {
140
- if (enabledSignals.has(heuristic.name) && heuristic.test(diff)) {
141
- detected.push(heuristic.name);
142
- }
143
- }
144
-
145
- if (detected.length === 0) {
146
- process.exit(0);
147
- return;
148
- }
149
-
150
- // Record the fix detection
151
- const signal: FixSignal = {
152
- file: filePath,
153
- signals: detected,
154
- timestamp: new Date().toISOString(),
155
- };
156
-
157
- const flagPath = getSessionFlagPath(hookInput.session_id);
158
- appendFileSync(flagPath, JSON.stringify(signal) + '\n');
159
-
160
- // Count total fixes this session
161
- const lines = readFileSync(flagPath, 'utf-8').split('\n').filter(Boolean);
162
- if (lines.length === 1) {
163
- // First fix detected — output advisory
164
- console.log(
165
- `[Massu Auto-Learning] Bug fix detected in ${filePath} (signals: ${detected.join(', ')}). ` +
166
- `The auto-learning pipeline will prompt you at session end to create an incident report, ` +
167
- `derive a prevention rule, and add enforcement.`
168
- );
169
- }
170
- } catch {
171
- // Best-effort: never block Claude Code
172
- }
173
- process.exit(0);
174
- }
175
-
176
- function readStdin(): Promise<string> {
177
- return new Promise((resolve) => {
178
- let data = '';
179
- process.stdin.setEncoding('utf-8');
180
- process.stdin.on('data', (chunk: string) => { data += chunk; });
181
- process.stdin.on('end', () => resolve(data));
182
- setTimeout(() => resolve(data), 3000);
183
- });
184
- }
185
-
186
- main();
@@ -1,190 +0,0 @@
1
- #!/usr/bin/env node
2
- // Copyright (c) 2026 Massu. All rights reserved.
3
- // Licensed under BSL 1.1 - see LICENSE file for details.
4
-
5
- // ============================================================
6
- // PostToolUse Hook: Incident-to-Rule Pipeline
7
- // When a new incident report is written, automatically triggers
8
- // the next step: deriving a prevention rule.
9
- //
10
- // Part of the Auto-Learning Pipeline:
11
- // Fix Detected → [Incident Report] → RULE DERIVATION → Enforcement
12
- //
13
- // Triggers on: Write to docs/incidents/*.md (configurable)
14
- // Must complete in <500ms.
15
- // ============================================================
16
-
17
- import { existsSync, readFileSync, readdirSync } from 'fs';
18
- import { basename, dirname, resolve } from 'path';
19
- import { getProjectRoot, getConfig } from '../config.ts';
20
- import { getMemoryDb, scoreFailureClasses, appendIncidentToFailureClass, addFailureClass } from '../memory-db.ts';
21
-
22
- interface HookInput {
23
- session_id: string;
24
- tool_name: string;
25
- tool_input: { file_path?: string; content?: string };
26
- }
27
-
28
- async function main(): Promise<void> {
29
- try {
30
- const input = await readStdin();
31
- const hookInput = JSON.parse(input) as HookInput;
32
- const filePath = hookInput.tool_input?.file_path;
33
-
34
- if (!filePath) {
35
- process.exit(0);
36
- return;
37
- }
38
-
39
- const config = getConfig();
40
- if (config.autoLearning?.enabled === false) {
41
- process.exit(0);
42
- return;
43
- }
44
-
45
- const root = getProjectRoot();
46
- const incidentDir = config.autoLearning?.incidentDir ?? 'docs/incidents';
47
- const memoryDir = config.autoLearning?.memoryDir ?? 'memory';
48
- // Only trigger on incident report files
49
- const relPath = filePath.startsWith(root + '/') ? filePath.slice(root.length + 1) : filePath;
50
- if (!relPath.startsWith(incidentDir) || !relPath.endsWith('.md')) {
51
- process.exit(0);
52
- return;
53
- }
54
-
55
- if (!existsSync(filePath)) {
56
- process.exit(0);
57
- return;
58
- }
59
-
60
- // Extract title from the incident report
61
- const content = readFileSync(filePath, 'utf-8');
62
- const titleMatch = content.match(/^#\s+(.+)/m);
63
- const title = titleMatch?.[1] ?? basename(filePath, '.md');
64
-
65
- // Check if a corresponding rule already exists
66
- const slug = basename(filePath, '.md')
67
- .replace(/^\d{4}-\d{2}-\d{2}-?/, '')
68
- .toLowerCase();
69
-
70
- const memoryDirAbs = resolve(root, memoryDir);
71
- let hasExistingRule = false;
72
- if (existsSync(memoryDirAbs)) {
73
- const ruleFiles = readdirSync(memoryDirAbs).filter(f => f.startsWith('feedback_'));
74
- for (const ruleFile of ruleFiles) {
75
- if (ruleFile.toLowerCase().includes(slug.slice(0, 20))) {
76
- hasExistingRule = true;
77
- break;
78
- }
79
- }
80
- }
81
-
82
- if (hasExistingRule) {
83
- // Rule already exists — no action needed
84
- process.exit(0);
85
- return;
86
- }
87
-
88
- // Check if incident has prevention rules section
89
- const hasPrevention = /## Prevention Rules|## Prevention|## Rules/i.test(content);
90
-
91
- if (config.autoLearning?.pipeline?.requirePreventionRule === false) {
92
- process.exit(0);
93
- return;
94
- }
95
-
96
- // Output rule derivation instructions
97
- const lines: string[] = [];
98
- lines.push('');
99
- lines.push('============================================================================');
100
- lines.push(' AUTO-LEARNING: Incident Report Created — Rule Derivation Required');
101
- lines.push('============================================================================');
102
- lines.push('');
103
- lines.push(` Incident: ${title}`);
104
- lines.push(` File: ${filePath}`);
105
- lines.push('');
106
-
107
- if (!hasPrevention) {
108
- lines.push(' No "## Prevention Rules" section found in the incident report.');
109
- lines.push(' Add one first, then proceed with rule derivation.');
110
- lines.push('');
111
- }
112
-
113
- lines.push(' DERIVE A PREVENTION RULE:');
114
- lines.push(` a) Read the incident root cause and prevention rules`);
115
- lines.push(` b) Create: ${memoryDir}/feedback_<rule_name>.md`);
116
- lines.push(' Template:');
117
- lines.push(' ---');
118
- lines.push(' name: <Rule Name>');
119
- lines.push(' description: <one-line description>');
120
- lines.push(' type: feedback');
121
- lines.push(' ---');
122
- lines.push(' <Rule statement>');
123
- lines.push(' **Why:** <Root cause from incident>');
124
- lines.push(' **How to apply:** <Concrete steps>');
125
- lines.push('');
126
- lines.push(` c) Add one-line entry to ${config.autoLearning?.memoryIndexFile ?? 'MEMORY.md'}`);
127
- lines.push('');
128
- lines.push(' This step is MANDATORY per the auto-learning pipeline.');
129
- lines.push('============================================================================');
130
- lines.push('');
131
-
132
- console.log(lines.join('\n'));
133
-
134
- // ============================================================
135
- // Taxonomy Update: Score incident against failure classes
136
- // If KNOWN match → append incident to existing class
137
- // If NEW → create stub entry with needs_review=true
138
- // ============================================================
139
- try {
140
- const taxonomyConfig = config.autoLearning?.failureClassification;
141
- if (taxonomyConfig?.enabled !== false) {
142
- const db = getMemoryDb();
143
- try {
144
- const thresholds = taxonomyConfig?.thresholds ?? { known: 5, similar: 3 };
145
- const scoringWeights = taxonomyConfig?.scoring;
146
-
147
- // Extract incident number from filename (e.g., "incident-042.md" → "42")
148
- const incidentNumMatch = basename(filePath, '.md').match(/(\d+)/);
149
- const incidentId = incidentNumMatch ? incidentNumMatch[1] : basename(filePath, '.md');
150
-
151
- // Score incident content against taxonomy
152
- const bestMatch = scoreFailureClasses(db, content, filePath, title, scoringWeights);
153
-
154
- if (bestMatch && bestMatch.score >= thresholds.similar) {
155
- // Known or similar — append incident to existing class
156
- appendIncidentToFailureClass(db, bestMatch.name, incidentId);
157
- } else {
158
- // New pattern — create stub entry for review
159
- const stubName = `auto_${slug.replace(/[^a-z0-9_]/g, '_').slice(0, 50)}`;
160
- addFailureClass(db, {
161
- name: stubName,
162
- description: `Auto-created from incident ${incidentId}: ${title.slice(0, 100)}`,
163
- incidents: [incidentId],
164
- needsReview: true,
165
- });
166
- }
167
- } finally {
168
- db.close();
169
- }
170
- }
171
- } catch {
172
- // Best-effort: taxonomy update failure must not block the pipeline
173
- }
174
- } catch {
175
- // Best-effort: never block Claude Code
176
- }
177
- process.exit(0);
178
- }
179
-
180
- function readStdin(): Promise<string> {
181
- return new Promise((resolve) => {
182
- let data = '';
183
- process.stdin.setEncoding('utf-8');
184
- process.stdin.on('data', (chunk: string) => { data += chunk; });
185
- process.stdin.on('end', () => resolve(data));
186
- setTimeout(() => resolve(data), 3000);
187
- });
188
- }
189
-
190
- main();
@@ -1,131 +0,0 @@
1
- #!/usr/bin/env node
2
- // Copyright (c) 2026 Massu. All rights reserved.
3
- // Licensed under BSL 1.1 - see LICENSE file for details.
4
-
5
- // ============================================================
6
- // UserPromptSubmit Hook: Intent Suggester
7
- // Matches prompt keywords to relevant slash commands and
8
- // surfaces suggestions to the user as a non-blocking hint.
9
- // ============================================================
10
-
11
- // Force module mode for TypeScript (no external deps needed)
12
- export {};
13
-
14
- interface HookInput {
15
- session_id: string;
16
- transcript_path: string;
17
- cwd: string;
18
- hook_event_name: string;
19
- prompt: string;
20
- }
21
-
22
- interface CommandMapping {
23
- keywords: string[];
24
- command: string;
25
- description: string;
26
- }
27
-
28
- const COMMAND_MAPPINGS: CommandMapping[] = [
29
- {
30
- keywords: ['test', 'failing'],
31
- command: '/massu-test',
32
- description: 'Run and analyze tests',
33
- },
34
- {
35
- keywords: ['debug', 'bug'],
36
- command: '/massu-debug',
37
- description: 'Debug an issue',
38
- },
39
- {
40
- keywords: ['refactor'],
41
- command: '/massu-refactor',
42
- description: 'Guided refactoring workflow',
43
- },
44
- {
45
- keywords: ['cleanup', 'dead code', 'unused'],
46
- command: '/massu-cleanup',
47
- description: 'Clean up dead code and unused exports',
48
- },
49
- {
50
- keywords: ['document', 'jsdoc', 'readme'],
51
- command: '/massu-doc-gen',
52
- description: 'Generate documentation',
53
- },
54
- {
55
- keywords: ['estimate', 'effort', 'how long'],
56
- command: '/massu-estimate',
57
- description: 'Estimate implementation effort',
58
- },
59
- {
60
- keywords: ['release', 'deploy'],
61
- command: '/massu-release',
62
- description: 'Prepare a release',
63
- },
64
- {
65
- keywords: ['commit'],
66
- command: '/massu-commit',
67
- description: 'Pre-commit verification gate',
68
- },
69
- {
70
- keywords: ['push'],
71
- command: '/massu-push',
72
- description: 'Pre-push full verification gate',
73
- },
74
- {
75
- keywords: ['plan'],
76
- command: '/massu-create-plan',
77
- description: 'Create an implementation plan',
78
- },
79
- ];
80
-
81
- function findMatchingCommand(prompt: string): CommandMapping | null {
82
- const lowerPrompt = prompt.toLowerCase();
83
-
84
- for (const mapping of COMMAND_MAPPINGS) {
85
- for (const keyword of mapping.keywords) {
86
- if (lowerPrompt.includes(keyword.toLowerCase())) {
87
- return mapping;
88
- }
89
- }
90
- }
91
-
92
- return null;
93
- }
94
-
95
- async function main(): Promise<void> {
96
- try {
97
- const input = await readStdin();
98
- const hookInput = JSON.parse(input) as HookInput;
99
- const { prompt } = hookInput;
100
-
101
- if (!prompt || !prompt.trim()) {
102
- process.exit(0);
103
- return;
104
- }
105
-
106
- const match = findMatchingCommand(prompt);
107
- if (!match) {
108
- process.exit(0);
109
- return;
110
- }
111
-
112
- process.stdout.write(
113
- `Tip: Use ${match.command} to ${match.description}.`
114
- );
115
- } catch (_e) {
116
- // Best-effort: never block Claude Code
117
- }
118
- process.exit(0);
119
- }
120
-
121
- function readStdin(): Promise<string> {
122
- return new Promise((resolve) => {
123
- let data = '';
124
- process.stdin.setEncoding('utf-8');
125
- process.stdin.on('data', (chunk: string) => { data += chunk; });
126
- process.stdin.on('end', () => resolve(data));
127
- setTimeout(() => resolve(data), 3000);
128
- });
129
- }
130
-
131
- main();
@@ -1,91 +0,0 @@
1
- #!/usr/bin/env node
2
- // Copyright (c) 2026 Massu. All rights reserved.
3
- // Licensed under BSL 1.1 - see LICENSE file for details.
4
-
5
- // ============================================================
6
- // PostToolUse Context Hook
7
- // Surfaces applicable CLAUDE.md rules and warnings when editing
8
- // src/ files. Uses matchRules() and isInMiddlewareTree() from
9
- // the codegraph index - no MCP server HTTP call needed.
10
- // Must complete in <500ms.
11
- // ============================================================
12
-
13
- import Database from 'better-sqlite3';
14
- import { matchRules } from '../rules.ts';
15
- import { isInMiddlewareTree } from '../middleware-tree.ts';
16
- import { getResolvedPaths, getProjectRoot } from '../config.ts';
17
-
18
- interface HookInput {
19
- session_id: string;
20
- tool_name: string;
21
- tool_input: { file_path?: string };
22
- }
23
-
24
- async function main(): Promise<void> {
25
- try {
26
- const input = await readStdin();
27
- const hookInput = JSON.parse(input) as HookInput;
28
- const filePath = hookInput.tool_input?.file_path;
29
-
30
- if (!filePath) {
31
- process.exit(0);
32
- return;
33
- }
34
-
35
- // Convert absolute path to relative
36
- const root = getProjectRoot();
37
- const rel = filePath.startsWith(root + '/') ? filePath.slice(root.length + 1) : filePath;
38
-
39
- // Only process src/ files
40
- if (!rel.startsWith('src/') && !rel.endsWith('.py')) {
41
- process.exit(0);
42
- return;
43
- }
44
-
45
- const warnings: string[] = [];
46
-
47
- // 1. Check applicable rules (uses rules.ts PATTERN_RULES)
48
- const rules = matchRules(rel);
49
- for (const rule of rules) {
50
- if (rule.severity === 'CRITICAL' || rule.severity === 'HIGH') {
51
- for (const r of rule.rules) {
52
- warnings.push(`[${rule.severity}] ${r}`);
53
- }
54
- }
55
- }
56
-
57
- // 2. Check middleware tree membership
58
- try {
59
- const dataDb = new Database(getResolvedPaths().dataDbPath, { readonly: true });
60
- try {
61
- if (isInMiddlewareTree(dataDb, rel)) {
62
- warnings.push('[CRITICAL] This file is in the middleware import tree. No Node.js deps allowed.');
63
- }
64
- } finally {
65
- dataDb.close();
66
- }
67
- } catch (_e) {
68
- // DB may not exist yet - skip middleware check
69
- }
70
-
71
- // 3. Output warnings if any
72
- if (warnings.length > 0) {
73
- console.log(`[Massu] ${warnings.join(' | ')}`);
74
- }
75
- } catch (_e) {
76
- // Best-effort: never block Claude Code
77
- }
78
- process.exit(0);
79
- }
80
-
81
- function readStdin(): Promise<string> {
82
- return new Promise((resolve) => {
83
- let data = '';
84
- process.stdin.setEncoding('utf-8');
85
- process.stdin.on('data', (chunk: string) => { data += chunk; });
86
- process.stdin.on('end', () => resolve(data));
87
- setTimeout(() => resolve(data), 3000);
88
- });
89
- }
90
-
91
- main();