@massu/core 0.1.0 → 0.1.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 (114) hide show
  1. package/LICENSE +71 -0
  2. package/dist/hooks/cost-tracker.js +127 -11493
  3. package/dist/hooks/post-edit-context.js +125 -11491
  4. package/dist/hooks/post-tool-use.js +127 -11493
  5. package/dist/hooks/pre-compact.js +127 -11493
  6. package/dist/hooks/pre-delete-check.js +126 -11492
  7. package/dist/hooks/quality-event.js +127 -11493
  8. package/dist/hooks/session-end.js +127 -11493
  9. package/dist/hooks/session-start.js +127 -11493
  10. package/dist/hooks/user-prompt.js +127 -11493
  11. package/package.json +9 -8
  12. package/src/__tests__/adr-generator.test.ts +260 -0
  13. package/src/__tests__/analytics.test.ts +282 -0
  14. package/src/__tests__/audit-trail.test.ts +382 -0
  15. package/src/__tests__/backfill-sessions.test.ts +690 -0
  16. package/src/__tests__/cli.test.ts +290 -0
  17. package/src/__tests__/cloud-sync.test.ts +261 -0
  18. package/src/__tests__/config-sections.test.ts +359 -0
  19. package/src/__tests__/config.test.ts +732 -0
  20. package/src/__tests__/cost-tracker.test.ts +348 -0
  21. package/src/__tests__/db.test.ts +177 -0
  22. package/src/__tests__/dependency-scorer.test.ts +325 -0
  23. package/src/__tests__/docs-integration.test.ts +178 -0
  24. package/src/__tests__/docs-tools.test.ts +199 -0
  25. package/src/__tests__/domains.test.ts +236 -0
  26. package/src/__tests__/hooks.test.ts +221 -0
  27. package/src/__tests__/import-resolver.test.ts +95 -0
  28. package/src/__tests__/integration/path-traversal.test.ts +134 -0
  29. package/src/__tests__/integration/pricing-consistency.test.ts +88 -0
  30. package/src/__tests__/integration/tool-registration.test.ts +146 -0
  31. package/src/__tests__/memory-db.test.ts +404 -0
  32. package/src/__tests__/memory-enhancements.test.ts +316 -0
  33. package/src/__tests__/memory-tools.test.ts +199 -0
  34. package/src/__tests__/middleware-tree.test.ts +177 -0
  35. package/src/__tests__/observability-tools.test.ts +595 -0
  36. package/src/__tests__/observability.test.ts +437 -0
  37. package/src/__tests__/observation-extractor.test.ts +167 -0
  38. package/src/__tests__/page-deps.test.ts +60 -0
  39. package/src/__tests__/prompt-analyzer.test.ts +298 -0
  40. package/src/__tests__/regression-detector.test.ts +295 -0
  41. package/src/__tests__/rules.test.ts +87 -0
  42. package/src/__tests__/schema-mapper.test.ts +29 -0
  43. package/src/__tests__/security-scorer.test.ts +238 -0
  44. package/src/__tests__/security-utils.test.ts +175 -0
  45. package/src/__tests__/sentinel-db.test.ts +491 -0
  46. package/src/__tests__/sentinel-scanner.test.ts +750 -0
  47. package/src/__tests__/sentinel-tools.test.ts +324 -0
  48. package/src/__tests__/sentinel-types.test.ts +750 -0
  49. package/src/__tests__/server.test.ts +452 -0
  50. package/src/__tests__/session-archiver.test.ts +524 -0
  51. package/src/__tests__/session-state-generator.test.ts +900 -0
  52. package/src/__tests__/team-knowledge.test.ts +327 -0
  53. package/src/__tests__/tools.test.ts +340 -0
  54. package/src/__tests__/transcript-parser.test.ts +195 -0
  55. package/src/__tests__/trpc-index.test.ts +25 -0
  56. package/src/__tests__/validate-features-runner.test.ts +517 -0
  57. package/src/__tests__/validation-engine.test.ts +300 -0
  58. package/src/adr-generator.ts +285 -0
  59. package/src/analytics.ts +367 -0
  60. package/src/audit-trail.ts +443 -0
  61. package/src/backfill-sessions.ts +180 -0
  62. package/src/cli.ts +105 -0
  63. package/src/cloud-sync.ts +194 -0
  64. package/src/commands/doctor.ts +300 -0
  65. package/src/commands/init.ts +399 -0
  66. package/src/commands/install-hooks.ts +26 -0
  67. package/src/config.ts +357 -0
  68. package/src/core-tools.ts +685 -0
  69. package/src/cost-tracker.ts +350 -0
  70. package/src/db.ts +233 -0
  71. package/src/dependency-scorer.ts +330 -0
  72. package/src/docs-map.json +100 -0
  73. package/src/docs-tools.ts +514 -0
  74. package/src/domains.ts +181 -0
  75. package/src/hooks/cost-tracker.ts +66 -0
  76. package/src/hooks/intent-suggester.ts +131 -0
  77. package/src/hooks/post-edit-context.ts +91 -0
  78. package/src/hooks/post-tool-use.ts +175 -0
  79. package/src/hooks/pre-compact.ts +146 -0
  80. package/src/hooks/pre-delete-check.ts +153 -0
  81. package/src/hooks/quality-event.ts +127 -0
  82. package/src/hooks/security-gate.ts +121 -0
  83. package/src/hooks/session-end.ts +467 -0
  84. package/src/hooks/session-start.ts +210 -0
  85. package/src/hooks/user-prompt.ts +91 -0
  86. package/src/import-resolver.ts +224 -0
  87. package/src/memory-db.ts +48 -0
  88. package/src/memory-queries.ts +804 -0
  89. package/src/memory-schema.ts +546 -0
  90. package/src/memory-tools.ts +392 -0
  91. package/src/middleware-tree.ts +70 -0
  92. package/src/observability-tools.ts +332 -0
  93. package/src/observation-extractor.ts +411 -0
  94. package/src/page-deps.ts +283 -0
  95. package/src/prompt-analyzer.ts +325 -0
  96. package/src/regression-detector.ts +313 -0
  97. package/src/rules.ts +57 -0
  98. package/src/schema-mapper.ts +232 -0
  99. package/src/security-scorer.ts +398 -0
  100. package/src/security-utils.ts +133 -0
  101. package/src/sentinel-db.ts +623 -0
  102. package/src/sentinel-scanner.ts +405 -0
  103. package/src/sentinel-tools.ts +515 -0
  104. package/src/sentinel-types.ts +140 -0
  105. package/src/server.ts +190 -0
  106. package/src/session-archiver.ts +112 -0
  107. package/src/session-state-generator.ts +174 -0
  108. package/src/team-knowledge.ts +400 -0
  109. package/src/tool-helpers.ts +41 -0
  110. package/src/tools.ts +111 -0
  111. package/src/transcript-parser.ts +458 -0
  112. package/src/trpc-index.ts +214 -0
  113. package/src/validate-features-runner.ts +107 -0
  114. package/src/validation-engine.ts +351 -0
