@massu/core 1.10.7 → 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.
- package/CHANGELOG.md +1303 -0
- package/dist/adapter.js +6 -4
- package/dist/cli.js +559 -400
- package/dist/detect/adapters/.bundle-shasums.json +4 -7
- package/dist/detect/adapters/rails.js +6 -4
- package/dist/detect/adapters/spring.js +6 -4
- package/dist/detect/adapters/tree-sitter-loader.js +6 -4
- package/dist/hooks/auto-learning-pipeline.js +24 -1
- package/dist/hooks/classify-failure.js +32 -3
- package/dist/hooks/cost-tracker.js +24 -0
- package/dist/hooks/fix-detector.js +97 -1
- package/dist/hooks/incident-pipeline.js +30 -1
- package/dist/hooks/intent-suggester.js +6 -3
- package/dist/hooks/post-edit-context.js +24 -1
- package/dist/hooks/post-tool-use.js +66 -18
- package/dist/hooks/pre-compact.js +27 -2
- package/dist/hooks/pre-delete-check.js +48 -22
- package/dist/hooks/pre-tool-use-gate.js +962 -0
- package/dist/hooks/quality-event.js +24 -0
- package/dist/hooks/rule-enforcement-pipeline.js +24 -1
- package/dist/hooks/security-gate.js +29 -16
- package/dist/hooks/session-end.js +24 -0
- package/dist/hooks/session-start.js +8710 -8460
- package/dist/hooks/user-prompt.js +24 -0
- package/package.json +15 -18
- package/commands/README.md +0 -134
- package/src/adapter.ts +0 -121
- package/src/adr-generator.ts +0 -292
- package/src/analytics.ts +0 -373
- package/src/audit-trail.ts +0 -450
- package/src/backfill-sessions.ts +0 -182
- package/src/changelog-generator.ts +0 -178
- package/src/cli.ts +0 -246
- package/src/cloud-sync.ts +0 -234
- package/src/commands/adapters.ts +0 -824
- package/src/commands/changelog.ts +0 -165
- package/src/commands/config-check-drift.ts +0 -133
- package/src/commands/config-refresh.ts +0 -396
- package/src/commands/config-upgrade.ts +0 -127
- package/src/commands/doctor.ts +0 -485
- package/src/commands/hook-runner.ts +0 -145
- package/src/commands/init.ts +0 -1823
- package/src/commands/install-commands.ts +0 -692
- package/src/commands/install-hooks.ts +0 -27
- package/src/commands/permissions.ts +0 -150
- package/src/commands/refresh-log.ts +0 -37
- package/src/commands/show-template.ts +0 -65
- package/src/commands/template-engine.ts +0 -301
- package/src/commands/watch.ts +0 -430
- package/src/config.ts +0 -765
- package/src/cost-tracker.ts +0 -355
- package/src/db.ts +0 -370
- package/src/dependency-scorer.ts +0 -337
- package/src/detect/adapters/aspnet.ts +0 -4
- package/src/detect/adapters/discover.ts +0 -469
- package/src/detect/adapters/file-sampler.ts +0 -225
- package/src/detect/adapters/go-chi.ts +0 -4
- package/src/detect/adapters/index.ts +0 -51
- package/src/detect/adapters/nextjs-trpc.ts +0 -166
- package/src/detect/adapters/parse-guard.ts +0 -133
- package/src/detect/adapters/phoenix.ts +0 -4
- package/src/detect/adapters/python-django.ts +0 -208
- package/src/detect/adapters/python-fastapi.ts +0 -223
- package/src/detect/adapters/python-flask.ts +0 -235
- package/src/detect/adapters/query-helpers.ts +0 -170
- package/src/detect/adapters/rails.ts +0 -4
- package/src/detect/adapters/runner.ts +0 -284
- package/src/detect/adapters/spring.ts +0 -4
- package/src/detect/adapters/swift-swiftui.ts +0 -171
- package/src/detect/adapters/tree-sitter-loader.ts +0 -517
- package/src/detect/adapters/types.ts +0 -191
- package/src/detect/codebase-introspector.ts +0 -210
- package/src/detect/domain-inferrer.ts +0 -197
- package/src/detect/drift.ts +0 -199
- package/src/detect/framework-detector.ts +0 -307
- package/src/detect/index.ts +0 -209
- package/src/detect/manifest-registry.ts +0 -261
- package/src/detect/migrate.ts +0 -362
- package/src/detect/monorepo-detector.ts +0 -348
- package/src/detect/package-detector.ts +0 -828
- package/src/detect/passthrough.ts +0 -108
- package/src/detect/regex-fallback.ts +0 -449
- package/src/detect/source-dir-detector.ts +0 -297
- package/src/detect/vr-command-map.ts +0 -167
- package/src/docs-map.json +0 -100
- package/src/docs-tools.ts +0 -519
- package/src/domains.ts +0 -182
- package/src/hooks/auto-learning-pipeline.ts +0 -228
- package/src/hooks/classify-failure.ts +0 -259
- package/src/hooks/cost-tracker.ts +0 -66
- package/src/hooks/fix-detector.ts +0 -186
- package/src/hooks/incident-pipeline.ts +0 -190
- package/src/hooks/intent-suggester.ts +0 -131
- package/src/hooks/post-edit-context.ts +0 -91
- package/src/hooks/post-tool-use.ts +0 -413
- package/src/hooks/pre-compact.ts +0 -168
- package/src/hooks/pre-delete-check.ts +0 -243
- package/src/hooks/quality-event.ts +0 -127
- package/src/hooks/rule-enforcement-pipeline.ts +0 -159
- package/src/hooks/security-gate.ts +0 -153
- package/src/hooks/session-end.ts +0 -467
- package/src/hooks/session-start.ts +0 -436
- package/src/hooks/user-prompt.ts +0 -157
- package/src/import-resolver.ts +0 -226
- package/src/knowledge-db.ts +0 -176
- package/src/knowledge-indexer.ts +0 -708
- package/src/knowledge-tools.ts +0 -1414
- package/src/lib/fileLock.ts +0 -203
- package/src/lib/gitToplevel.ts +0 -22
- package/src/lib/hook-registry.ts +0 -43
- package/src/lib/installLock.ts +0 -66
- package/src/lib/memory-path.ts +0 -49
- package/src/lib/pidLiveness.ts +0 -67
- package/src/lib/settings-local.ts +0 -110
- package/src/lib/sql-table-names.ts +0 -71
- package/src/license.ts +0 -547
- package/src/lsp/auto-detect.ts +0 -98
- package/src/lsp/client.ts +0 -776
- package/src/lsp/enrich.ts +0 -127
- package/src/lsp/types.ts +0 -221
- package/src/memory-db.ts +0 -1614
- package/src/memory-file-ingest.ts +0 -128
- package/src/memory-tools.ts +0 -424
- package/src/middleware-tree.ts +0 -71
- package/src/observability-tools.ts +0 -343
- package/src/observation-extractor.ts +0 -418
- package/src/page-deps.ts +0 -285
- package/src/permissions.ts +0 -422
- package/src/prompt-analyzer.ts +0 -332
- package/src/python/coupling-detector.ts +0 -125
- package/src/python/domain-enforcer.ts +0 -84
- package/src/python/impact-analyzer.ts +0 -96
- package/src/python/import-parser.ts +0 -244
- package/src/python/import-resolver.ts +0 -136
- package/src/python/migration-indexer.ts +0 -116
- package/src/python/migration-parser.ts +0 -332
- package/src/python/model-indexer.ts +0 -71
- package/src/python/model-parser.ts +0 -279
- package/src/python/route-indexer.ts +0 -59
- package/src/python/route-parser.ts +0 -317
- package/src/python-tools.ts +0 -630
- package/src/regression-detector.ts +0 -319
- package/src/rules.ts +0 -57
- package/src/schema-mapper.ts +0 -232
- package/src/security/adapter-origin.ts +0 -130
- package/src/security/adapter-verifier.ts +0 -319
- package/src/security/atomic-write.ts +0 -164
- package/src/security/fetcher.ts +0 -200
- package/src/security/install-tracking.ts +0 -319
- package/src/security/license-pubkey.generated.ts +0 -30
- package/src/security/license-response-verifier.ts +0 -149
- package/src/security/local-fingerprint.ts +0 -225
- package/src/security/manifest-cache.ts +0 -333
- package/src/security/manifest-schema.ts +0 -129
- package/src/security/registry-pubkey.generated.ts +0 -35
- package/src/security/telemetry.ts +0 -320
- package/src/security-scorer.ts +0 -405
- package/src/security-utils.ts +0 -133
- package/src/sentinel-db.ts +0 -580
- package/src/sentinel-scanner.ts +0 -406
- package/src/sentinel-tools.ts +0 -513
- package/src/sentinel-types.ts +0 -140
- package/src/server-dispatch.ts +0 -225
- package/src/server.ts +0 -107
- package/src/session-archiver.ts +0 -111
- package/src/session-state-generator.ts +0 -174
- package/src/team-knowledge.ts +0 -407
- package/src/tool-db-needs.ts +0 -232
- package/src/tools.ts +0 -1205
- package/src/transcript-parser.ts +0 -458
- package/src/trpc-index.ts +0 -215
- package/src/validate-features-runner.ts +0 -107
- package/src/validation-engine.ts +0 -358
- package/src/watch/daemon.ts +0 -385
- package/src/watch/lockfile-detector.ts +0 -65
- package/src/watch/paths.ts +0 -279
- package/src/watch/state.ts +0 -178
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2026 Massu. All rights reserved.
|
|
2
|
-
// Licensed under BSL 1.1 - see LICENSE file for details.
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Source Directory Detector (P1-003)
|
|
6
|
-
* ==================================
|
|
7
|
-
*
|
|
8
|
-
* For each detected language, glob files of that type repo-wide (honoring
|
|
9
|
-
* .gitignore and a hard-coded ignore set), then cluster by common prefix
|
|
10
|
-
* directory and pick the directory with the highest file density.
|
|
11
|
-
*
|
|
12
|
-
* Handles:
|
|
13
|
-
* - Colocated tests (src/**/*.test.ts next to source). Sets `colocated: true`
|
|
14
|
-
* on the language entry when test files are <30% of a dir's file count
|
|
15
|
-
* AND no dedicated tests/__tests__/ dir exists.
|
|
16
|
-
* - Top-level `tests/` or `__tests__/` directories (honored as test_dirs).
|
|
17
|
-
* - Monorepo packages/apps/services/libs/modules subtrees (each treated as
|
|
18
|
-
* a workspace root).
|
|
19
|
-
*
|
|
20
|
-
* Security (CR-3, CR-9):
|
|
21
|
-
* - Glob roots strictly inside projectRoot. Symlinks escaping projectRoot
|
|
22
|
-
* are rejected.
|
|
23
|
-
* - Secret-ish files (.env*, *.pem, *.key, .aws/, .ssh/, credentials.json)
|
|
24
|
-
* are excluded from globs.
|
|
25
|
-
* - Only the filename is ever read — never file contents.
|
|
26
|
-
*
|
|
27
|
-
* Usage:
|
|
28
|
-
* ```ts
|
|
29
|
-
* import { detectSourceDirs } from './detect/source-dir-detector.ts';
|
|
30
|
-
* const map = detectSourceDirs('/repo', ['python', 'typescript']);
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
import { realpathSync } from 'fs';
|
|
35
|
-
import { resolve } from 'path';
|
|
36
|
-
import fg from 'fast-glob';
|
|
37
|
-
import type { SupportedLanguage } from './package-detector.ts';
|
|
38
|
-
|
|
39
|
-
export interface SourceDirInfo {
|
|
40
|
-
/** Source directories (relative to projectRoot, forward-slash). */
|
|
41
|
-
source_dirs: string[];
|
|
42
|
-
/** Test directories (relative to projectRoot, forward-slash). */
|
|
43
|
-
test_dirs: string[];
|
|
44
|
-
/** True when tests live next to source (no dedicated tests/ dir). */
|
|
45
|
-
colocated: boolean;
|
|
46
|
-
/** Number of source files detected for this language. */
|
|
47
|
-
file_count: number;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export type SourceDirMap = Partial<Record<SupportedLanguage, SourceDirInfo>>;
|
|
51
|
-
|
|
52
|
-
const IGNORE_PATTERNS: string[] = [
|
|
53
|
-
'**/node_modules/**',
|
|
54
|
-
'**/.venv/**',
|
|
55
|
-
'**/venv/**',
|
|
56
|
-
'**/__pycache__/**',
|
|
57
|
-
'**/dist/**',
|
|
58
|
-
'**/build/**',
|
|
59
|
-
'**/.build/**',
|
|
60
|
-
'**/target/**',
|
|
61
|
-
'**/.next/**',
|
|
62
|
-
'**/.nuxt/**',
|
|
63
|
-
'**/coverage/**',
|
|
64
|
-
'**/.git/**',
|
|
65
|
-
'**/.massu/**',
|
|
66
|
-
'**/.turbo/**',
|
|
67
|
-
'**/.cache/**',
|
|
68
|
-
'**/.pytest_cache/**',
|
|
69
|
-
'**/.mypy_cache/**',
|
|
70
|
-
'**/DerivedData/**',
|
|
71
|
-
'**/Pods/**',
|
|
72
|
-
// Secret-ish patterns
|
|
73
|
-
'**/.env',
|
|
74
|
-
'**/.env.*',
|
|
75
|
-
'**/*.pem',
|
|
76
|
-
'**/*.key',
|
|
77
|
-
'**/.aws/**',
|
|
78
|
-
'**/.ssh/**',
|
|
79
|
-
'**/credentials.json',
|
|
80
|
-
'**/*.p12',
|
|
81
|
-
'**/*.pfx',
|
|
82
|
-
];
|
|
83
|
-
|
|
84
|
-
const EXTENSIONS: Record<SupportedLanguage, string[]> = {
|
|
85
|
-
python: ['py'],
|
|
86
|
-
typescript: ['ts', 'tsx'],
|
|
87
|
-
javascript: ['js', 'jsx', 'mjs', 'cjs'],
|
|
88
|
-
rust: ['rs'],
|
|
89
|
-
swift: ['swift'],
|
|
90
|
-
go: ['go'],
|
|
91
|
-
java: ['java', 'kt'],
|
|
92
|
-
ruby: ['rb'],
|
|
93
|
-
// Plan 1.5.1 — closing CR-39 init gap for Phoenix + ASP.NET projects.
|
|
94
|
-
elixir: ['ex', 'exs'],
|
|
95
|
-
csharp: ['cs'],
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const TEST_FILE_PATTERNS: Record<SupportedLanguage, RegExp[]> = {
|
|
99
|
-
python: [/_test\.py$/, /test_[^/]*\.py$/],
|
|
100
|
-
typescript: [/\.test\.tsx?$/, /\.spec\.tsx?$/],
|
|
101
|
-
javascript: [/\.test\.[mc]?jsx?$/, /\.spec\.[mc]?jsx?$/],
|
|
102
|
-
rust: [/tests\/.*\.rs$/],
|
|
103
|
-
swift: [/Tests\//],
|
|
104
|
-
go: [/_test\.go$/],
|
|
105
|
-
java: [/Test[^/]*\.(java|kt)$/, /[^/]*Test\.(java|kt)$/],
|
|
106
|
-
ruby: [/_spec\.rb$/, /_test\.rb$/],
|
|
107
|
-
// Phoenix/ExUnit canonical: `test/**_test.exs`. ASP.NET / xUnit
|
|
108
|
-
// canonical: `*Tests.cs` or `*.Tests/...`.
|
|
109
|
-
elixir: [/_test\.exs$/, /\/test\//],
|
|
110
|
-
csharp: [/Tests?\.cs$/, /\.Tests?\//],
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const TEST_DIR_KEYWORDS = ['tests', 'test', '__tests__', 'spec', 'specs'];
|
|
114
|
-
|
|
115
|
-
function extsFor(language: SupportedLanguage): string[] {
|
|
116
|
-
return EXTENSIONS[language] ?? [];
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Extensions for a language, with optional fallback for javascript-only repos
|
|
121
|
-
* that still contain `.ts`/`.tsx` files (no `typescript` manifest, no
|
|
122
|
-
* `tsconfig.json`). Fixes the bug where `apps/web/page.tsx` in a plain-JS
|
|
123
|
-
* monorepo is invisible to the javascript glob, causing `init --ci` to fall
|
|
124
|
-
* back to the nonexistent `src/`. See plan item P1-001 and incident
|
|
125
|
-
* `2026-04-20-massu-core-monorepo-paths-source.md`.
|
|
126
|
-
*/
|
|
127
|
-
function extsWithFallback(
|
|
128
|
-
language: SupportedLanguage,
|
|
129
|
-
fallbackTsForJs: boolean
|
|
130
|
-
): string[] {
|
|
131
|
-
const base = extsFor(language);
|
|
132
|
-
if (language === 'javascript' && fallbackTsForJs) {
|
|
133
|
-
return [...base, 'ts', 'tsx'];
|
|
134
|
-
}
|
|
135
|
-
return base;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function isTestPath(language: SupportedLanguage, path: string): boolean {
|
|
139
|
-
// Any dedicated test-dir keyword in the path segments
|
|
140
|
-
const segments = path.split('/');
|
|
141
|
-
for (const seg of segments) {
|
|
142
|
-
if (TEST_DIR_KEYWORDS.includes(seg)) return true;
|
|
143
|
-
}
|
|
144
|
-
const patterns = TEST_FILE_PATTERNS[language] ?? [];
|
|
145
|
-
return patterns.some((re) => re.test(path));
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/** Get the top-level directory segment of a relative path (or '.' for root). */
|
|
149
|
-
function topSegment(rel: string): string {
|
|
150
|
-
const parts = rel.split('/');
|
|
151
|
-
return parts.length > 1 ? parts[0] : '.';
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Check that a path (after realpath) is inside the projectRoot.
|
|
156
|
-
* Symlinks escaping the tree are rejected.
|
|
157
|
-
*/
|
|
158
|
-
function isInsideRoot(root: string, candidate: string): boolean {
|
|
159
|
-
try {
|
|
160
|
-
const realRoot = realpathSync(root);
|
|
161
|
-
const realCand = realpathSync(resolve(root, candidate));
|
|
162
|
-
return realCand === realRoot || realCand.startsWith(realRoot + '/');
|
|
163
|
-
} catch {
|
|
164
|
-
return false;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Detect source and test directories per language.
|
|
170
|
-
*
|
|
171
|
-
* @param projectRoot absolute path to repo root
|
|
172
|
-
* @param languages list of languages to probe (derived from P1-001 manifests)
|
|
173
|
-
* @param opts optional flags:
|
|
174
|
-
* - `fallbackTsForJs`: when true AND the language is `javascript` AND no
|
|
175
|
-
* `typescript` manifest was discovered, also glob `.ts`/`.tsx`. This is
|
|
176
|
-
* the P1-001 fix for plain-JS monorepos (e.g. turbo with `next` in a
|
|
177
|
-
* package.json that lacks a typescript dep) that still use `.tsx`.
|
|
178
|
-
*/
|
|
179
|
-
export function detectSourceDirs(
|
|
180
|
-
projectRoot: string,
|
|
181
|
-
languages: SupportedLanguage[],
|
|
182
|
-
opts?: { fallbackTsForJs?: boolean }
|
|
183
|
-
): SourceDirMap {
|
|
184
|
-
const fallbackTsForJs = opts?.fallbackTsForJs ?? false;
|
|
185
|
-
const out: SourceDirMap = {};
|
|
186
|
-
for (const lang of languages) {
|
|
187
|
-
const exts = extsWithFallback(lang, fallbackTsForJs);
|
|
188
|
-
if (exts.length === 0) continue;
|
|
189
|
-
const patterns = exts.map((e) => `**/*.${e}`);
|
|
190
|
-
let files: string[];
|
|
191
|
-
try {
|
|
192
|
-
files = fg.sync(patterns, {
|
|
193
|
-
cwd: projectRoot,
|
|
194
|
-
dot: false,
|
|
195
|
-
ignore: IGNORE_PATTERNS,
|
|
196
|
-
followSymbolicLinks: false,
|
|
197
|
-
suppressErrors: true,
|
|
198
|
-
});
|
|
199
|
-
} catch {
|
|
200
|
-
files = [];
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Drop any file whose resolved realpath escapes the root (defence in depth).
|
|
204
|
-
files = files.filter((f) => isInsideRoot(projectRoot, f));
|
|
205
|
-
|
|
206
|
-
if (files.length === 0) {
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Split into source vs test files.
|
|
211
|
-
const sourceFiles: string[] = [];
|
|
212
|
-
const testFiles: string[] = [];
|
|
213
|
-
for (const f of files) {
|
|
214
|
-
if (isTestPath(lang, f)) testFiles.push(f);
|
|
215
|
-
else sourceFiles.push(f);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Cluster by top segment.
|
|
219
|
-
const srcCluster = new Map<string, number>();
|
|
220
|
-
for (const f of sourceFiles) {
|
|
221
|
-
const k = topSegment(f);
|
|
222
|
-
srcCluster.set(k, (srcCluster.get(k) ?? 0) + 1);
|
|
223
|
-
}
|
|
224
|
-
const testCluster = new Map<string, number>();
|
|
225
|
-
for (const f of testFiles) {
|
|
226
|
-
const k = topSegment(f);
|
|
227
|
-
testCluster.set(k, (testCluster.get(k) ?? 0) + 1);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const source_dirs: string[] = [];
|
|
231
|
-
const test_dirs: string[] = [];
|
|
232
|
-
|
|
233
|
-
// Any top segment with at least one source file counts.
|
|
234
|
-
// Sort by density desc, then name asc for determinism.
|
|
235
|
-
const srcSorted = [...srcCluster.entries()].sort((a, b) => {
|
|
236
|
-
if (b[1] !== a[1]) return b[1] - a[1];
|
|
237
|
-
return a[0].localeCompare(b[0]);
|
|
238
|
-
});
|
|
239
|
-
for (const [seg] of srcSorted) source_dirs.push(seg);
|
|
240
|
-
|
|
241
|
-
// Test dirs: any top-level dir named in TEST_DIR_KEYWORDS that accrued files,
|
|
242
|
-
// plus any top segment with test files.
|
|
243
|
-
const testSet = new Set<string>();
|
|
244
|
-
for (const [seg] of testCluster.entries()) {
|
|
245
|
-
if (TEST_DIR_KEYWORDS.includes(seg)) testSet.add(seg);
|
|
246
|
-
}
|
|
247
|
-
// Also pick up "tests/" under each source dir — e.g. apps/ai-service/tests/
|
|
248
|
-
// Glob again just for test dirs this language may have.
|
|
249
|
-
let testDirHits: string[] = [];
|
|
250
|
-
try {
|
|
251
|
-
testDirHits = fg.sync(
|
|
252
|
-
TEST_DIR_KEYWORDS.map((k) => `**/${k}/**/*.${exts[0]}`),
|
|
253
|
-
{
|
|
254
|
-
cwd: projectRoot,
|
|
255
|
-
dot: false,
|
|
256
|
-
ignore: IGNORE_PATTERNS,
|
|
257
|
-
followSymbolicLinks: false,
|
|
258
|
-
suppressErrors: true,
|
|
259
|
-
}
|
|
260
|
-
);
|
|
261
|
-
} catch {
|
|
262
|
-
testDirHits = [];
|
|
263
|
-
}
|
|
264
|
-
const testPrefixes = new Set<string>();
|
|
265
|
-
for (const f of testDirHits) {
|
|
266
|
-
// Keep the prefix up to and including the test keyword dir
|
|
267
|
-
const segs = f.split('/');
|
|
268
|
-
for (let i = 0; i < segs.length; i++) {
|
|
269
|
-
if (TEST_DIR_KEYWORDS.includes(segs[i])) {
|
|
270
|
-
testPrefixes.add(segs.slice(0, i + 1).join('/'));
|
|
271
|
-
break;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
for (const p of testPrefixes) testSet.add(p);
|
|
276
|
-
for (const seg of testSet) test_dirs.push(seg);
|
|
277
|
-
test_dirs.sort();
|
|
278
|
-
|
|
279
|
-
// Colocated if test_dirs set is empty AND test files exist AND testFiles/(src+test) < 0.3
|
|
280
|
-
const totalFiles = sourceFiles.length + testFiles.length;
|
|
281
|
-
const testRatio = totalFiles === 0 ? 0 : testFiles.length / totalFiles;
|
|
282
|
-
const hasDedicatedTestDir = test_dirs.length > 0;
|
|
283
|
-
const colocated =
|
|
284
|
-
!hasDedicatedTestDir && testFiles.length > 0 && testRatio < 0.3;
|
|
285
|
-
if (colocated) {
|
|
286
|
-
for (const s of source_dirs) if (!test_dirs.includes(s)) test_dirs.push(s);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
out[lang] = {
|
|
290
|
-
source_dirs,
|
|
291
|
-
test_dirs,
|
|
292
|
-
colocated,
|
|
293
|
-
file_count: files.length,
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
return out;
|
|
297
|
-
}
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2026 Massu. All rights reserved.
|
|
2
|
-
// Licensed under BSL 1.1 - see LICENSE file for details.
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* VR Command Map (P1-005)
|
|
6
|
-
* =======================
|
|
7
|
-
*
|
|
8
|
-
* Given a language + framework info + workspace directory, produce the
|
|
9
|
-
* verification command set (VR-TEST, VR-TYPE, VR-BUILD, VR-SYNTAX, VR-LINT)
|
|
10
|
-
* that the skill runners should invoke.
|
|
11
|
-
*
|
|
12
|
-
* User overrides from `massu.config.yaml` `verification.<language>.*` take
|
|
13
|
-
* precedence over the built-in mapping.
|
|
14
|
-
*
|
|
15
|
-
* Usage:
|
|
16
|
-
* ```ts
|
|
17
|
-
* import { getVRCommands } from './detect/vr-command-map.ts';
|
|
18
|
-
* const cmds = getVRCommands('python', { test_framework: 'pytest', ... }, 'apps/ai-service');
|
|
19
|
-
* // => { test: 'cd apps/ai-service && python3 -m pytest -q', ... }
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
import type { SupportedLanguage } from './package-detector.ts';
|
|
24
|
-
import type { FrameworkInfo } from './framework-detector.ts';
|
|
25
|
-
|
|
26
|
-
export interface VRCommandSet {
|
|
27
|
-
/** VR-TEST command. */
|
|
28
|
-
test: string | null;
|
|
29
|
-
/** VR-TYPE command. */
|
|
30
|
-
type: string | null;
|
|
31
|
-
/** VR-BUILD command. */
|
|
32
|
-
build: string | null;
|
|
33
|
-
/** VR-SYNTAX command. */
|
|
34
|
-
syntax: string | null;
|
|
35
|
-
/** VR-LINT command. */
|
|
36
|
-
lint: string | null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** Shape read from `config.verification[<language>]`. */
|
|
40
|
-
export interface UserVerificationEntry {
|
|
41
|
-
type?: string;
|
|
42
|
-
test?: string;
|
|
43
|
-
syntax?: string;
|
|
44
|
-
lint?: string;
|
|
45
|
-
build?: string;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function prefix(dir: string, cmd: string): string {
|
|
49
|
-
if (!dir || dir === '.') return cmd;
|
|
50
|
-
return `cd ${dir} && ${cmd}`;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function defaultsFor(
|
|
54
|
-
language: SupportedLanguage,
|
|
55
|
-
fw: FrameworkInfo,
|
|
56
|
-
dir: string
|
|
57
|
-
): VRCommandSet {
|
|
58
|
-
switch (language) {
|
|
59
|
-
case 'python': {
|
|
60
|
-
const testFw = fw.test_framework ?? 'pytest';
|
|
61
|
-
return {
|
|
62
|
-
test:
|
|
63
|
-
testFw === 'unittest'
|
|
64
|
-
? prefix(dir, 'python3 -m unittest')
|
|
65
|
-
: prefix(dir, 'python3 -m pytest -q'),
|
|
66
|
-
type: prefix(dir, 'python3 -m mypy .'),
|
|
67
|
-
build: null,
|
|
68
|
-
syntax: prefix(dir, 'python3 -m py_compile'),
|
|
69
|
-
lint: prefix(dir, 'python3 -m ruff check .'),
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
case 'typescript': {
|
|
73
|
-
const testFw = fw.test_framework ?? 'vitest';
|
|
74
|
-
// Test command uses npm test (respecting package.json script); fallback mapping
|
|
75
|
-
// is fine because npm test routes through whatever runner is wired.
|
|
76
|
-
return {
|
|
77
|
-
test: prefix(dir, 'npm test'),
|
|
78
|
-
type: prefix(dir, 'npx tsc --noEmit'),
|
|
79
|
-
build: prefix(dir, 'npm run build'),
|
|
80
|
-
syntax: null,
|
|
81
|
-
lint: prefix(dir, 'npx eslint .'),
|
|
82
|
-
// testFw currently only affects defaults; npm test is runner-agnostic
|
|
83
|
-
...(testFw === 'mocha'
|
|
84
|
-
? { test: prefix(dir, 'npx mocha') }
|
|
85
|
-
: {}),
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
case 'javascript': {
|
|
89
|
-
return {
|
|
90
|
-
test: prefix(dir, 'npm test'),
|
|
91
|
-
type: null,
|
|
92
|
-
build: prefix(dir, 'npm run build'),
|
|
93
|
-
syntax: null,
|
|
94
|
-
lint: prefix(dir, 'npx eslint .'),
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
case 'rust': {
|
|
98
|
-
return {
|
|
99
|
-
test: prefix(dir, 'cargo test'),
|
|
100
|
-
type: prefix(dir, 'cargo check'),
|
|
101
|
-
build: prefix(dir, 'cargo build'),
|
|
102
|
-
syntax: null,
|
|
103
|
-
lint: prefix(dir, 'cargo clippy -- -D warnings'),
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
case 'swift': {
|
|
107
|
-
return {
|
|
108
|
-
test: prefix(dir, 'swift test'),
|
|
109
|
-
type: prefix(dir, 'swift build'),
|
|
110
|
-
build: prefix(dir, 'xcodebuild build'),
|
|
111
|
-
syntax: null,
|
|
112
|
-
lint: prefix(dir, 'swiftlint'),
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
case 'go': {
|
|
116
|
-
return {
|
|
117
|
-
test: prefix(dir, 'go test ./...'),
|
|
118
|
-
type: prefix(dir, 'go vet ./...'),
|
|
119
|
-
build: prefix(dir, 'go build ./...'),
|
|
120
|
-
syntax: null,
|
|
121
|
-
lint: prefix(dir, 'golangci-lint run'),
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
case 'java': {
|
|
125
|
-
return {
|
|
126
|
-
test: prefix(dir, 'mvn test'),
|
|
127
|
-
type: prefix(dir, 'mvn compile'),
|
|
128
|
-
build: prefix(dir, 'mvn package'),
|
|
129
|
-
syntax: null,
|
|
130
|
-
lint: null,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
case 'ruby': {
|
|
134
|
-
return {
|
|
135
|
-
test: prefix(dir, 'bundle exec rspec'),
|
|
136
|
-
type: null,
|
|
137
|
-
build: null,
|
|
138
|
-
syntax: prefix(dir, 'ruby -c'),
|
|
139
|
-
lint: prefix(dir, 'bundle exec rubocop'),
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
default:
|
|
143
|
-
return { test: null, type: null, build: null, syntax: null, lint: null };
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Produce the VR command set for a language.
|
|
149
|
-
*
|
|
150
|
-
* User-provided entries (if any) override built-ins key-by-key.
|
|
151
|
-
*/
|
|
152
|
-
export function getVRCommands(
|
|
153
|
-
language: SupportedLanguage,
|
|
154
|
-
framework: FrameworkInfo,
|
|
155
|
-
dir: string,
|
|
156
|
-
userOverrides?: UserVerificationEntry
|
|
157
|
-
): VRCommandSet {
|
|
158
|
-
const built = defaultsFor(language, framework, dir);
|
|
159
|
-
if (!userOverrides) return built;
|
|
160
|
-
return {
|
|
161
|
-
test: userOverrides.test ?? built.test,
|
|
162
|
-
type: userOverrides.type ?? built.type,
|
|
163
|
-
build: userOverrides.build ?? built.build,
|
|
164
|
-
syntax: userOverrides.syntax ?? built.syntax,
|
|
165
|
-
lint: userOverrides.lint ?? built.lint,
|
|
166
|
-
};
|
|
167
|
-
}
|
package/src/docs-map.json
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 1,
|
|
3
|
-
"description": "Maps app code (routes, routers, components) to help site pages. Used by massu_docs_audit to detect which docs need updating when code changes.",
|
|
4
|
-
"mappings": [
|
|
5
|
-
{
|
|
6
|
-
"id": "dashboard",
|
|
7
|
-
"helpPage": "pages/dashboard.mdx",
|
|
8
|
-
"appRoutes": ["src/app/dashboard/**"],
|
|
9
|
-
"routers": ["dashboard.ts", "analytics.ts"],
|
|
10
|
-
"components": ["src/components/dashboard/**", "src/components/analytics/**"],
|
|
11
|
-
"keywords": ["dashboard", "home", "overview", "analytics", "KPI"]
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"id": "auth",
|
|
15
|
-
"helpPage": "pages/auth.mdx",
|
|
16
|
-
"appRoutes": ["src/app/auth/**", "src/app/login/**"],
|
|
17
|
-
"routers": ["auth.ts", "sessions.ts", "oauth.ts"],
|
|
18
|
-
"components": ["src/components/auth/**"],
|
|
19
|
-
"keywords": ["login", "signup", "authentication", "password", "SSO", "OAuth"]
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
"id": "users",
|
|
23
|
-
"helpPage": "pages/users.mdx",
|
|
24
|
-
"appRoutes": ["src/app/users/**", "src/app/profile/**"],
|
|
25
|
-
"routers": ["users.ts", "user-profiles.ts", "user-preferences.ts"],
|
|
26
|
-
"components": ["src/components/users/**", "src/components/profile/**"],
|
|
27
|
-
"keywords": ["user", "profile", "account", "preferences"]
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
"id": "teams",
|
|
31
|
-
"helpPage": "pages/teams.mdx",
|
|
32
|
-
"appRoutes": ["src/app/teams/**", "src/app/organizations/**"],
|
|
33
|
-
"routers": ["teams.ts", "team-members.ts", "organizations.ts", "invitations.ts"],
|
|
34
|
-
"components": ["src/components/teams/**", "src/components/organizations/**"],
|
|
35
|
-
"keywords": ["team", "organization", "member", "invite", "role"]
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"id": "billing",
|
|
39
|
-
"helpPage": "pages/billing.mdx",
|
|
40
|
-
"appRoutes": ["src/app/billing/**", "src/app/subscriptions/**"],
|
|
41
|
-
"routers": ["billing.ts", "subscriptions.ts", "invoices.ts", "payment-methods.ts"],
|
|
42
|
-
"components": ["src/components/billing/**", "src/components/subscriptions/**"],
|
|
43
|
-
"keywords": ["billing", "subscription", "invoice", "payment", "plan", "pricing"]
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"id": "settings",
|
|
47
|
-
"helpPage": "pages/settings.mdx",
|
|
48
|
-
"appRoutes": ["src/app/settings/**", "src/app/admin/**"],
|
|
49
|
-
"routers": ["settings.ts", "admin.ts", "feature-flags.ts"],
|
|
50
|
-
"components": ["src/components/settings/**", "src/components/admin/**"],
|
|
51
|
-
"keywords": ["settings", "configuration", "admin", "preferences", "feature flag"]
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
"id": "notifications",
|
|
55
|
-
"helpPage": "pages/notifications.mdx",
|
|
56
|
-
"appRoutes": ["src/app/notifications/**"],
|
|
57
|
-
"routers": ["notifications.ts", "notification-preferences.ts", "push-notifications.ts"],
|
|
58
|
-
"components": ["src/components/notifications/**"],
|
|
59
|
-
"keywords": ["notification", "alert", "email", "push", "webhook"]
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
"id": "projects",
|
|
63
|
-
"helpPage": "pages/projects.mdx",
|
|
64
|
-
"appRoutes": ["src/app/projects/**"],
|
|
65
|
-
"routers": ["projects.ts", "project-members.ts", "project-settings.ts"],
|
|
66
|
-
"components": ["src/components/projects/**"],
|
|
67
|
-
"keywords": ["project", "workspace", "task", "milestone"]
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
"id": "api-docs",
|
|
71
|
-
"helpPage": "pages/api-docs.mdx",
|
|
72
|
-
"appRoutes": ["src/app/api-docs/**"],
|
|
73
|
-
"routers": ["api-keys.ts", "webhooks.ts"],
|
|
74
|
-
"components": ["src/components/api-docs/**"],
|
|
75
|
-
"keywords": ["API", "key", "webhook", "endpoint", "documentation"]
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
"id": "integrations",
|
|
79
|
-
"helpPage": "pages/integrations.mdx",
|
|
80
|
-
"appRoutes": ["src/app/integrations/**"],
|
|
81
|
-
"routers": ["integrations.ts", "oauth-connections.ts", "sync.ts"],
|
|
82
|
-
"components": ["src/components/integrations/**"],
|
|
83
|
-
"keywords": ["integration", "connect", "sync", "OAuth", "third-party"]
|
|
84
|
-
}
|
|
85
|
-
],
|
|
86
|
-
"userGuideInheritance": {
|
|
87
|
-
"description": "User guide pages inherit their parent feature's mapping. The guide name contains the feature keyword. For example, billing-guide inherits from the 'billing' mapping.",
|
|
88
|
-
"pattern": "pages/user-guides/{feature}-guide/**",
|
|
89
|
-
"examples": {
|
|
90
|
-
"dashboard-guide": "dashboard",
|
|
91
|
-
"billing-guide": "billing",
|
|
92
|
-
"team-management-guide": "teams",
|
|
93
|
-
"user-management-guide": "users",
|
|
94
|
-
"notifications-guide": "notifications",
|
|
95
|
-
"analytics-guide": "dashboard",
|
|
96
|
-
"project-setup-guide": "projects",
|
|
97
|
-
"api-integration-guide": "integrations"
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|