@massu/core 1.5.3 → 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
package/dist/cli.js
CHANGED
|
@@ -1673,16 +1673,16 @@ import { parse as parseYaml2 } from "yaml";
|
|
|
1673
1673
|
function ingestMemoryFile(db, sessionId, filePath) {
|
|
1674
1674
|
if (!existsSync3(filePath)) return "skipped";
|
|
1675
1675
|
const content = readFileSync2(filePath, "utf-8");
|
|
1676
|
-
const
|
|
1676
|
+
const basename11 = (filePath.split("/").pop() ?? "").replace(".md", "");
|
|
1677
1677
|
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
1678
|
-
let name2 =
|
|
1678
|
+
let name2 = basename11;
|
|
1679
1679
|
let description = "";
|
|
1680
1680
|
let type = "discovery";
|
|
1681
1681
|
let confidence;
|
|
1682
1682
|
if (frontmatterMatch) {
|
|
1683
1683
|
try {
|
|
1684
1684
|
const fm = parseYaml2(frontmatterMatch[1]);
|
|
1685
|
-
name2 = fm.name ??
|
|
1685
|
+
name2 = fm.name ?? basename11;
|
|
1686
1686
|
description = fm.description ?? "";
|
|
1687
1687
|
type = fm.type ?? "discovery";
|
|
1688
1688
|
confidence = fm.confidence != null ? Number(fm.confidence) : void 0;
|
|
@@ -7374,8 +7374,8 @@ var require_pattern = __commonJS({
|
|
|
7374
7374
|
}
|
|
7375
7375
|
exports.endsWithSlashGlobStar = endsWithSlashGlobStar;
|
|
7376
7376
|
function isAffectDepthOfReadingPattern(pattern) {
|
|
7377
|
-
const
|
|
7378
|
-
return endsWithSlashGlobStar(pattern) || isStaticPattern(
|
|
7377
|
+
const basename11 = path.basename(pattern);
|
|
7378
|
+
return endsWithSlashGlobStar(pattern) || isStaticPattern(basename11);
|
|
7379
7379
|
}
|
|
7380
7380
|
exports.isAffectDepthOfReadingPattern = isAffectDepthOfReadingPattern;
|
|
7381
7381
|
function expandPatternsWithBraceExpansion(patterns) {
|
|
@@ -7420,15 +7420,15 @@ var require_pattern = __commonJS({
|
|
|
7420
7420
|
exports.removeDuplicateSlashes = removeDuplicateSlashes;
|
|
7421
7421
|
function partitionAbsoluteAndRelative(patterns) {
|
|
7422
7422
|
const absolute = [];
|
|
7423
|
-
const
|
|
7423
|
+
const relative13 = [];
|
|
7424
7424
|
for (const pattern of patterns) {
|
|
7425
7425
|
if (isAbsolute3(pattern)) {
|
|
7426
7426
|
absolute.push(pattern);
|
|
7427
7427
|
} else {
|
|
7428
|
-
|
|
7428
|
+
relative13.push(pattern);
|
|
7429
7429
|
}
|
|
7430
7430
|
}
|
|
7431
|
-
return [absolute,
|
|
7431
|
+
return [absolute, relative13];
|
|
7432
7432
|
}
|
|
7433
7433
|
exports.partitionAbsoluteAndRelative = partitionAbsoluteAndRelative;
|
|
7434
7434
|
function isAbsolute3(pattern) {
|
|
@@ -7849,11 +7849,11 @@ var require_out = __commonJS({
|
|
|
7849
7849
|
async.read(path, getSettings(optionsOrSettingsOrCallback), callback);
|
|
7850
7850
|
}
|
|
7851
7851
|
exports.stat = stat;
|
|
7852
|
-
function
|
|
7852
|
+
function statSync15(path, optionsOrSettings) {
|
|
7853
7853
|
const settings = getSettings(optionsOrSettings);
|
|
7854
7854
|
return sync.read(path, settings);
|
|
7855
7855
|
}
|
|
7856
|
-
exports.statSync =
|
|
7856
|
+
exports.statSync = statSync15;
|
|
7857
7857
|
function getSettings(settingsOrOptions = {}) {
|
|
7858
7858
|
if (settingsOrOptions instanceof settings_1.default) {
|
|
7859
7859
|
return settingsOrOptions;
|
|
@@ -10623,15 +10623,204 @@ var init_regex_fallback = __esm({
|
|
|
10623
10623
|
});
|
|
10624
10624
|
|
|
10625
10625
|
// src/detect/adapters/parse-guard.ts
|
|
10626
|
-
|
|
10626
|
+
function isParsableSource(source, sizeBytes) {
|
|
10627
|
+
const bytes = sizeBytes ?? Buffer.byteLength(source, "utf-8");
|
|
10628
|
+
if (bytes > MAX_AST_FILE_BYTES) {
|
|
10629
|
+
return {
|
|
10630
|
+
reason: "size-cap",
|
|
10631
|
+
detail: `${bytes} bytes > ${MAX_AST_FILE_BYTES} cap`
|
|
10632
|
+
};
|
|
10633
|
+
}
|
|
10634
|
+
let depth = 0;
|
|
10635
|
+
let maxDepth = 0;
|
|
10636
|
+
for (let i2 = 0; i2 < source.length; i2++) {
|
|
10637
|
+
const c2 = source.charCodeAt(i2);
|
|
10638
|
+
if (c2 === 0) {
|
|
10639
|
+
return { reason: "control-bytes", detail: "NUL byte at offset " + i2 };
|
|
10640
|
+
}
|
|
10641
|
+
if (c2 === 40 || c2 === 91 || c2 === 123) {
|
|
10642
|
+
depth++;
|
|
10643
|
+
if (depth > maxDepth) maxDepth = depth;
|
|
10644
|
+
if (depth > MAX_AST_PARSE_DEPTH) {
|
|
10645
|
+
return {
|
|
10646
|
+
reason: "depth-cap",
|
|
10647
|
+
detail: `nesting depth exceeded ${MAX_AST_PARSE_DEPTH}`
|
|
10648
|
+
};
|
|
10649
|
+
}
|
|
10650
|
+
} else if (c2 === 41 || c2 === 93 || c2 === 125) {
|
|
10651
|
+
depth = depth > 0 ? depth - 1 : 0;
|
|
10652
|
+
}
|
|
10653
|
+
}
|
|
10654
|
+
return null;
|
|
10655
|
+
}
|
|
10656
|
+
var MAX_AST_FILE_BYTES, MAX_AST_PARSE_DEPTH;
|
|
10627
10657
|
var init_parse_guard = __esm({
|
|
10628
10658
|
"src/detect/adapters/parse-guard.ts"() {
|
|
10629
10659
|
"use strict";
|
|
10630
10660
|
MAX_AST_FILE_BYTES = 1 * 1024 * 1024;
|
|
10661
|
+
MAX_AST_PARSE_DEPTH = 5e3;
|
|
10631
10662
|
}
|
|
10632
10663
|
});
|
|
10633
10664
|
|
|
10634
10665
|
// src/detect/adapters/runner.ts
|
|
10666
|
+
import { basename as basename3, relative as relative4 } from "path";
|
|
10667
|
+
import { existsSync as existsSync9, readdirSync as readdirSync7, readFileSync as readFileSync7, statSync as statSync5 } from "fs";
|
|
10668
|
+
import { join as join7 } from "path";
|
|
10669
|
+
async function runAdapters(adapters, rootDir, signals, options = {}) {
|
|
10670
|
+
const out2 = {
|
|
10671
|
+
byAdapter: {},
|
|
10672
|
+
skipped: [],
|
|
10673
|
+
errored: []
|
|
10674
|
+
};
|
|
10675
|
+
for (const adapter of adapters) {
|
|
10676
|
+
if (out2.byAdapter[adapter.id] || out2.skipped.includes(adapter.id)) {
|
|
10677
|
+
continue;
|
|
10678
|
+
}
|
|
10679
|
+
let matches;
|
|
10680
|
+
try {
|
|
10681
|
+
matches = adapter.matches(signals);
|
|
10682
|
+
} catch (e2) {
|
|
10683
|
+
out2.errored.push({
|
|
10684
|
+
adapterId: adapter.id,
|
|
10685
|
+
error: `matches() threw: ${e2 instanceof Error ? e2.message : String(e2)}`
|
|
10686
|
+
});
|
|
10687
|
+
continue;
|
|
10688
|
+
}
|
|
10689
|
+
if (!matches) {
|
|
10690
|
+
out2.skipped.push(adapter.id);
|
|
10691
|
+
continue;
|
|
10692
|
+
}
|
|
10693
|
+
let files;
|
|
10694
|
+
try {
|
|
10695
|
+
files = options.sampleFiles ? await options.sampleFiles(adapter, rootDir) : [];
|
|
10696
|
+
} catch (e2) {
|
|
10697
|
+
out2.errored.push({
|
|
10698
|
+
adapterId: adapter.id,
|
|
10699
|
+
error: `sampleFiles threw: ${e2 instanceof Error ? e2.message : String(e2)}`
|
|
10700
|
+
});
|
|
10701
|
+
continue;
|
|
10702
|
+
}
|
|
10703
|
+
const safeFiles = [];
|
|
10704
|
+
for (const f2 of files) {
|
|
10705
|
+
const skip = isParsableSource(f2.content, f2.size);
|
|
10706
|
+
if (skip) {
|
|
10707
|
+
process.stderr.write(
|
|
10708
|
+
`[massu/ast] WARN: skipping ${f2.path} for adapter ${adapter.id}: ${skip.reason} (${skip.detail}). Cap=${MAX_AST_FILE_BYTES} bytes. (Phase 3.5 mitigation)
|
|
10709
|
+
`
|
|
10710
|
+
);
|
|
10711
|
+
continue;
|
|
10712
|
+
}
|
|
10713
|
+
safeFiles.push(f2);
|
|
10714
|
+
}
|
|
10715
|
+
files = safeFiles;
|
|
10716
|
+
let result;
|
|
10717
|
+
try {
|
|
10718
|
+
result = await adapter.introspect(files, rootDir);
|
|
10719
|
+
} catch (e2) {
|
|
10720
|
+
out2.errored.push({
|
|
10721
|
+
adapterId: adapter.id,
|
|
10722
|
+
error: `introspect() threw: ${e2 instanceof Error ? e2.message : String(e2)}`
|
|
10723
|
+
});
|
|
10724
|
+
continue;
|
|
10725
|
+
}
|
|
10726
|
+
if (result.confidence === "none") {
|
|
10727
|
+
out2.byAdapter[adapter.id] = {
|
|
10728
|
+
conventions: {},
|
|
10729
|
+
_provenance: {},
|
|
10730
|
+
confidence: "none"
|
|
10731
|
+
};
|
|
10732
|
+
continue;
|
|
10733
|
+
}
|
|
10734
|
+
const conventions = {};
|
|
10735
|
+
const provenanceMap = {};
|
|
10736
|
+
for (const [field, value] of Object.entries(result.conventions)) {
|
|
10737
|
+
if (value === null || value === void 0) continue;
|
|
10738
|
+
if (field in conventions) continue;
|
|
10739
|
+
conventions[field] = value;
|
|
10740
|
+
}
|
|
10741
|
+
for (const p19 of result.provenance) {
|
|
10742
|
+
if (p19.field in provenanceMap) continue;
|
|
10743
|
+
provenanceMap[p19.field] = formatProvenance(p19, rootDir);
|
|
10744
|
+
}
|
|
10745
|
+
const resolved = {
|
|
10746
|
+
conventions,
|
|
10747
|
+
_provenance: provenanceMap,
|
|
10748
|
+
confidence: result.confidence
|
|
10749
|
+
};
|
|
10750
|
+
out2.byAdapter[adapter.id] = resolved;
|
|
10751
|
+
}
|
|
10752
|
+
return out2;
|
|
10753
|
+
}
|
|
10754
|
+
function formatProvenance(p19, rootDir) {
|
|
10755
|
+
const rel = p19.sourceFile.startsWith(rootDir + "/") ? relative4(rootDir, p19.sourceFile) : basename3(p19.sourceFile);
|
|
10756
|
+
return `${rel}:${p19.line} :: ${p19.query}`;
|
|
10757
|
+
}
|
|
10758
|
+
function buildDetectionSignals(rootDir) {
|
|
10759
|
+
const presentDirs = /* @__PURE__ */ new Set();
|
|
10760
|
+
const presentFiles = /* @__PURE__ */ new Set();
|
|
10761
|
+
try {
|
|
10762
|
+
for (const entry of readdirSync7(rootDir)) {
|
|
10763
|
+
if (entry.startsWith(".")) continue;
|
|
10764
|
+
try {
|
|
10765
|
+
const st = statSync5(join7(rootDir, entry));
|
|
10766
|
+
if (st.isDirectory()) presentDirs.add(entry);
|
|
10767
|
+
else if (st.isFile()) presentFiles.add(entry);
|
|
10768
|
+
} catch {
|
|
10769
|
+
}
|
|
10770
|
+
}
|
|
10771
|
+
} catch {
|
|
10772
|
+
}
|
|
10773
|
+
return {
|
|
10774
|
+
packageJson: tryReadJson(join7(rootDir, "package.json")),
|
|
10775
|
+
pyprojectToml: tryReadToml(join7(rootDir, "pyproject.toml")),
|
|
10776
|
+
gemfile: tryReadString(join7(rootDir, "Gemfile")),
|
|
10777
|
+
cargoToml: tryReadToml(join7(rootDir, "Cargo.toml")),
|
|
10778
|
+
goMod: tryReadString(join7(rootDir, "go.mod")),
|
|
10779
|
+
mixExs: tryReadString(join7(rootDir, "mix.exs")),
|
|
10780
|
+
csproj: tryReadFirstCsproj(rootDir, presentFiles),
|
|
10781
|
+
pomXml: tryReadString(join7(rootDir, "pom.xml")),
|
|
10782
|
+
gradleBuild: tryReadGradleBuild(rootDir, presentFiles),
|
|
10783
|
+
presentDirs,
|
|
10784
|
+
presentFiles
|
|
10785
|
+
};
|
|
10786
|
+
}
|
|
10787
|
+
function tryReadFirstCsproj(rootDir, presentFiles) {
|
|
10788
|
+
const csprojNames = [...presentFiles].filter((f2) => f2.endsWith(".csproj")).sort();
|
|
10789
|
+
if (csprojNames.length === 0) return void 0;
|
|
10790
|
+
return tryReadString(join7(rootDir, csprojNames[0]));
|
|
10791
|
+
}
|
|
10792
|
+
function tryReadGradleBuild(rootDir, presentFiles) {
|
|
10793
|
+
if (presentFiles.has("build.gradle.kts")) {
|
|
10794
|
+
return tryReadString(join7(rootDir, "build.gradle.kts"));
|
|
10795
|
+
}
|
|
10796
|
+
if (presentFiles.has("build.gradle")) {
|
|
10797
|
+
return tryReadString(join7(rootDir, "build.gradle"));
|
|
10798
|
+
}
|
|
10799
|
+
return void 0;
|
|
10800
|
+
}
|
|
10801
|
+
function tryReadString(path) {
|
|
10802
|
+
if (!existsSync9(path)) return void 0;
|
|
10803
|
+
try {
|
|
10804
|
+
return readFileSync7(path, "utf-8");
|
|
10805
|
+
} catch {
|
|
10806
|
+
return void 0;
|
|
10807
|
+
}
|
|
10808
|
+
}
|
|
10809
|
+
function tryReadJson(path) {
|
|
10810
|
+
const txt = tryReadString(path);
|
|
10811
|
+
if (!txt) return void 0;
|
|
10812
|
+
try {
|
|
10813
|
+
const parsed = JSON.parse(txt);
|
|
10814
|
+
return typeof parsed === "object" && parsed !== null ? parsed : void 0;
|
|
10815
|
+
} catch {
|
|
10816
|
+
return void 0;
|
|
10817
|
+
}
|
|
10818
|
+
}
|
|
10819
|
+
function tryReadToml(path) {
|
|
10820
|
+
const txt = tryReadString(path);
|
|
10821
|
+
if (!txt) return void 0;
|
|
10822
|
+
return { __raw: txt };
|
|
10823
|
+
}
|
|
10635
10824
|
var init_runner = __esm({
|
|
10636
10825
|
"src/detect/adapters/runner.ts"() {
|
|
10637
10826
|
"use strict";
|
|
@@ -14588,22 +14777,408 @@ ${JSON.stringify(symbolNames, null, 2)}`);
|
|
|
14588
14777
|
});
|
|
14589
14778
|
|
|
14590
14779
|
// src/detect/adapters/query-helpers.ts
|
|
14780
|
+
function compileQuery(language, source, queryName) {
|
|
14781
|
+
let perLang = queryCache.get(language);
|
|
14782
|
+
if (!perLang) {
|
|
14783
|
+
perLang = /* @__PURE__ */ new Map();
|
|
14784
|
+
queryCache.set(language, perLang);
|
|
14785
|
+
}
|
|
14786
|
+
const cached = perLang.get(source);
|
|
14787
|
+
if (cached) return cached;
|
|
14788
|
+
let q2;
|
|
14789
|
+
try {
|
|
14790
|
+
q2 = new Query(language, source);
|
|
14791
|
+
} catch (e2) {
|
|
14792
|
+
throw new InvalidQueryError(queryName, source, e2);
|
|
14793
|
+
}
|
|
14794
|
+
perLang.set(source, q2);
|
|
14795
|
+
return q2;
|
|
14796
|
+
}
|
|
14797
|
+
function runQuery(parser, source, queryText, queryName, filePath) {
|
|
14798
|
+
const language = parser.language;
|
|
14799
|
+
if (!language) {
|
|
14800
|
+
throw new InvalidQueryError(
|
|
14801
|
+
queryName,
|
|
14802
|
+
queryText,
|
|
14803
|
+
new Error("Parser has no language assigned")
|
|
14804
|
+
);
|
|
14805
|
+
}
|
|
14806
|
+
const query = compileQuery(language, queryText, queryName);
|
|
14807
|
+
const tree = parser.parse(source);
|
|
14808
|
+
if (!tree) return [];
|
|
14809
|
+
let matches;
|
|
14810
|
+
try {
|
|
14811
|
+
matches = query.matches(tree.rootNode);
|
|
14812
|
+
} catch (e2) {
|
|
14813
|
+
throw new InvalidQueryError(queryName, queryText, e2);
|
|
14814
|
+
}
|
|
14815
|
+
const out2 = [];
|
|
14816
|
+
for (const match of matches) {
|
|
14817
|
+
if (!match.captures || match.captures.length === 0) continue;
|
|
14818
|
+
const captures = {};
|
|
14819
|
+
let earliestLine = Number.POSITIVE_INFINITY;
|
|
14820
|
+
for (const cap of match.captures) {
|
|
14821
|
+
const node = cap.node;
|
|
14822
|
+
captures[cap.name] = node.text;
|
|
14823
|
+
if (node.startPosition.row + 1 < earliestLine) {
|
|
14824
|
+
earliestLine = node.startPosition.row + 1;
|
|
14825
|
+
}
|
|
14826
|
+
}
|
|
14827
|
+
out2.push({
|
|
14828
|
+
captures,
|
|
14829
|
+
file: filePath,
|
|
14830
|
+
line: Number.isFinite(earliestLine) ? earliestLine : 1,
|
|
14831
|
+
queryName
|
|
14832
|
+
});
|
|
14833
|
+
}
|
|
14834
|
+
try {
|
|
14835
|
+
tree.delete();
|
|
14836
|
+
} catch {
|
|
14837
|
+
}
|
|
14838
|
+
return out2;
|
|
14839
|
+
}
|
|
14840
|
+
var InvalidQueryError, queryCache;
|
|
14591
14841
|
var init_query_helpers = __esm({
|
|
14592
14842
|
"src/detect/adapters/query-helpers.ts"() {
|
|
14593
14843
|
"use strict";
|
|
14594
14844
|
init_tree_sitter();
|
|
14845
|
+
InvalidQueryError = class extends Error {
|
|
14846
|
+
queryName;
|
|
14847
|
+
querySource;
|
|
14848
|
+
cause;
|
|
14849
|
+
constructor(queryName, querySource, cause) {
|
|
14850
|
+
const causeMsg = cause instanceof Error ? cause.message : String(cause);
|
|
14851
|
+
super(
|
|
14852
|
+
`[query-helpers] Invalid Tree-sitter query "${queryName}": ${causeMsg}
|
|
14853
|
+
Query source:
|
|
14854
|
+
${querySource}`
|
|
14855
|
+
);
|
|
14856
|
+
this.name = "InvalidQueryError";
|
|
14857
|
+
this.queryName = queryName;
|
|
14858
|
+
this.querySource = querySource;
|
|
14859
|
+
this.cause = cause;
|
|
14860
|
+
}
|
|
14861
|
+
};
|
|
14862
|
+
queryCache = /* @__PURE__ */ new WeakMap();
|
|
14595
14863
|
}
|
|
14596
14864
|
});
|
|
14597
14865
|
|
|
14598
14866
|
// src/detect/adapters/tree-sitter-loader.ts
|
|
14867
|
+
import { createHash as createHash2 } from "crypto";
|
|
14868
|
+
import {
|
|
14869
|
+
mkdirSync as mkdirSync3,
|
|
14870
|
+
readdirSync as readdirSync8,
|
|
14871
|
+
readFileSync as readFileSync8,
|
|
14872
|
+
writeFileSync,
|
|
14873
|
+
renameSync as renameSync2,
|
|
14874
|
+
unlinkSync,
|
|
14875
|
+
lstatSync as lstatSync3,
|
|
14876
|
+
chmodSync,
|
|
14877
|
+
utimesSync
|
|
14878
|
+
} from "fs";
|
|
14879
|
+
import { homedir as homedir2 } from "os";
|
|
14880
|
+
import { dirname as dirname4, join as join8 } from "path";
|
|
14881
|
+
function getCacheDir() {
|
|
14882
|
+
return process.env.MASSU_WASM_CACHE_DIR ?? join8(homedir2(), ".massu", "wasm-cache");
|
|
14883
|
+
}
|
|
14884
|
+
function getCachedPath(language, sha) {
|
|
14885
|
+
return join8(getCacheDir(), `${language}-${sha}.wasm`);
|
|
14886
|
+
}
|
|
14887
|
+
function getCacheRetainCount() {
|
|
14888
|
+
const env = process.env.MASSU_WASM_CACHE_RETAIN;
|
|
14889
|
+
if (env) {
|
|
14890
|
+
const n = Number(env);
|
|
14891
|
+
if (Number.isFinite(n) && n >= 1 && n <= 1024) return Math.floor(n);
|
|
14892
|
+
}
|
|
14893
|
+
return DEFAULT_CACHE_RETAIN_COUNT;
|
|
14894
|
+
}
|
|
14895
|
+
function touchCacheFile(path) {
|
|
14896
|
+
try {
|
|
14897
|
+
const now = /* @__PURE__ */ new Date();
|
|
14898
|
+
utimesSync(path, now, now);
|
|
14899
|
+
} catch {
|
|
14900
|
+
}
|
|
14901
|
+
}
|
|
14902
|
+
function evictBeyondRetainCount(retain = getCacheRetainCount()) {
|
|
14903
|
+
const dir = getCacheDir();
|
|
14904
|
+
let entries;
|
|
14905
|
+
try {
|
|
14906
|
+
entries = readdirSync8(dir);
|
|
14907
|
+
} catch {
|
|
14908
|
+
return;
|
|
14909
|
+
}
|
|
14910
|
+
const candidates = [];
|
|
14911
|
+
for (const name2 of entries) {
|
|
14912
|
+
if (!name2.endsWith(".wasm")) continue;
|
|
14913
|
+
const path = join8(dir, name2);
|
|
14914
|
+
let stat;
|
|
14915
|
+
try {
|
|
14916
|
+
stat = lstatSync3(path);
|
|
14917
|
+
} catch {
|
|
14918
|
+
continue;
|
|
14919
|
+
}
|
|
14920
|
+
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
14921
|
+
console.error(
|
|
14922
|
+
`[tree-sitter-loader] cache eviction skipped non-regular file: ${path} (possible symlink attack \u2014 see Phase 3.5 finding F-008).`
|
|
14923
|
+
);
|
|
14924
|
+
continue;
|
|
14925
|
+
}
|
|
14926
|
+
candidates.push({ path, mtimeMs: stat.mtimeMs });
|
|
14927
|
+
}
|
|
14928
|
+
if (candidates.length <= retain) return;
|
|
14929
|
+
candidates.sort((a2, b2) => b2.mtimeMs - a2.mtimeMs);
|
|
14930
|
+
for (const victim of candidates.slice(retain)) {
|
|
14931
|
+
try {
|
|
14932
|
+
unlinkSync(victim.path);
|
|
14933
|
+
} catch {
|
|
14934
|
+
}
|
|
14935
|
+
}
|
|
14936
|
+
}
|
|
14937
|
+
function sha256(bytes) {
|
|
14938
|
+
return createHash2("sha256").update(bytes).digest("hex");
|
|
14939
|
+
}
|
|
14940
|
+
async function ensureParserInitialized() {
|
|
14941
|
+
if (parserInitPromise) return parserInitPromise;
|
|
14942
|
+
parserInitPromise = Parser.init();
|
|
14943
|
+
return parserInitPromise;
|
|
14944
|
+
}
|
|
14945
|
+
async function loadGrammar(language, options = {}) {
|
|
14946
|
+
await ensureParserInitialized();
|
|
14947
|
+
const cached = loadedGrammars.get(language);
|
|
14948
|
+
if (cached) return cached;
|
|
14949
|
+
const manifest = options.manifestOverride?.[language] ?? GRAMMAR_MANIFEST[language];
|
|
14950
|
+
if (!manifest) {
|
|
14951
|
+
throw new GrammarUnavailableError(
|
|
14952
|
+
language,
|
|
14953
|
+
new Error(`No manifest entry for language "${language}". v1 supports: ${Object.keys(GRAMMAR_MANIFEST).join(", ")}.`)
|
|
14954
|
+
);
|
|
14955
|
+
}
|
|
14956
|
+
const cachePath = getCachedPath(language, manifest.sha256);
|
|
14957
|
+
let cacheLstat;
|
|
14958
|
+
try {
|
|
14959
|
+
cacheLstat = lstatSync3(cachePath);
|
|
14960
|
+
} catch {
|
|
14961
|
+
cacheLstat = null;
|
|
14962
|
+
}
|
|
14963
|
+
if (cacheLstat) {
|
|
14964
|
+
if (cacheLstat.isSymbolicLink() || !cacheLstat.isFile()) {
|
|
14965
|
+
throw new GrammarCacheSymlinkError(cachePath);
|
|
14966
|
+
}
|
|
14967
|
+
let bytes;
|
|
14968
|
+
try {
|
|
14969
|
+
bytes = readFileSync8(cachePath);
|
|
14970
|
+
} catch (e2) {
|
|
14971
|
+
bytes = new Uint8Array(0);
|
|
14972
|
+
}
|
|
14973
|
+
if (bytes.byteLength > 0) {
|
|
14974
|
+
const actualSha = sha256(bytes);
|
|
14975
|
+
if (actualSha !== manifest.sha256) {
|
|
14976
|
+
throw new GrammarSHAMismatchError(language, manifest.sha256, actualSha);
|
|
14977
|
+
}
|
|
14978
|
+
const lang2 = await Language.load(bytes);
|
|
14979
|
+
loadedGrammars.set(language, lang2);
|
|
14980
|
+
touchCacheFile(cachePath);
|
|
14981
|
+
return lang2;
|
|
14982
|
+
}
|
|
14983
|
+
}
|
|
14984
|
+
if (!/^https:\/\//i.test(manifest.url)) {
|
|
14985
|
+
throw new GrammarUrlNotHttpsError(manifest.url);
|
|
14986
|
+
}
|
|
14987
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
14988
|
+
if (!fetchImpl) {
|
|
14989
|
+
throw new GrammarUnavailableError(
|
|
14990
|
+
language,
|
|
14991
|
+
new Error("No fetch implementation available (Node < 18?)")
|
|
14992
|
+
);
|
|
14993
|
+
}
|
|
14994
|
+
let body2;
|
|
14995
|
+
try {
|
|
14996
|
+
const res = await fetchImpl(manifest.url);
|
|
14997
|
+
if (!res.ok) {
|
|
14998
|
+
throw new Error(`HTTP ${res.status ?? "unknown"} from ${manifest.url}`);
|
|
14999
|
+
}
|
|
15000
|
+
body2 = new Uint8Array(await res.arrayBuffer());
|
|
15001
|
+
} catch (e2) {
|
|
15002
|
+
throw new GrammarUnavailableError(language, e2);
|
|
15003
|
+
}
|
|
15004
|
+
const downloadedSha = sha256(body2);
|
|
15005
|
+
if (downloadedSha !== manifest.sha256) {
|
|
15006
|
+
throw new GrammarSHAMismatchError(language, manifest.sha256, downloadedSha);
|
|
15007
|
+
}
|
|
15008
|
+
try {
|
|
15009
|
+
mkdirSync3(dirname4(cachePath), { recursive: true, mode: 448 });
|
|
15010
|
+
try {
|
|
15011
|
+
chmodSync(dirname4(cachePath), 448);
|
|
15012
|
+
} catch {
|
|
15013
|
+
}
|
|
15014
|
+
const tmpPath = `${cachePath}.tmp.${process.pid}`;
|
|
15015
|
+
writeFileSync(tmpPath, body2, { mode: 384 });
|
|
15016
|
+
try {
|
|
15017
|
+
chmodSync(tmpPath, 384);
|
|
15018
|
+
} catch {
|
|
15019
|
+
}
|
|
15020
|
+
try {
|
|
15021
|
+
renameSync2(tmpPath, cachePath);
|
|
15022
|
+
try {
|
|
15023
|
+
chmodSync(cachePath, 384);
|
|
15024
|
+
} catch {
|
|
15025
|
+
}
|
|
15026
|
+
} catch (e2) {
|
|
15027
|
+
try {
|
|
15028
|
+
unlinkSync(tmpPath);
|
|
15029
|
+
} catch {
|
|
15030
|
+
}
|
|
15031
|
+
throw e2;
|
|
15032
|
+
}
|
|
15033
|
+
evictBeyondRetainCount();
|
|
15034
|
+
} catch (e2) {
|
|
15035
|
+
console.error(
|
|
15036
|
+
`[tree-sitter-loader] cache write failed for ${language}: ${e2 instanceof Error ? e2.message : String(e2)} \u2014 loading directly from memory.`
|
|
15037
|
+
);
|
|
15038
|
+
}
|
|
15039
|
+
const lang = await Language.load(body2);
|
|
15040
|
+
loadedGrammars.set(language, lang);
|
|
15041
|
+
return lang;
|
|
15042
|
+
}
|
|
15043
|
+
var GrammarSHAMismatchError, GrammarUnavailableError, GrammarCacheSymlinkError, GrammarUrlNotHttpsError, GRAMMAR_MANIFEST, DEFAULT_CACHE_RETAIN_COUNT, parserInitPromise, loadedGrammars;
|
|
14599
15044
|
var init_tree_sitter_loader = __esm({
|
|
14600
15045
|
"src/detect/adapters/tree-sitter-loader.ts"() {
|
|
14601
15046
|
"use strict";
|
|
14602
15047
|
init_tree_sitter();
|
|
15048
|
+
GrammarSHAMismatchError = class extends Error {
|
|
15049
|
+
language;
|
|
15050
|
+
expected;
|
|
15051
|
+
actual;
|
|
15052
|
+
constructor(language, expected, actual) {
|
|
15053
|
+
super(
|
|
15054
|
+
`[tree-sitter-loader] SHA-256 mismatch for grammar "${language}". Expected ${expected}, got ${actual}. REFUSING to load \u2014 see Phase 3.5 audit attack vector #3.`
|
|
15055
|
+
);
|
|
15056
|
+
this.name = "GrammarSHAMismatchError";
|
|
15057
|
+
this.language = language;
|
|
15058
|
+
this.expected = expected;
|
|
15059
|
+
this.actual = actual;
|
|
15060
|
+
}
|
|
15061
|
+
};
|
|
15062
|
+
GrammarUnavailableError = class extends Error {
|
|
15063
|
+
language;
|
|
15064
|
+
cause;
|
|
15065
|
+
constructor(language, cause) {
|
|
15066
|
+
const causeMsg = cause instanceof Error ? cause.message : cause ? String(cause) : "no cached grammar and download failed";
|
|
15067
|
+
super(
|
|
15068
|
+
`[tree-sitter-loader] Grammar for "${language}" is unavailable: ${causeMsg}. Falling back to regex introspection for files in ${language}.`
|
|
15069
|
+
);
|
|
15070
|
+
this.name = "GrammarUnavailableError";
|
|
15071
|
+
this.language = language;
|
|
15072
|
+
this.cause = cause;
|
|
15073
|
+
}
|
|
15074
|
+
};
|
|
15075
|
+
GrammarCacheSymlinkError = class extends Error {
|
|
15076
|
+
cachePath;
|
|
15077
|
+
constructor(cachePath) {
|
|
15078
|
+
super(
|
|
15079
|
+
`[tree-sitter-loader] Refusing to load grammar \u2014 cache path "${cachePath}" is a symlink or non-regular file. (Phase 3.5 finding #3 \u2014 symlink attack vector.)`
|
|
15080
|
+
);
|
|
15081
|
+
this.name = "GrammarCacheSymlinkError";
|
|
15082
|
+
this.cachePath = cachePath;
|
|
15083
|
+
}
|
|
15084
|
+
};
|
|
15085
|
+
GrammarUrlNotHttpsError = class extends Error {
|
|
15086
|
+
url;
|
|
15087
|
+
constructor(url) {
|
|
15088
|
+
super(
|
|
15089
|
+
`[tree-sitter-loader] Refusing to download grammar from non-HTTPS URL: ${url}. Only https:// URLs are accepted. (Phase 3.5 finding #3.)`
|
|
15090
|
+
);
|
|
15091
|
+
this.name = "GrammarUrlNotHttpsError";
|
|
15092
|
+
this.url = url;
|
|
15093
|
+
}
|
|
15094
|
+
};
|
|
15095
|
+
GRAMMAR_MANIFEST = {
|
|
15096
|
+
python: {
|
|
15097
|
+
url: "https://unpkg.com/tree-sitter-wasms@0.1.13/out/tree-sitter-python.wasm",
|
|
15098
|
+
sha256: "9056d0fb0c337810d019fae350e8167786119da98f0f282aceae7ab89ee8253b",
|
|
15099
|
+
version: "0.1.13"
|
|
15100
|
+
},
|
|
15101
|
+
typescript: {
|
|
15102
|
+
url: "https://unpkg.com/tree-sitter-wasms@0.1.13/out/tree-sitter-typescript.wasm",
|
|
15103
|
+
sha256: "8515404dceed38e1ed86aa34b09fcf3379fff1b4ff9dd3967bcd6d1eb5ac3d8f",
|
|
15104
|
+
version: "0.1.13"
|
|
15105
|
+
},
|
|
15106
|
+
javascript: {
|
|
15107
|
+
url: "https://unpkg.com/tree-sitter-wasms@0.1.13/out/tree-sitter-javascript.wasm",
|
|
15108
|
+
sha256: "63812b9e275d26851264734868d27a1656bd44a2ef6eb3e85e6b03728c595ab5",
|
|
15109
|
+
version: "0.1.13"
|
|
15110
|
+
},
|
|
15111
|
+
swift: {
|
|
15112
|
+
url: "https://unpkg.com/tree-sitter-wasms@0.1.13/out/tree-sitter-swift.wasm",
|
|
15113
|
+
sha256: "41c4fdb2249a3aa6d87eed0d383081ff09725c2248b4977043a43825980ffcc7",
|
|
15114
|
+
version: "0.1.13"
|
|
15115
|
+
},
|
|
15116
|
+
// ----------------------------------------------------------------
|
|
15117
|
+
// Plan 3c Phase 7 expansion (2026-05-07):
|
|
15118
|
+
//
|
|
15119
|
+
// Six additional grammars to support the registry-verified framework
|
|
15120
|
+
// adapters (go-chi, rails, aspnet, spring, ktor, phoenix) plus the
|
|
15121
|
+
// bundled adapters in the same language families (gin/echo/fiber,
|
|
15122
|
+
// sinatra, etc.). All entries use the SAME pinned tree-sitter-wasms
|
|
15123
|
+
// version (0.1.13) as the v1 four to keep the dependency surface
|
|
15124
|
+
// single-source.
|
|
15125
|
+
//
|
|
15126
|
+
// SHA-256s computed 2026-05-07 via:
|
|
15127
|
+
// curl -fsSL <url> | shasum -a 256
|
|
15128
|
+
//
|
|
15129
|
+
// The unpkg filename for C# uses an underscore (`c_sharp`) while the
|
|
15130
|
+
// TreeSitterLanguage identifier uses no separator (`csharp`); the map
|
|
15131
|
+
// key is the type identifier, the URL is the storage path — they do
|
|
15132
|
+
// NOT need to match, the same as how `python` maps to `tree-sitter-
|
|
15133
|
+
// python.wasm`. This is intentional and validated by the manifest
|
|
15134
|
+
// shape test in tree-sitter-loader-manifest.test.ts.
|
|
15135
|
+
// ----------------------------------------------------------------
|
|
15136
|
+
go: {
|
|
15137
|
+
url: "https://unpkg.com/tree-sitter-wasms@0.1.13/out/tree-sitter-go.wasm",
|
|
15138
|
+
sha256: "9963ca89b616eaf04b08a43bc1fb0f07b85395bec313330851f1f1ead2f755b6",
|
|
15139
|
+
version: "0.1.13"
|
|
15140
|
+
},
|
|
15141
|
+
ruby: {
|
|
15142
|
+
url: "https://unpkg.com/tree-sitter-wasms@0.1.13/out/tree-sitter-ruby.wasm",
|
|
15143
|
+
sha256: "93a5022855314cdb45458c7bb026a24a0ebc3a5ff6439e542e881f14dfa13a39",
|
|
15144
|
+
version: "0.1.13"
|
|
15145
|
+
},
|
|
15146
|
+
csharp: {
|
|
15147
|
+
url: "https://unpkg.com/tree-sitter-wasms@0.1.13/out/tree-sitter-c_sharp.wasm",
|
|
15148
|
+
sha256: "6266a7e32d68a3459104d994dc848df15d5672b0ea8e86d327274b694f8e6991",
|
|
15149
|
+
version: "0.1.13"
|
|
15150
|
+
},
|
|
15151
|
+
java: {
|
|
15152
|
+
url: "https://unpkg.com/tree-sitter-wasms@0.1.13/out/tree-sitter-java.wasm",
|
|
15153
|
+
sha256: "637aac4415fb39a211a4f4292d63c66b5ce9c32fa2cd35464af4f681d91b9a1f",
|
|
15154
|
+
version: "0.1.13"
|
|
15155
|
+
},
|
|
15156
|
+
kotlin: {
|
|
15157
|
+
url: "https://unpkg.com/tree-sitter-wasms@0.1.13/out/tree-sitter-kotlin.wasm",
|
|
15158
|
+
sha256: "b5cb00c8d06ed0f10f1dbe497205b437809d7e87db1f638721a8cfb30e044449",
|
|
15159
|
+
version: "0.1.13"
|
|
15160
|
+
},
|
|
15161
|
+
elixir: {
|
|
15162
|
+
url: "https://unpkg.com/tree-sitter-wasms@0.1.13/out/tree-sitter-elixir.wasm",
|
|
15163
|
+
sha256: "82e91b9759ddca30d8978ebbfa8e347b4451b64c931f9ae62112e6db9b8fac20",
|
|
15164
|
+
version: "0.1.13"
|
|
15165
|
+
}
|
|
15166
|
+
};
|
|
15167
|
+
DEFAULT_CACHE_RETAIN_COUNT = 16;
|
|
15168
|
+
parserInitPromise = null;
|
|
15169
|
+
loadedGrammars = /* @__PURE__ */ new Map();
|
|
14603
15170
|
}
|
|
14604
15171
|
});
|
|
14605
15172
|
|
|
14606
15173
|
// src/detect/adapters/python-fastapi.ts
|
|
15174
|
+
function extractPrefixBase2(prefix3) {
|
|
15175
|
+
if (!prefix3.startsWith("/")) return null;
|
|
15176
|
+
const stripped = prefix3.replace(/^\/+/, "");
|
|
15177
|
+
const firstSeg = stripped.split("/")[0];
|
|
15178
|
+
if (!firstSeg) return null;
|
|
15179
|
+
return "/" + firstSeg;
|
|
15180
|
+
}
|
|
15181
|
+
var AUTH_DEP_QUERY, API_PREFIX_QUERY, PYTEST_ASYNCIO_QUERY, pythonFastApiAdapter;
|
|
14607
15182
|
var init_python_fastapi = __esm({
|
|
14608
15183
|
"src/detect/adapters/python-fastapi.ts"() {
|
|
14609
15184
|
"use strict";
|
|
@@ -14611,10 +15186,137 @@ var init_python_fastapi = __esm({
|
|
|
14611
15186
|
init_query_helpers();
|
|
14612
15187
|
init_tree_sitter_loader();
|
|
14613
15188
|
init_parse_guard();
|
|
15189
|
+
AUTH_DEP_QUERY = `
|
|
15190
|
+
(call
|
|
15191
|
+
function: (identifier) @_callee (#eq? @_callee "Depends")
|
|
15192
|
+
arguments: (argument_list
|
|
15193
|
+
(identifier) @auth_dep))
|
|
15194
|
+
`;
|
|
15195
|
+
API_PREFIX_QUERY = `
|
|
15196
|
+
(call
|
|
15197
|
+
function: (identifier) @_callee (#eq? @_callee "APIRouter")
|
|
15198
|
+
arguments: (argument_list
|
|
15199
|
+
(keyword_argument
|
|
15200
|
+
name: (identifier) @_kw (#eq? @_kw "prefix")
|
|
15201
|
+
value: (string) @prefix_value)))
|
|
15202
|
+
`;
|
|
15203
|
+
PYTEST_ASYNCIO_QUERY = `
|
|
15204
|
+
(decorator
|
|
15205
|
+
(attribute
|
|
15206
|
+
object: (attribute
|
|
15207
|
+
object: (identifier) @_pkg (#eq? @_pkg "pytest")
|
|
15208
|
+
attribute: (identifier) @_mark (#eq? @_mark "mark"))
|
|
15209
|
+
attribute: (identifier) @_marker (#eq? @_marker "asyncio"))) @decorator
|
|
15210
|
+
`;
|
|
15211
|
+
pythonFastApiAdapter = {
|
|
15212
|
+
id: "python-fastapi",
|
|
15213
|
+
languages: ["python"],
|
|
15214
|
+
matches(signals) {
|
|
15215
|
+
const pyToml = signals.pyprojectToml;
|
|
15216
|
+
if (pyToml?.__raw && /\bfastapi\b/i.test(pyToml.__raw)) return true;
|
|
15217
|
+
if (signals.presentDirs.has("routers")) return true;
|
|
15218
|
+
if (signals.presentDirs.has("app") && signals.presentFiles.has("main.py")) return true;
|
|
15219
|
+
return false;
|
|
15220
|
+
},
|
|
15221
|
+
async introspect(files, _rootDir) {
|
|
15222
|
+
if (files.length === 0) {
|
|
15223
|
+
return { conventions: {}, provenance: [], confidence: "none" };
|
|
15224
|
+
}
|
|
15225
|
+
let language;
|
|
15226
|
+
try {
|
|
15227
|
+
language = await loadGrammar("python");
|
|
15228
|
+
} catch (e2) {
|
|
15229
|
+
return { conventions: {}, provenance: [], confidence: "none" };
|
|
15230
|
+
}
|
|
15231
|
+
const parser = new Parser();
|
|
15232
|
+
parser.setLanguage(language);
|
|
15233
|
+
const authDeps = /* @__PURE__ */ new Map();
|
|
15234
|
+
const prefixBases = /* @__PURE__ */ new Map();
|
|
15235
|
+
const testAsyncPatterns = /* @__PURE__ */ new Map();
|
|
15236
|
+
try {
|
|
15237
|
+
for (const file of files) {
|
|
15238
|
+
const skip = isParsableSource(file.content, file.size);
|
|
15239
|
+
if (skip) {
|
|
15240
|
+
process.stderr.write(
|
|
15241
|
+
`[massu/ast] WARN: python-fastapi skipping ${file.path}: ${skip.reason} (${skip.detail}). Cap=${MAX_AST_FILE_BYTES}. (Phase 3.5 mitigation)
|
|
15242
|
+
`
|
|
15243
|
+
);
|
|
15244
|
+
continue;
|
|
15245
|
+
}
|
|
15246
|
+
try {
|
|
15247
|
+
for (const hit of runQuery(parser, file.content, AUTH_DEP_QUERY, "fastapi-auth-dep", file.path)) {
|
|
15248
|
+
const name2 = hit.captures.auth_dep;
|
|
15249
|
+
if (name2 && !authDeps.has(name2)) {
|
|
15250
|
+
authDeps.set(name2, { line: hit.line, file: file.path });
|
|
15251
|
+
}
|
|
15252
|
+
}
|
|
15253
|
+
for (const hit of runQuery(parser, file.content, API_PREFIX_QUERY, "fastapi-api-prefix", file.path)) {
|
|
15254
|
+
const raw = hit.captures.prefix_value;
|
|
15255
|
+
if (!raw) continue;
|
|
15256
|
+
const literal = raw.replace(/^['"]/, "").replace(/['"]$/, "");
|
|
15257
|
+
const base = extractPrefixBase2(literal);
|
|
15258
|
+
if (base && !prefixBases.has(base)) {
|
|
15259
|
+
prefixBases.set(base, { line: hit.line, file: file.path });
|
|
15260
|
+
}
|
|
15261
|
+
}
|
|
15262
|
+
for (const hit of runQuery(parser, file.content, PYTEST_ASYNCIO_QUERY, "fastapi-pytest-asyncio", file.path)) {
|
|
15263
|
+
const pat = "@pytest.mark.asyncio";
|
|
15264
|
+
if (!testAsyncPatterns.has(pat)) {
|
|
15265
|
+
testAsyncPatterns.set(pat, { line: hit.line, file: file.path });
|
|
15266
|
+
}
|
|
15267
|
+
}
|
|
15268
|
+
} catch (e2) {
|
|
15269
|
+
if (e2 instanceof InvalidQueryError) {
|
|
15270
|
+
throw e2;
|
|
15271
|
+
}
|
|
15272
|
+
continue;
|
|
15273
|
+
}
|
|
15274
|
+
}
|
|
15275
|
+
} finally {
|
|
15276
|
+
try {
|
|
15277
|
+
parser.delete();
|
|
15278
|
+
} catch {
|
|
15279
|
+
}
|
|
15280
|
+
}
|
|
15281
|
+
const conventions = {};
|
|
15282
|
+
const provenance = [];
|
|
15283
|
+
if (authDeps.size === 1) {
|
|
15284
|
+
const [name2, { line, file }] = authDeps.entries().next().value;
|
|
15285
|
+
conventions.auth_dep = name2;
|
|
15286
|
+
provenance.push({ field: "auth_dep", sourceFile: file, line, query: "fastapi-auth-dep" });
|
|
15287
|
+
} else if (authDeps.size >= 2) {
|
|
15288
|
+
const [name2, { line, file }] = authDeps.entries().next().value;
|
|
15289
|
+
conventions.auth_dep = name2;
|
|
15290
|
+
provenance.push({ field: "auth_dep", sourceFile: file, line, query: "fastapi-auth-dep" });
|
|
15291
|
+
}
|
|
15292
|
+
if (prefixBases.size >= 1) {
|
|
15293
|
+
const [base, { line, file }] = prefixBases.entries().next().value;
|
|
15294
|
+
conventions.api_prefix_base = base;
|
|
15295
|
+
provenance.push({ field: "api_prefix_base", sourceFile: file, line, query: "fastapi-api-prefix" });
|
|
15296
|
+
}
|
|
15297
|
+
if (testAsyncPatterns.size >= 1) {
|
|
15298
|
+
const [pat, { line, file }] = testAsyncPatterns.entries().next().value;
|
|
15299
|
+
conventions.test_async_pattern = pat;
|
|
15300
|
+
provenance.push({ field: "test_async_pattern", sourceFile: file, line, query: "fastapi-pytest-asyncio" });
|
|
15301
|
+
}
|
|
15302
|
+
let confidence;
|
|
15303
|
+
if (Object.keys(conventions).length === 0) {
|
|
15304
|
+
confidence = "none";
|
|
15305
|
+
} else if (authDeps.size === 1 || authDeps.size === 0 && prefixBases.size > 0) {
|
|
15306
|
+
confidence = "high";
|
|
15307
|
+
} else if (authDeps.size >= 2) {
|
|
15308
|
+
confidence = "low";
|
|
15309
|
+
} else {
|
|
15310
|
+
confidence = "medium";
|
|
15311
|
+
}
|
|
15312
|
+
return { conventions, provenance, confidence };
|
|
15313
|
+
}
|
|
15314
|
+
};
|
|
14614
15315
|
}
|
|
14615
15316
|
});
|
|
14616
15317
|
|
|
14617
15318
|
// src/detect/adapters/python-django.ts
|
|
15319
|
+
var DECORATOR_QUERY, CLASS_BASE_QUERY, URLPATTERNS_QUERY, DJANGO_MIXIN_NAMES, DJANGO_DECORATOR_PATTERNS, pythonDjangoAdapter;
|
|
14618
15320
|
var init_python_django = __esm({
|
|
14619
15321
|
"src/detect/adapters/python-django.ts"() {
|
|
14620
15322
|
"use strict";
|
|
@@ -14622,10 +15324,151 @@ var init_python_django = __esm({
|
|
|
14622
15324
|
init_query_helpers();
|
|
14623
15325
|
init_tree_sitter_loader();
|
|
14624
15326
|
init_parse_guard();
|
|
15327
|
+
DECORATOR_QUERY = `
|
|
15328
|
+
(decorator
|
|
15329
|
+
(identifier) @decorator_name)
|
|
15330
|
+
`;
|
|
15331
|
+
CLASS_BASE_QUERY = `
|
|
15332
|
+
(class_definition
|
|
15333
|
+
name: (identifier) @class_name
|
|
15334
|
+
superclasses: (argument_list
|
|
15335
|
+
(identifier) @base_name))
|
|
15336
|
+
`;
|
|
15337
|
+
URLPATTERNS_QUERY = `
|
|
15338
|
+
(assignment
|
|
15339
|
+
left: (identifier) @_target (#eq? @_target "urlpatterns")
|
|
15340
|
+
right: (list) @urlpatterns_list)
|
|
15341
|
+
`;
|
|
15342
|
+
DJANGO_MIXIN_NAMES = /* @__PURE__ */ new Set([
|
|
15343
|
+
"LoginRequiredMixin",
|
|
15344
|
+
"PermissionRequiredMixin",
|
|
15345
|
+
"UserPassesTestMixin",
|
|
15346
|
+
"StaffuserRequiredMixin"
|
|
15347
|
+
]);
|
|
15348
|
+
DJANGO_DECORATOR_PATTERNS = [
|
|
15349
|
+
/^login_required$/,
|
|
15350
|
+
/^permission_required$/,
|
|
15351
|
+
/_required$/,
|
|
15352
|
+
/^require_/
|
|
15353
|
+
];
|
|
15354
|
+
pythonDjangoAdapter = {
|
|
15355
|
+
id: "python-django",
|
|
15356
|
+
languages: ["python"],
|
|
15357
|
+
matches(signals) {
|
|
15358
|
+
if (signals.presentFiles.has("manage.py")) return true;
|
|
15359
|
+
const pyToml = signals.pyprojectToml;
|
|
15360
|
+
if (pyToml?.__raw && /\bdjango\b/i.test(pyToml.__raw)) return true;
|
|
15361
|
+
return false;
|
|
15362
|
+
},
|
|
15363
|
+
async introspect(files, _rootDir) {
|
|
15364
|
+
if (files.length === 0) {
|
|
15365
|
+
return { conventions: {}, provenance: [], confidence: "none" };
|
|
15366
|
+
}
|
|
15367
|
+
let language;
|
|
15368
|
+
try {
|
|
15369
|
+
language = await loadGrammar("python");
|
|
15370
|
+
} catch {
|
|
15371
|
+
return { conventions: {}, provenance: [], confidence: "none" };
|
|
15372
|
+
}
|
|
15373
|
+
const parser = new Parser();
|
|
15374
|
+
parser.setLanguage(language);
|
|
15375
|
+
const decoratorsFound = /* @__PURE__ */ new Map();
|
|
15376
|
+
const mixinsFound = /* @__PURE__ */ new Map();
|
|
15377
|
+
const urlpatternsShape = {
|
|
15378
|
+
value: null,
|
|
15379
|
+
line: 0,
|
|
15380
|
+
file: ""
|
|
15381
|
+
};
|
|
15382
|
+
try {
|
|
15383
|
+
for (const file of files) {
|
|
15384
|
+
const skip = isParsableSource(file.content, file.size);
|
|
15385
|
+
if (skip) {
|
|
15386
|
+
process.stderr.write(
|
|
15387
|
+
`[massu/ast] WARN: python-django skipping ${file.path}: ${skip.reason} (${skip.detail}). Cap=${MAX_AST_FILE_BYTES}. (Phase 3.5 mitigation)
|
|
15388
|
+
`
|
|
15389
|
+
);
|
|
15390
|
+
continue;
|
|
15391
|
+
}
|
|
15392
|
+
try {
|
|
15393
|
+
for (const hit of runQuery(parser, file.content, DECORATOR_QUERY, "django-decorator", file.path)) {
|
|
15394
|
+
const name2 = hit.captures.decorator_name;
|
|
15395
|
+
if (!name2) continue;
|
|
15396
|
+
if (DJANGO_DECORATOR_PATTERNS.some((re2) => re2.test(name2)) && !decoratorsFound.has(name2)) {
|
|
15397
|
+
decoratorsFound.set(name2, { line: hit.line, file: file.path });
|
|
15398
|
+
}
|
|
15399
|
+
}
|
|
15400
|
+
for (const hit of runQuery(parser, file.content, CLASS_BASE_QUERY, "django-mixin", file.path)) {
|
|
15401
|
+
const base = hit.captures.base_name;
|
|
15402
|
+
if (base && DJANGO_MIXIN_NAMES.has(base) && !mixinsFound.has(base)) {
|
|
15403
|
+
mixinsFound.set(base, { line: hit.line, file: file.path });
|
|
15404
|
+
}
|
|
15405
|
+
}
|
|
15406
|
+
for (const hit of runQuery(parser, file.content, URLPATTERNS_QUERY, "django-urlpatterns", file.path)) {
|
|
15407
|
+
const listText = hit.captures.urlpatterns_list ?? "";
|
|
15408
|
+
const hasFunctionForm = /\bpath\s*\(/.test(listText) || /\bre_path\s*\(/.test(listText);
|
|
15409
|
+
const hasClassForm = /\.as_view\s*\(/.test(listText);
|
|
15410
|
+
let shape = null;
|
|
15411
|
+
if (hasFunctionForm && hasClassForm) shape = "mixed";
|
|
15412
|
+
else if (hasFunctionForm) shape = "function-views";
|
|
15413
|
+
else if (hasClassForm) shape = "class-views";
|
|
15414
|
+
if (shape && !urlpatternsShape.value) {
|
|
15415
|
+
urlpatternsShape.value = shape;
|
|
15416
|
+
urlpatternsShape.line = hit.line;
|
|
15417
|
+
urlpatternsShape.file = file.path;
|
|
15418
|
+
}
|
|
15419
|
+
}
|
|
15420
|
+
} catch (e2) {
|
|
15421
|
+
if (e2 instanceof InvalidQueryError) throw e2;
|
|
15422
|
+
continue;
|
|
15423
|
+
}
|
|
15424
|
+
}
|
|
15425
|
+
} finally {
|
|
15426
|
+
try {
|
|
15427
|
+
parser.delete();
|
|
15428
|
+
} catch {
|
|
15429
|
+
}
|
|
15430
|
+
}
|
|
15431
|
+
const conventions = {};
|
|
15432
|
+
const provenance = [];
|
|
15433
|
+
if (decoratorsFound.size > 0) {
|
|
15434
|
+
const list = Array.from(decoratorsFound.keys());
|
|
15435
|
+
conventions.decorator_usage = list;
|
|
15436
|
+
const [first, { line, file }] = decoratorsFound.entries().next().value;
|
|
15437
|
+
provenance.push({ field: "decorator_usage", sourceFile: file, line, query: "django-decorator" });
|
|
15438
|
+
conventions.auth_dep = first;
|
|
15439
|
+
provenance.push({ field: "auth_dep", sourceFile: file, line, query: "django-decorator" });
|
|
15440
|
+
}
|
|
15441
|
+
if (mixinsFound.size > 0) {
|
|
15442
|
+
const list = Array.from(mixinsFound.keys());
|
|
15443
|
+
conventions.mixin_classes = list;
|
|
15444
|
+
const [, { line, file }] = mixinsFound.entries().next().value;
|
|
15445
|
+
provenance.push({ field: "mixin_classes", sourceFile: file, line, query: "django-mixin" });
|
|
15446
|
+
}
|
|
15447
|
+
if (urlpatternsShape.value) {
|
|
15448
|
+
conventions.urlpatterns_shape = urlpatternsShape.value;
|
|
15449
|
+
provenance.push({
|
|
15450
|
+
field: "urlpatterns_shape",
|
|
15451
|
+
sourceFile: urlpatternsShape.file,
|
|
15452
|
+
line: urlpatternsShape.line,
|
|
15453
|
+
query: "django-urlpatterns"
|
|
15454
|
+
});
|
|
15455
|
+
}
|
|
15456
|
+
let confidence;
|
|
15457
|
+
if (Object.keys(conventions).length === 0) {
|
|
15458
|
+
confidence = "none";
|
|
15459
|
+
} else if (decoratorsFound.size > 0 || mixinsFound.size > 0) {
|
|
15460
|
+
confidence = "high";
|
|
15461
|
+
} else {
|
|
15462
|
+
confidence = "medium";
|
|
15463
|
+
}
|
|
15464
|
+
return { conventions, provenance, confidence };
|
|
15465
|
+
}
|
|
15466
|
+
};
|
|
14625
15467
|
}
|
|
14626
15468
|
});
|
|
14627
15469
|
|
|
14628
15470
|
// src/detect/adapters/nextjs-trpc.ts
|
|
15471
|
+
var ROUTER_BUILDER_QUERY, PROCEDURE_QUERY, KNOWN_BUILDERS, nextjsTrpcAdapter;
|
|
14629
15472
|
var init_nextjs_trpc = __esm({
|
|
14630
15473
|
"src/detect/adapters/nextjs-trpc.ts"() {
|
|
14631
15474
|
"use strict";
|
|
@@ -14633,10 +15476,123 @@ var init_nextjs_trpc = __esm({
|
|
|
14633
15476
|
init_query_helpers();
|
|
14634
15477
|
init_tree_sitter_loader();
|
|
14635
15478
|
init_parse_guard();
|
|
15479
|
+
ROUTER_BUILDER_QUERY = `
|
|
15480
|
+
(call_expression
|
|
15481
|
+
function: (identifier) @builder_id (#match? @builder_id "^(createTRPCRouter|router)$"))
|
|
15482
|
+
|
|
15483
|
+
(call_expression
|
|
15484
|
+
function: (member_expression
|
|
15485
|
+
object: (identifier) @_obj
|
|
15486
|
+
property: (property_identifier) @_prop (#eq? @_prop "router"))) @member_call
|
|
15487
|
+
`;
|
|
15488
|
+
PROCEDURE_QUERY = `
|
|
15489
|
+
(member_expression
|
|
15490
|
+
object: (identifier) @procedure_id (#match? @procedure_id "Procedure$"))
|
|
15491
|
+
|
|
15492
|
+
(call_expression
|
|
15493
|
+
function: (identifier) @procedure_call (#match? @procedure_call "Procedure$"))
|
|
15494
|
+
`;
|
|
15495
|
+
KNOWN_BUILDERS = /* @__PURE__ */ new Set(["createTRPCRouter", "router"]);
|
|
15496
|
+
nextjsTrpcAdapter = {
|
|
15497
|
+
id: "nextjs-trpc",
|
|
15498
|
+
languages: ["typescript"],
|
|
15499
|
+
matches(signals) {
|
|
15500
|
+
const pkgJson = signals.packageJson;
|
|
15501
|
+
if (pkgJson) {
|
|
15502
|
+
const deps = pkgJson.dependencies;
|
|
15503
|
+
const devDeps = pkgJson.devDependencies;
|
|
15504
|
+
const all = { ...deps ?? {}, ...devDeps ?? {} };
|
|
15505
|
+
if (Object.keys(all).some((k3) => k3.startsWith("@trpc/"))) return true;
|
|
15506
|
+
}
|
|
15507
|
+
if (signals.presentDirs.has("server")) return true;
|
|
15508
|
+
return false;
|
|
15509
|
+
},
|
|
15510
|
+
async introspect(files, _rootDir) {
|
|
15511
|
+
if (files.length === 0) {
|
|
15512
|
+
return { conventions: {}, provenance: [], confidence: "none" };
|
|
15513
|
+
}
|
|
15514
|
+
let language;
|
|
15515
|
+
try {
|
|
15516
|
+
language = await loadGrammar("typescript");
|
|
15517
|
+
} catch {
|
|
15518
|
+
return { conventions: {}, provenance: [], confidence: "none" };
|
|
15519
|
+
}
|
|
15520
|
+
const parser = new Parser();
|
|
15521
|
+
parser.setLanguage(language);
|
|
15522
|
+
const builders = /* @__PURE__ */ new Map();
|
|
15523
|
+
const procedures = /* @__PURE__ */ new Map();
|
|
15524
|
+
try {
|
|
15525
|
+
for (const file of files) {
|
|
15526
|
+
const skip = isParsableSource(file.content, file.size);
|
|
15527
|
+
if (skip) {
|
|
15528
|
+
process.stderr.write(
|
|
15529
|
+
`[massu/ast] WARN: nextjs-trpc skipping ${file.path}: ${skip.reason} (${skip.detail}). Cap=${MAX_AST_FILE_BYTES}. (Phase 3.5 mitigation)
|
|
15530
|
+
`
|
|
15531
|
+
);
|
|
15532
|
+
continue;
|
|
15533
|
+
}
|
|
15534
|
+
try {
|
|
15535
|
+
for (const hit of runQuery(parser, file.content, ROUTER_BUILDER_QUERY, "trpc-router-builder", file.path)) {
|
|
15536
|
+
const directId = hit.captures.builder_id;
|
|
15537
|
+
const memberCall = hit.captures.member_call;
|
|
15538
|
+
let label = null;
|
|
15539
|
+
if (directId && KNOWN_BUILDERS.has(directId)) {
|
|
15540
|
+
label = directId;
|
|
15541
|
+
} else if (memberCall) {
|
|
15542
|
+
const m3 = memberCall.match(/([A-Za-z_$][A-Za-z0-9_$]*)\.router/);
|
|
15543
|
+
if (m3) label = `${m3[1]}.router`;
|
|
15544
|
+
}
|
|
15545
|
+
if (label && !builders.has(label)) {
|
|
15546
|
+
builders.set(label, { line: hit.line, file: file.path });
|
|
15547
|
+
}
|
|
15548
|
+
}
|
|
15549
|
+
for (const hit of runQuery(parser, file.content, PROCEDURE_QUERY, "trpc-procedure", file.path)) {
|
|
15550
|
+
const proc = hit.captures.procedure_id ?? hit.captures.procedure_call;
|
|
15551
|
+
if (proc && !procedures.has(proc)) {
|
|
15552
|
+
procedures.set(proc, { line: hit.line, file: file.path });
|
|
15553
|
+
}
|
|
15554
|
+
}
|
|
15555
|
+
} catch (e2) {
|
|
15556
|
+
if (e2 instanceof InvalidQueryError) throw e2;
|
|
15557
|
+
continue;
|
|
15558
|
+
}
|
|
15559
|
+
}
|
|
15560
|
+
} finally {
|
|
15561
|
+
try {
|
|
15562
|
+
parser.delete();
|
|
15563
|
+
} catch {
|
|
15564
|
+
}
|
|
15565
|
+
}
|
|
15566
|
+
const conventions = {};
|
|
15567
|
+
const provenance = [];
|
|
15568
|
+
if (builders.size > 0) {
|
|
15569
|
+
const [name2, { line, file }] = builders.entries().next().value;
|
|
15570
|
+
conventions.trpc_router_builder = name2;
|
|
15571
|
+
provenance.push({ field: "trpc_router_builder", sourceFile: file, line, query: "trpc-router-builder" });
|
|
15572
|
+
}
|
|
15573
|
+
if (procedures.size > 0) {
|
|
15574
|
+
const [name2, { line, file }] = procedures.entries().next().value;
|
|
15575
|
+
conventions.procedure_pattern = name2;
|
|
15576
|
+
provenance.push({ field: "procedure_pattern", sourceFile: file, line, query: "trpc-procedure" });
|
|
15577
|
+
}
|
|
15578
|
+
let confidence;
|
|
15579
|
+
if (Object.keys(conventions).length === 0) {
|
|
15580
|
+
confidence = "none";
|
|
15581
|
+
} else if (builders.size === 1 && procedures.size <= 2) {
|
|
15582
|
+
confidence = "high";
|
|
15583
|
+
} else if (builders.size > 1) {
|
|
15584
|
+
confidence = "low";
|
|
15585
|
+
} else {
|
|
15586
|
+
confidence = "medium";
|
|
15587
|
+
}
|
|
15588
|
+
return { conventions, provenance, confidence };
|
|
15589
|
+
}
|
|
15590
|
+
};
|
|
14636
15591
|
}
|
|
14637
15592
|
});
|
|
14638
15593
|
|
|
14639
15594
|
// src/detect/adapters/swift-swiftui.ts
|
|
15595
|
+
var API_CLASS_QUERY, POLICY_QUERY, NAV_QUERY, POLICY_NAMES, swiftSwiftUiAdapter;
|
|
14640
15596
|
var init_swift_swiftui = __esm({
|
|
14641
15597
|
"src/detect/adapters/swift-swiftui.ts"() {
|
|
14642
15598
|
"use strict";
|
|
@@ -14644,10 +15600,289 @@ var init_swift_swiftui = __esm({
|
|
|
14644
15600
|
init_query_helpers();
|
|
14645
15601
|
init_tree_sitter_loader();
|
|
14646
15602
|
init_parse_guard();
|
|
15603
|
+
API_CLASS_QUERY = `
|
|
15604
|
+
(simple_identifier) @ident
|
|
15605
|
+
`;
|
|
15606
|
+
POLICY_QUERY = `
|
|
15607
|
+
(navigation_expression
|
|
15608
|
+
suffix: (navigation_suffix
|
|
15609
|
+
(simple_identifier) @policy_name))
|
|
15610
|
+
`;
|
|
15611
|
+
NAV_QUERY = `
|
|
15612
|
+
(simple_identifier) @nav_ident
|
|
15613
|
+
`;
|
|
15614
|
+
POLICY_NAMES = /* @__PURE__ */ new Set([
|
|
15615
|
+
"deviceOwnerAuthentication",
|
|
15616
|
+
"deviceOwnerAuthenticationWithBiometrics"
|
|
15617
|
+
]);
|
|
15618
|
+
swiftSwiftUiAdapter = {
|
|
15619
|
+
id: "swift-swiftui",
|
|
15620
|
+
languages: ["swift"],
|
|
15621
|
+
matches(signals) {
|
|
15622
|
+
if (signals.presentFiles.has("Package.swift")) return true;
|
|
15623
|
+
for (const dir of signals.presentDirs) {
|
|
15624
|
+
if (dir.endsWith(".xcodeproj") || dir.endsWith(".xcworkspace")) return true;
|
|
15625
|
+
if (dir === "Sources") return true;
|
|
15626
|
+
}
|
|
15627
|
+
for (const file of signals.presentFiles) {
|
|
15628
|
+
if (file.endsWith(".swift")) return true;
|
|
15629
|
+
}
|
|
15630
|
+
return false;
|
|
15631
|
+
},
|
|
15632
|
+
async introspect(files, _rootDir) {
|
|
15633
|
+
if (files.length === 0) {
|
|
15634
|
+
return { conventions: {}, provenance: [], confidence: "none" };
|
|
15635
|
+
}
|
|
15636
|
+
let language;
|
|
15637
|
+
try {
|
|
15638
|
+
language = await loadGrammar("swift");
|
|
15639
|
+
} catch {
|
|
15640
|
+
return { conventions: {}, provenance: [], confidence: "none" };
|
|
15641
|
+
}
|
|
15642
|
+
const parser = new Parser();
|
|
15643
|
+
parser.setLanguage(language);
|
|
15644
|
+
const apiClasses = /* @__PURE__ */ new Map();
|
|
15645
|
+
const policies = /* @__PURE__ */ new Map();
|
|
15646
|
+
const navs = /* @__PURE__ */ new Map();
|
|
15647
|
+
try {
|
|
15648
|
+
for (const file of files) {
|
|
15649
|
+
const skip = isParsableSource(file.content, file.size);
|
|
15650
|
+
if (skip) {
|
|
15651
|
+
process.stderr.write(
|
|
15652
|
+
`[massu/ast] WARN: swift-swiftui skipping ${file.path}: ${skip.reason} (${skip.detail}). Cap=${MAX_AST_FILE_BYTES}. (Phase 3.5 mitigation)
|
|
15653
|
+
`
|
|
15654
|
+
);
|
|
15655
|
+
continue;
|
|
15656
|
+
}
|
|
15657
|
+
try {
|
|
15658
|
+
for (const hit of runQuery(parser, file.content, API_CLASS_QUERY, "swift-api-class", file.path)) {
|
|
15659
|
+
const ident = hit.captures.ident;
|
|
15660
|
+
if (ident && /^[A-Z][A-Za-z0-9_]*API$/.test(ident) && !apiClasses.has(ident)) {
|
|
15661
|
+
apiClasses.set(ident, { line: hit.line, file: file.path });
|
|
15662
|
+
}
|
|
15663
|
+
}
|
|
15664
|
+
for (const hit of runQuery(parser, file.content, POLICY_QUERY, "swift-biometric-policy", file.path)) {
|
|
15665
|
+
const name2 = hit.captures.policy_name;
|
|
15666
|
+
if (name2 && POLICY_NAMES.has(name2) && !policies.has(name2)) {
|
|
15667
|
+
policies.set(name2, { line: hit.line, file: file.path });
|
|
15668
|
+
}
|
|
15669
|
+
}
|
|
15670
|
+
for (const hit of runQuery(parser, file.content, NAV_QUERY, "swift-navigation", file.path)) {
|
|
15671
|
+
const ident = hit.captures.nav_ident;
|
|
15672
|
+
if ((ident === "NavigationStack" || ident === "NavigationView") && !navs.has(ident)) {
|
|
15673
|
+
navs.set(ident, { line: hit.line, file: file.path });
|
|
15674
|
+
}
|
|
15675
|
+
}
|
|
15676
|
+
} catch (e2) {
|
|
15677
|
+
if (e2 instanceof InvalidQueryError) throw e2;
|
|
15678
|
+
continue;
|
|
15679
|
+
}
|
|
15680
|
+
}
|
|
15681
|
+
} finally {
|
|
15682
|
+
try {
|
|
15683
|
+
parser.delete();
|
|
15684
|
+
} catch {
|
|
15685
|
+
}
|
|
15686
|
+
}
|
|
15687
|
+
const conventions = {};
|
|
15688
|
+
const provenance = [];
|
|
15689
|
+
if (apiClasses.size > 0) {
|
|
15690
|
+
const [name2, { line, file }] = apiClasses.entries().next().value;
|
|
15691
|
+
conventions.api_client_class = name2;
|
|
15692
|
+
provenance.push({ field: "api_client_class", sourceFile: file, line, query: "swift-api-class" });
|
|
15693
|
+
}
|
|
15694
|
+
if (policies.size > 0) {
|
|
15695
|
+
const [name2, { line, file }] = policies.entries().next().value;
|
|
15696
|
+
conventions.biometric_policy = name2;
|
|
15697
|
+
provenance.push({ field: "biometric_policy", sourceFile: file, line, query: "swift-biometric-policy" });
|
|
15698
|
+
}
|
|
15699
|
+
if (navs.size > 0) {
|
|
15700
|
+
const [name2, { line, file }] = navs.entries().next().value;
|
|
15701
|
+
conventions.navigation_pattern = name2;
|
|
15702
|
+
provenance.push({ field: "navigation_pattern", sourceFile: file, line, query: "swift-navigation" });
|
|
15703
|
+
}
|
|
15704
|
+
let confidence;
|
|
15705
|
+
if (Object.keys(conventions).length === 0) {
|
|
15706
|
+
confidence = "none";
|
|
15707
|
+
} else if (apiClasses.size === 1 && policies.size <= 1) {
|
|
15708
|
+
confidence = "high";
|
|
15709
|
+
} else if (apiClasses.size > 1) {
|
|
15710
|
+
confidence = "low";
|
|
15711
|
+
} else {
|
|
15712
|
+
confidence = "medium";
|
|
15713
|
+
}
|
|
15714
|
+
return { conventions, provenance, confidence };
|
|
15715
|
+
}
|
|
15716
|
+
};
|
|
15717
|
+
}
|
|
15718
|
+
});
|
|
15719
|
+
|
|
15720
|
+
// src/detect/adapters/file-sampler.ts
|
|
15721
|
+
var file_sampler_exports = {};
|
|
15722
|
+
__export(file_sampler_exports, {
|
|
15723
|
+
SAMPLE_EXTENSIONS: () => SAMPLE_EXTENSIONS,
|
|
15724
|
+
SAMPLE_TEST_FILE_PATTERNS: () => SAMPLE_TEST_FILE_PATTERNS,
|
|
15725
|
+
sampleFilesForAdapter: () => sampleFilesForAdapter
|
|
15726
|
+
});
|
|
15727
|
+
import { readdirSync as readdirSync9, readFileSync as readFileSync9, lstatSync as lstatSync4 } from "node:fs";
|
|
15728
|
+
import { join as join9, extname } from "node:path";
|
|
15729
|
+
function sampleFilesForAdapter(adapter, projectRoot, detection, options = {}) {
|
|
15730
|
+
const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
15731
|
+
const maxFiles = options.maxFilesPerAdapter ?? DEFAULT_MAX_FILES;
|
|
15732
|
+
const out2 = [];
|
|
15733
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15734
|
+
for (const lang of adapter.languages) {
|
|
15735
|
+
if (out2.length >= maxFiles) break;
|
|
15736
|
+
const exts = SAMPLE_EXTENSIONS[lang];
|
|
15737
|
+
if (!exts || exts.length === 0) continue;
|
|
15738
|
+
const testPatterns = SAMPLE_TEST_FILE_PATTERNS[lang] ?? [];
|
|
15739
|
+
const langKey = lang;
|
|
15740
|
+
const langDetection = detection.sourceDirs[langKey];
|
|
15741
|
+
const candidateDirs = [];
|
|
15742
|
+
if (langDetection?.source_dirs && langDetection.source_dirs.length > 0) {
|
|
15743
|
+
candidateDirs.push(...langDetection.source_dirs.map((d2) => join9(projectRoot, d2)));
|
|
15744
|
+
} else {
|
|
15745
|
+
candidateDirs.push(projectRoot);
|
|
15746
|
+
}
|
|
15747
|
+
for (const dir of candidateDirs) {
|
|
15748
|
+
if (out2.length >= maxFiles) break;
|
|
15749
|
+
walkDir(dir, exts, testPatterns, lang, maxDepth, 0, out2, seen, maxFiles);
|
|
15750
|
+
}
|
|
15751
|
+
}
|
|
15752
|
+
return out2;
|
|
15753
|
+
}
|
|
15754
|
+
function walkDir(dir, exts, testPatterns, lang, maxDepth, curDepth, out2, seen, maxFiles) {
|
|
15755
|
+
if (curDepth > maxDepth) return;
|
|
15756
|
+
if (out2.length >= maxFiles) return;
|
|
15757
|
+
let entries;
|
|
15758
|
+
try {
|
|
15759
|
+
entries = readdirSync9(dir);
|
|
15760
|
+
} catch {
|
|
15761
|
+
return;
|
|
15762
|
+
}
|
|
15763
|
+
for (const entry of entries) {
|
|
15764
|
+
if (out2.length >= maxFiles) return;
|
|
15765
|
+
if (entry.startsWith(".")) continue;
|
|
15766
|
+
if (IGNORED_DIRS3.has(entry)) continue;
|
|
15767
|
+
const fullPath = join9(dir, entry);
|
|
15768
|
+
let st;
|
|
15769
|
+
try {
|
|
15770
|
+
st = lstatSync4(fullPath);
|
|
15771
|
+
} catch {
|
|
15772
|
+
continue;
|
|
15773
|
+
}
|
|
15774
|
+
if (st.isSymbolicLink()) continue;
|
|
15775
|
+
if (st.isDirectory()) {
|
|
15776
|
+
walkDir(fullPath, exts, testPatterns, lang, maxDepth, curDepth + 1, out2, seen, maxFiles);
|
|
15777
|
+
continue;
|
|
15778
|
+
}
|
|
15779
|
+
if (!st.isFile()) continue;
|
|
15780
|
+
if (st.size > MAX_AST_FILE_BYTES) continue;
|
|
15781
|
+
const ext = extname(entry).slice(1);
|
|
15782
|
+
if (!exts.includes(ext)) continue;
|
|
15783
|
+
if (testPatterns.some((p19) => p19.test(fullPath))) continue;
|
|
15784
|
+
if (seen.has(fullPath)) continue;
|
|
15785
|
+
seen.add(fullPath);
|
|
15786
|
+
let content;
|
|
15787
|
+
try {
|
|
15788
|
+
content = readFileSync9(fullPath, "utf-8");
|
|
15789
|
+
} catch {
|
|
15790
|
+
continue;
|
|
15791
|
+
}
|
|
15792
|
+
out2.push({
|
|
15793
|
+
path: fullPath,
|
|
15794
|
+
content,
|
|
15795
|
+
language: lang,
|
|
15796
|
+
size: st.size
|
|
15797
|
+
});
|
|
15798
|
+
}
|
|
15799
|
+
}
|
|
15800
|
+
var SAMPLE_EXTENSIONS, SAMPLE_TEST_FILE_PATTERNS, IGNORED_DIRS3, DEFAULT_MAX_DEPTH, DEFAULT_MAX_FILES;
|
|
15801
|
+
var init_file_sampler = __esm({
|
|
15802
|
+
"src/detect/adapters/file-sampler.ts"() {
|
|
15803
|
+
"use strict";
|
|
15804
|
+
init_parse_guard();
|
|
15805
|
+
SAMPLE_EXTENSIONS = {
|
|
15806
|
+
python: ["py"],
|
|
15807
|
+
typescript: ["ts", "tsx"],
|
|
15808
|
+
javascript: ["js", "jsx", "mjs", "cjs"],
|
|
15809
|
+
swift: ["swift"],
|
|
15810
|
+
rust: ["rs"],
|
|
15811
|
+
go: ["go"],
|
|
15812
|
+
ruby: ["rb"],
|
|
15813
|
+
php: ["php"],
|
|
15814
|
+
java: ["java", "kt"],
|
|
15815
|
+
kotlin: ["kt", "kts"],
|
|
15816
|
+
elixir: ["ex", "exs"],
|
|
15817
|
+
erlang: ["erl", "hrl"],
|
|
15818
|
+
csharp: ["cs"],
|
|
15819
|
+
cpp: ["cpp", "cc", "cxx", "h", "hpp"],
|
|
15820
|
+
haskell: ["hs", "lhs"],
|
|
15821
|
+
ocaml: ["ml", "mli"]
|
|
15822
|
+
};
|
|
15823
|
+
SAMPLE_TEST_FILE_PATTERNS = {
|
|
15824
|
+
python: [/_test\.py$/, /test_[^/]*\.py$/, /\/tests?\//],
|
|
15825
|
+
typescript: [/\.test\.tsx?$/, /\.spec\.tsx?$/, /\/__tests__\//],
|
|
15826
|
+
javascript: [/\.test\.[mc]?jsx?$/, /\.spec\.[mc]?jsx?$/, /\/__tests__\//],
|
|
15827
|
+
swift: [/Tests\//],
|
|
15828
|
+
rust: [/tests\/.*\.rs$/],
|
|
15829
|
+
go: [/_test\.go$/],
|
|
15830
|
+
ruby: [/_spec\.rb$/, /_test\.rb$/, /\/spec\//],
|
|
15831
|
+
php: [/Test\.php$/, /\/tests?\//i],
|
|
15832
|
+
java: [/Test[^/]*\.(java|kt)$/, /[^/]*Test\.(java|kt)$/, /\/test\//],
|
|
15833
|
+
kotlin: [/Test[^/]*\.kt$/, /[^/]*Test\.kt$/],
|
|
15834
|
+
elixir: [/_test\.exs$/, /\/test\//],
|
|
15835
|
+
erlang: [/_SUITE\.erl$/],
|
|
15836
|
+
csharp: [/Tests?\.cs$/, /\.Tests?\//],
|
|
15837
|
+
cpp: [/_test\.(cpp|cc)$/i, /\/tests?\//i],
|
|
15838
|
+
haskell: [/Spec\.hs$/, /\/test\//],
|
|
15839
|
+
ocaml: [/_test\.ml$/, /\/test\//]
|
|
15840
|
+
};
|
|
15841
|
+
IGNORED_DIRS3 = /* @__PURE__ */ new Set([
|
|
15842
|
+
"node_modules",
|
|
15843
|
+
".venv",
|
|
15844
|
+
"venv",
|
|
15845
|
+
"__pycache__",
|
|
15846
|
+
"dist",
|
|
15847
|
+
"build",
|
|
15848
|
+
".build",
|
|
15849
|
+
"target",
|
|
15850
|
+
".next",
|
|
15851
|
+
".nuxt",
|
|
15852
|
+
"coverage",
|
|
15853
|
+
".git",
|
|
15854
|
+
".massu",
|
|
15855
|
+
".turbo",
|
|
15856
|
+
".cache",
|
|
15857
|
+
".pytest_cache",
|
|
15858
|
+
".mypy_cache",
|
|
15859
|
+
"DerivedData",
|
|
15860
|
+
"Pods",
|
|
15861
|
+
"_build",
|
|
15862
|
+
"deps",
|
|
15863
|
+
"priv",
|
|
15864
|
+
"cover",
|
|
15865
|
+
".elixir_ls",
|
|
15866
|
+
// Elixir/Phoenix
|
|
15867
|
+
"bin",
|
|
15868
|
+
"obj",
|
|
15869
|
+
".vs",
|
|
15870
|
+
"packages",
|
|
15871
|
+
"publish",
|
|
15872
|
+
"TestResults"
|
|
15873
|
+
// .NET
|
|
15874
|
+
]);
|
|
15875
|
+
DEFAULT_MAX_DEPTH = 3;
|
|
15876
|
+
DEFAULT_MAX_FILES = 50;
|
|
14647
15877
|
}
|
|
14648
15878
|
});
|
|
14649
15879
|
|
|
14650
15880
|
// src/detect/codebase-introspector.ts
|
|
15881
|
+
var codebase_introspector_exports = {};
|
|
15882
|
+
__export(codebase_introspector_exports, {
|
|
15883
|
+
introspect: () => introspect,
|
|
15884
|
+
introspectAsync: () => introspectAsync
|
|
15885
|
+
});
|
|
14651
15886
|
function introspect(detection, projectRoot) {
|
|
14652
15887
|
const out2 = {};
|
|
14653
15888
|
const languages = Array.from(
|
|
@@ -14667,6 +15902,35 @@ function introspect(detection, projectRoot) {
|
|
|
14667
15902
|
}
|
|
14668
15903
|
return out2;
|
|
14669
15904
|
}
|
|
15905
|
+
async function introspectAsync(detection, projectRoot) {
|
|
15906
|
+
const out2 = introspect(detection, projectRoot);
|
|
15907
|
+
const signals = buildDetectionSignals(projectRoot);
|
|
15908
|
+
const { sampleFilesForAdapter: sampleFilesForAdapter2 } = await Promise.resolve().then(() => (init_file_sampler(), file_sampler_exports));
|
|
15909
|
+
let merged;
|
|
15910
|
+
try {
|
|
15911
|
+
merged = await runAdapters(FIRST_PARTY_ADAPTERS, projectRoot, signals, {
|
|
15912
|
+
sampleFiles: async (adapter, root) => {
|
|
15913
|
+
return sampleFilesForAdapter2(adapter, root, detection);
|
|
15914
|
+
}
|
|
15915
|
+
});
|
|
15916
|
+
} catch {
|
|
15917
|
+
return out2;
|
|
15918
|
+
}
|
|
15919
|
+
for (const [adapterId, resolved] of Object.entries(merged.byAdapter)) {
|
|
15920
|
+
if (resolved.confidence === "none") continue;
|
|
15921
|
+
out2[adapterId] = serializeAdapterBlock(resolved);
|
|
15922
|
+
}
|
|
15923
|
+
return out2;
|
|
15924
|
+
}
|
|
15925
|
+
function serializeAdapterBlock(r2) {
|
|
15926
|
+
const block = { ...r2.conventions };
|
|
15927
|
+
if (Object.keys(r2._provenance).length > 0) {
|
|
15928
|
+
block._provenance = r2._provenance;
|
|
15929
|
+
}
|
|
15930
|
+
block._confidence = r2.confidence;
|
|
15931
|
+
return block;
|
|
15932
|
+
}
|
|
15933
|
+
var FIRST_PARTY_ADAPTERS;
|
|
14670
15934
|
var init_codebase_introspector = __esm({
|
|
14671
15935
|
"src/detect/codebase-introspector.ts"() {
|
|
14672
15936
|
"use strict";
|
|
@@ -14676,6 +15940,12 @@ var init_codebase_introspector = __esm({
|
|
|
14676
15940
|
init_python_django();
|
|
14677
15941
|
init_nextjs_trpc();
|
|
14678
15942
|
init_swift_swiftui();
|
|
15943
|
+
FIRST_PARTY_ADAPTERS = [
|
|
15944
|
+
pythonFastApiAdapter,
|
|
15945
|
+
pythonDjangoAdapter,
|
|
15946
|
+
nextjsTrpcAdapter,
|
|
15947
|
+
swiftSwiftUiAdapter
|
|
15948
|
+
];
|
|
14679
15949
|
}
|
|
14680
15950
|
});
|
|
14681
15951
|
|
|
@@ -14741,7 +16011,7 @@ var init_detect = __esm({
|
|
|
14741
16011
|
});
|
|
14742
16012
|
|
|
14743
16013
|
// src/detect/drift.ts
|
|
14744
|
-
import { createHash as
|
|
16014
|
+
import { createHash as createHash3 } from "crypto";
|
|
14745
16015
|
function summarizeDetection(det) {
|
|
14746
16016
|
const languages = Array.from(new Set(det.manifests.map((m3) => m3.language))).sort();
|
|
14747
16017
|
const frameworks = {};
|
|
@@ -14772,7 +16042,7 @@ function summarizeDetection(det) {
|
|
|
14772
16042
|
function computeFingerprint(det) {
|
|
14773
16043
|
const data = summarizeDetection(det);
|
|
14774
16044
|
const stable = JSON.stringify(data, Object.keys(data).sort());
|
|
14775
|
-
return
|
|
16045
|
+
return createHash3("sha256").update(stable).digest("hex");
|
|
14776
16046
|
}
|
|
14777
16047
|
function stringOf(v3) {
|
|
14778
16048
|
if (typeof v3 === "string") return v3;
|
|
@@ -15931,10 +17201,10 @@ __export(init_exports, {
|
|
|
15931
17201
|
validateWrittenConfig: () => validateWrittenConfig,
|
|
15932
17202
|
writeConfigAtomic: () => writeConfigAtomic
|
|
15933
17203
|
});
|
|
15934
|
-
import { closeSync as closeSync2, existsSync as
|
|
15935
|
-
import { resolve as resolve6, basename as
|
|
17204
|
+
import { closeSync as closeSync2, existsSync as existsSync10, fsyncSync as fsyncSync2, openSync as openSync2, readFileSync as readFileSync10, writeFileSync as writeFileSync2, writeSync as writeSync2, mkdirSync as mkdirSync4, readdirSync as readdirSync10, renameSync as renameSync3, rmSync as rmSync2, statSync as statSync7, chmodSync as chmodSync2 } from "fs";
|
|
17205
|
+
import { resolve as resolve6, basename as basename4, dirname as dirname5 } from "path";
|
|
15936
17206
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
15937
|
-
import { homedir as
|
|
17207
|
+
import { homedir as homedir3 } from "os";
|
|
15938
17208
|
import { stringify as yamlStringify, parse as yamlParse } from "yaml";
|
|
15939
17209
|
function detectFramework(projectRoot) {
|
|
15940
17210
|
const result = {
|
|
@@ -15944,9 +17214,9 @@ function detectFramework(projectRoot) {
|
|
|
15944
17214
|
ui: "none"
|
|
15945
17215
|
};
|
|
15946
17216
|
const pkgPath = resolve6(projectRoot, "package.json");
|
|
15947
|
-
if (!
|
|
17217
|
+
if (!existsSync10(pkgPath)) return result;
|
|
15948
17218
|
try {
|
|
15949
|
-
const pkg = JSON.parse(
|
|
17219
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
15950
17220
|
const allDeps = {
|
|
15951
17221
|
...pkg.dependencies,
|
|
15952
17222
|
...pkg.devDependencies
|
|
@@ -15980,7 +17250,7 @@ function detectPython(projectRoot) {
|
|
|
15980
17250
|
alembicDir: null
|
|
15981
17251
|
};
|
|
15982
17252
|
const markers = ["pyproject.toml", "setup.py", "requirements.txt", "Pipfile"];
|
|
15983
|
-
const hasMarker = markers.some((m3) =>
|
|
17253
|
+
const hasMarker = markers.some((m3) => existsSync10(resolve6(projectRoot, m3)));
|
|
15984
17254
|
if (!hasMarker) return result;
|
|
15985
17255
|
result.detected = true;
|
|
15986
17256
|
const depFiles = [
|
|
@@ -15991,34 +17261,34 @@ function detectPython(projectRoot) {
|
|
|
15991
17261
|
];
|
|
15992
17262
|
for (const { file } of depFiles) {
|
|
15993
17263
|
const filePath = resolve6(projectRoot, file);
|
|
15994
|
-
if (
|
|
17264
|
+
if (existsSync10(filePath)) {
|
|
15995
17265
|
try {
|
|
15996
|
-
const content =
|
|
17266
|
+
const content = readFileSync10(filePath, "utf-8").toLowerCase();
|
|
15997
17267
|
if (content.includes("fastapi")) result.hasFastapi = true;
|
|
15998
17268
|
if (content.includes("sqlalchemy")) result.hasSqlalchemy = true;
|
|
15999
17269
|
} catch {
|
|
16000
17270
|
}
|
|
16001
17271
|
}
|
|
16002
17272
|
}
|
|
16003
|
-
if (
|
|
17273
|
+
if (existsSync10(resolve6(projectRoot, "alembic.ini"))) {
|
|
16004
17274
|
result.hasAlembic = true;
|
|
16005
|
-
if (
|
|
17275
|
+
if (existsSync10(resolve6(projectRoot, "alembic"))) {
|
|
16006
17276
|
result.alembicDir = "alembic";
|
|
16007
17277
|
}
|
|
16008
|
-
} else if (
|
|
17278
|
+
} else if (existsSync10(resolve6(projectRoot, "alembic"))) {
|
|
16009
17279
|
result.hasAlembic = true;
|
|
16010
17280
|
result.alembicDir = "alembic";
|
|
16011
17281
|
}
|
|
16012
17282
|
const candidateRoots = ["app", "src", "backend", "api"];
|
|
16013
17283
|
for (const candidate of candidateRoots) {
|
|
16014
17284
|
const candidatePath = resolve6(projectRoot, candidate);
|
|
16015
|
-
if (
|
|
17285
|
+
if (existsSync10(candidatePath) && existsSync10(resolve6(candidatePath, "__init__.py"))) {
|
|
16016
17286
|
result.root = candidate;
|
|
16017
17287
|
break;
|
|
16018
17288
|
}
|
|
16019
|
-
if (
|
|
17289
|
+
if (existsSync10(candidatePath)) {
|
|
16020
17290
|
try {
|
|
16021
|
-
const files =
|
|
17291
|
+
const files = readdirSync10(candidatePath);
|
|
16022
17292
|
if (files.some((f2) => f2.endsWith(".py"))) {
|
|
16023
17293
|
result.root = candidate;
|
|
16024
17294
|
break;
|
|
@@ -16037,10 +17307,10 @@ function generateConfig(projectRoot, framework) {
|
|
|
16037
17307
|
"[@massu/core] generateConfig() is deprecated since 1.2.1 \u2014 use buildConfigFromDetection instead. It cannot produce valid configs for monorepos."
|
|
16038
17308
|
);
|
|
16039
17309
|
const configPath = resolve6(projectRoot, "massu.config.yaml");
|
|
16040
|
-
if (
|
|
17310
|
+
if (existsSync10(configPath)) {
|
|
16041
17311
|
return false;
|
|
16042
17312
|
}
|
|
16043
|
-
const projectName =
|
|
17313
|
+
const projectName = basename4(projectRoot);
|
|
16044
17314
|
const config = {
|
|
16045
17315
|
project: {
|
|
16046
17316
|
name: projectName,
|
|
@@ -16083,7 +17353,7 @@ function generateConfig(projectRoot, framework) {
|
|
|
16083
17353
|
# Documentation: https://massu.ai/docs/getting-started/configuration
|
|
16084
17354
|
|
|
16085
17355
|
${yamlStringify(config)}`;
|
|
16086
|
-
|
|
17356
|
+
writeFileSync2(configPath, yamlContent, "utf-8");
|
|
16087
17357
|
return true;
|
|
16088
17358
|
}
|
|
16089
17359
|
function monorepoCommonRoot(packages) {
|
|
@@ -16105,7 +17375,7 @@ function buildConfigFromDetection(opts) {
|
|
|
16105
17375
|
if (!detection) {
|
|
16106
17376
|
throw new Error("buildConfigFromDetection requires a detection result");
|
|
16107
17377
|
}
|
|
16108
|
-
const projectName = opts.projectName ??
|
|
17378
|
+
const projectName = opts.projectName ?? basename4(projectRoot);
|
|
16109
17379
|
const languages = Array.from(
|
|
16110
17380
|
new Set(detection.manifests.map((m3) => m3.language))
|
|
16111
17381
|
);
|
|
@@ -16226,7 +17496,7 @@ function buildConfigFromDetection(opts) {
|
|
|
16226
17496
|
};
|
|
16227
17497
|
if (pyFw?.framework) pythonBlock.framework = pyFw.framework;
|
|
16228
17498
|
if (pyFw?.orm) pythonBlock.orm = pyFw.orm;
|
|
16229
|
-
if (
|
|
17499
|
+
if (existsSync10(resolve6(projectRoot, "alembic.ini")) || existsSync10(resolve6(projectRoot, "alembic"))) {
|
|
16230
17500
|
pythonBlock.alembic_dir = "alembic";
|
|
16231
17501
|
}
|
|
16232
17502
|
config.python = pythonBlock;
|
|
@@ -16251,10 +17521,10 @@ function applyVariantTemplate(config, templatesDir) {
|
|
|
16251
17521
|
}
|
|
16252
17522
|
if (templateId === null) return config;
|
|
16253
17523
|
const templatePath = resolve6(templatesDir, templateId, "massu.config.yaml");
|
|
16254
|
-
if (!
|
|
17524
|
+
if (!existsSync10(templatePath)) return config;
|
|
16255
17525
|
let template;
|
|
16256
17526
|
try {
|
|
16257
|
-
template = yamlParse(
|
|
17527
|
+
template = yamlParse(readFileSync10(templatePath, "utf-8"));
|
|
16258
17528
|
} catch {
|
|
16259
17529
|
return config;
|
|
16260
17530
|
}
|
|
@@ -16313,15 +17583,15 @@ ${yamlStringify(config)}`;
|
|
|
16313
17583
|
function writeConfigAtomic(configPath, content) {
|
|
16314
17584
|
const tmpPath = `${configPath}.tmp`;
|
|
16315
17585
|
let existingMode;
|
|
16316
|
-
if (
|
|
17586
|
+
if (existsSync10(configPath)) {
|
|
16317
17587
|
try {
|
|
16318
|
-
existingMode =
|
|
17588
|
+
existingMode = statSync7(configPath).mode;
|
|
16319
17589
|
} catch {
|
|
16320
17590
|
existingMode = void 0;
|
|
16321
17591
|
}
|
|
16322
17592
|
}
|
|
16323
17593
|
try {
|
|
16324
|
-
|
|
17594
|
+
mkdirSync4(dirname5(configPath), { recursive: true });
|
|
16325
17595
|
const fd = openSync2(tmpPath, "w", 420);
|
|
16326
17596
|
try {
|
|
16327
17597
|
const buf = Buffer.from(content, "utf-8");
|
|
@@ -16334,16 +17604,16 @@ function writeConfigAtomic(configPath, content) {
|
|
|
16334
17604
|
if (parsed === null || typeof parsed !== "object") {
|
|
16335
17605
|
throw new Error("Generated config is not a valid YAML object");
|
|
16336
17606
|
}
|
|
16337
|
-
|
|
17607
|
+
renameSync3(tmpPath, configPath);
|
|
16338
17608
|
if (existingMode !== void 0) {
|
|
16339
17609
|
try {
|
|
16340
|
-
|
|
17610
|
+
chmodSync2(configPath, existingMode);
|
|
16341
17611
|
} catch {
|
|
16342
17612
|
}
|
|
16343
17613
|
}
|
|
16344
17614
|
return { validated: true };
|
|
16345
17615
|
} catch (err2) {
|
|
16346
|
-
if (
|
|
17616
|
+
if (existsSync10(tmpPath)) {
|
|
16347
17617
|
try {
|
|
16348
17618
|
rmSync2(tmpPath, { force: true });
|
|
16349
17619
|
} catch {
|
|
@@ -16354,8 +17624,8 @@ function writeConfigAtomic(configPath, content) {
|
|
|
16354
17624
|
}
|
|
16355
17625
|
function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
16356
17626
|
try {
|
|
16357
|
-
if (!
|
|
16358
|
-
const content =
|
|
17627
|
+
if (!existsSync10(configPath)) return "Config file does not exist after write";
|
|
17628
|
+
const content = readFileSync10(configPath, "utf-8");
|
|
16359
17629
|
const parsed = yamlParse(content);
|
|
16360
17630
|
if (parsed === null || typeof parsed !== "object") {
|
|
16361
17631
|
return "Config is not a valid YAML object";
|
|
@@ -16376,7 +17646,7 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
|
16376
17646
|
const src = cfg.paths.source;
|
|
16377
17647
|
if (src && src !== ".") {
|
|
16378
17648
|
const srcAbs = resolve6(projectRoot, src);
|
|
16379
|
-
if (!
|
|
17649
|
+
if (!existsSync10(srcAbs)) {
|
|
16380
17650
|
return `paths.source '${src}' does not exist on disk`;
|
|
16381
17651
|
}
|
|
16382
17652
|
}
|
|
@@ -16387,7 +17657,7 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
|
16387
17657
|
for (const d2 of rawDirs) {
|
|
16388
17658
|
if (typeof d2 !== "string" || d2 === ".") continue;
|
|
16389
17659
|
const abs = resolve6(projectRoot, d2);
|
|
16390
|
-
if (!
|
|
17660
|
+
if (!existsSync10(abs)) {
|
|
16391
17661
|
return `framework.languages.${lang}.source_dirs '${d2}' does not exist on disk`;
|
|
16392
17662
|
}
|
|
16393
17663
|
}
|
|
@@ -16396,7 +17666,7 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
|
16396
17666
|
if (Array.isArray(mRoots)) {
|
|
16397
17667
|
for (const r2 of mRoots) {
|
|
16398
17668
|
if (typeof r2 !== "string" || r2 === ".") continue;
|
|
16399
|
-
if (!
|
|
17669
|
+
if (!existsSync10(resolve6(projectRoot, r2))) {
|
|
16400
17670
|
return `paths.monorepo_roots '${r2}' does not exist on disk`;
|
|
16401
17671
|
}
|
|
16402
17672
|
}
|
|
@@ -16442,7 +17712,7 @@ function resolveTemplatesDir() {
|
|
|
16442
17712
|
resolve6(__dirname2, "../../../templates")
|
|
16443
17713
|
];
|
|
16444
17714
|
for (const c2 of candidates) {
|
|
16445
|
-
if (
|
|
17715
|
+
if (existsSync10(c2)) return c2;
|
|
16446
17716
|
}
|
|
16447
17717
|
return null;
|
|
16448
17718
|
}
|
|
@@ -16452,13 +17722,13 @@ function copyTemplateConfig(templateName, targetPath, projectName) {
|
|
|
16452
17722
|
return { success: false, error: `Templates directory not found (looked in node_modules and dist/src)` };
|
|
16453
17723
|
}
|
|
16454
17724
|
const srcPath = resolve6(templatesDir, templateName, "massu.config.yaml");
|
|
16455
|
-
if (!
|
|
17725
|
+
if (!existsSync10(srcPath)) {
|
|
16456
17726
|
return { success: false, error: `Template '${templateName}' not found at ${srcPath}` };
|
|
16457
17727
|
}
|
|
16458
17728
|
try {
|
|
16459
|
-
let content =
|
|
17729
|
+
let content = readFileSync10(srcPath, "utf-8");
|
|
16460
17730
|
content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectName);
|
|
16461
|
-
|
|
17731
|
+
writeFileSync2(targetPath, content, "utf-8");
|
|
16462
17732
|
return { success: true };
|
|
16463
17733
|
} catch (err2) {
|
|
16464
17734
|
return { success: false, error: err2 instanceof Error ? err2.message : String(err2) };
|
|
@@ -16467,9 +17737,9 @@ function copyTemplateConfig(templateName, targetPath, projectName) {
|
|
|
16467
17737
|
function registerMcpServer(projectRoot) {
|
|
16468
17738
|
const mcpPath = resolve6(projectRoot, ".mcp.json");
|
|
16469
17739
|
let existing = {};
|
|
16470
|
-
if (
|
|
17740
|
+
if (existsSync10(mcpPath)) {
|
|
16471
17741
|
try {
|
|
16472
|
-
existing = JSON.parse(
|
|
17742
|
+
existing = JSON.parse(readFileSync10(mcpPath, "utf-8"));
|
|
16473
17743
|
} catch {
|
|
16474
17744
|
existing = {};
|
|
16475
17745
|
}
|
|
@@ -16484,17 +17754,17 @@ function registerMcpServer(projectRoot) {
|
|
|
16484
17754
|
args: ["-y", "@massu/core"]
|
|
16485
17755
|
};
|
|
16486
17756
|
existing.mcpServers = servers;
|
|
16487
|
-
|
|
17757
|
+
writeFileSync2(mcpPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
|
|
16488
17758
|
return true;
|
|
16489
17759
|
}
|
|
16490
17760
|
function resolveHooksDir() {
|
|
16491
17761
|
const cwd = process.cwd();
|
|
16492
17762
|
const nodeModulesPath = resolve6(cwd, "node_modules/@massu/core/dist/hooks");
|
|
16493
|
-
if (
|
|
17763
|
+
if (existsSync10(nodeModulesPath)) {
|
|
16494
17764
|
return "node_modules/@massu/core/dist/hooks";
|
|
16495
17765
|
}
|
|
16496
17766
|
const localPath = resolve6(__dirname2, "../dist/hooks");
|
|
16497
|
-
if (
|
|
17767
|
+
if (existsSync10(localPath)) {
|
|
16498
17768
|
return localPath;
|
|
16499
17769
|
}
|
|
16500
17770
|
return "node_modules/@massu/core/dist/hooks";
|
|
@@ -16588,13 +17858,13 @@ function installHooks(projectRoot) {
|
|
|
16588
17858
|
}
|
|
16589
17859
|
const claudeDir = resolve6(projectRoot, claudeDirName);
|
|
16590
17860
|
const settingsPath = resolve6(claudeDir, "settings.local.json");
|
|
16591
|
-
if (!
|
|
16592
|
-
|
|
17861
|
+
if (!existsSync10(claudeDir)) {
|
|
17862
|
+
mkdirSync4(claudeDir, { recursive: true });
|
|
16593
17863
|
}
|
|
16594
17864
|
let settings = {};
|
|
16595
|
-
if (
|
|
17865
|
+
if (existsSync10(settingsPath)) {
|
|
16596
17866
|
try {
|
|
16597
|
-
settings = JSON.parse(
|
|
17867
|
+
settings = JSON.parse(readFileSync10(settingsPath, "utf-8"));
|
|
16598
17868
|
} catch {
|
|
16599
17869
|
settings = {};
|
|
16600
17870
|
}
|
|
@@ -16608,21 +17878,21 @@ function installHooks(projectRoot) {
|
|
|
16608
17878
|
}
|
|
16609
17879
|
}
|
|
16610
17880
|
settings.hooks = hooksConfig;
|
|
16611
|
-
|
|
17881
|
+
writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
|
|
16612
17882
|
return { installed: true, count: hookCount };
|
|
16613
17883
|
}
|
|
16614
17884
|
function initMemoryDir(projectRoot) {
|
|
16615
17885
|
const encodedRoot = "-" + projectRoot.replace(/\//g, "-");
|
|
16616
|
-
const memoryDir = resolve6(
|
|
17886
|
+
const memoryDir = resolve6(homedir3(), `.claude/projects/${encodedRoot}/memory`);
|
|
16617
17887
|
let created = false;
|
|
16618
|
-
if (!
|
|
16619
|
-
|
|
17888
|
+
if (!existsSync10(memoryDir)) {
|
|
17889
|
+
mkdirSync4(memoryDir, { recursive: true });
|
|
16620
17890
|
created = true;
|
|
16621
17891
|
}
|
|
16622
17892
|
const memoryMdPath = resolve6(memoryDir, "MEMORY.md");
|
|
16623
17893
|
let memoryMdCreated = false;
|
|
16624
|
-
if (!
|
|
16625
|
-
const projectName =
|
|
17894
|
+
if (!existsSync10(memoryMdPath)) {
|
|
17895
|
+
const projectName = basename4(projectRoot);
|
|
16626
17896
|
const memoryContent = `# ${projectName} - Massu Memory
|
|
16627
17897
|
|
|
16628
17898
|
## Key Learnings
|
|
@@ -16637,7 +17907,7 @@ function initMemoryDir(projectRoot) {
|
|
|
16637
17907
|
## File Index
|
|
16638
17908
|
<!-- Significant files and directories -->
|
|
16639
17909
|
`;
|
|
16640
|
-
|
|
17910
|
+
writeFileSync2(memoryMdPath, memoryContent, "utf-8");
|
|
16641
17911
|
memoryMdCreated = true;
|
|
16642
17912
|
}
|
|
16643
17913
|
return { created, memoryMdCreated };
|
|
@@ -16649,6 +17919,7 @@ function parseInitArgs(argv) {
|
|
|
16649
17919
|
if (a2 === "--ci") opts.ci = true;
|
|
16650
17920
|
else if (a2 === "--force") opts.force = true;
|
|
16651
17921
|
else if (a2 === "--skip-commands") opts.skipCommands = true;
|
|
17922
|
+
else if (a2 === "--no-introspect") opts.skipIntrospect = true;
|
|
16652
17923
|
else if (a2 === "--help" || a2 === "-h") opts.help = true;
|
|
16653
17924
|
else if (a2 === "--template") {
|
|
16654
17925
|
const next = argv[i2 + 1];
|
|
@@ -16727,7 +17998,7 @@ async function runInit(argv, overrides) {
|
|
|
16727
17998
|
log("========================");
|
|
16728
17999
|
log("");
|
|
16729
18000
|
const configPath = resolve6(projectRoot, "massu.config.yaml");
|
|
16730
|
-
if (
|
|
18001
|
+
if (existsSync10(configPath)) {
|
|
16731
18002
|
if (opts.ci && !opts.force) {
|
|
16732
18003
|
errLog(`error: massu.config.yaml already exists at ${configPath}`);
|
|
16733
18004
|
errLog(" rerun with --force to overwrite, or remove the file first");
|
|
@@ -16746,7 +18017,7 @@ async function runInit(argv, overrides) {
|
|
|
16746
18017
|
errLog(`error: unknown template '${opts.template}'. Available: ${TEMPLATE_NAMES.join(", ")}`);
|
|
16747
18018
|
throw new Error(`Unknown template: ${opts.template}`);
|
|
16748
18019
|
}
|
|
16749
|
-
const projectName =
|
|
18020
|
+
const projectName = basename4(projectRoot);
|
|
16750
18021
|
const res = copyTemplateConfig(opts.template, configPath, projectName);
|
|
16751
18022
|
if (!res.success) {
|
|
16752
18023
|
errLog(`error: template copy failed: ${res.error}`);
|
|
@@ -16805,7 +18076,25 @@ async function runInit(argv, overrides) {
|
|
|
16805
18076
|
}
|
|
16806
18077
|
}
|
|
16807
18078
|
const baseConfig = buildConfigFromDetection({ projectRoot, detection });
|
|
16808
|
-
const
|
|
18079
|
+
const withVariant = applyVariantTemplate(baseConfig, resolveTemplatesDir());
|
|
18080
|
+
let config = withVariant;
|
|
18081
|
+
if (!opts.skipIntrospect) {
|
|
18082
|
+
try {
|
|
18083
|
+
const { introspectAsync: introspectAsync2 } = await Promise.resolve().then(() => (init_codebase_introspector(), codebase_introspector_exports));
|
|
18084
|
+
const introspected = await introspectAsync2(detection, projectRoot);
|
|
18085
|
+
const detectedBlocks = {};
|
|
18086
|
+
for (const [key, block] of Object.entries(introspected)) {
|
|
18087
|
+
if (block && typeof block === "object" && "_confidence" in block) {
|
|
18088
|
+
detectedBlocks[key] = block;
|
|
18089
|
+
}
|
|
18090
|
+
}
|
|
18091
|
+
if (Object.keys(detectedBlocks).length > 0) {
|
|
18092
|
+
config = { ...config, detected: detectedBlocks };
|
|
18093
|
+
}
|
|
18094
|
+
} catch (err2) {
|
|
18095
|
+
errLog(`warning: AST adapter introspection failed: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
18096
|
+
}
|
|
18097
|
+
}
|
|
16809
18098
|
const content = renderConfigYaml(config);
|
|
16810
18099
|
const writeRes = writeConfigAtomic(configPath, content);
|
|
16811
18100
|
if (!writeRes.validated) {
|
|
@@ -16849,7 +18138,7 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
|
|
|
16849
18138
|
const stackResolved = !emptyStack && commandStats && (commandStats.installed > 0 || commandStats.updated > 0 || commandStats.kept > 0);
|
|
16850
18139
|
if (!stackResolved) {
|
|
16851
18140
|
const placeholderPath = resolve6(cmdResult.claudeDir, "commands", "_massu-needs-stack.md");
|
|
16852
|
-
if (!
|
|
18141
|
+
if (!existsSync10(placeholderPath)) {
|
|
16853
18142
|
const placeholderBody = [
|
|
16854
18143
|
"# Massu \u2014 stack not yet detected",
|
|
16855
18144
|
"",
|
|
@@ -16870,8 +18159,8 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
|
|
|
16870
18159
|
"\u2014 Massu"
|
|
16871
18160
|
].join("\n");
|
|
16872
18161
|
try {
|
|
16873
|
-
|
|
16874
|
-
|
|
18162
|
+
mkdirSync4(resolve6(cmdResult.claudeDir, "commands"), { recursive: true });
|
|
18163
|
+
writeFileSync2(placeholderPath, placeholderBody, "utf-8");
|
|
16875
18164
|
log(" Wrote _massu-needs-stack.md placeholder (no stack detected yet)");
|
|
16876
18165
|
} catch {
|
|
16877
18166
|
}
|
|
@@ -16890,8 +18179,8 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
|
|
|
16890
18179
|
(async () => {
|
|
16891
18180
|
try {
|
|
16892
18181
|
const encodedRoot = projectRoot.replace(/\//g, "-");
|
|
16893
|
-
const memoryDir = resolve6(
|
|
16894
|
-
const memFiles =
|
|
18182
|
+
const memoryDir = resolve6(homedir3(), ".claude", "projects", encodedRoot, "memory");
|
|
18183
|
+
const memFiles = existsSync10(memoryDir) ? readdirSync10(memoryDir).filter((f2) => f2.endsWith(".md") && f2 !== "MEMORY.md") : [];
|
|
16895
18184
|
if (memFiles.length > 0) {
|
|
16896
18185
|
const { getMemoryDb: getMemoryDb2 } = await Promise.resolve().then(() => (init_memory_db(), memory_db_exports));
|
|
16897
18186
|
const db = getMemoryDb2();
|
|
@@ -16951,7 +18240,7 @@ var init_init = __esm({
|
|
|
16951
18240
|
init_detect();
|
|
16952
18241
|
init_drift();
|
|
16953
18242
|
__filename2 = fileURLToPath2(import.meta.url);
|
|
16954
|
-
__dirname2 =
|
|
18243
|
+
__dirname2 = dirname5(__filename2);
|
|
16955
18244
|
FRAMEWORK_TO_TEMPLATE_ID = {
|
|
16956
18245
|
rails: "rails",
|
|
16957
18246
|
phoenix: "phoenix",
|
|
@@ -16973,7 +18262,7 @@ var init_init = __esm({
|
|
|
16973
18262
|
});
|
|
16974
18263
|
|
|
16975
18264
|
// src/license.ts
|
|
16976
|
-
import { createHash as
|
|
18265
|
+
import { createHash as createHash4 } from "crypto";
|
|
16977
18266
|
function tierLevel(tier) {
|
|
16978
18267
|
return TIER_LEVELS[tier] ?? 0;
|
|
16979
18268
|
}
|
|
@@ -16998,7 +18287,7 @@ function annotateToolDefinitions(defs) {
|
|
|
16998
18287
|
});
|
|
16999
18288
|
}
|
|
17000
18289
|
async function validateLicense(apiKey) {
|
|
17001
|
-
const keyHash =
|
|
18290
|
+
const keyHash = createHash4("sha256").update(apiKey).digest("hex");
|
|
17002
18291
|
const memDb = getMemoryDb();
|
|
17003
18292
|
try {
|
|
17004
18293
|
const cached = memDb.prepare(
|
|
@@ -17059,7 +18348,7 @@ async function validateLicense(apiKey) {
|
|
|
17059
18348
|
}
|
|
17060
18349
|
}
|
|
17061
18350
|
function updateLicenseCache(apiKey, tier, validUntil, features = []) {
|
|
17062
|
-
const keyHash =
|
|
18351
|
+
const keyHash = createHash4("sha256").update(apiKey).digest("hex");
|
|
17063
18352
|
const memDb = getMemoryDb();
|
|
17064
18353
|
try {
|
|
17065
18354
|
memDb.prepare(`
|
|
@@ -17267,17 +18556,17 @@ __export(doctor_exports, {
|
|
|
17267
18556
|
runDoctor: () => runDoctor,
|
|
17268
18557
|
runValidateConfig: () => runValidateConfig
|
|
17269
18558
|
});
|
|
17270
|
-
import { existsSync as
|
|
17271
|
-
import { resolve as resolve7, dirname as
|
|
18559
|
+
import { existsSync as existsSync11, readFileSync as readFileSync11, readdirSync as readdirSync11 } from "fs";
|
|
18560
|
+
import { resolve as resolve7, dirname as dirname6 } from "path";
|
|
17272
18561
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
17273
18562
|
import { parse as parseYaml4 } from "yaml";
|
|
17274
18563
|
function checkConfig(projectRoot) {
|
|
17275
18564
|
const configPath = resolve7(projectRoot, "massu.config.yaml");
|
|
17276
|
-
if (!
|
|
18565
|
+
if (!existsSync11(configPath)) {
|
|
17277
18566
|
return { name: "Configuration", status: "fail", detail: "massu.config.yaml not found. Run: npx massu init" };
|
|
17278
18567
|
}
|
|
17279
18568
|
try {
|
|
17280
|
-
const content =
|
|
18569
|
+
const content = readFileSync11(configPath, "utf-8");
|
|
17281
18570
|
const parsed = parseYaml4(content);
|
|
17282
18571
|
if (!parsed || typeof parsed !== "object") {
|
|
17283
18572
|
return { name: "Configuration", status: "fail", detail: "massu.config.yaml is empty or invalid YAML" };
|
|
@@ -17289,11 +18578,11 @@ function checkConfig(projectRoot) {
|
|
|
17289
18578
|
}
|
|
17290
18579
|
function checkMcpServer(projectRoot) {
|
|
17291
18580
|
const mcpPath = getResolvedPaths().mcpJsonPath;
|
|
17292
|
-
if (!
|
|
18581
|
+
if (!existsSync11(mcpPath)) {
|
|
17293
18582
|
return { name: "MCP Server", status: "fail", detail: ".mcp.json not found. Run: npx massu init" };
|
|
17294
18583
|
}
|
|
17295
18584
|
try {
|
|
17296
|
-
const content = JSON.parse(
|
|
18585
|
+
const content = JSON.parse(readFileSync11(mcpPath, "utf-8"));
|
|
17297
18586
|
const servers = content.mcpServers ?? {};
|
|
17298
18587
|
if (!servers.massu) {
|
|
17299
18588
|
return { name: "MCP Server", status: "fail", detail: "massu not registered in .mcp.json. Run: npx massu init" };
|
|
@@ -17305,11 +18594,11 @@ function checkMcpServer(projectRoot) {
|
|
|
17305
18594
|
}
|
|
17306
18595
|
function checkHooksConfig(projectRoot) {
|
|
17307
18596
|
const settingsPath = getResolvedPaths().settingsLocalPath;
|
|
17308
|
-
if (!
|
|
18597
|
+
if (!existsSync11(settingsPath)) {
|
|
17309
18598
|
return { name: "Hooks Config", status: "fail", detail: ".claude/settings.local.json not found. Run: npx massu init" };
|
|
17310
18599
|
}
|
|
17311
18600
|
try {
|
|
17312
|
-
const content = JSON.parse(
|
|
18601
|
+
const content = JSON.parse(readFileSync11(settingsPath, "utf-8"));
|
|
17313
18602
|
if (!content.hooks) {
|
|
17314
18603
|
return { name: "Hooks Config", status: "fail", detail: "No hooks configured. Run: npx massu install-hooks" };
|
|
17315
18604
|
}
|
|
@@ -17335,9 +18624,9 @@ function checkHooksConfig(projectRoot) {
|
|
|
17335
18624
|
function checkHookFiles(projectRoot) {
|
|
17336
18625
|
const nodeModulesHooksDir = resolve7(projectRoot, "node_modules/@massu/core/dist/hooks");
|
|
17337
18626
|
let hooksDir = nodeModulesHooksDir;
|
|
17338
|
-
if (!
|
|
18627
|
+
if (!existsSync11(nodeModulesHooksDir)) {
|
|
17339
18628
|
const devHooksDir = resolve7(__dirname3, "../../dist/hooks");
|
|
17340
|
-
if (
|
|
18629
|
+
if (existsSync11(devHooksDir)) {
|
|
17341
18630
|
hooksDir = devHooksDir;
|
|
17342
18631
|
} else {
|
|
17343
18632
|
return { name: "Hook Files", status: "fail", detail: "Compiled hooks not found. Run: npm install @massu/core" };
|
|
@@ -17345,7 +18634,7 @@ function checkHookFiles(projectRoot) {
|
|
|
17345
18634
|
}
|
|
17346
18635
|
const missing = [];
|
|
17347
18636
|
for (const hookFile of EXPECTED_HOOKS) {
|
|
17348
|
-
if (!
|
|
18637
|
+
if (!existsSync11(resolve7(hooksDir, hookFile))) {
|
|
17349
18638
|
missing.push(hookFile);
|
|
17350
18639
|
}
|
|
17351
18640
|
}
|
|
@@ -17372,7 +18661,7 @@ function checkNodeVersion() {
|
|
|
17372
18661
|
}
|
|
17373
18662
|
async function checkGitRepo(projectRoot) {
|
|
17374
18663
|
const gitDir = resolve7(projectRoot, ".git");
|
|
17375
|
-
if (!
|
|
18664
|
+
if (!existsSync11(gitDir)) {
|
|
17376
18665
|
return { name: "Git Repository", status: "warn", detail: "Not a git repository (optional but recommended)" };
|
|
17377
18666
|
}
|
|
17378
18667
|
try {
|
|
@@ -17390,7 +18679,7 @@ async function checkGitRepo(projectRoot) {
|
|
|
17390
18679
|
}
|
|
17391
18680
|
function checkKnowledgeDb(projectRoot) {
|
|
17392
18681
|
const knowledgeDbPath = getResolvedPaths().memoryDbPath;
|
|
17393
|
-
if (!
|
|
18682
|
+
if (!existsSync11(knowledgeDbPath)) {
|
|
17394
18683
|
return {
|
|
17395
18684
|
name: "Knowledge DB",
|
|
17396
18685
|
status: "warn",
|
|
@@ -17401,7 +18690,7 @@ function checkKnowledgeDb(projectRoot) {
|
|
|
17401
18690
|
}
|
|
17402
18691
|
function checkMemoryDir(_projectRoot2) {
|
|
17403
18692
|
const memoryDir = getResolvedPaths().memoryDir;
|
|
17404
|
-
if (!
|
|
18693
|
+
if (!existsSync11(memoryDir)) {
|
|
17405
18694
|
return {
|
|
17406
18695
|
name: "Memory Directory",
|
|
17407
18696
|
status: "warn",
|
|
@@ -17412,7 +18701,7 @@ function checkMemoryDir(_projectRoot2) {
|
|
|
17412
18701
|
}
|
|
17413
18702
|
function checkShellHooksWired(_projectRoot2) {
|
|
17414
18703
|
const settingsPath = getResolvedPaths().settingsLocalPath;
|
|
17415
|
-
if (!
|
|
18704
|
+
if (!existsSync11(settingsPath)) {
|
|
17416
18705
|
return {
|
|
17417
18706
|
name: "Shell Hooks",
|
|
17418
18707
|
status: "fail",
|
|
@@ -17420,7 +18709,7 @@ function checkShellHooksWired(_projectRoot2) {
|
|
|
17420
18709
|
};
|
|
17421
18710
|
}
|
|
17422
18711
|
try {
|
|
17423
|
-
const content = JSON.parse(
|
|
18712
|
+
const content = JSON.parse(readFileSync11(settingsPath, "utf-8"));
|
|
17424
18713
|
const hooks = content.hooks ?? {};
|
|
17425
18714
|
const hasSessionStart = Array.isArray(hooks.SessionStart) && hooks.SessionStart.length > 0;
|
|
17426
18715
|
const hasPreToolUse = Array.isArray(hooks.PreToolUse) && hooks.PreToolUse.length > 0;
|
|
@@ -17472,7 +18761,7 @@ function checkPythonHealth(projectRoot) {
|
|
|
17472
18761
|
const config = getConfig();
|
|
17473
18762
|
if (!config.python?.root) return null;
|
|
17474
18763
|
const pythonRoot = resolve7(projectRoot, config.python.root);
|
|
17475
|
-
if (!
|
|
18764
|
+
if (!existsSync11(pythonRoot)) {
|
|
17476
18765
|
return {
|
|
17477
18766
|
name: "Python",
|
|
17478
18767
|
status: "fail",
|
|
@@ -17486,15 +18775,15 @@ function checkPythonHealth(projectRoot) {
|
|
|
17486
18775
|
function scanDir(dir, depth) {
|
|
17487
18776
|
if (depth > 5) return;
|
|
17488
18777
|
try {
|
|
17489
|
-
const entries =
|
|
18778
|
+
const entries = readdirSync11(dir, { withFileTypes: true });
|
|
17490
18779
|
for (const entry of entries) {
|
|
17491
18780
|
if (entry.isDirectory()) {
|
|
17492
18781
|
const excludeDirs = config.python?.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
|
|
17493
18782
|
if (!excludeDirs.includes(entry.name)) {
|
|
17494
18783
|
const subdir = resolve7(dir, entry.name);
|
|
17495
|
-
if (depth <= 2 && !
|
|
18784
|
+
if (depth <= 2 && !existsSync11(resolve7(subdir, "__init__.py"))) {
|
|
17496
18785
|
try {
|
|
17497
|
-
const subEntries =
|
|
18786
|
+
const subEntries = readdirSync11(subdir);
|
|
17498
18787
|
if (subEntries.some((f2) => f2.endsWith(".py") && f2 !== "__init__.py")) {
|
|
17499
18788
|
initPyMissing.push(entry.name);
|
|
17500
18789
|
}
|
|
@@ -17588,14 +18877,14 @@ async function runDoctor() {
|
|
|
17588
18877
|
async function runValidateConfig() {
|
|
17589
18878
|
const projectRoot = process.cwd();
|
|
17590
18879
|
const configPath = resolve7(projectRoot, "massu.config.yaml");
|
|
17591
|
-
if (!
|
|
18880
|
+
if (!existsSync11(configPath)) {
|
|
17592
18881
|
console.error("Error: massu.config.yaml not found in current directory");
|
|
17593
18882
|
console.error("Run: npx massu init");
|
|
17594
18883
|
process.exit(1);
|
|
17595
18884
|
return;
|
|
17596
18885
|
}
|
|
17597
18886
|
try {
|
|
17598
|
-
const content =
|
|
18887
|
+
const content = readFileSync11(configPath, "utf-8");
|
|
17599
18888
|
const parsed = parseYaml4(content);
|
|
17600
18889
|
if (!parsed || typeof parsed !== "object") {
|
|
17601
18890
|
console.error("Error: massu.config.yaml is empty or not a valid YAML object");
|
|
@@ -17634,7 +18923,7 @@ var init_doctor = __esm({
|
|
|
17634
18923
|
init_config();
|
|
17635
18924
|
init_license();
|
|
17636
18925
|
__filename3 = fileURLToPath3(import.meta.url);
|
|
17637
|
-
__dirname3 =
|
|
18926
|
+
__dirname3 = dirname6(__filename3);
|
|
17638
18927
|
EXPECTED_HOOKS = [
|
|
17639
18928
|
"session-start.js",
|
|
17640
18929
|
"session-end.js",
|
|
@@ -17680,7 +18969,7 @@ var show_template_exports = {};
|
|
|
17680
18969
|
__export(show_template_exports, {
|
|
17681
18970
|
runShowTemplate: () => runShowTemplate
|
|
17682
18971
|
});
|
|
17683
|
-
import { existsSync as
|
|
18972
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12 } from "fs";
|
|
17684
18973
|
import { resolve as resolve8 } from "path";
|
|
17685
18974
|
function normalizeBaseName(input) {
|
|
17686
18975
|
return input.endsWith(".md") ? input.slice(0, -".md".length) : input;
|
|
@@ -17710,13 +18999,13 @@ async function runShowTemplate(args3) {
|
|
|
17710
18999
|
}
|
|
17711
19000
|
const suffix = choice.kind === "hit" ? choice.suffix : "";
|
|
17712
19001
|
const file = suffix === "" ? resolve8(sourceDir, `${baseName}.md`) : resolve8(sourceDir, `${baseName}${suffix}.md`);
|
|
17713
|
-
if (!
|
|
19002
|
+
if (!existsSync12(file)) {
|
|
17714
19003
|
process.stderr.write(`massu: resolved template "${file}" no longer exists
|
|
17715
19004
|
`);
|
|
17716
19005
|
process.exit(1);
|
|
17717
19006
|
return;
|
|
17718
19007
|
}
|
|
17719
|
-
process.stdout.write(
|
|
19008
|
+
process.stdout.write(readFileSync12(file, "utf-8"));
|
|
17720
19009
|
}
|
|
17721
19010
|
var init_show_template = __esm({
|
|
17722
19011
|
"src/commands/show-template.ts"() {
|
|
@@ -17769,12 +19058,12 @@ var init_passthrough = __esm({
|
|
|
17769
19058
|
});
|
|
17770
19059
|
|
|
17771
19060
|
// src/lib/fileLock.ts
|
|
17772
|
-
import { mkdirSync as
|
|
17773
|
-
import { dirname as
|
|
19061
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync13, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
19062
|
+
import { dirname as dirname7 } from "path";
|
|
17774
19063
|
import * as lockfile from "proper-lockfile";
|
|
17775
19064
|
function readLockHolderPid(lockPath) {
|
|
17776
19065
|
try {
|
|
17777
|
-
const raw =
|
|
19066
|
+
const raw = readFileSync13(`${lockPath}.pid`, "utf-8").trim();
|
|
17778
19067
|
const pid = Number.parseInt(raw, 10);
|
|
17779
19068
|
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
17780
19069
|
return pid;
|
|
@@ -17793,7 +19082,7 @@ function busyWaitSync(ms) {
|
|
|
17793
19082
|
Atomics.wait(view, 0, 0, ms);
|
|
17794
19083
|
}
|
|
17795
19084
|
function withFileLockSync(lockPath, fn, opts = {}) {
|
|
17796
|
-
|
|
19085
|
+
mkdirSync5(dirname7(lockPath), { recursive: true });
|
|
17797
19086
|
const staleMs = opts.staleMs ?? 3e4;
|
|
17798
19087
|
const blockMs = opts.retries === 0 ? 0 : opts.blockMs ?? 3e4;
|
|
17799
19088
|
const pollIntervalMs = opts.pollIntervalMs ?? 100;
|
|
@@ -17810,7 +19099,7 @@ function withFileLockSync(lockPath, fn, opts = {}) {
|
|
|
17810
19099
|
realpath: false
|
|
17811
19100
|
});
|
|
17812
19101
|
try {
|
|
17813
|
-
|
|
19102
|
+
writeFileSync3(`${lockPath}.pid`, String(process.pid), "utf-8");
|
|
17814
19103
|
} catch {
|
|
17815
19104
|
}
|
|
17816
19105
|
break;
|
|
@@ -17899,7 +19188,7 @@ __export(config_refresh_exports, {
|
|
|
17899
19188
|
mergeRefresh: () => mergeRefresh,
|
|
17900
19189
|
runConfigRefresh: () => runConfigRefresh
|
|
17901
19190
|
});
|
|
17902
|
-
import { existsSync as
|
|
19191
|
+
import { existsSync as existsSync13, readFileSync as readFileSync14, rmSync as rmSync4 } from "fs";
|
|
17903
19192
|
import { resolve as resolve10 } from "path";
|
|
17904
19193
|
import { parse as parseYaml5 } from "yaml";
|
|
17905
19194
|
function flatten(obj, prefix3 = "") {
|
|
@@ -18031,14 +19320,14 @@ async function runConfigRefresh(opts = {}) {
|
|
|
18031
19320
|
const configPath = resolve10(cwd, "massu.config.yaml");
|
|
18032
19321
|
const log = opts.silent ? () => {
|
|
18033
19322
|
} : (s) => process.stdout.write(s);
|
|
18034
|
-
if (!
|
|
19323
|
+
if (!existsSync13(configPath)) {
|
|
18035
19324
|
const message = "massu.config.yaml not found. Run: npx massu init";
|
|
18036
19325
|
if (!opts.silent) process.stderr.write(message + "\n");
|
|
18037
19326
|
return { exitCode: 1, applied: false, dryRun: !!opts.dryRun, diff: [], message };
|
|
18038
19327
|
}
|
|
18039
19328
|
let existing;
|
|
18040
19329
|
try {
|
|
18041
|
-
const content =
|
|
19330
|
+
const content = readFileSync14(configPath, "utf-8");
|
|
18042
19331
|
const parsed = parseYaml5(content);
|
|
18043
19332
|
if (!parsed || typeof parsed !== "object") {
|
|
18044
19333
|
throw new Error("config is not a YAML object");
|
|
@@ -18112,7 +19401,7 @@ async function runConfigRefresh(opts = {}) {
|
|
|
18112
19401
|
const stackResolved = installResult.totalInstalled > 0 || installResult.totalUpdated > 0;
|
|
18113
19402
|
if (stackResolved) {
|
|
18114
19403
|
const placeholderPath = resolve10(installResult.claudeDir, "commands", "_massu-needs-stack.md");
|
|
18115
|
-
if (
|
|
19404
|
+
if (existsSync13(placeholderPath)) {
|
|
18116
19405
|
try {
|
|
18117
19406
|
rmSync4(placeholderPath, { force: true });
|
|
18118
19407
|
log("Removed _massu-needs-stack.md (stack now declared).\n");
|
|
@@ -18221,14 +19510,14 @@ var init_gitToplevel = __esm({
|
|
|
18221
19510
|
});
|
|
18222
19511
|
|
|
18223
19512
|
// src/watch/lockfile-detector.ts
|
|
18224
|
-
import { existsSync as
|
|
19513
|
+
import { existsSync as existsSync14, statSync as statSync8 } from "fs";
|
|
18225
19514
|
import { resolve as resolve11 } from "path";
|
|
18226
19515
|
function lockfileMidWrite(projectRoot, now = Date.now(), windowMs = LOCKFILE_WINDOW_MS) {
|
|
18227
19516
|
for (const lf of KNOWN_LOCKFILES) {
|
|
18228
19517
|
const p19 = resolve11(projectRoot, lf);
|
|
18229
|
-
if (!
|
|
19518
|
+
if (!existsSync14(p19)) continue;
|
|
18230
19519
|
try {
|
|
18231
|
-
const stat =
|
|
19520
|
+
const stat = statSync8(p19);
|
|
18232
19521
|
const delta = now - stat.mtimeMs;
|
|
18233
19522
|
if (delta >= 0 && delta < windowMs) return true;
|
|
18234
19523
|
} catch {
|
|
@@ -18239,7 +19528,7 @@ function lockfileMidWrite(projectRoot, now = Date.now(), windowMs = LOCKFILE_WIN
|
|
|
18239
19528
|
function gitMidOperation(projectRoot) {
|
|
18240
19529
|
const sentinels = ["MERGE_HEAD", "REBASE_HEAD", "CHERRY_PICK_HEAD", "rebase-apply", "rebase-merge"];
|
|
18241
19530
|
for (const s of sentinels) {
|
|
18242
|
-
if (
|
|
19531
|
+
if (existsSync14(resolve11(projectRoot, ".git", s))) return true;
|
|
18243
19532
|
}
|
|
18244
19533
|
return false;
|
|
18245
19534
|
}
|
|
@@ -18436,8 +19725,8 @@ var init_paths = __esm({
|
|
|
18436
19725
|
});
|
|
18437
19726
|
|
|
18438
19727
|
// src/watch/state.ts
|
|
18439
|
-
import { closeSync as closeSync3, existsSync as
|
|
18440
|
-
import { dirname as
|
|
19728
|
+
import { closeSync as closeSync3, existsSync as existsSync15, fsyncSync as fsyncSync3, mkdirSync as mkdirSync6, openSync as openSync3, readFileSync as readFileSync15, renameSync as renameSync4, rmSync as rmSync5, writeFileSync as writeFileSync4, writeSync as writeSync3 } from "fs";
|
|
19729
|
+
import { dirname as dirname8, resolve as resolve12 } from "path";
|
|
18441
19730
|
function watchStatePath(projectRoot) {
|
|
18442
19731
|
return resolve12(projectRoot, ".massu", "watch-state.json");
|
|
18443
19732
|
}
|
|
@@ -18446,8 +19735,8 @@ function backupStatePath(projectRoot) {
|
|
|
18446
19735
|
}
|
|
18447
19736
|
function readState(projectRoot) {
|
|
18448
19737
|
const path = watchStatePath(projectRoot);
|
|
18449
|
-
if (!
|
|
18450
|
-
const content =
|
|
19738
|
+
if (!existsSync15(path)) return { ...DEFAULT_STATE };
|
|
19739
|
+
const content = readFileSync15(path, "utf-8");
|
|
18451
19740
|
let raw;
|
|
18452
19741
|
try {
|
|
18453
19742
|
raw = JSON.parse(content);
|
|
@@ -18489,14 +19778,14 @@ function readState(projectRoot) {
|
|
|
18489
19778
|
}
|
|
18490
19779
|
function archiveCorrupt(projectRoot, content) {
|
|
18491
19780
|
const bak = backupStatePath(projectRoot);
|
|
18492
|
-
|
|
18493
|
-
|
|
19781
|
+
mkdirSync6(dirname8(bak), { recursive: true });
|
|
19782
|
+
writeFileSync4(bak, content, "utf-8");
|
|
18494
19783
|
}
|
|
18495
19784
|
function writeStateAtomic(projectRoot, state) {
|
|
18496
19785
|
const path = watchStatePath(projectRoot);
|
|
18497
19786
|
writeStateAtomicCounter = writeStateAtomicCounter + 1 >>> 0;
|
|
18498
19787
|
const tmp = `${path}.${process.pid}.${writeStateAtomicCounter}.tmp`;
|
|
18499
|
-
|
|
19788
|
+
mkdirSync6(dirname8(path), { recursive: true });
|
|
18500
19789
|
let renamed = false;
|
|
18501
19790
|
try {
|
|
18502
19791
|
const fd = openSync3(tmp, "w");
|
|
@@ -18507,10 +19796,10 @@ function writeStateAtomic(projectRoot, state) {
|
|
|
18507
19796
|
} finally {
|
|
18508
19797
|
closeSync3(fd);
|
|
18509
19798
|
}
|
|
18510
|
-
|
|
19799
|
+
renameSync4(tmp, path);
|
|
18511
19800
|
renamed = true;
|
|
18512
19801
|
} finally {
|
|
18513
|
-
if (!renamed &&
|
|
19802
|
+
if (!renamed && existsSync15(tmp)) {
|
|
18514
19803
|
try {
|
|
18515
19804
|
rmSync5(tmp, { force: true });
|
|
18516
19805
|
} catch {
|
|
@@ -18790,8 +20079,8 @@ __export(watch_exports, {
|
|
|
18790
20079
|
runWatch: () => runWatch
|
|
18791
20080
|
});
|
|
18792
20081
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
18793
|
-
import { basename as
|
|
18794
|
-
import { appendFileSync, existsSync as
|
|
20082
|
+
import { basename as basename5, dirname as dirname9, resolve as resolve13 } from "path";
|
|
20083
|
+
import { appendFileSync, existsSync as existsSync16, mkdirSync as mkdirSync7, readFileSync as readFileSync16 } from "fs";
|
|
18795
20084
|
function parseFlags(args3) {
|
|
18796
20085
|
const out2 = {
|
|
18797
20086
|
foreground: false,
|
|
@@ -18817,7 +20106,7 @@ function parseFlags(args3) {
|
|
|
18817
20106
|
function findClaudeBg() {
|
|
18818
20107
|
const home = process.env.HOME ?? "";
|
|
18819
20108
|
const fixed = home ? resolve13(home, ".claude", "bin", "claude-bg") : null;
|
|
18820
|
-
if (fixed &&
|
|
20109
|
+
if (fixed && existsSync16(fixed)) return fixed;
|
|
18821
20110
|
const which = spawnSync3("which", ["claude-bg"], { encoding: "utf-8" });
|
|
18822
20111
|
if (which.status === 0 && which.stdout) {
|
|
18823
20112
|
const p19 = which.stdout.trim();
|
|
@@ -18826,7 +20115,7 @@ function findClaudeBg() {
|
|
|
18826
20115
|
return null;
|
|
18827
20116
|
}
|
|
18828
20117
|
function watchName(root) {
|
|
18829
|
-
return `massu-watch-${
|
|
20118
|
+
return `massu-watch-${basename5(root)}`;
|
|
18830
20119
|
}
|
|
18831
20120
|
function printHelp(out2) {
|
|
18832
20121
|
out2(`
|
|
@@ -18878,7 +20167,7 @@ async function runWatch(args3) {
|
|
|
18878
20167
|
}
|
|
18879
20168
|
function runStatus(root) {
|
|
18880
20169
|
const path = watchStatePath(root);
|
|
18881
|
-
if (!
|
|
20170
|
+
if (!existsSync16(path)) {
|
|
18882
20171
|
process.stdout.write("massu watch: not running (no state file)\n");
|
|
18883
20172
|
return { exitCode: 0 };
|
|
18884
20173
|
}
|
|
@@ -19036,18 +20325,18 @@ function refreshLogPath(projectRoot) {
|
|
|
19036
20325
|
function appendRefreshLog(projectRoot, event) {
|
|
19037
20326
|
const path = refreshLogPath(projectRoot);
|
|
19038
20327
|
try {
|
|
19039
|
-
|
|
20328
|
+
mkdirSync7(dirname9(path), { recursive: true });
|
|
19040
20329
|
appendFileSync(path, JSON.stringify(event) + "\n", "utf-8");
|
|
19041
20330
|
} catch {
|
|
19042
20331
|
}
|
|
19043
20332
|
}
|
|
19044
20333
|
function readRefreshLog(projectRoot, limit = 10, opts = {}) {
|
|
19045
20334
|
const path = refreshLogPath(projectRoot);
|
|
19046
|
-
if (!
|
|
20335
|
+
if (!existsSync16(path)) return [];
|
|
19047
20336
|
const warn = opts.warn ?? ((s) => {
|
|
19048
20337
|
process.stderr.write(s);
|
|
19049
20338
|
});
|
|
19050
|
-
const lines =
|
|
20339
|
+
const lines = readFileSync16(path, "utf-8").split("\n").filter(Boolean);
|
|
19051
20340
|
const tail = lines.slice(-limit);
|
|
19052
20341
|
const out2 = [];
|
|
19053
20342
|
let corrupt = 0;
|
|
@@ -19118,28 +20407,28 @@ var init_refresh_log = __esm({
|
|
|
19118
20407
|
|
|
19119
20408
|
// src/security/atomic-write.ts
|
|
19120
20409
|
import {
|
|
19121
|
-
chmodSync as
|
|
20410
|
+
chmodSync as chmodSync3,
|
|
19122
20411
|
closeSync as closeSync4,
|
|
19123
|
-
existsSync as
|
|
20412
|
+
existsSync as existsSync17,
|
|
19124
20413
|
fsyncSync as fsyncSync4,
|
|
19125
|
-
mkdirSync as
|
|
20414
|
+
mkdirSync as mkdirSync8,
|
|
19126
20415
|
openSync as openSync4,
|
|
19127
|
-
renameSync as
|
|
20416
|
+
renameSync as renameSync5,
|
|
19128
20417
|
rmSync as rmSync6,
|
|
19129
|
-
statSync as
|
|
20418
|
+
statSync as statSync9,
|
|
19130
20419
|
writeSync as writeSync4
|
|
19131
20420
|
} from "node:fs";
|
|
19132
|
-
import { dirname as
|
|
20421
|
+
import { dirname as dirname10 } from "node:path";
|
|
19133
20422
|
function atomicWrite(path, content, opts = {}) {
|
|
19134
20423
|
const tmpPath = `${path}.tmp`;
|
|
19135
|
-
const parentDir =
|
|
20424
|
+
const parentDir = dirname10(path);
|
|
19136
20425
|
try {
|
|
19137
|
-
if (!
|
|
20426
|
+
if (!existsSync17(parentDir)) {
|
|
19138
20427
|
const mkdirOpts = { recursive: true };
|
|
19139
20428
|
if (opts.ensureParentDirMode !== void 0) {
|
|
19140
20429
|
mkdirOpts.mode = opts.ensureParentDirMode;
|
|
19141
20430
|
}
|
|
19142
|
-
|
|
20431
|
+
mkdirSync8(parentDir, mkdirOpts);
|
|
19143
20432
|
}
|
|
19144
20433
|
const buf = typeof content === "string" ? Buffer.from(content, "utf-8") : content;
|
|
19145
20434
|
const openMode = opts.mode ?? 420;
|
|
@@ -19151,12 +20440,12 @@ function atomicWrite(path, content, opts = {}) {
|
|
|
19151
20440
|
closeSync4(fd);
|
|
19152
20441
|
}
|
|
19153
20442
|
if (opts.mode !== void 0) {
|
|
19154
|
-
|
|
20443
|
+
chmodSync3(tmpPath, opts.mode);
|
|
19155
20444
|
}
|
|
19156
|
-
|
|
20445
|
+
renameSync5(tmpPath, path);
|
|
19157
20446
|
return { written: true };
|
|
19158
20447
|
} catch (err2) {
|
|
19159
|
-
if (
|
|
20448
|
+
if (existsSync17(tmpPath)) {
|
|
19160
20449
|
try {
|
|
19161
20450
|
rmSync6(tmpPath, { force: true });
|
|
19162
20451
|
} catch {
|
|
@@ -21460,9 +22749,9 @@ var init_registry_pubkey_generated = __esm({
|
|
|
21460
22749
|
});
|
|
21461
22750
|
|
|
21462
22751
|
// src/security/adapter-verifier.ts
|
|
21463
|
-
import { createHash as
|
|
22752
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
21464
22753
|
function sha256Hex(bytes) {
|
|
21465
|
-
return
|
|
22754
|
+
return createHash5("sha256").update(bytes).digest("hex");
|
|
21466
22755
|
}
|
|
21467
22756
|
function reviver(key, value) {
|
|
21468
22757
|
if (key === "__proto__" || key === "constructor" || key === "prototype") {
|
|
@@ -21724,24 +23013,24 @@ var init_fetcher = __esm({
|
|
|
21724
23013
|
});
|
|
21725
23014
|
|
|
21726
23015
|
// src/security/manifest-cache.ts
|
|
21727
|
-
import { existsSync as
|
|
21728
|
-
import { homedir as
|
|
23016
|
+
import { existsSync as existsSync18, readFileSync as readFileSync17, statSync as statSync10 } from "node:fs";
|
|
23017
|
+
import { homedir as homedir4 } from "node:os";
|
|
21729
23018
|
import { resolve as resolve14 } from "node:path";
|
|
21730
23019
|
import { z as z4 } from "zod";
|
|
21731
23020
|
function defaultCachePaths() {
|
|
21732
|
-
const dir = resolve14(
|
|
23021
|
+
const dir = resolve14(homedir4(), ".massu");
|
|
21733
23022
|
return {
|
|
21734
23023
|
cachePath: resolve14(dir, "adapter-manifest.json"),
|
|
21735
23024
|
lockPath: resolve14(dir, ".adapter-manifest.lock")
|
|
21736
23025
|
};
|
|
21737
23026
|
}
|
|
21738
23027
|
function loadCachedManifest(paths = defaultCachePaths()) {
|
|
21739
|
-
if (!
|
|
23028
|
+
if (!existsSync18(paths.cachePath)) {
|
|
21740
23029
|
return { kind: "absent" };
|
|
21741
23030
|
}
|
|
21742
23031
|
let raw;
|
|
21743
23032
|
try {
|
|
21744
|
-
const content =
|
|
23033
|
+
const content = readFileSync17(paths.cachePath, "utf-8");
|
|
21745
23034
|
raw = JSON.parse(content);
|
|
21746
23035
|
} catch (err2) {
|
|
21747
23036
|
return {
|
|
@@ -21930,10 +23219,10 @@ var init_adapter_origin = __esm({
|
|
|
21930
23219
|
});
|
|
21931
23220
|
|
|
21932
23221
|
// src/security/local-fingerprint.ts
|
|
21933
|
-
import { existsSync as
|
|
21934
|
-
import { homedir as
|
|
23222
|
+
import { existsSync as existsSync19, readFileSync as readFileSync18, lstatSync as lstatSync5 } from "node:fs";
|
|
23223
|
+
import { homedir as homedir5 } from "node:os";
|
|
21935
23224
|
import { resolve as resolve15, isAbsolute } from "node:path";
|
|
21936
|
-
import { createHash as
|
|
23225
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
21937
23226
|
import { z as z5 } from "zod";
|
|
21938
23227
|
function computeLocalFingerprint(localPaths, projectRoot) {
|
|
21939
23228
|
const tuples = [];
|
|
@@ -21941,13 +23230,13 @@ function computeLocalFingerprint(localPaths, projectRoot) {
|
|
|
21941
23230
|
const abs = isAbsolute(p19) ? p19 : resolve15(projectRoot, p19);
|
|
21942
23231
|
let contentTag;
|
|
21943
23232
|
try {
|
|
21944
|
-
const lst =
|
|
23233
|
+
const lst = lstatSync5(abs);
|
|
21945
23234
|
if (lst.isSymbolicLink()) {
|
|
21946
23235
|
contentTag = "<symlink>";
|
|
21947
23236
|
} else if (!lst.isFile()) {
|
|
21948
23237
|
contentTag = "<not-a-file>";
|
|
21949
23238
|
} else {
|
|
21950
|
-
contentTag =
|
|
23239
|
+
contentTag = createHash6("sha256").update(readFileSync18(abs)).digest("hex");
|
|
21951
23240
|
}
|
|
21952
23241
|
} catch {
|
|
21953
23242
|
contentTag = "<missing>";
|
|
@@ -21956,13 +23245,13 @@ function computeLocalFingerprint(localPaths, projectRoot) {
|
|
|
21956
23245
|
}
|
|
21957
23246
|
tuples.sort((a2, b2) => a2.path < b2.path ? -1 : a2.path > b2.path ? 1 : 0);
|
|
21958
23247
|
const canonical = JSON.stringify(tuples);
|
|
21959
|
-
return
|
|
23248
|
+
return createHash6("sha256").update(canonical).digest("hex");
|
|
21960
23249
|
}
|
|
21961
23250
|
function readFingerprintSentinel(path = FINGERPRINT_PATH) {
|
|
21962
|
-
if (!
|
|
23251
|
+
if (!existsSync19(path)) return null;
|
|
21963
23252
|
let raw;
|
|
21964
23253
|
try {
|
|
21965
|
-
raw = JSON.parse(
|
|
23254
|
+
raw = JSON.parse(readFileSync18(path, "utf-8"));
|
|
21966
23255
|
} catch {
|
|
21967
23256
|
return null;
|
|
21968
23257
|
}
|
|
@@ -22010,7 +23299,7 @@ var init_local_fingerprint = __esm({
|
|
|
22010
23299
|
"use strict";
|
|
22011
23300
|
init_atomic_write();
|
|
22012
23301
|
init_manifest_schema();
|
|
22013
|
-
FINGERPRINT_PATH = resolve15(
|
|
23302
|
+
FINGERPRINT_PATH = resolve15(homedir5(), ".massu", "adapters-local-fingerprint.json");
|
|
22014
23303
|
FingerprintSentinelSchema = z5.object({
|
|
22015
23304
|
fingerprint: z5.string().regex(/^[0-9a-f]{64}$/),
|
|
22016
23305
|
source: z5.enum(["cli", "cli-resync"]),
|
|
@@ -22026,15 +23315,15 @@ var init_local_fingerprint = __esm({
|
|
|
22026
23315
|
});
|
|
22027
23316
|
|
|
22028
23317
|
// src/security/install-tracking.ts
|
|
22029
|
-
import { readFileSync as
|
|
22030
|
-
import { join as
|
|
22031
|
-
import { homedir as
|
|
23318
|
+
import { readFileSync as readFileSync19, readdirSync as readdirSync12, lstatSync as lstatSync6, existsSync as existsSync20 } from "node:fs";
|
|
23319
|
+
import { join as join10, relative as relative5, sep } from "node:path";
|
|
23320
|
+
import { homedir as homedir6 } from "node:os";
|
|
22032
23321
|
import { resolve as resolve16 } from "node:path";
|
|
22033
|
-
import { createHash as
|
|
23322
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
22034
23323
|
import { z as z6 } from "zod";
|
|
22035
23324
|
function containsHiddenDirs(packageDir) {
|
|
22036
23325
|
for (const hidden of EXCLUDED_DIR_NAMES) {
|
|
22037
|
-
if (
|
|
23326
|
+
if (existsSync20(`${packageDir}/${hidden}`)) {
|
|
22038
23327
|
return hidden;
|
|
22039
23328
|
}
|
|
22040
23329
|
}
|
|
@@ -22046,15 +23335,15 @@ function sha256OfDir(dir, opts = {}) {
|
|
|
22046
23335
|
function walk(currentDir) {
|
|
22047
23336
|
let entries;
|
|
22048
23337
|
try {
|
|
22049
|
-
entries =
|
|
23338
|
+
entries = readdirSync12(currentDir);
|
|
22050
23339
|
} catch {
|
|
22051
23340
|
return;
|
|
22052
23341
|
}
|
|
22053
23342
|
for (const entry of entries.sort()) {
|
|
22054
|
-
const absPath =
|
|
23343
|
+
const absPath = join10(currentDir, entry);
|
|
22055
23344
|
let lst;
|
|
22056
23345
|
try {
|
|
22057
|
-
lst =
|
|
23346
|
+
lst = lstatSync6(absPath);
|
|
22058
23347
|
} catch {
|
|
22059
23348
|
continue;
|
|
22060
23349
|
}
|
|
@@ -22072,15 +23361,15 @@ function sha256OfDir(dir, opts = {}) {
|
|
|
22072
23361
|
`sha256OfDir: file ${absPath} exceeds maxFileBytes (${lst.size} > ${maxFileBytes}); adapter packages should not ship files this large.`
|
|
22073
23362
|
);
|
|
22074
23363
|
}
|
|
22075
|
-
const rel =
|
|
23364
|
+
const rel = relative5(dir, absPath).split(sep).join("/");
|
|
22076
23365
|
files.push({ relativePath: rel, absPath });
|
|
22077
23366
|
}
|
|
22078
23367
|
}
|
|
22079
23368
|
walk(dir);
|
|
22080
23369
|
files.sort((a2, b2) => a2.relativePath < b2.relativePath ? -1 : a2.relativePath > b2.relativePath ? 1 : 0);
|
|
22081
|
-
const top =
|
|
23370
|
+
const top = createHash7("sha256");
|
|
22082
23371
|
for (const f2 of files) {
|
|
22083
|
-
const fileHash =
|
|
23372
|
+
const fileHash = createHash7("sha256").update(readFileSync19(f2.absPath)).digest("hex");
|
|
22084
23373
|
top.update(f2.relativePath, "utf-8");
|
|
22085
23374
|
top.update("\0", "utf-8");
|
|
22086
23375
|
top.update(fileHash, "utf-8");
|
|
@@ -22089,10 +23378,10 @@ function sha256OfDir(dir, opts = {}) {
|
|
|
22089
23378
|
return top.digest("hex");
|
|
22090
23379
|
}
|
|
22091
23380
|
function readInstalledManifest(path = INSTALLED_MANIFEST_PATH) {
|
|
22092
|
-
if (!
|
|
23381
|
+
if (!existsSync20(path)) return {};
|
|
22093
23382
|
let raw;
|
|
22094
23383
|
try {
|
|
22095
|
-
raw = JSON.parse(
|
|
23384
|
+
raw = JSON.parse(readFileSync19(path, "utf-8"));
|
|
22096
23385
|
} catch {
|
|
22097
23386
|
return {};
|
|
22098
23387
|
}
|
|
@@ -22156,7 +23445,7 @@ var init_install_tracking = __esm({
|
|
|
22156
23445
|
"src/security/install-tracking.ts"() {
|
|
22157
23446
|
"use strict";
|
|
22158
23447
|
init_atomic_write();
|
|
22159
|
-
INSTALLED_MANIFEST_PATH = resolve16(
|
|
23448
|
+
INSTALLED_MANIFEST_PATH = resolve16(homedir6(), ".massu", "adapter-manifest-installed.json");
|
|
22160
23449
|
DEFAULT_MAX_FILE_BYTES = 64 * 1024 * 1024;
|
|
22161
23450
|
EXCLUDED_DIR_NAMES = /* @__PURE__ */ new Set([".git", "node_modules", ".cache", ".tmp"]);
|
|
22162
23451
|
InstallEntrySchema = z6.object({
|
|
@@ -22179,18 +23468,18 @@ var init_install_tracking = __esm({
|
|
|
22179
23468
|
});
|
|
22180
23469
|
|
|
22181
23470
|
// src/detect/adapters/discover.ts
|
|
22182
|
-
import { existsSync as
|
|
23471
|
+
import { existsSync as existsSync21, readdirSync as readdirSync13, readFileSync as readFileSync20, lstatSync as lstatSync7 } from "node:fs";
|
|
22183
23472
|
import { resolve as resolve17, isAbsolute as isAbsolute2 } from "node:path";
|
|
22184
23473
|
import { z as z7 } from "zod";
|
|
22185
23474
|
function walkNodeModules(projectRoot, warnings) {
|
|
22186
23475
|
const nodeModulesDir = resolve17(projectRoot, "node_modules");
|
|
22187
|
-
if (!
|
|
23476
|
+
if (!existsSync21(nodeModulesDir)) {
|
|
22188
23477
|
return [];
|
|
22189
23478
|
}
|
|
22190
23479
|
const candidates = [];
|
|
22191
23480
|
let topLevelEntries;
|
|
22192
23481
|
try {
|
|
22193
|
-
topLevelEntries =
|
|
23482
|
+
topLevelEntries = readdirSync13(nodeModulesDir);
|
|
22194
23483
|
} catch (err2) {
|
|
22195
23484
|
warnings.push(`failed to read node_modules: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
22196
23485
|
return [];
|
|
@@ -22200,7 +23489,7 @@ function walkNodeModules(projectRoot, warnings) {
|
|
|
22200
23489
|
const entryPath = resolve17(nodeModulesDir, entry);
|
|
22201
23490
|
let entryStat;
|
|
22202
23491
|
try {
|
|
22203
|
-
entryStat =
|
|
23492
|
+
entryStat = lstatSync7(entryPath);
|
|
22204
23493
|
} catch {
|
|
22205
23494
|
continue;
|
|
22206
23495
|
}
|
|
@@ -22214,7 +23503,7 @@ function walkNodeModules(projectRoot, warnings) {
|
|
|
22214
23503
|
if (entry.startsWith("@")) {
|
|
22215
23504
|
let scopedEntries;
|
|
22216
23505
|
try {
|
|
22217
|
-
scopedEntries =
|
|
23506
|
+
scopedEntries = readdirSync13(entryPath);
|
|
22218
23507
|
} catch {
|
|
22219
23508
|
continue;
|
|
22220
23509
|
}
|
|
@@ -22222,7 +23511,7 @@ function walkNodeModules(projectRoot, warnings) {
|
|
|
22222
23511
|
const subPath = resolve17(entryPath, sub);
|
|
22223
23512
|
let subStat;
|
|
22224
23513
|
try {
|
|
22225
|
-
subStat =
|
|
23514
|
+
subStat = lstatSync7(subPath);
|
|
22226
23515
|
} catch {
|
|
22227
23516
|
continue;
|
|
22228
23517
|
}
|
|
@@ -22239,10 +23528,10 @@ function walkNodeModules(projectRoot, warnings) {
|
|
|
22239
23528
|
}
|
|
22240
23529
|
function tryReadAdapterPackage(packageDir, warnings) {
|
|
22241
23530
|
const pkgJsonPath = resolve17(packageDir, "package.json");
|
|
22242
|
-
if (!
|
|
23531
|
+
if (!existsSync21(pkgJsonPath)) return null;
|
|
22243
23532
|
let raw;
|
|
22244
23533
|
try {
|
|
22245
|
-
raw = JSON.parse(
|
|
23534
|
+
raw = JSON.parse(readFileSync20(pkgJsonPath, "utf-8"));
|
|
22246
23535
|
} catch (err2) {
|
|
22247
23536
|
warnings.push(
|
|
22248
23537
|
`skipping ${packageDir}: package.json parse failed (${err2 instanceof Error ? err2.message : String(err2)})`
|
|
@@ -22377,7 +23666,7 @@ function discoverAdapters(opts) {
|
|
|
22377
23666
|
for (const localPath of opts.configLocalPaths) {
|
|
22378
23667
|
if (seenIds.has(localPath)) continue;
|
|
22379
23668
|
const absPath = isAbsolute2(localPath) ? localPath : resolve17(opts.projectRoot, localPath);
|
|
22380
|
-
if (!
|
|
23669
|
+
if (!existsSync21(absPath)) {
|
|
22381
23670
|
warnings.push(
|
|
22382
23671
|
`local adapter file not found: ${localPath} (resolved to ${absPath}). Remove via: massu adapters remove-local ${localPath}`
|
|
22383
23672
|
);
|
|
@@ -22458,7 +23747,7 @@ __export(adapters_exports, {
|
|
|
22458
23747
|
runAdaptersResyncLocalFingerprint: () => runAdaptersResyncLocalFingerprint,
|
|
22459
23748
|
runAdaptersSearch: () => runAdaptersSearch
|
|
22460
23749
|
});
|
|
22461
|
-
import { existsSync as
|
|
23750
|
+
import { existsSync as existsSync22, readFileSync as readFileSync21 } from "node:fs";
|
|
22462
23751
|
import { resolve as resolve18 } from "node:path";
|
|
22463
23752
|
import { parseDocument } from "yaml";
|
|
22464
23753
|
async function handleAdaptersSubcommand(args3) {
|
|
@@ -22727,7 +24016,7 @@ function mutateLocalArray(mutator, command) {
|
|
|
22727
24016
|
return { exitCode: 2 };
|
|
22728
24017
|
}
|
|
22729
24018
|
const yamlPath = resolve18(projectRoot, "massu.config.yaml");
|
|
22730
|
-
if (!
|
|
24019
|
+
if (!existsSync22(yamlPath)) {
|
|
22731
24020
|
process.stderr.write(
|
|
22732
24021
|
`${command}: massu.config.yaml not found at ${yamlPath}. Run \`massu init\` first.
|
|
22733
24022
|
`
|
|
@@ -22736,7 +24025,7 @@ function mutateLocalArray(mutator, command) {
|
|
|
22736
24025
|
}
|
|
22737
24026
|
let yamlText;
|
|
22738
24027
|
try {
|
|
22739
|
-
yamlText =
|
|
24028
|
+
yamlText = readFileSync21(yamlPath, "utf-8");
|
|
22740
24029
|
} catch (err2) {
|
|
22741
24030
|
process.stderr.write(`${command}: failed to read ${yamlPath}: ${err2 instanceof Error ? err2.message : String(err2)}
|
|
22742
24031
|
`);
|
|
@@ -22827,7 +24116,7 @@ async function runAdaptersInstall(args3) {
|
|
|
22827
24116
|
return { exitCode: 1 };
|
|
22828
24117
|
}
|
|
22829
24118
|
const packageDir = resolve18(projectRoot, "node_modules", ...packageName.split("/"));
|
|
22830
|
-
if (!
|
|
24119
|
+
if (!existsSync22(packageDir)) {
|
|
22831
24120
|
process.stderr.write(
|
|
22832
24121
|
`install: ${packageName} is not installed in node_modules. Run \`npm install ${packageName}\` first.
|
|
22833
24122
|
`
|
|
@@ -22835,14 +24124,14 @@ async function runAdaptersInstall(args3) {
|
|
|
22835
24124
|
return { exitCode: 1 };
|
|
22836
24125
|
}
|
|
22837
24126
|
const pkgJsonPath = resolve18(packageDir, "package.json");
|
|
22838
|
-
if (!
|
|
24127
|
+
if (!existsSync22(pkgJsonPath)) {
|
|
22839
24128
|
process.stderr.write(`install: ${packageName} has no package.json at ${pkgJsonPath}
|
|
22840
24129
|
`);
|
|
22841
24130
|
return { exitCode: 1 };
|
|
22842
24131
|
}
|
|
22843
24132
|
let pkgJson;
|
|
22844
24133
|
try {
|
|
22845
|
-
pkgJson = JSON.parse(
|
|
24134
|
+
pkgJson = JSON.parse(readFileSync21(pkgJsonPath, "utf-8"));
|
|
22846
24135
|
} catch (err2) {
|
|
22847
24136
|
process.stderr.write(`install: ${packageName} has malformed package.json: ${err2 instanceof Error ? err2.message : String(err2)}
|
|
22848
24137
|
`);
|
|
@@ -22962,7 +24251,7 @@ async function runAdaptersResign(_args) {
|
|
|
22962
24251
|
continue;
|
|
22963
24252
|
}
|
|
22964
24253
|
const packageDir = resolve18(projectRoot, "node_modules", ...name2.split("/"));
|
|
22965
|
-
if (!
|
|
24254
|
+
if (!existsSync22(packageDir)) {
|
|
22966
24255
|
removed++;
|
|
22967
24256
|
warnings.push(`${name2}@${entry.version}: not present in node_modules \u2014 REMOVED from sidecar`);
|
|
22968
24257
|
removeInstalledManifestEntry(name2);
|
|
@@ -23049,11 +24338,11 @@ var init_adapters2 = __esm({
|
|
|
23049
24338
|
|
|
23050
24339
|
// src/db.ts
|
|
23051
24340
|
import Database2 from "better-sqlite3";
|
|
23052
|
-
import { dirname as
|
|
23053
|
-
import { existsSync as
|
|
24341
|
+
import { dirname as dirname11, join as join11 } from "path";
|
|
24342
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync9, readdirSync as readdirSync14, statSync as statSync11 } from "fs";
|
|
23054
24343
|
function getCodeGraphDb() {
|
|
23055
24344
|
const dbPath = getResolvedPaths().codegraphDbPath;
|
|
23056
|
-
if (!
|
|
24345
|
+
if (!existsSync23(dbPath)) {
|
|
23057
24346
|
throw new Error(`CodeGraph database not found at ${dbPath}. Run 'npx @colbymchenry/codegraph sync' first.`);
|
|
23058
24347
|
}
|
|
23059
24348
|
const db = new Database2(dbPath, { readonly: true });
|
|
@@ -23062,9 +24351,9 @@ function getCodeGraphDb() {
|
|
|
23062
24351
|
}
|
|
23063
24352
|
function getDataDb() {
|
|
23064
24353
|
const dbPath = getResolvedPaths().dataDbPath;
|
|
23065
|
-
const dir =
|
|
23066
|
-
if (!
|
|
23067
|
-
|
|
24354
|
+
const dir = dirname11(dbPath);
|
|
24355
|
+
if (!existsSync23(dir)) {
|
|
24356
|
+
mkdirSync9(dir, { recursive: true });
|
|
23068
24357
|
}
|
|
23069
24358
|
const db = new Database2(dbPath);
|
|
23070
24359
|
db.pragma("journal_mode = WAL");
|
|
@@ -23334,14 +24623,14 @@ function isPythonDataStale(dataDb2, pythonRoot) {
|
|
|
23334
24623
|
const lastBuildTime = new Date(lastBuild.value).getTime();
|
|
23335
24624
|
function checkDir(dir) {
|
|
23336
24625
|
try {
|
|
23337
|
-
const entries =
|
|
24626
|
+
const entries = readdirSync14(dir, { withFileTypes: true });
|
|
23338
24627
|
for (const entry of entries) {
|
|
23339
|
-
const fullPath =
|
|
24628
|
+
const fullPath = join11(dir, entry.name);
|
|
23340
24629
|
if (entry.isDirectory()) {
|
|
23341
24630
|
if (["__pycache__", ".venv", "venv", "node_modules", ".mypy_cache", ".pytest_cache"].includes(entry.name)) continue;
|
|
23342
24631
|
if (checkDir(fullPath)) return true;
|
|
23343
24632
|
} else if (entry.name.endsWith(".py")) {
|
|
23344
|
-
if (
|
|
24633
|
+
if (statSync11(fullPath).mtimeMs > lastBuildTime) return true;
|
|
23345
24634
|
}
|
|
23346
24635
|
}
|
|
23347
24636
|
} catch {
|
|
@@ -23437,8 +24726,8 @@ var init_rules = __esm({
|
|
|
23437
24726
|
});
|
|
23438
24727
|
|
|
23439
24728
|
// src/import-resolver.ts
|
|
23440
|
-
import { readFileSync as
|
|
23441
|
-
import { resolve as resolve20, dirname as
|
|
24729
|
+
import { readFileSync as readFileSync22, existsSync as existsSync24, statSync as statSync12 } from "fs";
|
|
24730
|
+
import { resolve as resolve20, dirname as dirname12, join as join12 } from "path";
|
|
23442
24731
|
function parseImports(source) {
|
|
23443
24732
|
const imports = [];
|
|
23444
24733
|
const lines = source.split("\n");
|
|
@@ -23496,21 +24785,21 @@ function resolveImportPath(specifier, fromFile) {
|
|
|
23496
24785
|
const paths = getResolvedPaths();
|
|
23497
24786
|
basePath = resolve20(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
|
|
23498
24787
|
} else {
|
|
23499
|
-
basePath = resolve20(
|
|
24788
|
+
basePath = resolve20(dirname12(fromFile), specifier);
|
|
23500
24789
|
}
|
|
23501
|
-
if (
|
|
24790
|
+
if (existsSync24(basePath) && !isDirectory(basePath)) {
|
|
23502
24791
|
return toRelative(basePath);
|
|
23503
24792
|
}
|
|
23504
24793
|
const resolvedPaths = getResolvedPaths();
|
|
23505
24794
|
for (const ext of resolvedPaths.extensions) {
|
|
23506
24795
|
const withExt = basePath + ext;
|
|
23507
|
-
if (
|
|
24796
|
+
if (existsSync24(withExt)) {
|
|
23508
24797
|
return toRelative(withExt);
|
|
23509
24798
|
}
|
|
23510
24799
|
}
|
|
23511
24800
|
for (const indexFile of resolvedPaths.indexFiles) {
|
|
23512
|
-
const indexPath =
|
|
23513
|
-
if (
|
|
24801
|
+
const indexPath = join12(basePath, indexFile);
|
|
24802
|
+
if (existsSync24(indexPath)) {
|
|
23514
24803
|
return toRelative(indexPath);
|
|
23515
24804
|
}
|
|
23516
24805
|
}
|
|
@@ -23518,7 +24807,7 @@ function resolveImportPath(specifier, fromFile) {
|
|
|
23518
24807
|
}
|
|
23519
24808
|
function isDirectory(path) {
|
|
23520
24809
|
try {
|
|
23521
|
-
return
|
|
24810
|
+
return statSync12(path).isDirectory();
|
|
23522
24811
|
} catch {
|
|
23523
24812
|
return false;
|
|
23524
24813
|
}
|
|
@@ -23547,10 +24836,10 @@ function buildImportIndex(dataDb2, codegraphDb2) {
|
|
|
23547
24836
|
let batch = [];
|
|
23548
24837
|
for (const file of files) {
|
|
23549
24838
|
const absPath = ensureWithinRoot(resolve20(projectRoot, file.path), projectRoot);
|
|
23550
|
-
if (!
|
|
24839
|
+
if (!existsSync24(absPath)) continue;
|
|
23551
24840
|
let source;
|
|
23552
24841
|
try {
|
|
23553
|
-
source =
|
|
24842
|
+
source = readFileSync22(absPath, "utf-8");
|
|
23554
24843
|
} catch {
|
|
23555
24844
|
continue;
|
|
23556
24845
|
}
|
|
@@ -23586,15 +24875,15 @@ var init_import_resolver = __esm({
|
|
|
23586
24875
|
});
|
|
23587
24876
|
|
|
23588
24877
|
// src/trpc-index.ts
|
|
23589
|
-
import { readFileSync as
|
|
23590
|
-
import { resolve as resolve21, join as
|
|
24878
|
+
import { readFileSync as readFileSync23, existsSync as existsSync25, readdirSync as readdirSync15 } from "fs";
|
|
24879
|
+
import { resolve as resolve21, join as join13 } from "path";
|
|
23591
24880
|
function parseRootRouter() {
|
|
23592
24881
|
const paths = getResolvedPaths();
|
|
23593
24882
|
const rootPath = paths.rootRouterPath;
|
|
23594
|
-
if (!
|
|
24883
|
+
if (!existsSync25(rootPath)) {
|
|
23595
24884
|
throw new Error(`Root router not found at ${rootPath}`);
|
|
23596
24885
|
}
|
|
23597
|
-
const source =
|
|
24886
|
+
const source = readFileSync23(rootPath, "utf-8");
|
|
23598
24887
|
const mappings = [];
|
|
23599
24888
|
const importMap = /* @__PURE__ */ new Map();
|
|
23600
24889
|
const importRegex = /import\s+\{[^}]*?(\w+Router)[^}]*\}\s+from\s+['"]\.\/routers\/([^'"]+)['"]/g;
|
|
@@ -23606,12 +24895,12 @@ function parseRootRouter() {
|
|
|
23606
24895
|
for (const ext of [".ts", ".tsx", ""]) {
|
|
23607
24896
|
const candidate = fullPath + ext;
|
|
23608
24897
|
const routersRelPath = getConfig().paths.routers ?? "src/server/api/routers";
|
|
23609
|
-
if (
|
|
24898
|
+
if (existsSync25(candidate)) {
|
|
23610
24899
|
filePath = routersRelPath + "/" + filePath + ext;
|
|
23611
24900
|
break;
|
|
23612
24901
|
}
|
|
23613
|
-
const indexCandidate =
|
|
23614
|
-
if (
|
|
24902
|
+
const indexCandidate = join13(fullPath, "index.ts");
|
|
24903
|
+
if (existsSync25(indexCandidate)) {
|
|
23615
24904
|
filePath = routersRelPath + "/" + filePath + "/index.ts";
|
|
23616
24905
|
break;
|
|
23617
24906
|
}
|
|
@@ -23631,8 +24920,8 @@ function parseRootRouter() {
|
|
|
23631
24920
|
}
|
|
23632
24921
|
function extractProcedures(routerFilePath) {
|
|
23633
24922
|
const absPath = resolve21(getProjectRoot(), routerFilePath);
|
|
23634
|
-
if (!
|
|
23635
|
-
const source =
|
|
24923
|
+
if (!existsSync25(absPath)) return [];
|
|
24924
|
+
const source = readFileSync23(absPath, "utf-8");
|
|
23636
24925
|
const procedures = [];
|
|
23637
24926
|
const seen = /* @__PURE__ */ new Set();
|
|
23638
24927
|
const procRegex = /(\w+)\s*:\s*(protected|public)Procedure/g;
|
|
@@ -23661,21 +24950,21 @@ function findUICallSites(routerKey, procedureName) {
|
|
|
23661
24950
|
];
|
|
23662
24951
|
const searchPattern = `api.${routerKey}.${procedureName}`;
|
|
23663
24952
|
for (const dir of searchDirs) {
|
|
23664
|
-
if (!
|
|
24953
|
+
if (!existsSync25(dir)) continue;
|
|
23665
24954
|
searchDirectory(dir, searchPattern, callSites);
|
|
23666
24955
|
}
|
|
23667
24956
|
return callSites;
|
|
23668
24957
|
}
|
|
23669
24958
|
function searchDirectory(dir, pattern, results) {
|
|
23670
|
-
const entries =
|
|
24959
|
+
const entries = readdirSync15(dir, { withFileTypes: true });
|
|
23671
24960
|
for (const entry of entries) {
|
|
23672
|
-
const fullPath =
|
|
24961
|
+
const fullPath = join13(dir, entry.name);
|
|
23673
24962
|
if (entry.isDirectory()) {
|
|
23674
24963
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
23675
24964
|
searchDirectory(fullPath, pattern, results);
|
|
23676
24965
|
} else if (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx")) {
|
|
23677
24966
|
try {
|
|
23678
|
-
const source =
|
|
24967
|
+
const source = readFileSync23(fullPath, "utf-8");
|
|
23679
24968
|
const lines = source.split("\n");
|
|
23680
24969
|
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
23681
24970
|
if (lines[i2].includes(pattern)) {
|
|
@@ -23738,7 +25027,7 @@ var init_trpc_index = __esm({
|
|
|
23738
25027
|
});
|
|
23739
25028
|
|
|
23740
25029
|
// src/page-deps.ts
|
|
23741
|
-
import { readFileSync as
|
|
25030
|
+
import { readFileSync as readFileSync24, existsSync as existsSync26 } from "fs";
|
|
23742
25031
|
import { resolve as resolve22 } from "path";
|
|
23743
25032
|
function deriveRoute(pageFile) {
|
|
23744
25033
|
let route = pageFile.replace(/^src\/app/, "").replace(/\/page\.tsx?$/, "").replace(/\/page\.jsx?$/, "");
|
|
@@ -23778,9 +25067,9 @@ function findRouterCalls(files) {
|
|
|
23778
25067
|
const projectRoot = getProjectRoot();
|
|
23779
25068
|
for (const file of files) {
|
|
23780
25069
|
const absPath = ensureWithinRoot(resolve22(projectRoot, file), projectRoot);
|
|
23781
|
-
if (!
|
|
25070
|
+
if (!existsSync26(absPath)) continue;
|
|
23782
25071
|
try {
|
|
23783
|
-
const source =
|
|
25072
|
+
const source = readFileSync24(absPath, "utf-8");
|
|
23784
25073
|
const apiCallRegex = /api\.(\w+)\.\w+/g;
|
|
23785
25074
|
let match;
|
|
23786
25075
|
while ((match = apiCallRegex.exec(source)) !== null) {
|
|
@@ -23799,9 +25088,9 @@ function findTablesFromRouters(routerNames, dataDb2) {
|
|
|
23799
25088
|
).all(routerName);
|
|
23800
25089
|
for (const proc of procs) {
|
|
23801
25090
|
const absPath = ensureWithinRoot(resolve22(getProjectRoot(), proc.router_file), getProjectRoot());
|
|
23802
|
-
if (!
|
|
25091
|
+
if (!existsSync26(absPath)) continue;
|
|
23803
25092
|
try {
|
|
23804
|
-
const source =
|
|
25093
|
+
const source = readFileSync24(absPath, "utf-8");
|
|
23805
25094
|
const dbPattern = getConfig().dbAccessPattern ?? "ctx.db.{table}";
|
|
23806
25095
|
const regexStr = dbPattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace("\\{table\\}", "(\\w+)");
|
|
23807
25096
|
const tableRegex = new RegExp(regexStr + "\\.", "g");
|
|
@@ -24070,14 +25359,14 @@ var init_domains = __esm({
|
|
|
24070
25359
|
});
|
|
24071
25360
|
|
|
24072
25361
|
// src/schema-mapper.ts
|
|
24073
|
-
import { readFileSync as
|
|
24074
|
-
import { join as
|
|
25362
|
+
import { readFileSync as readFileSync25, existsSync as existsSync27, readdirSync as readdirSync16 } from "fs";
|
|
25363
|
+
import { join as join14 } from "path";
|
|
24075
25364
|
function parsePrismaSchema() {
|
|
24076
25365
|
const schemaPath = getResolvedPaths().prismaSchemaPath;
|
|
24077
|
-
if (!
|
|
25366
|
+
if (!existsSync27(schemaPath)) {
|
|
24078
25367
|
throw new Error(`Prisma schema not found at ${schemaPath}`);
|
|
24079
25368
|
}
|
|
24080
|
-
const source =
|
|
25369
|
+
const source = readFileSync25(schemaPath, "utf-8");
|
|
24081
25370
|
const models = [];
|
|
24082
25371
|
const sourceLines = source.split("\n");
|
|
24083
25372
|
let i2 = 0;
|
|
@@ -24135,14 +25424,14 @@ function toSnakeCase(str) {
|
|
|
24135
25424
|
function findColumnUsageInRouters(tableName) {
|
|
24136
25425
|
const usage = /* @__PURE__ */ new Map();
|
|
24137
25426
|
const routersDir = getResolvedPaths().routersDir;
|
|
24138
|
-
if (!
|
|
25427
|
+
if (!existsSync27(routersDir)) return usage;
|
|
24139
25428
|
scanDirectory(routersDir, tableName, usage);
|
|
24140
25429
|
return usage;
|
|
24141
25430
|
}
|
|
24142
25431
|
function scanDirectory(dir, tableName, usage) {
|
|
24143
|
-
const entries =
|
|
25432
|
+
const entries = readdirSync16(dir, { withFileTypes: true });
|
|
24144
25433
|
for (const entry of entries) {
|
|
24145
|
-
const fullPath =
|
|
25434
|
+
const fullPath = join14(dir, entry.name);
|
|
24146
25435
|
if (entry.isDirectory()) {
|
|
24147
25436
|
scanDirectory(fullPath, tableName, usage);
|
|
24148
25437
|
} else if (entry.name.endsWith(".ts")) {
|
|
@@ -24152,7 +25441,7 @@ function scanDirectory(dir, tableName, usage) {
|
|
|
24152
25441
|
}
|
|
24153
25442
|
function scanFile(absPath, tableName, usage) {
|
|
24154
25443
|
try {
|
|
24155
|
-
const source =
|
|
25444
|
+
const source = readFileSync25(absPath, "utf-8");
|
|
24156
25445
|
if (!source.includes(tableName)) return;
|
|
24157
25446
|
const relPath = absPath.slice(getProjectRoot().length + 1);
|
|
24158
25447
|
const lines = source.split("\n");
|
|
@@ -24197,15 +25486,15 @@ function detectMismatches(models) {
|
|
|
24197
25486
|
}
|
|
24198
25487
|
function findFilesUsingColumn(dir, column, tableName) {
|
|
24199
25488
|
const result = [];
|
|
24200
|
-
if (!
|
|
24201
|
-
const entries =
|
|
25489
|
+
if (!existsSync27(dir)) return result;
|
|
25490
|
+
const entries = readdirSync16(dir, { withFileTypes: true });
|
|
24202
25491
|
for (const entry of entries) {
|
|
24203
|
-
const fullPath =
|
|
25492
|
+
const fullPath = join14(dir, entry.name);
|
|
24204
25493
|
if (entry.isDirectory()) {
|
|
24205
25494
|
result.push(...findFilesUsingColumn(fullPath, column, tableName));
|
|
24206
25495
|
} else if (entry.name.endsWith(".ts")) {
|
|
24207
25496
|
try {
|
|
24208
|
-
const source =
|
|
25497
|
+
const source = readFileSync25(fullPath, "utf-8");
|
|
24209
25498
|
if (source.includes(tableName) && source.includes(column)) {
|
|
24210
25499
|
result.push(fullPath.slice(getProjectRoot().length + 1));
|
|
24211
25500
|
}
|
|
@@ -24350,48 +25639,48 @@ var init_import_parser = __esm({
|
|
|
24350
25639
|
});
|
|
24351
25640
|
|
|
24352
25641
|
// src/python/import-resolver.ts
|
|
24353
|
-
import { readFileSync as
|
|
24354
|
-
import { resolve as resolve24, join as
|
|
25642
|
+
import { readFileSync as readFileSync26, existsSync as existsSync28, readdirSync as readdirSync17 } from "fs";
|
|
25643
|
+
import { resolve as resolve24, join as join15, relative as relative6, dirname as dirname13 } from "path";
|
|
24355
25644
|
function resolvePythonModulePath(module2, fromFile, pythonRoot, level) {
|
|
24356
25645
|
const projectRoot = getProjectRoot();
|
|
24357
25646
|
if (level > 0) {
|
|
24358
|
-
let baseDir =
|
|
25647
|
+
let baseDir = dirname13(resolve24(projectRoot, fromFile));
|
|
24359
25648
|
for (let i2 = 1; i2 < level; i2++) {
|
|
24360
|
-
baseDir =
|
|
25649
|
+
baseDir = dirname13(baseDir);
|
|
24361
25650
|
}
|
|
24362
25651
|
const modulePart = module2.replace(/^\.+/, "");
|
|
24363
25652
|
if (modulePart) {
|
|
24364
25653
|
const parts3 = modulePart.split(".");
|
|
24365
|
-
return tryResolvePythonPath(
|
|
25654
|
+
return tryResolvePythonPath(join15(baseDir, ...parts3), projectRoot);
|
|
24366
25655
|
}
|
|
24367
25656
|
return tryResolvePythonPath(baseDir, projectRoot);
|
|
24368
25657
|
}
|
|
24369
25658
|
const parts2 = module2.split(".");
|
|
24370
|
-
const candidate =
|
|
25659
|
+
const candidate = join15(resolve24(projectRoot, pythonRoot), ...parts2);
|
|
24371
25660
|
return tryResolvePythonPath(candidate, projectRoot);
|
|
24372
25661
|
}
|
|
24373
25662
|
function tryResolvePythonPath(basePath, projectRoot) {
|
|
24374
|
-
if (
|
|
24375
|
-
return
|
|
25663
|
+
if (existsSync28(basePath + ".py")) {
|
|
25664
|
+
return relative6(projectRoot, basePath + ".py");
|
|
24376
25665
|
}
|
|
24377
|
-
if (
|
|
24378
|
-
return
|
|
25666
|
+
if (existsSync28(join15(basePath, "__init__.py"))) {
|
|
25667
|
+
return relative6(projectRoot, join15(basePath, "__init__.py"));
|
|
24379
25668
|
}
|
|
24380
|
-
if (basePath.endsWith(".py") &&
|
|
24381
|
-
return
|
|
25669
|
+
if (basePath.endsWith(".py") && existsSync28(basePath)) {
|
|
25670
|
+
return relative6(projectRoot, basePath);
|
|
24382
25671
|
}
|
|
24383
25672
|
return null;
|
|
24384
25673
|
}
|
|
24385
25674
|
function walkPythonFiles(dir, excludeDirs) {
|
|
24386
25675
|
const files = [];
|
|
24387
25676
|
try {
|
|
24388
|
-
const entries =
|
|
25677
|
+
const entries = readdirSync17(dir, { withFileTypes: true });
|
|
24389
25678
|
for (const entry of entries) {
|
|
24390
25679
|
if (entry.isDirectory()) {
|
|
24391
25680
|
if (excludeDirs.includes(entry.name)) continue;
|
|
24392
|
-
files.push(...walkPythonFiles(
|
|
25681
|
+
files.push(...walkPythonFiles(join15(dir, entry.name), excludeDirs));
|
|
24393
25682
|
} else if (entry.name.endsWith(".py")) {
|
|
24394
|
-
files.push(
|
|
25683
|
+
files.push(join15(dir, entry.name));
|
|
24395
25684
|
}
|
|
24396
25685
|
}
|
|
24397
25686
|
} catch {
|
|
@@ -24414,10 +25703,10 @@ function buildPythonImportIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__
|
|
|
24414
25703
|
});
|
|
24415
25704
|
const batch = [];
|
|
24416
25705
|
for (const absFile of files) {
|
|
24417
|
-
const relFile =
|
|
25706
|
+
const relFile = relative6(projectRoot, absFile);
|
|
24418
25707
|
let source;
|
|
24419
25708
|
try {
|
|
24420
|
-
source =
|
|
25709
|
+
source = readFileSync26(absFile, "utf-8");
|
|
24421
25710
|
} catch {
|
|
24422
25711
|
continue;
|
|
24423
25712
|
}
|
|
@@ -24683,18 +25972,18 @@ var init_route_parser = __esm({
|
|
|
24683
25972
|
});
|
|
24684
25973
|
|
|
24685
25974
|
// src/python/route-indexer.ts
|
|
24686
|
-
import { readFileSync as
|
|
24687
|
-
import { join as
|
|
25975
|
+
import { readFileSync as readFileSync27, readdirSync as readdirSync18 } from "fs";
|
|
25976
|
+
import { join as join16, relative as relative7 } from "path";
|
|
24688
25977
|
function walkPyFiles(dir, excludeDirs) {
|
|
24689
25978
|
const files = [];
|
|
24690
25979
|
try {
|
|
24691
|
-
const entries =
|
|
25980
|
+
const entries = readdirSync18(dir, { withFileTypes: true });
|
|
24692
25981
|
for (const entry of entries) {
|
|
24693
25982
|
if (entry.isDirectory()) {
|
|
24694
25983
|
if (excludeDirs.includes(entry.name)) continue;
|
|
24695
|
-
files.push(...walkPyFiles(
|
|
25984
|
+
files.push(...walkPyFiles(join16(dir, entry.name), excludeDirs));
|
|
24696
25985
|
} else if (entry.name.endsWith(".py")) {
|
|
24697
|
-
files.push(
|
|
25986
|
+
files.push(join16(dir, entry.name));
|
|
24698
25987
|
}
|
|
24699
25988
|
}
|
|
24700
25989
|
} catch {
|
|
@@ -24703,7 +25992,7 @@ function walkPyFiles(dir, excludeDirs) {
|
|
|
24703
25992
|
}
|
|
24704
25993
|
function buildPythonRouteIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
|
|
24705
25994
|
const projectRoot = getProjectRoot();
|
|
24706
|
-
const absRoot =
|
|
25995
|
+
const absRoot = join16(projectRoot, pythonRoot);
|
|
24707
25996
|
dataDb2.exec("DELETE FROM massu_py_routes");
|
|
24708
25997
|
dataDb2.exec("DELETE FROM massu_py_route_callers");
|
|
24709
25998
|
const insertStmt = dataDb2.prepare(
|
|
@@ -24713,10 +26002,10 @@ function buildPythonRouteIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__"
|
|
|
24713
26002
|
let count = 0;
|
|
24714
26003
|
dataDb2.transaction(() => {
|
|
24715
26004
|
for (const absFile of files) {
|
|
24716
|
-
const relFile =
|
|
26005
|
+
const relFile = relative7(projectRoot, absFile);
|
|
24717
26006
|
let source;
|
|
24718
26007
|
try {
|
|
24719
|
-
source =
|
|
26008
|
+
source = readFileSync27(absFile, "utf-8");
|
|
24720
26009
|
} catch {
|
|
24721
26010
|
continue;
|
|
24722
26011
|
}
|
|
@@ -24927,18 +26216,18 @@ var init_model_parser = __esm({
|
|
|
24927
26216
|
});
|
|
24928
26217
|
|
|
24929
26218
|
// src/python/model-indexer.ts
|
|
24930
|
-
import { readFileSync as
|
|
24931
|
-
import { join as
|
|
26219
|
+
import { readFileSync as readFileSync28, readdirSync as readdirSync19 } from "fs";
|
|
26220
|
+
import { join as join17, relative as relative8 } from "path";
|
|
24932
26221
|
function walkPyFiles2(dir, excludeDirs) {
|
|
24933
26222
|
const files = [];
|
|
24934
26223
|
try {
|
|
24935
|
-
const entries =
|
|
26224
|
+
const entries = readdirSync19(dir, { withFileTypes: true });
|
|
24936
26225
|
for (const entry of entries) {
|
|
24937
26226
|
if (entry.isDirectory()) {
|
|
24938
26227
|
if (excludeDirs.includes(entry.name)) continue;
|
|
24939
|
-
files.push(...walkPyFiles2(
|
|
26228
|
+
files.push(...walkPyFiles2(join17(dir, entry.name), excludeDirs));
|
|
24940
26229
|
} else if (entry.name.endsWith(".py")) {
|
|
24941
|
-
files.push(
|
|
26230
|
+
files.push(join17(dir, entry.name));
|
|
24942
26231
|
}
|
|
24943
26232
|
}
|
|
24944
26233
|
} catch {
|
|
@@ -24947,7 +26236,7 @@ function walkPyFiles2(dir, excludeDirs) {
|
|
|
24947
26236
|
}
|
|
24948
26237
|
function buildPythonModelIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
|
|
24949
26238
|
const projectRoot = getProjectRoot();
|
|
24950
|
-
const absRoot =
|
|
26239
|
+
const absRoot = join17(projectRoot, pythonRoot);
|
|
24951
26240
|
dataDb2.exec("DELETE FROM massu_py_models");
|
|
24952
26241
|
dataDb2.exec("DELETE FROM massu_py_fk_edges");
|
|
24953
26242
|
const insertModel = dataDb2.prepare(
|
|
@@ -24960,10 +26249,10 @@ function buildPythonModelIndex(dataDb2, pythonRoot, excludeDirs = ["__pycache__"
|
|
|
24960
26249
|
let count = 0;
|
|
24961
26250
|
dataDb2.transaction(() => {
|
|
24962
26251
|
for (const absFile of files) {
|
|
24963
|
-
const relFile =
|
|
26252
|
+
const relFile = relative8(projectRoot, absFile);
|
|
24964
26253
|
let source;
|
|
24965
26254
|
try {
|
|
24966
|
-
source =
|
|
26255
|
+
source = readFileSync28(absFile, "utf-8");
|
|
24967
26256
|
} catch {
|
|
24968
26257
|
continue;
|
|
24969
26258
|
}
|
|
@@ -25223,19 +26512,19 @@ var init_migration_parser = __esm({
|
|
|
25223
26512
|
});
|
|
25224
26513
|
|
|
25225
26514
|
// src/python/migration-indexer.ts
|
|
25226
|
-
import { readFileSync as
|
|
25227
|
-
import { join as
|
|
26515
|
+
import { readFileSync as readFileSync29, readdirSync as readdirSync20 } from "fs";
|
|
26516
|
+
import { join as join18, relative as relative9 } from "path";
|
|
25228
26517
|
function buildPythonMigrationIndex(dataDb2, alembicDir) {
|
|
25229
26518
|
const projectRoot = getProjectRoot();
|
|
25230
|
-
const absDir =
|
|
26519
|
+
const absDir = join18(projectRoot, alembicDir);
|
|
25231
26520
|
dataDb2.exec("DELETE FROM massu_py_migrations");
|
|
25232
|
-
const versionsDir =
|
|
26521
|
+
const versionsDir = join18(absDir, "versions");
|
|
25233
26522
|
let files = [];
|
|
25234
26523
|
try {
|
|
25235
|
-
files =
|
|
26524
|
+
files = readdirSync20(versionsDir).filter((f2) => f2.endsWith(".py")).map((f2) => join18(versionsDir, f2));
|
|
25236
26525
|
} catch {
|
|
25237
26526
|
try {
|
|
25238
|
-
files =
|
|
26527
|
+
files = readdirSync20(absDir).filter((f2) => f2.endsWith(".py") && f2 !== "env.py").map((f2) => join18(absDir, f2));
|
|
25239
26528
|
} catch {
|
|
25240
26529
|
}
|
|
25241
26530
|
}
|
|
@@ -25249,7 +26538,7 @@ function buildPythonMigrationIndex(dataDb2, alembicDir) {
|
|
|
25249
26538
|
for (const absFile of files) {
|
|
25250
26539
|
let source;
|
|
25251
26540
|
try {
|
|
25252
|
-
source =
|
|
26541
|
+
source = readFileSync29(absFile, "utf-8");
|
|
25253
26542
|
} catch {
|
|
25254
26543
|
continue;
|
|
25255
26544
|
}
|
|
@@ -25260,7 +26549,7 @@ function buildPythonMigrationIndex(dataDb2, alembicDir) {
|
|
|
25260
26549
|
rows.push({
|
|
25261
26550
|
revision: parsed.revision,
|
|
25262
26551
|
downRevision: parsed.downRevision,
|
|
25263
|
-
file:
|
|
26552
|
+
file: relative9(projectRoot, absFile),
|
|
25264
26553
|
description: parsed.description,
|
|
25265
26554
|
operations: JSON.stringify(parsed.operations)
|
|
25266
26555
|
});
|
|
@@ -25283,12 +26572,12 @@ var init_migration_indexer = __esm({
|
|
|
25283
26572
|
});
|
|
25284
26573
|
|
|
25285
26574
|
// src/python/coupling-detector.ts
|
|
25286
|
-
import { readFileSync as
|
|
25287
|
-
import { join as
|
|
26575
|
+
import { readFileSync as readFileSync30, readdirSync as readdirSync21 } from "fs";
|
|
26576
|
+
import { join as join19, relative as relative10 } from "path";
|
|
25288
26577
|
function buildPythonCouplingIndex(dataDb2) {
|
|
25289
26578
|
const projectRoot = getProjectRoot();
|
|
25290
26579
|
const config = getConfig();
|
|
25291
|
-
const srcDir =
|
|
26580
|
+
const srcDir = join19(projectRoot, config.paths.source);
|
|
25292
26581
|
const routes = dataDb2.prepare("SELECT id, method, path FROM massu_py_routes").all();
|
|
25293
26582
|
if (routes.length === 0) return 0;
|
|
25294
26583
|
dataDb2.exec("DELETE FROM massu_py_route_callers");
|
|
@@ -25317,10 +26606,10 @@ function buildPythonCouplingIndex(dataDb2) {
|
|
|
25317
26606
|
];
|
|
25318
26607
|
dataDb2.transaction(() => {
|
|
25319
26608
|
for (const absFile of frontendFiles) {
|
|
25320
|
-
const relFile =
|
|
26609
|
+
const relFile = relative10(projectRoot, absFile);
|
|
25321
26610
|
let source;
|
|
25322
26611
|
try {
|
|
25323
|
-
source =
|
|
26612
|
+
source = readFileSync30(absFile, "utf-8");
|
|
25324
26613
|
} catch {
|
|
25325
26614
|
continue;
|
|
25326
26615
|
}
|
|
@@ -25348,13 +26637,13 @@ function walkFrontendFiles(dir) {
|
|
|
25348
26637
|
const files = [];
|
|
25349
26638
|
const exclude = ["node_modules", ".next", "dist", ".git", "__pycache__", ".venv", "venv"];
|
|
25350
26639
|
try {
|
|
25351
|
-
const entries =
|
|
26640
|
+
const entries = readdirSync21(dir, { withFileTypes: true });
|
|
25352
26641
|
for (const entry of entries) {
|
|
25353
26642
|
if (entry.isDirectory()) {
|
|
25354
26643
|
if (exclude.includes(entry.name)) continue;
|
|
25355
|
-
files.push(...walkFrontendFiles(
|
|
26644
|
+
files.push(...walkFrontendFiles(join19(dir, entry.name)));
|
|
25356
26645
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
25357
|
-
files.push(
|
|
26646
|
+
files.push(join19(dir, entry.name));
|
|
25358
26647
|
}
|
|
25359
26648
|
}
|
|
25360
26649
|
} catch {
|
|
@@ -25725,8 +27014,8 @@ var init_memory_tools = __esm({
|
|
|
25725
27014
|
});
|
|
25726
27015
|
|
|
25727
27016
|
// src/docs-tools.ts
|
|
25728
|
-
import { readFileSync as
|
|
25729
|
-
import { resolve as resolve25, basename as
|
|
27017
|
+
import { readFileSync as readFileSync31, existsSync as existsSync29 } from "fs";
|
|
27018
|
+
import { resolve as resolve25, basename as basename6 } from "path";
|
|
25730
27019
|
function p3(baseName) {
|
|
25731
27020
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
25732
27021
|
}
|
|
@@ -25781,10 +27070,10 @@ function handleDocsToolCall(name2, args3) {
|
|
|
25781
27070
|
}
|
|
25782
27071
|
function loadDocsMap() {
|
|
25783
27072
|
const mapPath = getResolvedPaths().docsMapPath;
|
|
25784
|
-
if (!
|
|
27073
|
+
if (!existsSync29(mapPath)) {
|
|
25785
27074
|
throw new Error(`docs-map.json not found at ${mapPath}`);
|
|
25786
27075
|
}
|
|
25787
|
-
return JSON.parse(
|
|
27076
|
+
return JSON.parse(readFileSync31(mapPath, "utf-8"));
|
|
25788
27077
|
}
|
|
25789
27078
|
function matchesPattern(filePath, pattern) {
|
|
25790
27079
|
const regexStr = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
@@ -25793,7 +27082,7 @@ function matchesPattern(filePath, pattern) {
|
|
|
25793
27082
|
function findAffectedMappings(docsMap, changedFiles) {
|
|
25794
27083
|
const affected = /* @__PURE__ */ new Map();
|
|
25795
27084
|
for (const file of changedFiles) {
|
|
25796
|
-
const fileName =
|
|
27085
|
+
const fileName = basename6(file);
|
|
25797
27086
|
for (const mapping of docsMap.mappings) {
|
|
25798
27087
|
let matched = false;
|
|
25799
27088
|
for (const routePattern of mapping.appRoutes) {
|
|
@@ -25855,12 +27144,12 @@ function extractFrontmatter(content) {
|
|
|
25855
27144
|
function extractProcedureNames(routerPath) {
|
|
25856
27145
|
const root = getProjectRoot();
|
|
25857
27146
|
const absPath = ensureWithinRoot(resolve25(getResolvedPaths().srcDir, "..", routerPath), root);
|
|
25858
|
-
if (!
|
|
25859
|
-
const altPath = ensureWithinRoot(resolve25(getResolvedPaths().srcDir, "../server/api/routers",
|
|
25860
|
-
if (!
|
|
25861
|
-
return extractProcedureNamesFromContent(
|
|
27147
|
+
if (!existsSync29(absPath)) {
|
|
27148
|
+
const altPath = ensureWithinRoot(resolve25(getResolvedPaths().srcDir, "../server/api/routers", basename6(routerPath)), root);
|
|
27149
|
+
if (!existsSync29(altPath)) return [];
|
|
27150
|
+
return extractProcedureNamesFromContent(readFileSync31(altPath, "utf-8"));
|
|
25862
27151
|
}
|
|
25863
|
-
return extractProcedureNamesFromContent(
|
|
27152
|
+
return extractProcedureNamesFromContent(readFileSync31(absPath, "utf-8"));
|
|
25864
27153
|
}
|
|
25865
27154
|
function extractProcedureNamesFromContent(content) {
|
|
25866
27155
|
const procRegex = /\.(?:query|mutation)\s*\(/g;
|
|
@@ -25901,7 +27190,7 @@ function handleDocsAudit(args3) {
|
|
|
25901
27190
|
const mapping = docsMap.mappings.find((m3) => m3.id === mappingId);
|
|
25902
27191
|
if (!mapping) continue;
|
|
25903
27192
|
const helpPagePath = ensureWithinRoot(resolve25(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
|
|
25904
|
-
if (!
|
|
27193
|
+
if (!existsSync29(helpPagePath)) {
|
|
25905
27194
|
results.push({
|
|
25906
27195
|
helpPage: mapping.helpPage,
|
|
25907
27196
|
mappingId,
|
|
@@ -25913,12 +27202,12 @@ function handleDocsAudit(args3) {
|
|
|
25913
27202
|
});
|
|
25914
27203
|
continue;
|
|
25915
27204
|
}
|
|
25916
|
-
const content =
|
|
27205
|
+
const content = readFileSync31(helpPagePath, "utf-8");
|
|
25917
27206
|
const sections = extractSections(content);
|
|
25918
27207
|
const frontmatter = extractFrontmatter(content);
|
|
25919
27208
|
const staleReasons = [];
|
|
25920
27209
|
for (const file of triggeringFiles) {
|
|
25921
|
-
const fileName =
|
|
27210
|
+
const fileName = basename6(file);
|
|
25922
27211
|
if (mapping.routers.includes(fileName)) {
|
|
25923
27212
|
const procedures = extractProcedureNames(file);
|
|
25924
27213
|
const undocumented = procedures.filter((p19) => !contentMentions(content, p19));
|
|
@@ -25949,13 +27238,13 @@ function handleDocsAudit(args3) {
|
|
|
25949
27238
|
reason: staleReasons.length > 0 ? staleReasons.join("; ") : "Content appears current",
|
|
25950
27239
|
sections,
|
|
25951
27240
|
changedFiles: triggeringFiles,
|
|
25952
|
-
suggestedAction: status === "STALE" ? `Review and update ${mapping.helpPage} to reflect changes in: ${triggeringFiles.map((f2) =>
|
|
27241
|
+
suggestedAction: status === "STALE" ? `Review and update ${mapping.helpPage} to reflect changes in: ${triggeringFiles.map((f2) => basename6(f2)).join(", ")}` : "No action needed"
|
|
25953
27242
|
});
|
|
25954
27243
|
for (const [guideName, parentId] of Object.entries(docsMap.userGuideInheritance.examples)) {
|
|
25955
27244
|
if (parentId === mappingId) {
|
|
25956
27245
|
const guidePath = ensureWithinRoot(resolve25(getResolvedPaths().helpSitePath, `pages/user-guides/${guideName}/index.mdx`), getProjectRoot());
|
|
25957
|
-
if (
|
|
25958
|
-
const guideContent =
|
|
27246
|
+
if (existsSync29(guidePath)) {
|
|
27247
|
+
const guideContent = readFileSync31(guidePath, "utf-8");
|
|
25959
27248
|
const guideFrontmatter = extractFrontmatter(guideContent);
|
|
25960
27249
|
if (!guideFrontmatter?.lastVerified || status === "STALE") {
|
|
25961
27250
|
results.push({
|
|
@@ -25989,13 +27278,13 @@ function handleDocsCoverage(args3) {
|
|
|
25989
27278
|
const mappings = filterDomain ? docsMap.mappings.filter((m3) => m3.id === filterDomain) : docsMap.mappings;
|
|
25990
27279
|
for (const mapping of mappings) {
|
|
25991
27280
|
const helpPagePath = ensureWithinRoot(resolve25(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
|
|
25992
|
-
const exists =
|
|
27281
|
+
const exists = existsSync29(helpPagePath);
|
|
25993
27282
|
let hasContent = false;
|
|
25994
27283
|
let lineCount = 0;
|
|
25995
27284
|
let lastVerified = null;
|
|
25996
27285
|
let status = null;
|
|
25997
27286
|
if (exists) {
|
|
25998
|
-
const content =
|
|
27287
|
+
const content = readFileSync31(helpPagePath, "utf-8");
|
|
25999
27288
|
lineCount = content.split("\n").length;
|
|
26000
27289
|
hasContent = lineCount > 10;
|
|
26001
27290
|
const frontmatter = extractFrontmatter(content);
|
|
@@ -26336,7 +27625,7 @@ var init_observability_tools = __esm({
|
|
|
26336
27625
|
});
|
|
26337
27626
|
|
|
26338
27627
|
// src/sentinel-db.ts
|
|
26339
|
-
import { existsSync as
|
|
27628
|
+
import { existsSync as existsSync30 } from "fs";
|
|
26340
27629
|
import { resolve as resolve26 } from "path";
|
|
26341
27630
|
function parsePortalScope(raw) {
|
|
26342
27631
|
if (!raw) return [];
|
|
@@ -26574,22 +27863,22 @@ function validateFeatures(db, domainFilter) {
|
|
|
26574
27863
|
const missingPages = [];
|
|
26575
27864
|
for (const comp of components) {
|
|
26576
27865
|
const absPath = resolve26(PROJECT_ROOT, comp.component_file);
|
|
26577
|
-
if (!
|
|
27866
|
+
if (!existsSync30(absPath)) {
|
|
26578
27867
|
missingComponents.push(comp.component_file);
|
|
26579
27868
|
}
|
|
26580
27869
|
}
|
|
26581
27870
|
for (const proc of procedures) {
|
|
26582
27871
|
const routerPath = resolve26(PROJECT_ROOT, `src/server/api/routers/${proc.router_name}.ts`);
|
|
26583
|
-
if (!
|
|
27872
|
+
if (!existsSync30(routerPath)) {
|
|
26584
27873
|
missingProcedures.push({ router: proc.router_name, procedure: proc.procedure_name });
|
|
26585
27874
|
}
|
|
26586
27875
|
}
|
|
26587
27876
|
for (const page of pages) {
|
|
26588
27877
|
const routeToPath = page.page_route.replace(/^\/(portal-[^/]+\/)?/, "src/app/").replace(/\/$/, "") + "/page.tsx";
|
|
26589
27878
|
const absPath = resolve26(PROJECT_ROOT, routeToPath);
|
|
26590
|
-
if (page.page_route.startsWith("/") && !
|
|
27879
|
+
if (page.page_route.startsWith("/") && !existsSync30(absPath)) {
|
|
26591
27880
|
const altPath = resolve26(PROJECT_ROOT, `src/app${page.page_route}/page.tsx`);
|
|
26592
|
-
if (!
|
|
27881
|
+
if (!existsSync30(altPath)) {
|
|
26593
27882
|
missingPages.push(page.page_route);
|
|
26594
27883
|
}
|
|
26595
27884
|
}
|
|
@@ -27113,8 +28402,8 @@ var init_sentinel_tools = __esm({
|
|
|
27113
28402
|
});
|
|
27114
28403
|
|
|
27115
28404
|
// src/sentinel-scanner.ts
|
|
27116
|
-
import { readFileSync as
|
|
27117
|
-
import { resolve as resolve27, join as
|
|
28405
|
+
import { readFileSync as readFileSync32, existsSync as existsSync31, readdirSync as readdirSync22, statSync as statSync13 } from "fs";
|
|
28406
|
+
import { resolve as resolve27, join as join20, basename as basename7, dirname as dirname14, relative as relative11 } from "path";
|
|
27118
28407
|
function inferDomain(filePath) {
|
|
27119
28408
|
const domains = getConfig().domains;
|
|
27120
28409
|
const path = filePath.toLowerCase();
|
|
@@ -27244,9 +28533,9 @@ function scanComponentExports(dataDb2) {
|
|
|
27244
28533
|
const componentsBase = config.paths.components ?? config.paths.source + "/components";
|
|
27245
28534
|
const componentDirs = [];
|
|
27246
28535
|
const basePath = resolve27(projectRoot, componentsBase);
|
|
27247
|
-
if (
|
|
28536
|
+
if (existsSync31(basePath)) {
|
|
27248
28537
|
try {
|
|
27249
|
-
const entries =
|
|
28538
|
+
const entries = readdirSync22(basePath, { withFileTypes: true });
|
|
27250
28539
|
for (const entry of entries) {
|
|
27251
28540
|
if (entry.isDirectory()) {
|
|
27252
28541
|
componentDirs.push(componentsBase + "/" + entry.name);
|
|
@@ -27257,11 +28546,11 @@ function scanComponentExports(dataDb2) {
|
|
|
27257
28546
|
}
|
|
27258
28547
|
for (const dir of componentDirs) {
|
|
27259
28548
|
const absDir = resolve27(projectRoot, dir);
|
|
27260
|
-
if (!
|
|
27261
|
-
const files =
|
|
28549
|
+
if (!existsSync31(absDir)) continue;
|
|
28550
|
+
const files = walkDir2(absDir).filter((f2) => f2.endsWith(".tsx") || f2.endsWith(".ts"));
|
|
27262
28551
|
for (const file of files) {
|
|
27263
|
-
const relPath =
|
|
27264
|
-
const source =
|
|
28552
|
+
const relPath = relative11(projectRoot, file);
|
|
28553
|
+
const source = readFileSync32(file, "utf-8");
|
|
27265
28554
|
const annotations = parseFeatureAnnotations(source);
|
|
27266
28555
|
if (annotations.length > 0) {
|
|
27267
28556
|
for (const ann of annotations) {
|
|
@@ -27287,7 +28576,7 @@ function scanComponentExports(dataDb2) {
|
|
|
27287
28576
|
if (hasHandlers && exportMatch) {
|
|
27288
28577
|
const componentName = exportMatch[1];
|
|
27289
28578
|
const domain = inferDomain(relPath);
|
|
27290
|
-
const subdomain =
|
|
28579
|
+
const subdomain = basename7(dirname14(relPath));
|
|
27291
28580
|
const featureKey = `component.${subdomain}.${componentName.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "")}`;
|
|
27292
28581
|
if (!annotations.some((a2) => a2.featureKey === featureKey)) {
|
|
27293
28582
|
features.push({
|
|
@@ -27307,16 +28596,16 @@ function scanComponentExports(dataDb2) {
|
|
|
27307
28596
|
}
|
|
27308
28597
|
return features;
|
|
27309
28598
|
}
|
|
27310
|
-
function
|
|
28599
|
+
function walkDir2(dir) {
|
|
27311
28600
|
const results = [];
|
|
27312
28601
|
try {
|
|
27313
|
-
const entries =
|
|
28602
|
+
const entries = readdirSync22(dir);
|
|
27314
28603
|
for (const entry of entries) {
|
|
27315
|
-
const fullPath =
|
|
28604
|
+
const fullPath = join20(dir, entry);
|
|
27316
28605
|
try {
|
|
27317
|
-
const stat =
|
|
28606
|
+
const stat = statSync13(fullPath);
|
|
27318
28607
|
if (stat.isDirectory()) {
|
|
27319
|
-
results.push(...
|
|
28608
|
+
results.push(...walkDir2(fullPath));
|
|
27320
28609
|
} else {
|
|
27321
28610
|
results.push(fullPath);
|
|
27322
28611
|
}
|
|
@@ -28300,7 +29589,7 @@ var init_audit_trail = __esm({
|
|
|
28300
29589
|
});
|
|
28301
29590
|
|
|
28302
29591
|
// src/validation-engine.ts
|
|
28303
|
-
import { existsSync as
|
|
29592
|
+
import { existsSync as existsSync32, readFileSync as readFileSync33 } from "fs";
|
|
28304
29593
|
function p10(baseName) {
|
|
28305
29594
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
28306
29595
|
}
|
|
@@ -28331,7 +29620,7 @@ function validateFile(filePath, projectRoot) {
|
|
|
28331
29620
|
});
|
|
28332
29621
|
return checks;
|
|
28333
29622
|
}
|
|
28334
|
-
if (!
|
|
29623
|
+
if (!existsSync32(absPath)) {
|
|
28335
29624
|
checks.push({
|
|
28336
29625
|
name: "file_exists",
|
|
28337
29626
|
severity: "error",
|
|
@@ -28340,7 +29629,7 @@ function validateFile(filePath, projectRoot) {
|
|
|
28340
29629
|
});
|
|
28341
29630
|
return checks;
|
|
28342
29631
|
}
|
|
28343
|
-
const source =
|
|
29632
|
+
const source = readFileSync33(absPath, "utf-8");
|
|
28344
29633
|
const lines = source.split("\n");
|
|
28345
29634
|
if (activeChecks.rule_compliance !== false) {
|
|
28346
29635
|
for (const ruleSet of config.rules) {
|
|
@@ -28785,7 +30074,7 @@ var init_adr_generator = __esm({
|
|
|
28785
30074
|
});
|
|
28786
30075
|
|
|
28787
30076
|
// src/security-scorer.ts
|
|
28788
|
-
import { existsSync as
|
|
30077
|
+
import { existsSync as existsSync33, readFileSync as readFileSync34 } from "fs";
|
|
28789
30078
|
function p12(baseName) {
|
|
28790
30079
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
28791
30080
|
}
|
|
@@ -28809,12 +30098,12 @@ function scoreFileSecurity(filePath, projectRoot) {
|
|
|
28809
30098
|
}]
|
|
28810
30099
|
};
|
|
28811
30100
|
}
|
|
28812
|
-
if (!
|
|
30101
|
+
if (!existsSync33(absPath)) {
|
|
28813
30102
|
return { riskScore: 0, findings: [] };
|
|
28814
30103
|
}
|
|
28815
30104
|
let source;
|
|
28816
30105
|
try {
|
|
28817
|
-
source =
|
|
30106
|
+
source = readFileSync34(absPath, "utf-8");
|
|
28818
30107
|
} catch {
|
|
28819
30108
|
return { riskScore: 0, findings: [] };
|
|
28820
30109
|
}
|
|
@@ -29103,7 +30392,7 @@ var init_security_scorer = __esm({
|
|
|
29103
30392
|
});
|
|
29104
30393
|
|
|
29105
30394
|
// src/dependency-scorer.ts
|
|
29106
|
-
import { existsSync as
|
|
30395
|
+
import { existsSync as existsSync34, readFileSync as readFileSync35 } from "fs";
|
|
29107
30396
|
import { resolve as resolve28 } from "path";
|
|
29108
30397
|
function p13(baseName) {
|
|
29109
30398
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
@@ -29137,9 +30426,9 @@ function calculateDepRisk(factors) {
|
|
|
29137
30426
|
}
|
|
29138
30427
|
function getInstalledPackages(projectRoot) {
|
|
29139
30428
|
const pkgPath = resolve28(projectRoot, "package.json");
|
|
29140
|
-
if (!
|
|
30429
|
+
if (!existsSync34(pkgPath)) return /* @__PURE__ */ new Map();
|
|
29141
30430
|
try {
|
|
29142
|
-
const pkg = JSON.parse(
|
|
30431
|
+
const pkg = JSON.parse(readFileSync35(pkgPath, "utf-8"));
|
|
29143
30432
|
const packages = /* @__PURE__ */ new Map();
|
|
29144
30433
|
for (const [name2, version] of Object.entries(pkg.dependencies ?? {})) {
|
|
29145
30434
|
packages.set(name2, version);
|
|
@@ -29741,9 +31030,9 @@ var init_regression_detector = __esm({
|
|
|
29741
31030
|
});
|
|
29742
31031
|
|
|
29743
31032
|
// src/knowledge-indexer.ts
|
|
29744
|
-
import { createHash as
|
|
29745
|
-
import { readFileSync as
|
|
29746
|
-
import { resolve as resolve29, relative as
|
|
31033
|
+
import { createHash as createHash8 } from "crypto";
|
|
31034
|
+
import { readFileSync as readFileSync36, readdirSync as readdirSync23, statSync as statSync14, existsSync as existsSync35 } from "fs";
|
|
31035
|
+
import { resolve as resolve29, relative as relative12, basename as basename8, extname as extname2 } from "path";
|
|
29747
31036
|
function getKnowledgePaths() {
|
|
29748
31037
|
const resolved = getResolvedPaths();
|
|
29749
31038
|
const config = getConfig();
|
|
@@ -29769,7 +31058,7 @@ function discoverMarkdownFiles(baseDir) {
|
|
|
29769
31058
|
const files = [];
|
|
29770
31059
|
function walk(dir) {
|
|
29771
31060
|
try {
|
|
29772
|
-
const entries =
|
|
31061
|
+
const entries = readdirSync23(dir, { withFileTypes: true });
|
|
29773
31062
|
for (const entry of entries) {
|
|
29774
31063
|
const fullPath = resolve29(dir, entry.name);
|
|
29775
31064
|
if (entry.isDirectory()) {
|
|
@@ -29777,7 +31066,7 @@ function discoverMarkdownFiles(baseDir) {
|
|
|
29777
31066
|
if (entry.name === "archive" && dir.includes("status")) continue;
|
|
29778
31067
|
if (entry.name === "node_modules") continue;
|
|
29779
31068
|
walk(fullPath);
|
|
29780
|
-
} else if (entry.isFile() &&
|
|
31069
|
+
} else if (entry.isFile() && extname2(entry.name) === ".md") {
|
|
29781
31070
|
files.push(fullPath);
|
|
29782
31071
|
}
|
|
29783
31072
|
}
|
|
@@ -29791,7 +31080,7 @@ function categorizeFile(filePath) {
|
|
|
29791
31080
|
const paths = getKnowledgePaths();
|
|
29792
31081
|
if (filePath.startsWith(paths.plansDir)) return "plan";
|
|
29793
31082
|
if (filePath.startsWith(paths.docsDir)) {
|
|
29794
|
-
const relFromDocs =
|
|
31083
|
+
const relFromDocs = relative12(paths.docsDir, filePath).replace(/\\/g, "/").toLowerCase();
|
|
29795
31084
|
if (relFromDocs.startsWith("plans/")) return "plan";
|
|
29796
31085
|
if (relFromDocs.includes("architecture")) return "architecture";
|
|
29797
31086
|
if (relFromDocs.includes("security")) return "security";
|
|
@@ -29807,7 +31096,7 @@ function categorizeFile(filePath) {
|
|
|
29807
31096
|
}
|
|
29808
31097
|
const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
|
|
29809
31098
|
if (filePath.includes(`${claudeDirName}/projects/`) && filePath.includes("/memory/")) return "memory";
|
|
29810
|
-
const rel =
|
|
31099
|
+
const rel = relative12(paths.claudeDir, filePath).replace(/\\/g, "/");
|
|
29811
31100
|
const firstDir = rel.split("/")[0];
|
|
29812
31101
|
const knownCategories = getConfig().conventions?.knowledgeCategories ?? [
|
|
29813
31102
|
"patterns",
|
|
@@ -29829,7 +31118,7 @@ function categorizeFile(filePath) {
|
|
|
29829
31118
|
return "root";
|
|
29830
31119
|
}
|
|
29831
31120
|
function hashContent2(content) {
|
|
29832
|
-
return
|
|
31121
|
+
return createHash8("sha256").update(content).digest("hex");
|
|
29833
31122
|
}
|
|
29834
31123
|
function parseCRTable(content) {
|
|
29835
31124
|
const rules = [];
|
|
@@ -29948,7 +31237,7 @@ function parseCorrections(content) {
|
|
|
29948
31237
|
function extractTitle(content, filePath) {
|
|
29949
31238
|
const h1Match = content.match(/^#\s+(.+)/m);
|
|
29950
31239
|
if (h1Match) return h1Match[1].trim();
|
|
29951
|
-
return
|
|
31240
|
+
return basename8(filePath, ".md");
|
|
29952
31241
|
}
|
|
29953
31242
|
function extractDescription2(content) {
|
|
29954
31243
|
const fmMatch = content.match(/^---\s*\n[\s\S]*?description:\s*"?([^"\n]+)"?\s*\n[\s\S]*?---/);
|
|
@@ -29977,7 +31266,7 @@ function buildCrossReferences(db) {
|
|
|
29977
31266
|
}
|
|
29978
31267
|
}
|
|
29979
31268
|
if (rule.reference_path) {
|
|
29980
|
-
const patternName =
|
|
31269
|
+
const patternName = basename8(rule.reference_path, ".md");
|
|
29981
31270
|
insertEdge.run("cr", rule.rule_id, "pattern", patternName, "references");
|
|
29982
31271
|
edgeCount++;
|
|
29983
31272
|
}
|
|
@@ -30049,11 +31338,11 @@ function indexAllKnowledge(db) {
|
|
|
30049
31338
|
files.push(...memFiles);
|
|
30050
31339
|
} catch {
|
|
30051
31340
|
}
|
|
30052
|
-
if (
|
|
31341
|
+
if (existsSync35(paths.plansDir)) {
|
|
30053
31342
|
const planFiles = discoverMarkdownFiles(paths.plansDir);
|
|
30054
31343
|
files.push(...planFiles);
|
|
30055
31344
|
}
|
|
30056
|
-
if (
|
|
31345
|
+
if (existsSync35(paths.docsDir)) {
|
|
30057
31346
|
const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
|
|
30058
31347
|
const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f2) => !f2.includes("/plans/") && !excludePatterns.some((p19) => f2.includes(p19)));
|
|
30059
31348
|
files.push(...docsFiles);
|
|
@@ -30096,10 +31385,10 @@ function indexAllKnowledge(db) {
|
|
|
30096
31385
|
} catch {
|
|
30097
31386
|
}
|
|
30098
31387
|
for (const filePath of files) {
|
|
30099
|
-
if (!
|
|
30100
|
-
const content =
|
|
31388
|
+
if (!existsSync35(filePath)) continue;
|
|
31389
|
+
const content = readFileSync36(filePath, "utf-8");
|
|
30101
31390
|
const hash = hashContent2(content);
|
|
30102
|
-
const relPath = filePath.startsWith(paths.claudeDir) ?
|
|
31391
|
+
const relPath = filePath.startsWith(paths.claudeDir) ? relative12(paths.claudeDir, filePath) : filePath.startsWith(paths.plansDir) ? "plans/" + relative12(paths.plansDir, filePath) : filePath.startsWith(paths.docsDir) ? "docs/" + relative12(paths.docsDir, filePath) : filePath.startsWith(paths.memoryDir) ? `memory/${relative12(paths.memoryDir, filePath)}` : basename8(filePath);
|
|
30103
31392
|
const category = categorizeFile(filePath);
|
|
30104
31393
|
const title = extractTitle(content, filePath);
|
|
30105
31394
|
const description = extractDescription2(content);
|
|
@@ -30113,10 +31402,10 @@ function indexAllKnowledge(db) {
|
|
|
30113
31402
|
stats.chunksCreated++;
|
|
30114
31403
|
}
|
|
30115
31404
|
}
|
|
30116
|
-
const fileName =
|
|
31405
|
+
const fileName = basename8(filePath);
|
|
30117
31406
|
const fileNameLower = fileName.toLowerCase();
|
|
30118
31407
|
const relPathLower = relPath.toLowerCase();
|
|
30119
|
-
const claudeMdName =
|
|
31408
|
+
const claudeMdName = basename8(getResolvedPaths().claudeMdPath).toLowerCase();
|
|
30120
31409
|
if (fileNameLower === claudeMdName || relPathLower.includes(claudeMdName)) {
|
|
30121
31410
|
const crRules = parseCRTable(content);
|
|
30122
31411
|
for (const rule of crRules) {
|
|
@@ -30150,7 +31439,7 @@ function indexAllKnowledge(db) {
|
|
|
30150
31439
|
}
|
|
30151
31440
|
}
|
|
30152
31441
|
if (category === "commands" && fileName !== "_shared-preamble.md") {
|
|
30153
|
-
const cmdName =
|
|
31442
|
+
const cmdName = basename8(filePath, ".md");
|
|
30154
31443
|
insertChunk.run(docId, "command", cmdName, content.substring(0, 1e3), 1, null, JSON.stringify({ command_name: cmdName }));
|
|
30155
31444
|
stats.chunksCreated++;
|
|
30156
31445
|
}
|
|
@@ -30217,17 +31506,17 @@ function isKnowledgeStale(db) {
|
|
|
30217
31506
|
files.push(...discoverMarkdownFiles(paths.memoryDir));
|
|
30218
31507
|
} catch {
|
|
30219
31508
|
}
|
|
30220
|
-
if (
|
|
31509
|
+
if (existsSync35(paths.plansDir)) {
|
|
30221
31510
|
files.push(...discoverMarkdownFiles(paths.plansDir));
|
|
30222
31511
|
}
|
|
30223
|
-
if (
|
|
31512
|
+
if (existsSync35(paths.docsDir)) {
|
|
30224
31513
|
const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
|
|
30225
31514
|
const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f2) => !f2.includes("/plans/") && !excludePatterns.some((p19) => f2.includes(p19)));
|
|
30226
31515
|
files.push(...docsFiles);
|
|
30227
31516
|
}
|
|
30228
31517
|
for (const filePath of files) {
|
|
30229
31518
|
try {
|
|
30230
|
-
const stat =
|
|
31519
|
+
const stat = statSync14(filePath);
|
|
30231
31520
|
if (stat.mtimeMs > lastIndexTime) return true;
|
|
30232
31521
|
} catch {
|
|
30233
31522
|
continue;
|
|
@@ -30249,8 +31538,8 @@ var init_knowledge_indexer = __esm({
|
|
|
30249
31538
|
});
|
|
30250
31539
|
|
|
30251
31540
|
// src/knowledge-tools.ts
|
|
30252
|
-
import { readFileSync as
|
|
30253
|
-
import { resolve as resolve30, basename as
|
|
31541
|
+
import { readFileSync as readFileSync37, writeFileSync as writeFileSync5, appendFileSync as appendFileSync2, readdirSync as readdirSync24 } from "fs";
|
|
31542
|
+
import { resolve as resolve30, basename as basename9 } from "path";
|
|
30254
31543
|
function p16(baseName) {
|
|
30255
31544
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
30256
31545
|
}
|
|
@@ -31000,14 +32289,14 @@ ${crRule ? `- **CR**: ${crRule}
|
|
|
31000
32289
|
`;
|
|
31001
32290
|
let existing = "";
|
|
31002
32291
|
try {
|
|
31003
|
-
existing =
|
|
32292
|
+
existing = readFileSync37(correctionsPath, "utf-8");
|
|
31004
32293
|
} catch {
|
|
31005
32294
|
}
|
|
31006
32295
|
const archiveIdx = existing.indexOf("## Archived");
|
|
31007
32296
|
if (archiveIdx > 0) {
|
|
31008
32297
|
const before = existing.slice(0, archiveIdx);
|
|
31009
32298
|
const after = existing.slice(archiveIdx);
|
|
31010
|
-
|
|
32299
|
+
writeFileSync5(correctionsPath, before + entry + after);
|
|
31011
32300
|
} else {
|
|
31012
32301
|
appendFileSync2(correctionsPath, entry);
|
|
31013
32302
|
}
|
|
@@ -31049,7 +32338,7 @@ function handlePlan(db, args3) {
|
|
|
31049
32338
|
AND kc.content LIKE ?
|
|
31050
32339
|
ORDER BY kd.file_path DESC
|
|
31051
32340
|
LIMIT 20
|
|
31052
|
-
`).all(`%${
|
|
32341
|
+
`).all(`%${basename9(file)}%`);
|
|
31053
32342
|
lines.push(`## Plans referencing \`${file}\` (${results.length} found)`);
|
|
31054
32343
|
lines.push("");
|
|
31055
32344
|
for (const r2 of results) {
|
|
@@ -31182,7 +32471,7 @@ function handleGaps(db, args3) {
|
|
|
31182
32471
|
} else if (checkType === "routers") {
|
|
31183
32472
|
try {
|
|
31184
32473
|
const routersDir = getResolvedPaths().routersDir;
|
|
31185
|
-
const routerFiles =
|
|
32474
|
+
const routerFiles = readdirSync24(routersDir).filter((f2) => f2.endsWith(".ts") && !f2.startsWith("_"));
|
|
31186
32475
|
lines.push(`| Router | Knowledge Hits | Status |`);
|
|
31187
32476
|
lines.push(`|--------|----------------|--------|`);
|
|
31188
32477
|
for (const file of routerFiles) {
|
|
@@ -31338,13 +32627,13 @@ var init_knowledge_tools = __esm({
|
|
|
31338
32627
|
|
|
31339
32628
|
// src/knowledge-db.ts
|
|
31340
32629
|
import Database3 from "better-sqlite3";
|
|
31341
|
-
import { dirname as
|
|
31342
|
-
import { existsSync as
|
|
32630
|
+
import { dirname as dirname15 } from "path";
|
|
32631
|
+
import { existsSync as existsSync37, mkdirSync as mkdirSync10 } from "fs";
|
|
31343
32632
|
function getKnowledgeDb() {
|
|
31344
32633
|
const dbPath = getResolvedPaths().knowledgeDbPath;
|
|
31345
|
-
const dir =
|
|
31346
|
-
if (!
|
|
31347
|
-
|
|
32634
|
+
const dir = dirname15(dbPath);
|
|
32635
|
+
if (!existsSync37(dir)) {
|
|
32636
|
+
mkdirSync10(dir, { recursive: true });
|
|
31348
32637
|
}
|
|
31349
32638
|
const db = new Database3(dbPath);
|
|
31350
32639
|
db.pragma("journal_mode = WAL");
|
|
@@ -32077,8 +33366,8 @@ var init_python_tools = __esm({
|
|
|
32077
33366
|
});
|
|
32078
33367
|
|
|
32079
33368
|
// src/tools.ts
|
|
32080
|
-
import { readFileSync as
|
|
32081
|
-
import { resolve as resolve31, basename as
|
|
33369
|
+
import { readFileSync as readFileSync38, existsSync as existsSync38 } from "fs";
|
|
33370
|
+
import { resolve as resolve31, basename as basename10 } from "path";
|
|
32082
33371
|
function prefix2() {
|
|
32083
33372
|
return getConfig().toolPrefix;
|
|
32084
33373
|
}
|
|
@@ -32513,8 +33802,8 @@ function handleContext(file, dataDb2, codegraphDb2) {
|
|
|
32513
33802
|
const resolvedPaths = getResolvedPaths();
|
|
32514
33803
|
const root = getProjectRoot();
|
|
32515
33804
|
const absFilePath = ensureWithinRoot(resolve31(resolvedPaths.srcDir, "..", file), root);
|
|
32516
|
-
if (
|
|
32517
|
-
const fileContent =
|
|
33805
|
+
if (existsSync38(absFilePath)) {
|
|
33806
|
+
const fileContent = readFileSync38(absFilePath, "utf-8").slice(0, 3e3);
|
|
32518
33807
|
const keywords = [];
|
|
32519
33808
|
if (fileContent.includes("ctx.db")) keywords.push("database", "schema");
|
|
32520
33809
|
if (fileContent.includes("BigInt") || fileContent.includes("Decimal")) keywords.push("BigInt", "serialization");
|
|
@@ -32608,7 +33897,7 @@ function handleContext(file, dataDb2, codegraphDb2) {
|
|
|
32608
33897
|
WHERE o.files_involved LIKE ?
|
|
32609
33898
|
ORDER BY o.importance DESC, o.created_at_epoch DESC
|
|
32610
33899
|
LIMIT 5
|
|
32611
|
-
`).all(`%${
|
|
33900
|
+
`).all(`%${basename10(file)}%`);
|
|
32612
33901
|
if (fileObservations.length > 0) {
|
|
32613
33902
|
lines.push("## Past Observations (This File)");
|
|
32614
33903
|
for (const obs of fileObservations) {
|
|
@@ -32624,7 +33913,7 @@ function handleContext(file, dataDb2, codegraphDb2) {
|
|
|
32624
33913
|
WHERE type = 'failed_attempt' AND files_involved LIKE ?
|
|
32625
33914
|
ORDER BY recurrence_count DESC
|
|
32626
33915
|
LIMIT 3
|
|
32627
|
-
`).all(`%${
|
|
33916
|
+
`).all(`%${basename10(file)}%`);
|
|
32628
33917
|
if (failures.length > 0) {
|
|
32629
33918
|
lines.push("## Failed Attempts (DO NOT RETRY)");
|
|
32630
33919
|
for (const f2 of failures) {
|
|
@@ -32939,10 +34228,10 @@ function handleSchema(args3) {
|
|
|
32939
34228
|
lines.push("");
|
|
32940
34229
|
const projectRoot = getProjectRoot();
|
|
32941
34230
|
const absPath = ensureWithinRoot(resolve31(projectRoot, file), projectRoot);
|
|
32942
|
-
if (!
|
|
34231
|
+
if (!existsSync38(absPath)) {
|
|
32943
34232
|
return text17(`File not found: ${file}`);
|
|
32944
34233
|
}
|
|
32945
|
-
const source =
|
|
34234
|
+
const source = readFileSync38(absPath, "utf-8");
|
|
32946
34235
|
const config = getConfig();
|
|
32947
34236
|
const dbPattern = config.dbAccessPattern ?? "ctx.db.{table}";
|
|
32948
34237
|
const regexStr = dbPattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace("\\{table\\}", "(\\w+)");
|
|
@@ -33020,8 +34309,8 @@ var init_tools = __esm({
|
|
|
33020
34309
|
|
|
33021
34310
|
// src/server.ts
|
|
33022
34311
|
var server_exports = {};
|
|
33023
|
-
import { readFileSync as
|
|
33024
|
-
import { resolve as resolve32, dirname as
|
|
34312
|
+
import { readFileSync as readFileSync39 } from "fs";
|
|
34313
|
+
import { resolve as resolve32, dirname as dirname16 } from "path";
|
|
33025
34314
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
33026
34315
|
function getDb() {
|
|
33027
34316
|
if (!codegraphDb) codegraphDb = getCodeGraphDb();
|
|
@@ -33113,10 +34402,10 @@ var init_server = __esm({
|
|
|
33113
34402
|
init_tools();
|
|
33114
34403
|
init_memory_db();
|
|
33115
34404
|
init_license();
|
|
33116
|
-
__dirname4 =
|
|
34405
|
+
__dirname4 = dirname16(fileURLToPath4(import.meta.url));
|
|
33117
34406
|
PKG_VERSION = (() => {
|
|
33118
34407
|
try {
|
|
33119
|
-
const pkg = JSON.parse(
|
|
34408
|
+
const pkg = JSON.parse(readFileSync39(resolve32(__dirname4, "..", "package.json"), "utf-8"));
|
|
33120
34409
|
return pkg.version ?? "0.0.0";
|
|
33121
34410
|
} catch {
|
|
33122
34411
|
return "0.0.0";
|
|
@@ -33419,7 +34708,7 @@ var config_upgrade_exports = {};
|
|
|
33419
34708
|
__export(config_upgrade_exports, {
|
|
33420
34709
|
runConfigUpgrade: () => runConfigUpgrade
|
|
33421
34710
|
});
|
|
33422
|
-
import { existsSync as
|
|
34711
|
+
import { existsSync as existsSync39, readFileSync as readFileSync40, writeFileSync as writeFileSync6, copyFileSync, unlinkSync as unlinkSync2 } from "fs";
|
|
33423
34712
|
import { resolve as resolve33 } from "path";
|
|
33424
34713
|
import { parse as parseYaml6 } from "yaml";
|
|
33425
34714
|
async function runConfigUpgrade(opts = {}) {
|
|
@@ -33431,14 +34720,14 @@ async function runConfigUpgrade(opts = {}) {
|
|
|
33431
34720
|
const err2 = opts.silent ? () => {
|
|
33432
34721
|
} : (s) => process.stderr.write(s);
|
|
33433
34722
|
if (opts.rollback) {
|
|
33434
|
-
if (!
|
|
34723
|
+
if (!existsSync39(bakPath)) {
|
|
33435
34724
|
const message = `No backup found at ${bakPath}`;
|
|
33436
34725
|
err2(message + "\n");
|
|
33437
34726
|
return { exitCode: 1, action: "none", message };
|
|
33438
34727
|
}
|
|
33439
34728
|
try {
|
|
33440
34729
|
copyFileSync(bakPath, configPath);
|
|
33441
|
-
|
|
34730
|
+
unlinkSync2(bakPath);
|
|
33442
34731
|
log("Config restored from backup.\n");
|
|
33443
34732
|
return { exitCode: 0, action: "rolled-back" };
|
|
33444
34733
|
} catch (e2) {
|
|
@@ -33447,14 +34736,14 @@ async function runConfigUpgrade(opts = {}) {
|
|
|
33447
34736
|
return { exitCode: 2, action: "none", message };
|
|
33448
34737
|
}
|
|
33449
34738
|
}
|
|
33450
|
-
if (!
|
|
34739
|
+
if (!existsSync39(configPath)) {
|
|
33451
34740
|
const message = "massu.config.yaml not found. Run: npx massu init";
|
|
33452
34741
|
err2(message + "\n");
|
|
33453
34742
|
return { exitCode: 1, action: "none", message };
|
|
33454
34743
|
}
|
|
33455
34744
|
let existing;
|
|
33456
34745
|
try {
|
|
33457
|
-
const content =
|
|
34746
|
+
const content = readFileSync40(configPath, "utf-8");
|
|
33458
34747
|
const parsed = parseYaml6(content);
|
|
33459
34748
|
if (!parsed || typeof parsed !== "object") {
|
|
33460
34749
|
throw new Error("config is not a YAML object");
|
|
@@ -33477,8 +34766,8 @@ async function runConfigUpgrade(opts = {}) {
|
|
|
33477
34766
|
fingerprint: computeFingerprint(detection)
|
|
33478
34767
|
};
|
|
33479
34768
|
try {
|
|
33480
|
-
const original =
|
|
33481
|
-
|
|
34769
|
+
const original = readFileSync40(configPath, "utf-8");
|
|
34770
|
+
writeFileSync6(bakPath, original, "utf-8");
|
|
33482
34771
|
} catch (e2) {
|
|
33483
34772
|
const message = `Failed to write backup: ${e2 instanceof Error ? e2.message : String(e2)}`;
|
|
33484
34773
|
err2(message + "\n");
|
|
@@ -33511,7 +34800,7 @@ var config_check_drift_exports = {};
|
|
|
33511
34800
|
__export(config_check_drift_exports, {
|
|
33512
34801
|
runConfigCheckDrift: () => runConfigCheckDrift
|
|
33513
34802
|
});
|
|
33514
|
-
import { existsSync as
|
|
34803
|
+
import { existsSync as existsSync40, readFileSync as readFileSync41 } from "fs";
|
|
33515
34804
|
import { resolve as resolve34 } from "path";
|
|
33516
34805
|
import { parse as parseYaml7 } from "yaml";
|
|
33517
34806
|
function renderChanges(changes) {
|
|
@@ -33525,7 +34814,7 @@ async function runConfigCheckDrift(opts = {}) {
|
|
|
33525
34814
|
} : (s) => process.stdout.write(s);
|
|
33526
34815
|
const err2 = opts.silent ? () => {
|
|
33527
34816
|
} : (s) => process.stderr.write(s);
|
|
33528
|
-
if (!
|
|
34817
|
+
if (!existsSync40(configPath)) {
|
|
33529
34818
|
const message = "massu.config.yaml not found. Run: npx massu init";
|
|
33530
34819
|
err2(message + "\n");
|
|
33531
34820
|
return {
|
|
@@ -33539,7 +34828,7 @@ async function runConfigCheckDrift(opts = {}) {
|
|
|
33539
34828
|
}
|
|
33540
34829
|
let config;
|
|
33541
34830
|
try {
|
|
33542
|
-
const content =
|
|
34831
|
+
const content = readFileSync41(configPath, "utf-8");
|
|
33543
34832
|
const parsed = parseYaml7(content);
|
|
33544
34833
|
if (!parsed || typeof parsed !== "object") {
|
|
33545
34834
|
throw new Error("config is not a YAML object");
|
|
@@ -33606,11 +34895,11 @@ var init_config_check_drift = __esm({
|
|
|
33606
34895
|
});
|
|
33607
34896
|
|
|
33608
34897
|
// src/cli.ts
|
|
33609
|
-
import { readFileSync as
|
|
33610
|
-
import { resolve as resolve35, dirname as
|
|
34898
|
+
import { readFileSync as readFileSync42 } from "fs";
|
|
34899
|
+
import { resolve as resolve35, dirname as dirname17 } from "path";
|
|
33611
34900
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
33612
34901
|
var __filename4 = fileURLToPath5(import.meta.url);
|
|
33613
|
-
var __dirname5 =
|
|
34902
|
+
var __dirname5 = dirname17(__filename4);
|
|
33614
34903
|
var args2 = process.argv.slice(2);
|
|
33615
34904
|
var subcommand = args2[0];
|
|
33616
34905
|
async function main() {
|
|
@@ -33792,7 +35081,7 @@ Examples:
|
|
|
33792
35081
|
}
|
|
33793
35082
|
function printVersion() {
|
|
33794
35083
|
try {
|
|
33795
|
-
const pkg = JSON.parse(
|
|
35084
|
+
const pkg = JSON.parse(readFileSync42(resolve35(__dirname5, "../package.json"), "utf-8"));
|
|
33796
35085
|
console.log(`massu v${pkg.version}`);
|
|
33797
35086
|
} catch {
|
|
33798
35087
|
console.log("massu v0.1.0");
|