@@ -0,0 +1,399 @@
1
+ // Copyright (c) 2026 Massu. All rights reserved.
2
+ // Licensed under BSL 1.1 - see LICENSE file for details.
3
+
4
+ /**
5
+ * `massu init` — One-command full project setup.
6
+ *
7
+ * 1. Detects project framework (scans package.json)
8
+ * 2. Generates massu.config.yaml (or preserves existing)
9
+ * 3. Registers MCP server in .mcp.json (creates or merges)
10
+ * 4. Installs all 11 hooks in .claude/settings.local.json
11
+ * 5. Prints success summary
12
+ */
13
+
14
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
15
+ import { resolve, basename, dirname } from 'path';
16
+ import { fileURLToPath } from 'url';
17
+
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = dirname(__filename);
20
+ import { stringify as yamlStringify } from 'yaml';
21
+
22
+ // ============================================================
23
+ // Types
24
+ // ============================================================
25
+
26
+ interface FrameworkDetection {
27
+ type: string;
28
+ router: string;
29
+ orm: string;
30
+ ui: string;
31
+ }
32
+
33
+ interface InitResult {
34
+ configCreated: boolean;
35
+ configSkipped: boolean;
36
+ mcpRegistered: boolean;
37
+ mcpSkipped: boolean;
38
+ hooksInstalled: boolean;
39
+ hooksCount: number;
40
+ framework: FrameworkDetection;
41
+ }
42
+
43
+ // ============================================================
44
+ // Framework Auto-Detection
45
+ // ============================================================
46
+
47
+ export function detectFramework(projectRoot: string): FrameworkDetection {
48
+ const result: FrameworkDetection = {
49
+ type: 'javascript',
50
+ router: 'none',
51
+ orm: 'none',
52
+ ui: 'none',
53
+ };
54
+
55
+ const pkgPath = resolve(projectRoot, 'package.json');
56
+ if (!existsSync(pkgPath)) return result;
57
+
58
+ try {
59
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
60
+ const allDeps = {
61
+ ...pkg.dependencies,
62
+ ...pkg.devDependencies,
63
+ };
64
+
65
+ // Language detection
66
+ if (allDeps['typescript']) result.type = 'typescript';
67
+
68
+ // UI framework detection
69
+ if (allDeps['next']) result.ui = 'nextjs';
70
+ else if (allDeps['@sveltejs/kit']) result.ui = 'sveltekit';
71
+ else if (allDeps['nuxt']) result.ui = 'nuxt';
72
+ else if (allDeps['@angular/core']) result.ui = 'angular';
73
+ else if (allDeps['vue']) result.ui = 'vue';
74
+ else if (allDeps['react']) result.ui = 'react';
75
+
76
+ // Router detection
77
+ if (allDeps['@trpc/server']) result.router = 'trpc';
78
+ else if (allDeps['graphql'] || allDeps['@apollo/server']) result.router = 'graphql';
79
+ else if (allDeps['express'] || allDeps['fastify'] || allDeps['hono']) result.router = 'rest';
80
+
81
+ // ORM detection
82
+ if (allDeps['@prisma/client'] || allDeps['prisma']) result.orm = 'prisma';
83
+ else if (allDeps['drizzle-orm']) result.orm = 'drizzle';
84
+ else if (allDeps['typeorm']) result.orm = 'typeorm';
85
+ else if (allDeps['sequelize']) result.orm = 'sequelize';
86
+ else if (allDeps['mongoose']) result.orm = 'mongoose';
87
+ } catch {
88
+ // Best effort
89
+ }
90
+
91
+ return result;
92
+ }
93
+
94
+ // ============================================================
95
+ // Config File Generation
96
+ // ============================================================
97
+
98
+ export function generateConfig(projectRoot: string, framework: FrameworkDetection): boolean {
99
+ const configPath = resolve(projectRoot, 'massu.config.yaml');
100
+
101
+ if (existsSync(configPath)) {
102
+ return false; // Config already exists
103
+ }
104
+
105
+ const projectName = basename(projectRoot);
106
+
107
+ const config = {
108
+ project: {
109
+ name: projectName,
110
+ root: 'auto',
111
+ },
112
+ framework: {
113
+ type: framework.type,
114
+ router: framework.router,
115
+ orm: framework.orm,
116
+ ui: framework.ui,
117
+ },
118
+ paths: {
119
+ source: 'src',
120
+ aliases: { '@': 'src' },
121
+ },
122
+ toolPrefix: 'massu',
123
+ domains: [],
124
+ rules: [
125
+ {
126
+ pattern: 'src/**/*.ts',
127
+ rules: ['Use ESM imports, not CommonJS'],
128
+ },
129
+ ],
130
+ };
131
+
132
+ const yamlContent = `# Massu AI Configuration
133
+ # Generated by: npx massu init
134
+ # Documentation: https://massu.ai/docs/getting-started/configuration
135
+
136
+ ${yamlStringify(config)}`;
137
+
138
+ writeFileSync(configPath, yamlContent, 'utf-8');
139
+ return true;
140
+ }
141
+
142
+ // ============================================================
143
+ // MCP Server Registration
144
+ // ============================================================
145
+
146
+ export function registerMcpServer(projectRoot: string): boolean {
147
+ const mcpPath = resolve(projectRoot, '.mcp.json');
148
+
149
+ let existing: Record<string, unknown> = {};
150
+ if (existsSync(mcpPath)) {
151
+ try {
152
+ existing = JSON.parse(readFileSync(mcpPath, 'utf-8'));
153
+ } catch {
154
+ existing = {};
155
+ }
156
+ }
157
+
158
+ // Check if already registered
159
+ const servers = (existing.mcpServers ?? {}) as Record<string, unknown>;
160
+ if (servers.massu) {
161
+ return false; // Already registered
162
+ }
163
+
164
+ // Add massu server
165
+ servers.massu = {
166
+ type: 'stdio',
167
+ command: 'npx',
168
+ args: ['-y', '@massu/core'],
169
+ };
170
+
171
+ existing.mcpServers = servers;
172
+
173
+ writeFileSync(mcpPath, JSON.stringify(existing, null, 2) + '\n', 'utf-8');
174
+ return true;
175
+ }
176
+
177
+ // ============================================================
178
+ // Hook Installation
179
+ // ============================================================
180
+
181
+ interface HookEntry {
182
+ type: 'command';
183
+ command: string;
184
+ timeout: number;
185
+ }
186
+
187
+ interface HookGroup {
188
+ matcher?: string;
189
+ hooks: HookEntry[];
190
+ }
191
+
192
+ type HooksConfig = Record<string, HookGroup[]>;
193
+
194
+ /**
195
+ * Resolve the path to compiled hook files.
196
+ * Handles both local development and npm-installed scenarios.
197
+ */
198
+ export function resolveHooksDir(): string {
199
+ // Try to find the hooks in node_modules first (installed via npm)
200
+ const cwd = process.cwd();
201
+ const nodeModulesPath = resolve(cwd, 'node_modules/@massu/core/dist/hooks');
202
+ if (existsSync(nodeModulesPath)) {
203
+ return 'node_modules/@massu/core/dist/hooks';
204
+ }
205
+
206
+ // Fall back to finding relative to this source file
207
+ const localPath = resolve(__dirname, '../dist/hooks');
208
+ if (existsSync(localPath)) {
209
+ return localPath;
210
+ }
211
+
212
+ // Default to node_modules path (will be created on npm install)
213
+ return 'node_modules/@massu/core/dist/hooks';
214
+ }
215
+
216
+ function hookCmd(hooksDir: string, hookFile: string): string {
217
+ return `node ${hooksDir}/${hookFile}`;
218
+ }
219
+
220
+ export function buildHooksConfig(hooksDir: string): HooksConfig {
221
+ return {
222
+ SessionStart: [
223
+ {
224
+ hooks: [
225
+ { type: 'command', command: hookCmd(hooksDir, 'session-start.js'), timeout: 10 },
226
+ ],
227
+ },
228
+ ],
229
+ PreToolUse: [
230
+ {
231
+ matcher: 'Bash',
232
+ hooks: [
233
+ { type: 'command', command: hookCmd(hooksDir, 'security-gate.js'), timeout: 5 },
234
+ ],
235
+ },
236
+ {
237
+ matcher: 'Bash|Write',
238
+ hooks: [
239
+ { type: 'command', command: hookCmd(hooksDir, 'pre-delete-check.js'), timeout: 5 },
240
+ ],
241
+ },
242
+ ],
243
+ PostToolUse: [
244
+ {
245
+ hooks: [
246
+ { type: 'command', command: hookCmd(hooksDir, 'post-tool-use.js'), timeout: 10 },
247
+ { type: 'command', command: hookCmd(hooksDir, 'quality-event.js'), timeout: 5 },
248
+ { type: 'command', command: hookCmd(hooksDir, 'cost-tracker.js'), timeout: 5 },
249
+ ],
250
+ },
251
+ {
252
+ matcher: 'Edit|Write',
253
+ hooks: [
254
+ { type: 'command', command: hookCmd(hooksDir, 'post-edit-context.js'), timeout: 5 },
255
+ ],
256
+ },
257
+ ],
258
+ Stop: [
259
+ {
260
+ hooks: [
261
+ { type: 'command', command: hookCmd(hooksDir, 'session-end.js'), timeout: 15 },
262
+ ],
263
+ },
264
+ ],
265
+ PreCompact: [
266
+ {
267
+ hooks: [
268
+ { type: 'command', command: hookCmd(hooksDir, 'pre-compact.js'), timeout: 10 },
269
+ ],
270
+ },
271
+ ],
272
+ UserPromptSubmit: [
273
+ {
274
+ hooks: [
275
+ { type: 'command', command: hookCmd(hooksDir, 'user-prompt.js'), timeout: 5 },
276
+ { type: 'command', command: hookCmd(hooksDir, 'intent-suggester.js'), timeout: 5 },
277
+ ],
278
+ },
279
+ ],
280
+ };
281
+ }
282
+
283
+ export function installHooks(projectRoot: string): { installed: boolean; count: number } {
284
+ const claudeDir = resolve(projectRoot, '.claude');
285
+ const settingsPath = resolve(claudeDir, 'settings.local.json');
286
+
287
+ // Ensure .claude directory exists
288
+ if (!existsSync(claudeDir)) {
289
+ mkdirSync(claudeDir, { recursive: true });
290
+ }
291
+
292
+ // Read existing settings
293
+ let settings: Record<string, unknown> = {};
294
+ if (existsSync(settingsPath)) {
295
+ try {
296
+ settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
297
+ } catch {
298
+ settings = {};
299
+ }
300
+ }
301
+
302
+ // Resolve hook paths
303
+ const hooksDir = resolveHooksDir();
304
+
305
+ // Build hooks config
306
+ const hooksConfig = buildHooksConfig(hooksDir);
307
+
308
+ // Count total hooks
309
+ let hookCount = 0;
310
+ for (const groups of Object.values(hooksConfig)) {
311
+ for (const group of groups) {
312
+ hookCount += group.hooks.length;
313
+ }
314
+ }
315
+
316
+ // Merge hooks into settings (replace hooks section, preserve everything else)
317
+ settings.hooks = hooksConfig;
318
+
319
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
320
+
321
+ return { installed: true, count: hookCount };
322
+ }
323
+
324
+ // ============================================================
325
+ // Main Init Flow
326
+ // ============================================================
327
+
328
+ export async function runInit(): Promise<void> {
329
+ const projectRoot = process.cwd();
330
+
331
+ console.log('');
332
+ console.log('Massu AI - Project Setup');
333
+ console.log('========================');
334
+ console.log('');
335
+
336
+ // Step 1: Detect framework
337
+ const framework = detectFramework(projectRoot);
338
+ const frameworkParts: string[] = [];
339
+ if (framework.type !== 'javascript') frameworkParts.push(capitalize(framework.type));
340
+ if (framework.ui !== 'none') frameworkParts.push(formatName(framework.ui));
341
+ if (framework.orm !== 'none') frameworkParts.push(capitalize(framework.orm));
342
+ if (framework.router !== 'none') frameworkParts.push(framework.router.toUpperCase());
343
+ const detected = frameworkParts.length > 0 ? frameworkParts.join(', ') : 'JavaScript';
344
+ console.log(` Detected: ${detected}`);
345
+
346
+ // Step 2: Create config
347
+ const configCreated = generateConfig(projectRoot, framework);
348
+ if (configCreated) {
349
+ console.log(' Created massu.config.yaml');
350
+ } else {
351
+ console.log(' massu.config.yaml already exists (preserved)');
352
+ }
353
+
354
+ // Step 3: Register MCP server
355
+ const mcpRegistered = registerMcpServer(projectRoot);
356
+ if (mcpRegistered) {
357
+ console.log(' Registered MCP server in .mcp.json');
358
+ } else {
359
+ console.log(' MCP server already registered in .mcp.json');
360
+ }
361
+
362
+ // Step 4: Install hooks
363
+ const { count: hooksCount } = installHooks(projectRoot);
364
+ console.log(` Installed ${hooksCount} hooks in .claude/settings.local.json`);
365
+
366
+ // Step 5: Databases info
367
+ console.log(' Databases will auto-create on first session');
368
+
369
+ // Summary
370
+ console.log('');
371
+ console.log('Massu AI is ready. Start a Claude Code session to begin.');
372
+ console.log('');
373
+ console.log('Next steps:');
374
+ console.log(' claude # Start a session (hooks activate automatically)');
375
+ console.log(' npx massu doctor # Verify installation health');
376
+ console.log('');
377
+ console.log('Documentation: https://massu.ai/docs');
378
+ console.log('');
379
+ }
380
+
381
+ // ============================================================
382
+ // Helpers
383
+ // ============================================================
384
+
385
+ function capitalize(str: string): string {
386
+ return str.charAt(0).toUpperCase() + str.slice(1);
387
+ }
388
+
389
+ function formatName(name: string): string {
390
+ const names: Record<string, string> = {
391
+ nextjs: 'Next.js',
392
+ sveltekit: 'SvelteKit',
393
+ nuxt: 'Nuxt',
394
+ angular: 'Angular',
395
+ vue: 'Vue',
396
+ react: 'React',
397
+ };
398
+ return names[name] ?? capitalize(name);
399
+ }
@@ -0,0 +1,26 @@
1
+ // Copyright (c) 2026 Massu. All rights reserved.
2
+ // Licensed under BSL 1.1 - see LICENSE file for details.
3
+
4
+ /**
5
+ * `massu install-hooks` — Standalone hook installation.
6
+ *
7
+ * Installs or updates all 11 Claude Code hooks in .claude/settings.local.json.
8
+ * Uses the same logic as `massu init` but only handles hooks.
9
+ */
10
+
11
+ import { installHooks } from './init.ts';
12
+
13
+ export async function runInstallHooks(): Promise<void> {
14
+ const projectRoot = process.cwd();
15
+
16
+ console.log('');
17
+ console.log('Massu AI - Hook Installation');
18
+ console.log('============================');
19
+ console.log('');
20
+
21
+ const { count } = installHooks(projectRoot);
22
+ console.log(` Installed ${count} hooks in .claude/settings.local.json`);
23
+ console.log('');
24
+ console.log('Hooks will activate on your next Claude Code session.');
25
+ console.log('');
26
+ }