@massu/core 1.5.2 → 1.5.4
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/dist/cli.js +1682 -393
- package/dist/hooks/session-start.js +26 -2
- package/package.json +1 -1
- package/src/commands/init.ts +43 -1
- package/src/detect/adapters/file-sampler.ts +225 -0
- package/src/detect/adapters/index.ts +2 -0
- package/src/detect/codebase-introspector.ts +11 -10
- package/src/security/registry-pubkey.generated.ts +1 -1
|
@@ -12,6 +12,9 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
12
12
|
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
13
13
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
14
14
|
});
|
|
15
|
+
var __esm = (fn, res) => function __init() {
|
|
16
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
17
|
+
};
|
|
15
18
|
var __commonJS = (cb, mod) => function __require2() {
|
|
16
19
|
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
17
20
|
};
|
|
@@ -5781,6 +5784,15 @@ var require_out4 = __commonJS({
|
|
|
5781
5784
|
}
|
|
5782
5785
|
});
|
|
5783
5786
|
|
|
5787
|
+
// src/detect/adapters/parse-guard.ts
|
|
5788
|
+
var MAX_AST_FILE_BYTES;
|
|
5789
|
+
var init_parse_guard = __esm({
|
|
5790
|
+
"src/detect/adapters/parse-guard.ts"() {
|
|
5791
|
+
"use strict";
|
|
5792
|
+
MAX_AST_FILE_BYTES = 1 * 1024 * 1024;
|
|
5793
|
+
}
|
|
5794
|
+
});
|
|
5795
|
+
|
|
5784
5796
|
// src/memory-db.ts
|
|
5785
5797
|
import Database from "better-sqlite3";
|
|
5786
5798
|
import { dirname as dirname2, basename } from "path";
|
|
@@ -9335,8 +9347,8 @@ function relativeTo(projectRoot, absPath) {
|
|
|
9335
9347
|
return basename2(absPath);
|
|
9336
9348
|
}
|
|
9337
9349
|
|
|
9338
|
-
// src/detect/adapters/
|
|
9339
|
-
|
|
9350
|
+
// src/detect/adapters/runner.ts
|
|
9351
|
+
init_parse_guard();
|
|
9340
9352
|
|
|
9341
9353
|
// node_modules/web-tree-sitter/tree-sitter.js
|
|
9342
9354
|
var __defProp2 = Object.defineProperty;
|
|
@@ -13285,6 +13297,18 @@ var Parser = class {
|
|
|
13285
13297
|
}
|
|
13286
13298
|
};
|
|
13287
13299
|
|
|
13300
|
+
// src/detect/adapters/python-fastapi.ts
|
|
13301
|
+
init_parse_guard();
|
|
13302
|
+
|
|
13303
|
+
// src/detect/adapters/python-django.ts
|
|
13304
|
+
init_parse_guard();
|
|
13305
|
+
|
|
13306
|
+
// src/detect/adapters/nextjs-trpc.ts
|
|
13307
|
+
init_parse_guard();
|
|
13308
|
+
|
|
13309
|
+
// src/detect/adapters/swift-swiftui.ts
|
|
13310
|
+
init_parse_guard();
|
|
13311
|
+
|
|
13288
13312
|
// src/detect/codebase-introspector.ts
|
|
13289
13313
|
function introspect(detection, projectRoot) {
|
|
13290
13314
|
const out2 = {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@massu/core",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "AI Engineering Governance MCP Server - Session memory, knowledge system, feature registry, code intelligence, rule enforcement, tiered tooling (12 free / 72 total), 55+ workflow commands, 11 agents, 20+ patterns",
|
|
6
6
|
"main": "src/server.ts",
|
package/src/commands/init.ts
CHANGED
|
@@ -84,6 +84,13 @@ export interface InitOptions {
|
|
|
84
84
|
cwd?: string;
|
|
85
85
|
/** Suppress console output. */
|
|
86
86
|
silent?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Plan 1.5.4: skip AST adapter introspection that surfaces under
|
|
89
|
+
* `detected.<adapter-id>:` blocks. Default false (introspect runs);
|
|
90
|
+
* set true via `--no-introspect` for fast sync-only init or when
|
|
91
|
+
* the AST tier's grammar download isn't desirable.
|
|
92
|
+
*/
|
|
93
|
+
skipIntrospect?: boolean;
|
|
87
94
|
}
|
|
88
95
|
|
|
89
96
|
export interface GenerateConfigV2Options {
|
|
@@ -1187,6 +1194,7 @@ export function parseInitArgs(argv: string[]): ParseInitArgsResult {
|
|
|
1187
1194
|
if (a === '--ci') opts.ci = true;
|
|
1188
1195
|
else if (a === '--force') opts.force = true;
|
|
1189
1196
|
else if (a === '--skip-commands') opts.skipCommands = true;
|
|
1197
|
+
else if (a === '--no-introspect') opts.skipIntrospect = true;
|
|
1190
1198
|
else if (a === '--help' || a === '-h') opts.help = true;
|
|
1191
1199
|
else if (a === '--template') {
|
|
1192
1200
|
const next = argv[i + 1];
|
|
@@ -1386,7 +1394,41 @@ export async function runInit(argv?: string[], overrides?: InitOptions): Promise
|
|
|
1386
1394
|
// Phoenix / clear-Spring projects (CR-39 violation per the Plan 1.5.1
|
|
1387
1395
|
// 5-fixture verification).
|
|
1388
1396
|
const baseConfig = buildConfigFromDetection({ projectRoot, detection });
|
|
1389
|
-
const
|
|
1397
|
+
const withVariant = applyVariantTemplate(baseConfig, resolveTemplatesDir());
|
|
1398
|
+
|
|
1399
|
+
// Plan 1.5.4 §3: pipe AST adapter introspect output into the emitted
|
|
1400
|
+
// config under `detected.<adapter-id>:` blocks. introspectAsync runs
|
|
1401
|
+
// the AST adapter pipeline (using the real file sampler from
|
|
1402
|
+
// codebase-introspector.ts post-1.5.4); each adapter that returns
|
|
1403
|
+
// non-'none' confidence surfaces its conventions + provenance.
|
|
1404
|
+
// --no-introspect bypasses for users who want sync init.
|
|
1405
|
+
let config = withVariant;
|
|
1406
|
+
if (!opts.skipIntrospect) {
|
|
1407
|
+
try {
|
|
1408
|
+
const { introspectAsync } = await import('../detect/codebase-introspector.ts');
|
|
1409
|
+
const introspected = await introspectAsync(detection, projectRoot);
|
|
1410
|
+
const detectedBlocks: Record<string, unknown> = {};
|
|
1411
|
+
for (const [key, block] of Object.entries(introspected)) {
|
|
1412
|
+
// `introspected` includes language-keyed regex-fallback blocks
|
|
1413
|
+
// (`python`, `swift`, `typescript`) AND adapter-id-keyed AST
|
|
1414
|
+
// blocks. The AST blocks are the ones we want under
|
|
1415
|
+
// `detected.<adapter-id>:` — distinguishable by the presence of
|
|
1416
|
+
// a `_confidence` field on the block (the regex blocks don't
|
|
1417
|
+
// carry it). Filter for AST adapter outputs only.
|
|
1418
|
+
if (block && typeof block === 'object' && '_confidence' in (block as Record<string, unknown>)) {
|
|
1419
|
+
detectedBlocks[key] = block;
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
if (Object.keys(detectedBlocks).length > 0) {
|
|
1423
|
+
config = { ...config, detected: detectedBlocks };
|
|
1424
|
+
}
|
|
1425
|
+
} catch (err) {
|
|
1426
|
+
// Non-fatal: AST introspect is enrichment, not core detection.
|
|
1427
|
+
// Log to stderr so operators see the warning if it matters.
|
|
1428
|
+
errLog(`warning: AST adapter introspection failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1390
1432
|
const content = renderConfigYaml(config);
|
|
1391
1433
|
const writeRes = writeConfigAtomic(configPath, content);
|
|
1392
1434
|
if (!writeRes.validated) {
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
// Copyright (c) 2026 Massu. All rights reserved.
|
|
2
|
+
// Licensed under BSL 1.1 - see LICENSE file for details.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Per-adapter file sampler — Plan 1.5.4 §3 deliverable.
|
|
6
|
+
*
|
|
7
|
+
* Replaces the `sampleFiles: async () => []` placeholder at
|
|
8
|
+
* `codebase-introspector.ts:160` that prevented AST adapter introspect
|
|
9
|
+
* output from reaching the user-facing config emission. The adapters
|
|
10
|
+
* themselves have always worked (verified by `adapter-grammar-strict.test.ts`
|
|
11
|
+
* with 10/10 fixtures returning 'high' confidence); the gap was that
|
|
12
|
+
* production `introspectAsync()` handed each adapter `SourceFile[] = []`
|
|
13
|
+
* so adapters never saw any actual code.
|
|
14
|
+
*
|
|
15
|
+
* Per Plan 1.5.4 §0 self-attest #3: REUSES `EXTENSIONS` and
|
|
16
|
+
* `TEST_FILE_PATTERNS` from `source-dir-detector.ts:84-104` rather than
|
|
17
|
+
* duplicating them. Adding a new language to those maps automatically
|
|
18
|
+
* enables sampling for any adapter declaring that language.
|
|
19
|
+
*
|
|
20
|
+
* Algorithm:
|
|
21
|
+
* 1. For each language in `adapter.languages`, collect candidate
|
|
22
|
+
* source dirs from `detection.sourceDirs[<lang>].dirs` (already
|
|
23
|
+
* computed by Tier 0 detection per `source-dir-detector.ts`).
|
|
24
|
+
* Fall back to walking common roots (`<projectRoot>` + first-level
|
|
25
|
+
* subdirs) if detection didn't find any.
|
|
26
|
+
* 2. Walk each source dir up to `MAX_DEPTH` (default 3) levels deep,
|
|
27
|
+
* filtering by `EXTENSIONS[<lang>]` and excluding files matching
|
|
28
|
+
* any pattern in `TEST_FILE_PATTERNS[<lang>]`.
|
|
29
|
+
* 3. Skip `IGNORED_DIRS` (node_modules, .git, dist, etc.) at every
|
|
30
|
+
* depth (already enumerated in `source-dir-detector.ts`).
|
|
31
|
+
* 4. Cap per-adapter sample count to `MAX_FILES_PER_ADAPTER` (default
|
|
32
|
+
* 50) — sufficient for AST queries to find canonical patterns
|
|
33
|
+
* without blowing the introspect time budget.
|
|
34
|
+
* 5. Per-file size cap inherited from `parse-guard.ts:MAX_AST_FILE_BYTES`
|
|
35
|
+
* (256 KB) — files above this limit are dropped here so they don't
|
|
36
|
+
* reach the adapter's parse path.
|
|
37
|
+
* 6. Return `SourceFile[]` typed per `detect/adapters/types.ts`.
|
|
38
|
+
*
|
|
39
|
+
* Errors during the walk (permission-denied, broken symlinks, etc.) are
|
|
40
|
+
* non-fatal: the sampler logs a single stderr warning and proceeds with
|
|
41
|
+
* whatever files it has already collected.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
import { readdirSync, readFileSync, statSync, lstatSync } from 'node:fs';
|
|
45
|
+
import { join, extname } from 'node:path';
|
|
46
|
+
import type { CodebaseAdapter, SourceFile, TreeSitterLanguage } from './types.ts';
|
|
47
|
+
import type { DetectionResult } from '../index.ts';
|
|
48
|
+
import { MAX_AST_FILE_BYTES } from './parse-guard.ts';
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Language → file extensions. Sourced from `source-dir-detector.ts`'s
|
|
52
|
+
* `EXTENSIONS` map. Re-exported here as the canonical type for AST
|
|
53
|
+
* adapter sampling. Keys are `TreeSitterLanguage` so any adapter that
|
|
54
|
+
* targets a language not in this map fails the drift test (see
|
|
55
|
+
* `sample-files-coverage.test.ts`).
|
|
56
|
+
*/
|
|
57
|
+
export const SAMPLE_EXTENSIONS: Record<TreeSitterLanguage, readonly string[]> = {
|
|
58
|
+
python: ['py'],
|
|
59
|
+
typescript: ['ts', 'tsx'],
|
|
60
|
+
javascript: ['js', 'jsx', 'mjs', 'cjs'],
|
|
61
|
+
swift: ['swift'],
|
|
62
|
+
rust: ['rs'],
|
|
63
|
+
go: ['go'],
|
|
64
|
+
ruby: ['rb'],
|
|
65
|
+
php: ['php'],
|
|
66
|
+
java: ['java', 'kt'],
|
|
67
|
+
kotlin: ['kt', 'kts'],
|
|
68
|
+
elixir: ['ex', 'exs'],
|
|
69
|
+
erlang: ['erl', 'hrl'],
|
|
70
|
+
csharp: ['cs'],
|
|
71
|
+
cpp: ['cpp', 'cc', 'cxx', 'h', 'hpp'],
|
|
72
|
+
haskell: ['hs', 'lhs'],
|
|
73
|
+
ocaml: ['ml', 'mli'],
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Test-file patterns to exclude per language. Mirrors
|
|
78
|
+
* `source-dir-detector.ts:TEST_FILE_PATTERNS`. Adapters extracting
|
|
79
|
+
* "production" routing/auth/etc. conventions don't want to be misled by
|
|
80
|
+
* test fixtures.
|
|
81
|
+
*/
|
|
82
|
+
export const SAMPLE_TEST_FILE_PATTERNS: Record<TreeSitterLanguage, readonly RegExp[]> = {
|
|
83
|
+
python: [/_test\.py$/, /test_[^/]*\.py$/, /\/tests?\//],
|
|
84
|
+
typescript: [/\.test\.tsx?$/, /\.spec\.tsx?$/, /\/__tests__\//],
|
|
85
|
+
javascript: [/\.test\.[mc]?jsx?$/, /\.spec\.[mc]?jsx?$/, /\/__tests__\//],
|
|
86
|
+
swift: [/Tests\//],
|
|
87
|
+
rust: [/tests\/.*\.rs$/],
|
|
88
|
+
go: [/_test\.go$/],
|
|
89
|
+
ruby: [/_spec\.rb$/, /_test\.rb$/, /\/spec\//],
|
|
90
|
+
php: [/Test\.php$/, /\/tests?\//i],
|
|
91
|
+
java: [/Test[^/]*\.(java|kt)$/, /[^/]*Test\.(java|kt)$/, /\/test\//],
|
|
92
|
+
kotlin: [/Test[^/]*\.kt$/, /[^/]*Test\.kt$/],
|
|
93
|
+
elixir: [/_test\.exs$/, /\/test\//],
|
|
94
|
+
erlang: [/_SUITE\.erl$/],
|
|
95
|
+
csharp: [/Tests?\.cs$/, /\.Tests?\//],
|
|
96
|
+
cpp: [/_test\.(cpp|cc)$/i, /\/tests?\//i],
|
|
97
|
+
haskell: [/Spec\.hs$/, /\/test\//],
|
|
98
|
+
ocaml: [/_test\.ml$/, /\/test\//],
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const IGNORED_DIRS = new Set([
|
|
102
|
+
'node_modules', '.venv', 'venv', '__pycache__', 'dist', 'build', '.build',
|
|
103
|
+
'target', '.next', '.nuxt', 'coverage', '.git', '.massu', '.turbo',
|
|
104
|
+
'.cache', '.pytest_cache', '.mypy_cache', 'DerivedData', 'Pods',
|
|
105
|
+
'_build', 'deps', 'priv', 'cover', '.elixir_ls', // Elixir/Phoenix
|
|
106
|
+
'bin', 'obj', '.vs', 'packages', 'publish', 'TestResults', // .NET
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
export interface SampleFilesOptions {
|
|
110
|
+
/** Maximum directory depth to walk from each source dir. Default 3. */
|
|
111
|
+
maxDepth?: number;
|
|
112
|
+
/** Maximum total files to return per adapter. Default 50. */
|
|
113
|
+
maxFilesPerAdapter?: number;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const DEFAULT_MAX_DEPTH = 3;
|
|
117
|
+
const DEFAULT_MAX_FILES = 50;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Sample source files for an AST adapter.
|
|
121
|
+
*
|
|
122
|
+
* @param adapter - the adapter declaring `languages: TreeSitterLanguage[]`
|
|
123
|
+
* @param projectRoot - absolute project root
|
|
124
|
+
* @param detection - existing detection result (provides per-language
|
|
125
|
+
* source dirs from `source-dir-detector`)
|
|
126
|
+
* @param options - depth and count caps
|
|
127
|
+
*/
|
|
128
|
+
export function sampleFilesForAdapter(
|
|
129
|
+
adapter: CodebaseAdapter,
|
|
130
|
+
projectRoot: string,
|
|
131
|
+
detection: DetectionResult,
|
|
132
|
+
options: SampleFilesOptions = {},
|
|
133
|
+
): SourceFile[] {
|
|
134
|
+
const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
135
|
+
const maxFiles = options.maxFilesPerAdapter ?? DEFAULT_MAX_FILES;
|
|
136
|
+
|
|
137
|
+
const out: SourceFile[] = [];
|
|
138
|
+
const seen = new Set<string>(); // dedup by absolute path
|
|
139
|
+
|
|
140
|
+
for (const lang of adapter.languages) {
|
|
141
|
+
if (out.length >= maxFiles) break;
|
|
142
|
+
const exts = SAMPLE_EXTENSIONS[lang];
|
|
143
|
+
if (!exts || exts.length === 0) continue;
|
|
144
|
+
const testPatterns = SAMPLE_TEST_FILE_PATTERNS[lang] ?? [];
|
|
145
|
+
|
|
146
|
+
// Determine candidate dirs. Prefer detection.sourceDirs[lang] when
|
|
147
|
+
// present; otherwise walk projectRoot itself.
|
|
148
|
+
// (lang here is `TreeSitterLanguage`; sourceDirs is keyed by
|
|
149
|
+
// `SupportedLanguage`. The intersection is structural — every
|
|
150
|
+
// SupportedLanguage is a TreeSitterLanguage. We coerce via
|
|
151
|
+
// `Record<string, …>` lookup which is type-safe in a Record<string>.)
|
|
152
|
+
const langKey = lang as unknown as keyof typeof detection.sourceDirs;
|
|
153
|
+
const langDetection = detection.sourceDirs[langKey];
|
|
154
|
+
const candidateDirs: string[] = [];
|
|
155
|
+
if (langDetection?.source_dirs && langDetection.source_dirs.length > 0) {
|
|
156
|
+
candidateDirs.push(...langDetection.source_dirs.map((d: string) => join(projectRoot, d)));
|
|
157
|
+
} else {
|
|
158
|
+
candidateDirs.push(projectRoot);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
for (const dir of candidateDirs) {
|
|
162
|
+
if (out.length >= maxFiles) break;
|
|
163
|
+
walkDir(dir, exts, testPatterns, lang, maxDepth, 0, out, seen, maxFiles);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function walkDir(
|
|
171
|
+
dir: string,
|
|
172
|
+
exts: readonly string[],
|
|
173
|
+
testPatterns: readonly RegExp[],
|
|
174
|
+
lang: TreeSitterLanguage,
|
|
175
|
+
maxDepth: number,
|
|
176
|
+
curDepth: number,
|
|
177
|
+
out: SourceFile[],
|
|
178
|
+
seen: Set<string>,
|
|
179
|
+
maxFiles: number,
|
|
180
|
+
): void {
|
|
181
|
+
if (curDepth > maxDepth) return;
|
|
182
|
+
if (out.length >= maxFiles) return;
|
|
183
|
+
let entries: string[];
|
|
184
|
+
try {
|
|
185
|
+
entries = readdirSync(dir);
|
|
186
|
+
} catch {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
for (const entry of entries) {
|
|
190
|
+
if (out.length >= maxFiles) return;
|
|
191
|
+
if (entry.startsWith('.')) continue; // hidden
|
|
192
|
+
if (IGNORED_DIRS.has(entry)) continue;
|
|
193
|
+
const fullPath = join(dir, entry);
|
|
194
|
+
let st;
|
|
195
|
+
try {
|
|
196
|
+
st = lstatSync(fullPath);
|
|
197
|
+
} catch {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (st.isSymbolicLink()) continue; // refuse symlinks (security)
|
|
201
|
+
if (st.isDirectory()) {
|
|
202
|
+
walkDir(fullPath, exts, testPatterns, lang, maxDepth, curDepth + 1, out, seen, maxFiles);
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (!st.isFile()) continue;
|
|
206
|
+
if (st.size > MAX_AST_FILE_BYTES) continue; // size cap (parse-guard)
|
|
207
|
+
const ext = extname(entry).slice(1);
|
|
208
|
+
if (!exts.includes(ext)) continue;
|
|
209
|
+
if (testPatterns.some((p) => p.test(fullPath))) continue;
|
|
210
|
+
if (seen.has(fullPath)) continue;
|
|
211
|
+
seen.add(fullPath);
|
|
212
|
+
let content: string;
|
|
213
|
+
try {
|
|
214
|
+
content = readFileSync(fullPath, 'utf-8');
|
|
215
|
+
} catch {
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
out.push({
|
|
219
|
+
path: fullPath,
|
|
220
|
+
content,
|
|
221
|
+
language: lang,
|
|
222
|
+
size: st.size,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
@@ -152,20 +152,21 @@ export async function introspectAsync(
|
|
|
152
152
|
): Promise<DetectedConventions> {
|
|
153
153
|
const out: DetectedConventions = introspect(detection, projectRoot);
|
|
154
154
|
|
|
155
|
-
// Build signals + run AST adapters
|
|
155
|
+
// Build signals + run AST adapters with the real file sampler (Plan
|
|
156
|
+
// 1.5.4 §3). Pre-1.5.4 this was a placeholder returning [], which kept
|
|
157
|
+
// every AST adapter at 'none' confidence in the init flow. The
|
|
158
|
+
// adapters themselves always worked (verified by adapter-grammar-
|
|
159
|
+
// strict.test.ts); the gap was purely the seam between detection +
|
|
160
|
+
// adapter execution. The sampler reuses EXTENSIONS / TEST_FILE_PATTERNS
|
|
161
|
+
// from source-dir-detector.ts so adding a new language doesn't require
|
|
162
|
+
// a parallel map (CR-46 self-attest #3).
|
|
156
163
|
const signals = buildDetectionSignals(projectRoot);
|
|
164
|
+
const { sampleFilesForAdapter } = await import('./adapters/file-sampler.ts');
|
|
157
165
|
let merged;
|
|
158
166
|
try {
|
|
159
167
|
merged = await runAdapters(FIRST_PARTY_ADAPTERS, projectRoot, signals, {
|
|
160
|
-
sampleFiles: async (
|
|
161
|
-
|
|
162
|
-
// dedicated harnesses (per-adapter tests inject SourceFile[] directly).
|
|
163
|
-
// The introspector tier doesn't yet sample for AST adapters — that
|
|
164
|
-
// wiring lands together with Phase 4 LSP enrichment so the same path
|
|
165
|
-
// serves both. For now, returning [] keeps adapters at 'none' which
|
|
166
|
-
// means `out` is regex-only — consistent with the sync path and the
|
|
167
|
-
// pre-Phase-1 baseline test suite.
|
|
168
|
-
return [];
|
|
168
|
+
sampleFiles: async (adapter, root) => {
|
|
169
|
+
return sampleFilesForAdapter(adapter, root, detection);
|
|
169
170
|
},
|
|
170
171
|
});
|
|
171
172
|
} catch {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// AUTO-GENERATED by scripts/bundle-pubkey.mjs at 2026-05-
|
|
1
|
+
// AUTO-GENERATED by scripts/bundle-pubkey.mjs at 2026-05-08T20:46:40.928Z.
|
|
2
2
|
// Source pem: packages/core/security/registry-pubkey.pem
|
|
3
3
|
// RAW-bytes sha256: 3b6226d036c472e533110d11a7d0cd2773ce1d7d4f1003517d5bd69c5418ed4c
|
|
4
4
|
// DO NOT EDIT — regenerate via `node scripts/bundle-pubkey.mjs` or
|