@massu/core 1.6.3 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -0
- package/dist/cli.js +1023 -590
- package/dist/hooks/session-start.js +28 -8
- package/package.json +1 -1
- package/src/cli.ts +8 -1
- package/src/commands/doctor.ts +4 -3
- package/src/commands/init.ts +3 -10
- package/src/commands/install-commands.ts +62 -53
- package/src/commands/permissions.ts +150 -0
- package/src/detect/domain-inferrer.ts +67 -12
- package/src/lib/settings-local.ts +110 -0
- package/src/license.ts +20 -0
- package/src/permissions.ts +422 -0
- package/src/security/registry-pubkey.generated.ts +1 -1
- package/src/tools.ts +6 -3
package/dist/cli.js
CHANGED
|
@@ -1910,6 +1910,283 @@ var init_template_engine = __esm({
|
|
|
1910
1910
|
}
|
|
1911
1911
|
});
|
|
1912
1912
|
|
|
1913
|
+
// src/lib/settings-local.ts
|
|
1914
|
+
import {
|
|
1915
|
+
closeSync,
|
|
1916
|
+
existsSync as existsSync4,
|
|
1917
|
+
fsyncSync,
|
|
1918
|
+
mkdirSync as mkdirSync2,
|
|
1919
|
+
openSync,
|
|
1920
|
+
readFileSync as readFileSync3,
|
|
1921
|
+
renameSync,
|
|
1922
|
+
rmSync,
|
|
1923
|
+
writeSync
|
|
1924
|
+
} from "fs";
|
|
1925
|
+
import { dirname as dirname3, resolve as resolve3 } from "path";
|
|
1926
|
+
function atomicWriteFile(targetPath, content, mode = 420) {
|
|
1927
|
+
const tmpPath = `${targetPath}.${process.pid}.tmp`;
|
|
1928
|
+
try {
|
|
1929
|
+
const fd = openSync(tmpPath, "w", mode);
|
|
1930
|
+
try {
|
|
1931
|
+
const buf = Buffer.from(content, "utf-8");
|
|
1932
|
+
writeSync(fd, buf, 0, buf.length, 0);
|
|
1933
|
+
fsyncSync(fd);
|
|
1934
|
+
} finally {
|
|
1935
|
+
closeSync(fd);
|
|
1936
|
+
}
|
|
1937
|
+
renameSync(tmpPath, targetPath);
|
|
1938
|
+
} catch (err) {
|
|
1939
|
+
if (existsSync4(tmpPath)) {
|
|
1940
|
+
try {
|
|
1941
|
+
rmSync(tmpPath, { force: true });
|
|
1942
|
+
} catch {
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
throw err;
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
function readSettingsAtPath(absolutePath) {
|
|
1949
|
+
if (!existsSync4(absolutePath)) {
|
|
1950
|
+
return {};
|
|
1951
|
+
}
|
|
1952
|
+
try {
|
|
1953
|
+
const raw = readFileSync3(absolutePath, "utf-8");
|
|
1954
|
+
const parsed = JSON.parse(raw);
|
|
1955
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1956
|
+
return {};
|
|
1957
|
+
}
|
|
1958
|
+
return parsed;
|
|
1959
|
+
} catch {
|
|
1960
|
+
return {};
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
function readSettingsLocal(claudeDir) {
|
|
1964
|
+
return readSettingsAtPath(resolve3(claudeDir, "settings.local.json"));
|
|
1965
|
+
}
|
|
1966
|
+
function writeSettingsLocalAtomic(claudeDir, settings) {
|
|
1967
|
+
const targetPath = resolve3(claudeDir, "settings.local.json");
|
|
1968
|
+
const dir = dirname3(targetPath);
|
|
1969
|
+
if (!existsSync4(dir)) {
|
|
1970
|
+
mkdirSync2(dir, { recursive: true });
|
|
1971
|
+
}
|
|
1972
|
+
const content = JSON.stringify(settings, null, 2) + "\n";
|
|
1973
|
+
atomicWriteFile(targetPath, content);
|
|
1974
|
+
}
|
|
1975
|
+
var init_settings_local = __esm({
|
|
1976
|
+
"src/lib/settings-local.ts"() {
|
|
1977
|
+
"use strict";
|
|
1978
|
+
}
|
|
1979
|
+
});
|
|
1980
|
+
|
|
1981
|
+
// src/permissions.ts
|
|
1982
|
+
import { createHash } from "crypto";
|
|
1983
|
+
import { homedir as homedir2 } from "os";
|
|
1984
|
+
import { join as join2 } from "path";
|
|
1985
|
+
function canonicalJson(value) {
|
|
1986
|
+
if (value === null || typeof value !== "object") {
|
|
1987
|
+
return JSON.stringify(value);
|
|
1988
|
+
}
|
|
1989
|
+
if (Array.isArray(value)) {
|
|
1990
|
+
return "[" + value.map(canonicalJson).join(",") + "]";
|
|
1991
|
+
}
|
|
1992
|
+
const keys = Object.keys(value).sort();
|
|
1993
|
+
const parts = keys.map((k3) => {
|
|
1994
|
+
const v3 = value[k3];
|
|
1995
|
+
if (v3 === void 0) return "";
|
|
1996
|
+
return JSON.stringify(k3) + ":" + canonicalJson(v3);
|
|
1997
|
+
}).filter((s) => s !== "");
|
|
1998
|
+
return "{" + parts.join(",") + "}";
|
|
1999
|
+
}
|
|
2000
|
+
function sha256Hex(input) {
|
|
2001
|
+
return createHash("sha256").update(input, "utf-8").digest("hex");
|
|
2002
|
+
}
|
|
2003
|
+
function findMissingEntries(allow) {
|
|
2004
|
+
const allowSet = new Set(allow);
|
|
2005
|
+
return MASSU_PERMISSION_ENTRIES.filter((entry) => !allowSet.has(entry));
|
|
2006
|
+
}
|
|
2007
|
+
function detectInvalidDefaultMode(settings) {
|
|
2008
|
+
const permissions = settings.permissions;
|
|
2009
|
+
const mode = permissions?.defaultMode;
|
|
2010
|
+
if (typeof mode !== "string") {
|
|
2011
|
+
return { invalid: false };
|
|
2012
|
+
}
|
|
2013
|
+
if (LAUNCH_FLAG_REQUIRED_MODES.includes(mode)) {
|
|
2014
|
+
return {
|
|
2015
|
+
invalid: true,
|
|
2016
|
+
mode,
|
|
2017
|
+
reason: `defaultMode "${mode}" requires --permission-mode launch flag per https://code.claude.com/docs/en/permission-modes \u2014 settings-file value alone is inert. Remediation: launch with --permission-mode ${mode} OR change defaultMode to one of {default, acceptEdits, plan}.`
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
2020
|
+
return { invalid: false };
|
|
2021
|
+
}
|
|
2022
|
+
function readGlobalSettings() {
|
|
2023
|
+
return readSettingsAtPath(join2(homedir2(), ".claude", "settings.json"));
|
|
2024
|
+
}
|
|
2025
|
+
function mergedPermissionState(global2, local, canonical) {
|
|
2026
|
+
const globalPerm = global2.permissions ?? {};
|
|
2027
|
+
const localPerm = local.permissions ?? {};
|
|
2028
|
+
const localAllow = Array.isArray(localPerm.allow) ? localPerm.allow.filter((e2) => typeof e2 === "string") : [];
|
|
2029
|
+
const allowSet = new Set(localAllow);
|
|
2030
|
+
const allow = [...localAllow];
|
|
2031
|
+
for (const entry of canonical) {
|
|
2032
|
+
if (!allowSet.has(entry)) {
|
|
2033
|
+
allow.push(entry);
|
|
2034
|
+
allowSet.add(entry);
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
const result = { allow };
|
|
2038
|
+
if (typeof localPerm.defaultMode === "string") {
|
|
2039
|
+
result.defaultMode = localPerm.defaultMode;
|
|
2040
|
+
} else if (typeof globalPerm.defaultMode === "string") {
|
|
2041
|
+
result.defaultMode = globalPerm.defaultMode;
|
|
2042
|
+
}
|
|
2043
|
+
if (Array.isArray(localPerm.deny)) {
|
|
2044
|
+
result.deny = localPerm.deny.filter((e2) => typeof e2 === "string");
|
|
2045
|
+
}
|
|
2046
|
+
if (Array.isArray(localPerm.ask)) {
|
|
2047
|
+
result.ask = localPerm.ask.filter((e2) => typeof e2 === "string");
|
|
2048
|
+
}
|
|
2049
|
+
return result;
|
|
2050
|
+
}
|
|
2051
|
+
function resolveClaudeDir(claudeDir) {
|
|
2052
|
+
return claudeDir;
|
|
2053
|
+
}
|
|
2054
|
+
function hashOfPermissions(perm) {
|
|
2055
|
+
return sha256Hex(canonicalJson(perm));
|
|
2056
|
+
}
|
|
2057
|
+
function installPermissions(claudeDir, manifest, opts = {}) {
|
|
2058
|
+
const resolvedDir = resolveClaudeDir(claudeDir);
|
|
2059
|
+
const global2 = opts.global ?? readGlobalSettings();
|
|
2060
|
+
const local = readSettingsLocal(resolvedDir);
|
|
2061
|
+
const merged = mergedPermissionState(global2, local, MASSU_PERMISSION_ENTRIES);
|
|
2062
|
+
const expectedHash = hashOfPermissions(merged);
|
|
2063
|
+
const existingPerm = local.permissions ?? void 0;
|
|
2064
|
+
const existingHash = existingPerm ? hashOfPermissions(existingPerm) : void 0;
|
|
2065
|
+
const lastInstalledHash = manifest.entries[MANIFEST_KEY_PERMISSIONS];
|
|
2066
|
+
if (existingHash === expectedHash) {
|
|
2067
|
+
manifest.entries[MANIFEST_KEY_PERMISSIONS] = expectedHash;
|
|
2068
|
+
if (!opts.silent) {
|
|
2069
|
+
process.stderr.write(
|
|
2070
|
+
` Permissions: already in sync (allow: ${merged.allow.length} entries; defaultMode: ${merged.defaultMode ?? "omitted"}).
|
|
2071
|
+
`
|
|
2072
|
+
);
|
|
2073
|
+
}
|
|
2074
|
+
return { installed: 0, kept: 0, skipped: 1 };
|
|
2075
|
+
}
|
|
2076
|
+
if (lastInstalledHash !== void 0 && existingHash !== lastInstalledHash) {
|
|
2077
|
+
if (!opts.silent) {
|
|
2078
|
+
process.stderr.write(
|
|
2079
|
+
` Permissions: operator-edited since last install \u2014 preserving. Use \`npx massu permissions check-drift\` to inspect.
|
|
2080
|
+
`
|
|
2081
|
+
);
|
|
2082
|
+
}
|
|
2083
|
+
return { installed: 0, kept: 1, skipped: 0 };
|
|
2084
|
+
}
|
|
2085
|
+
const nextSettings = { ...local, permissions: merged };
|
|
2086
|
+
writeSettingsLocalAtomic(resolvedDir, nextSettings);
|
|
2087
|
+
manifest.entries[MANIFEST_KEY_PERMISSIONS] = expectedHash;
|
|
2088
|
+
const onDisk = readSettingsLocal(resolvedDir);
|
|
2089
|
+
const onDiskPerm = onDisk.permissions ?? {};
|
|
2090
|
+
if (merged.defaultMode !== void 0) {
|
|
2091
|
+
const diskDefaultMode = onDiskPerm.defaultMode;
|
|
2092
|
+
if (diskDefaultMode !== merged.defaultMode) {
|
|
2093
|
+
throw new InstallPermissionsAssertionError(
|
|
2094
|
+
`Post-write assertion failed: expected permissions.defaultMode="${merged.defaultMode}" on disk, got ${JSON.stringify(diskDefaultMode)}. This indicates a filesystem race or write failure.`
|
|
2095
|
+
);
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
const diskAllow = Array.isArray(onDiskPerm.allow) ? onDiskPerm.allow : [];
|
|
2099
|
+
for (const entry of MASSU_PERMISSION_ENTRIES) {
|
|
2100
|
+
if (!diskAllow.includes(entry)) {
|
|
2101
|
+
throw new InstallPermissionsAssertionError(
|
|
2102
|
+
`Post-write assertion failed: canonical entry "${entry}" missing from permissions.allow on disk.`
|
|
2103
|
+
);
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
if (!opts.silent) {
|
|
2107
|
+
process.stderr.write(
|
|
2108
|
+
` Wrote merged permissions block to .claude/settings.local.json (allow: ${merged.allow.length} entries; defaultMode: ${merged.defaultMode ?? "omitted"}).
|
|
2109
|
+
`
|
|
2110
|
+
);
|
|
2111
|
+
}
|
|
2112
|
+
return { installed: 1, kept: 0, skipped: 0 };
|
|
2113
|
+
}
|
|
2114
|
+
function verifyPermissions(claudeDir) {
|
|
2115
|
+
const local = readSettingsLocal(claudeDir);
|
|
2116
|
+
const permissions = local.permissions ?? {};
|
|
2117
|
+
const allow = Array.isArray(permissions.allow) ? permissions.allow.filter((e2) => typeof e2 === "string") : [];
|
|
2118
|
+
return {
|
|
2119
|
+
missing: findMissingEntries(allow),
|
|
2120
|
+
allowList: allow
|
|
2121
|
+
};
|
|
2122
|
+
}
|
|
2123
|
+
function checkPermissionsDrift(claudeDir, opts = {}) {
|
|
2124
|
+
const global2 = opts.global ?? readGlobalSettings();
|
|
2125
|
+
const local = readSettingsLocal(claudeDir);
|
|
2126
|
+
const items = [];
|
|
2127
|
+
const localPerm = local.permissions ?? {};
|
|
2128
|
+
const allow = Array.isArray(localPerm.allow) ? localPerm.allow.filter((e2) => typeof e2 === "string") : [];
|
|
2129
|
+
const missing = findMissingEntries(allow);
|
|
2130
|
+
for (const entry of missing) {
|
|
2131
|
+
items.push({
|
|
2132
|
+
kind: "missing-allow",
|
|
2133
|
+
detail: `Canonical massu allowlist entry missing from permissions.allow: "${entry}"`,
|
|
2134
|
+
remediation: "Run `npx massu permissions install` to seed."
|
|
2135
|
+
});
|
|
2136
|
+
}
|
|
2137
|
+
const invalidMode = detectInvalidDefaultMode(local);
|
|
2138
|
+
if (invalidMode.invalid && invalidMode.mode) {
|
|
2139
|
+
items.push({
|
|
2140
|
+
kind: "invalid-default-mode",
|
|
2141
|
+
detail: `defaultMode "${invalidMode.mode}" requires --permission-mode launch flag per code.claude.com/docs/en/permission-modes \u2014 settings-file value alone is inert.`,
|
|
2142
|
+
remediation: `Launch with --permission-mode ${invalidMode.mode} OR change defaultMode to one of {default, acceptEdits, plan}.`
|
|
2143
|
+
});
|
|
2144
|
+
}
|
|
2145
|
+
const globalPerm = global2.permissions ?? {};
|
|
2146
|
+
const localHasPermissions = local.permissions !== void 0 && local.permissions !== null && typeof local.permissions === "object";
|
|
2147
|
+
const localHasDefaultMode = typeof localPerm.defaultMode === "string";
|
|
2148
|
+
const globalHasDefaultMode = typeof globalPerm.defaultMode === "string";
|
|
2149
|
+
if (localHasPermissions && !localHasDefaultMode && globalHasDefaultMode) {
|
|
2150
|
+
items.push({
|
|
2151
|
+
kind: "strips-global-defaultmode",
|
|
2152
|
+
detail: `Project-local permissions object omits defaultMode while global ~/.claude/settings.json has defaultMode="${globalPerm.defaultMode}". Per empirical observation (2026-05-14) the merge unit is the entire permissions object, so the global defaultMode is silently stripped.`,
|
|
2153
|
+
remediation: "Run `npx massu permissions install` to write the full merged permissions block (auto-propagates global defaultMode)."
|
|
2154
|
+
});
|
|
2155
|
+
}
|
|
2156
|
+
for (const key of Object.keys(local)) {
|
|
2157
|
+
if (KNOWN_UNKNOWN_KEYS.has(key)) {
|
|
2158
|
+
items.push({
|
|
2159
|
+
kind: "unknown-key",
|
|
2160
|
+
detail: `Top-level settings key "${key}" is not documented at code.claude.com/docs/en/settings \u2014 silently ignored by Claude Code.`,
|
|
2161
|
+
remediation: `Remove or replace with a documented key (e.g. skipDangerousModePermissionPrompt).`
|
|
2162
|
+
});
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
return { driftItems: items };
|
|
2166
|
+
}
|
|
2167
|
+
var MASSU_PERMISSION_ENTRIES, LAUNCH_FLAG_REQUIRED_MODES, InstallPermissionsAssertionError, MANIFEST_KEY_PERMISSIONS, KNOWN_UNKNOWN_KEYS;
|
|
2168
|
+
var init_permissions = __esm({
|
|
2169
|
+
"src/permissions.ts"() {
|
|
2170
|
+
"use strict";
|
|
2171
|
+
init_config();
|
|
2172
|
+
init_settings_local();
|
|
2173
|
+
MASSU_PERMISSION_ENTRIES = ["mcp__massu__*"];
|
|
2174
|
+
LAUNCH_FLAG_REQUIRED_MODES = ["bypassPermissions", "auto", "dontAsk"];
|
|
2175
|
+
InstallPermissionsAssertionError = class extends Error {
|
|
2176
|
+
constructor(message) {
|
|
2177
|
+
super(message);
|
|
2178
|
+
this.name = "InstallPermissionsAssertionError";
|
|
2179
|
+
}
|
|
2180
|
+
};
|
|
2181
|
+
MANIFEST_KEY_PERMISSIONS = "__settings__/permissions";
|
|
2182
|
+
KNOWN_UNKNOWN_KEYS = /* @__PURE__ */ new Set([
|
|
2183
|
+
// From operator's observed ~/.claude/settings.json: top-level key that is
|
|
2184
|
+
// NOT in the docs (looks like a typo/wishful-thinking of skipDangerousModePermissionPrompt).
|
|
2185
|
+
"skipAutoPermissionPrompt"
|
|
2186
|
+
]);
|
|
2187
|
+
}
|
|
2188
|
+
});
|
|
2189
|
+
|
|
1913
2190
|
// src/commands/install-commands.ts
|
|
1914
2191
|
var install_commands_exports = {};
|
|
1915
2192
|
__export(install_commands_exports, {
|
|
@@ -1927,33 +2204,27 @@ __export(install_commands_exports, {
|
|
|
1927
2204
|
syncDirectory: () => syncDirectory
|
|
1928
2205
|
});
|
|
1929
2206
|
import {
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
openSync,
|
|
1934
|
-
readFileSync as readFileSync3,
|
|
1935
|
-
rmSync,
|
|
1936
|
-
writeSync,
|
|
1937
|
-
mkdirSync as mkdirSync2,
|
|
2207
|
+
existsSync as existsSync5,
|
|
2208
|
+
readFileSync as readFileSync4,
|
|
2209
|
+
mkdirSync as mkdirSync3,
|
|
1938
2210
|
readdirSync as readdirSync2,
|
|
1939
|
-
statSync
|
|
1940
|
-
renameSync
|
|
2211
|
+
statSync
|
|
1941
2212
|
} from "fs";
|
|
1942
|
-
import { resolve as
|
|
2213
|
+
import { resolve as resolve4, dirname as dirname4, join as join3 } from "path";
|
|
1943
2214
|
import { fileURLToPath } from "url";
|
|
1944
|
-
import { createHash } from "crypto";
|
|
2215
|
+
import { createHash as createHash2 } from "crypto";
|
|
1945
2216
|
function resolveAssetDir(assetName) {
|
|
1946
2217
|
const cwd = process.cwd();
|
|
1947
|
-
const nodeModulesPath =
|
|
1948
|
-
if (
|
|
2218
|
+
const nodeModulesPath = resolve4(cwd, "node_modules/@massu/core", assetName);
|
|
2219
|
+
if (existsSync5(nodeModulesPath)) {
|
|
1949
2220
|
return nodeModulesPath;
|
|
1950
2221
|
}
|
|
1951
|
-
const distRelPath =
|
|
1952
|
-
if (
|
|
2222
|
+
const distRelPath = resolve4(__dirname, "..", assetName);
|
|
2223
|
+
if (existsSync5(distRelPath)) {
|
|
1953
2224
|
return distRelPath;
|
|
1954
2225
|
}
|
|
1955
|
-
const srcRelPath =
|
|
1956
|
-
if (
|
|
2226
|
+
const srcRelPath = resolve4(__dirname, "../..", assetName);
|
|
2227
|
+
if (existsSync5(srcRelPath)) {
|
|
1957
2228
|
return srcRelPath;
|
|
1958
2229
|
}
|
|
1959
2230
|
return null;
|
|
@@ -1962,15 +2233,15 @@ function resolveCommandsDir() {
|
|
|
1962
2233
|
return resolveAssetDir("commands");
|
|
1963
2234
|
}
|
|
1964
2235
|
function hashContent(content) {
|
|
1965
|
-
return
|
|
2236
|
+
return createHash2("sha256").update(content, "utf-8").digest("hex");
|
|
1966
2237
|
}
|
|
1967
2238
|
function loadManifest(claudeDir) {
|
|
1968
|
-
const path =
|
|
1969
|
-
if (!
|
|
2239
|
+
const path = resolve4(claudeDir, MANIFEST_RELPATH);
|
|
2240
|
+
if (!existsSync5(path)) {
|
|
1970
2241
|
return emptyManifest();
|
|
1971
2242
|
}
|
|
1972
2243
|
try {
|
|
1973
|
-
const raw =
|
|
2244
|
+
const raw = readFileSync4(path, "utf-8");
|
|
1974
2245
|
const parsed = JSON.parse(raw);
|
|
1975
2246
|
if (!parsed || typeof parsed !== "object" || !parsed.entries) {
|
|
1976
2247
|
return emptyManifest();
|
|
@@ -1980,34 +2251,12 @@ function loadManifest(claudeDir) {
|
|
|
1980
2251
|
return emptyManifest();
|
|
1981
2252
|
}
|
|
1982
2253
|
}
|
|
1983
|
-
function atomicWriteFile(targetPath, content, mode = 420) {
|
|
1984
|
-
const tmpPath = `${targetPath}.${process.pid}.tmp`;
|
|
1985
|
-
try {
|
|
1986
|
-
const fd = openSync(tmpPath, "w", mode);
|
|
1987
|
-
try {
|
|
1988
|
-
const buf = Buffer.from(content, "utf-8");
|
|
1989
|
-
writeSync(fd, buf, 0, buf.length, 0);
|
|
1990
|
-
fsyncSync(fd);
|
|
1991
|
-
} finally {
|
|
1992
|
-
closeSync(fd);
|
|
1993
|
-
}
|
|
1994
|
-
renameSync(tmpPath, targetPath);
|
|
1995
|
-
} catch (err) {
|
|
1996
|
-
if (existsSync4(tmpPath)) {
|
|
1997
|
-
try {
|
|
1998
|
-
rmSync(tmpPath, { force: true });
|
|
1999
|
-
} catch {
|
|
2000
|
-
}
|
|
2001
|
-
}
|
|
2002
|
-
throw err;
|
|
2003
|
-
}
|
|
2004
|
-
}
|
|
2005
2254
|
function saveManifest(claudeDir, manifest) {
|
|
2006
|
-
const dir =
|
|
2007
|
-
if (!
|
|
2008
|
-
|
|
2255
|
+
const dir = resolve4(claudeDir, ".massu");
|
|
2256
|
+
if (!existsSync5(dir)) {
|
|
2257
|
+
mkdirSync3(dir, { recursive: true });
|
|
2009
2258
|
}
|
|
2010
|
-
const finalPath =
|
|
2259
|
+
const finalPath = resolve4(dir, "install-manifest.json");
|
|
2011
2260
|
manifest.generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2012
2261
|
atomicWriteFile(finalPath, JSON.stringify(manifest, null, 2));
|
|
2013
2262
|
}
|
|
@@ -2069,18 +2318,18 @@ function pickVariant(baseName, sourceDir, framework) {
|
|
|
2069
2318
|
}
|
|
2070
2319
|
for (const cand of candidates) {
|
|
2071
2320
|
if (cand.subFramework) {
|
|
2072
|
-
const subPath =
|
|
2073
|
-
if (
|
|
2321
|
+
const subPath = resolve4(sourceDir, `${baseName}.${cand.lang}-${cand.subFramework}.md`);
|
|
2322
|
+
if (existsSync5(subPath)) {
|
|
2074
2323
|
return { kind: "hit", suffix: `.${cand.lang}-${cand.subFramework}` };
|
|
2075
2324
|
}
|
|
2076
2325
|
}
|
|
2077
|
-
const langPath =
|
|
2078
|
-
if (
|
|
2326
|
+
const langPath = resolve4(sourceDir, `${baseName}.${cand.lang}.md`);
|
|
2327
|
+
if (existsSync5(langPath)) {
|
|
2079
2328
|
return { kind: "hit", suffix: `.${cand.lang}` };
|
|
2080
2329
|
}
|
|
2081
2330
|
}
|
|
2082
|
-
const defaultPath =
|
|
2083
|
-
if (
|
|
2331
|
+
const defaultPath = resolve4(sourceDir, `${baseName}.md`);
|
|
2332
|
+
if (existsSync5(defaultPath)) {
|
|
2084
2333
|
return { kind: "hit", suffix: "" };
|
|
2085
2334
|
}
|
|
2086
2335
|
if (framework.type === "multi" && !framework.primary) {
|
|
@@ -2096,15 +2345,15 @@ function isVariantFilename(entry) {
|
|
|
2096
2345
|
}
|
|
2097
2346
|
function syncDirectory(sourceDir, targetDir, framework, manifest, manifestKeyPrefix, topLevel = true, templateVars = {}) {
|
|
2098
2347
|
const stats = { installed: 0, updated: 0, skipped: 0, kept: 0 };
|
|
2099
|
-
if (!
|
|
2100
|
-
|
|
2348
|
+
if (!existsSync5(targetDir)) {
|
|
2349
|
+
mkdirSync3(targetDir, { recursive: true });
|
|
2101
2350
|
}
|
|
2102
2351
|
const entries = readdirSync2(sourceDir);
|
|
2103
2352
|
for (const entry of entries) {
|
|
2104
|
-
const sourcePath =
|
|
2353
|
+
const sourcePath = resolve4(sourceDir, entry);
|
|
2105
2354
|
const entryStat = statSync(sourcePath);
|
|
2106
2355
|
if (entryStat.isDirectory()) {
|
|
2107
|
-
const subTargetDir =
|
|
2356
|
+
const subTargetDir = resolve4(targetDir, entry);
|
|
2108
2357
|
const subPrefix = manifestKeyPrefix === "" ? entry : `${manifestKeyPrefix}/${entry}`;
|
|
2109
2358
|
const subStats = syncDirectory(
|
|
2110
2359
|
sourcePath,
|
|
@@ -2133,13 +2382,13 @@ function syncDirectory(sourceDir, targetDir, framework, manifest, manifestKeyPre
|
|
|
2133
2382
|
const suffix = choice.kind === "hit" ? choice.suffix : "";
|
|
2134
2383
|
sourceFilename = suffix === "" ? `${baseName}.md` : `${baseName}${suffix}.md`;
|
|
2135
2384
|
}
|
|
2136
|
-
const resolvedSourcePath =
|
|
2137
|
-
if (!
|
|
2385
|
+
const resolvedSourcePath = resolve4(sourceDir, sourceFilename);
|
|
2386
|
+
if (!existsSync5(resolvedSourcePath)) {
|
|
2138
2387
|
continue;
|
|
2139
2388
|
}
|
|
2140
2389
|
const targetFilename = topLevel ? `${baseName}.md` : entry;
|
|
2141
|
-
const targetPath =
|
|
2142
|
-
const rawContent =
|
|
2390
|
+
const targetPath = resolve4(targetDir, targetFilename);
|
|
2391
|
+
const rawContent = readFileSync4(resolvedSourcePath, "utf-8");
|
|
2143
2392
|
let sourceContent;
|
|
2144
2393
|
try {
|
|
2145
2394
|
sourceContent = renderTemplate(rawContent, templateVars);
|
|
@@ -2157,8 +2406,8 @@ function syncDirectory(sourceDir, targetDir, framework, manifest, manifestKeyPre
|
|
|
2157
2406
|
const sourceHash = hashContent(sourceContent);
|
|
2158
2407
|
const manifestKey = manifestKeyPrefix === "" ? targetFilename : `${manifestKeyPrefix}/${targetFilename}`;
|
|
2159
2408
|
const lastInstalledHash = manifest.entries[manifestKey];
|
|
2160
|
-
if (
|
|
2161
|
-
const existingContent =
|
|
2409
|
+
if (existsSync5(targetPath)) {
|
|
2410
|
+
const existingContent = readFileSync4(targetPath, "utf-8");
|
|
2162
2411
|
const existingHash = hashContent(existingContent);
|
|
2163
2412
|
if (existingHash === sourceHash) {
|
|
2164
2413
|
manifest.entries[manifestKey] = sourceHash;
|
|
@@ -2205,12 +2454,12 @@ function buildTemplateVars() {
|
|
|
2205
2454
|
config
|
|
2206
2455
|
};
|
|
2207
2456
|
}
|
|
2208
|
-
function installCommands(projectRoot) {
|
|
2457
|
+
function installCommands(projectRoot, opts = {}) {
|
|
2209
2458
|
const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
|
|
2210
|
-
const claudeDir =
|
|
2211
|
-
const targetDir =
|
|
2212
|
-
if (!
|
|
2213
|
-
|
|
2459
|
+
const claudeDir = resolve4(projectRoot, claudeDirName);
|
|
2460
|
+
const targetDir = resolve4(claudeDir, "commands");
|
|
2461
|
+
if (!existsSync5(targetDir)) {
|
|
2462
|
+
mkdirSync3(targetDir, { recursive: true });
|
|
2214
2463
|
}
|
|
2215
2464
|
const sourceDir = resolveAssetDir("commands");
|
|
2216
2465
|
if (!sourceDir) {
|
|
@@ -2220,27 +2469,39 @@ function installCommands(projectRoot) {
|
|
|
2220
2469
|
}
|
|
2221
2470
|
const framework = getConfig().framework;
|
|
2222
2471
|
const templateVars = buildTemplateVars();
|
|
2223
|
-
const stats = runWithManifest(
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2472
|
+
const stats = runWithManifest(claudeDir, (manifest) => {
|
|
2473
|
+
const syncStats = syncDirectory(
|
|
2474
|
+
sourceDir,
|
|
2475
|
+
targetDir,
|
|
2476
|
+
framework,
|
|
2477
|
+
manifest,
|
|
2478
|
+
"commands",
|
|
2479
|
+
true,
|
|
2480
|
+
templateVars
|
|
2481
|
+
);
|
|
2482
|
+
if (!opts.skipPermissions) {
|
|
2483
|
+
installPermissions(claudeDir, manifest, { silent: true });
|
|
2484
|
+
}
|
|
2485
|
+
return syncStats;
|
|
2486
|
+
});
|
|
2227
2487
|
return { ...stats, commandsDir: targetDir };
|
|
2228
2488
|
}
|
|
2229
|
-
function installAll(projectRoot) {
|
|
2489
|
+
function installAll(projectRoot, opts = {}) {
|
|
2230
2490
|
const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
|
|
2231
|
-
const claudeDir =
|
|
2491
|
+
const claudeDir = resolve4(projectRoot, claudeDirName);
|
|
2232
2492
|
const assets = {};
|
|
2233
2493
|
let totalInstalled = 0;
|
|
2234
2494
|
let totalUpdated = 0;
|
|
2235
2495
|
let totalSkipped = 0;
|
|
2236
2496
|
let totalKept = 0;
|
|
2497
|
+
let permissionsResult;
|
|
2237
2498
|
const framework = getConfig().framework;
|
|
2238
2499
|
const templateVars = buildTemplateVars();
|
|
2239
2500
|
runWithManifest(claudeDir, (manifest) => {
|
|
2240
2501
|
for (const assetType of ASSET_TYPES) {
|
|
2241
2502
|
const sourceDir = resolveAssetDir(assetType.name);
|
|
2242
2503
|
if (!sourceDir) continue;
|
|
2243
|
-
const targetDir =
|
|
2504
|
+
const targetDir = resolve4(claudeDir, assetType.targetSubdir);
|
|
2244
2505
|
const stats = syncDirectory(
|
|
2245
2506
|
sourceDir,
|
|
2246
2507
|
targetDir,
|
|
@@ -2256,6 +2517,9 @@ function installAll(projectRoot) {
|
|
|
2256
2517
|
totalSkipped += stats.skipped;
|
|
2257
2518
|
totalKept += stats.kept;
|
|
2258
2519
|
}
|
|
2520
|
+
if (!opts.skipPermissions) {
|
|
2521
|
+
permissionsResult = installPermissions(claudeDir, manifest, { silent: true });
|
|
2522
|
+
}
|
|
2259
2523
|
});
|
|
2260
2524
|
return {
|
|
2261
2525
|
assets,
|
|
@@ -2263,16 +2527,18 @@ function installAll(projectRoot) {
|
|
|
2263
2527
|
totalUpdated,
|
|
2264
2528
|
totalSkipped,
|
|
2265
2529
|
totalKept,
|
|
2266
|
-
claudeDir
|
|
2530
|
+
claudeDir,
|
|
2531
|
+
permissions: permissionsResult
|
|
2267
2532
|
};
|
|
2268
2533
|
}
|
|
2269
2534
|
async function runInstallCommands() {
|
|
2270
2535
|
const projectRoot = process.cwd();
|
|
2536
|
+
const skipPermissions = process.argv.slice(2).includes("--skip-permissions");
|
|
2271
2537
|
console.log("");
|
|
2272
2538
|
console.log("Massu AI - Install Project Assets");
|
|
2273
2539
|
console.log("==================================");
|
|
2274
2540
|
console.log("");
|
|
2275
|
-
const result = installAll(projectRoot);
|
|
2541
|
+
const result = installAll(projectRoot, { skipPermissions });
|
|
2276
2542
|
for (const assetType of ASSET_TYPES) {
|
|
2277
2543
|
const stats = result.assets[assetType.name];
|
|
2278
2544
|
if (!stats) {
|
|
@@ -2296,6 +2562,19 @@ async function runInstallCommands() {
|
|
|
2296
2562
|
` ${result.totalKept} file(s) had local edits and were preserved (see stderr above).`
|
|
2297
2563
|
);
|
|
2298
2564
|
}
|
|
2565
|
+
if (skipPermissions) {
|
|
2566
|
+
console.log(" Permission seeding skipped (--skip-permissions).");
|
|
2567
|
+
} else if (result.permissions) {
|
|
2568
|
+
if (result.permissions.installed > 0) {
|
|
2569
|
+
console.log(
|
|
2570
|
+
` Wrote merged permissions block to .claude/settings.local.json (use --skip-permissions to opt out).`
|
|
2571
|
+
);
|
|
2572
|
+
} else if (result.permissions.kept > 0) {
|
|
2573
|
+
console.log(
|
|
2574
|
+
` MCP allowlist entry was edited by operator; preserved. Use \`npx massu permissions check-drift\` to inspect.`
|
|
2575
|
+
);
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2299
2578
|
console.log("");
|
|
2300
2579
|
console.log(" Restart your Claude Code session to use them.");
|
|
2301
2580
|
console.log("");
|
|
@@ -2306,8 +2585,10 @@ var init_install_commands = __esm({
|
|
|
2306
2585
|
"use strict";
|
|
2307
2586
|
init_config();
|
|
2308
2587
|
init_template_engine();
|
|
2588
|
+
init_settings_local();
|
|
2589
|
+
init_permissions();
|
|
2309
2590
|
__filename = fileURLToPath(import.meta.url);
|
|
2310
|
-
__dirname =
|
|
2591
|
+
__dirname = dirname4(__filename);
|
|
2311
2592
|
ASSET_TYPES = [
|
|
2312
2593
|
{ name: "commands", targetSubdir: "commands", description: "slash commands" },
|
|
2313
2594
|
{ name: "agents", targetSubdir: "agents", description: "agent definitions" },
|
|
@@ -2316,7 +2597,7 @@ var init_install_commands = __esm({
|
|
|
2316
2597
|
{ name: "reference", targetSubdir: "reference", description: "reference files" }
|
|
2317
2598
|
];
|
|
2318
2599
|
MANIFEST_VERSION = 1;
|
|
2319
|
-
MANIFEST_RELPATH =
|
|
2600
|
+
MANIFEST_RELPATH = join3(".massu", "install-manifest.json");
|
|
2320
2601
|
PASSTHROUGH_LANG_KEYS = [
|
|
2321
2602
|
"typescript",
|
|
2322
2603
|
"javascript",
|
|
@@ -2490,17 +2771,17 @@ var init_manifest_registry = __esm({
|
|
|
2490
2771
|
});
|
|
2491
2772
|
|
|
2492
2773
|
// src/detect/package-detector.ts
|
|
2493
|
-
import { readFileSync as
|
|
2494
|
-
import { join as
|
|
2774
|
+
import { readFileSync as readFileSync5, existsSync as existsSync6, statSync as statSync2, lstatSync, readdirSync as readdirSync3 } from "fs";
|
|
2775
|
+
import { join as join4, relative as relative2 } from "path";
|
|
2495
2776
|
import { parse as parseToml } from "smol-toml";
|
|
2496
2777
|
function safeRead(path) {
|
|
2497
2778
|
try {
|
|
2498
|
-
if (!
|
|
2779
|
+
if (!existsSync6(path)) return null;
|
|
2499
2780
|
const ls = lstatSync(path);
|
|
2500
2781
|
if (ls.isSymbolicLink()) return null;
|
|
2501
2782
|
const st = statSync2(path);
|
|
2502
2783
|
if (!st.isFile()) return null;
|
|
2503
|
-
return
|
|
2784
|
+
return readFileSync5(path, "utf-8");
|
|
2504
2785
|
} catch {
|
|
2505
2786
|
return null;
|
|
2506
2787
|
}
|
|
@@ -2531,7 +2812,7 @@ function parsePackageJson(path, directory, root, warnings) {
|
|
|
2531
2812
|
const peer = Object.keys(
|
|
2532
2813
|
pkg.peerDependencies ?? {}
|
|
2533
2814
|
);
|
|
2534
|
-
const hasTs = deps.includes("typescript") || devDeps.includes("typescript") ||
|
|
2815
|
+
const hasTs = deps.includes("typescript") || devDeps.includes("typescript") || existsSync6(join4(directory, "tsconfig.json"));
|
|
2535
2816
|
const language = hasTs ? "typescript" : "javascript";
|
|
2536
2817
|
const scripts = Object.keys(
|
|
2537
2818
|
pkg.scripts ?? {}
|
|
@@ -2957,8 +3238,8 @@ function detectManifestsInDir(dir, root, warnings) {
|
|
|
2957
3238
|
let dirEntries = null;
|
|
2958
3239
|
for (const entry of getManifestRegistry2()) {
|
|
2959
3240
|
if (!entry.pattern.startsWith("*")) {
|
|
2960
|
-
const path =
|
|
2961
|
-
if (!
|
|
3241
|
+
const path = join4(dir, entry.pattern);
|
|
3242
|
+
if (!existsSync6(path)) continue;
|
|
2962
3243
|
const m3 = entry.parse(path, dir, root, warnings);
|
|
2963
3244
|
if (m3 !== null) out.push(m3);
|
|
2964
3245
|
} else {
|
|
@@ -2971,8 +3252,8 @@ function detectManifestsInDir(dir, root, warnings) {
|
|
|
2971
3252
|
}
|
|
2972
3253
|
for (const fname of dirEntries) {
|
|
2973
3254
|
if (!matchManifestPattern2(fname, entry.pattern)) continue;
|
|
2974
|
-
const path =
|
|
2975
|
-
if (!
|
|
3255
|
+
const path = join4(dir, fname);
|
|
3256
|
+
if (!existsSync6(path)) continue;
|
|
2976
3257
|
const m3 = entry.parse(path, dir, root, warnings);
|
|
2977
3258
|
if (m3 !== null) out.push(m3);
|
|
2978
3259
|
}
|
|
@@ -2982,7 +3263,7 @@ function detectManifestsInDir(dir, root, warnings) {
|
|
|
2982
3263
|
}
|
|
2983
3264
|
function listSubdirs(dir) {
|
|
2984
3265
|
try {
|
|
2985
|
-
return readdirSync3(dir, { withFileTypes: true }).filter((e2) => e2.isDirectory() && !IGNORED_DIRS.has(e2.name)).map((e2) =>
|
|
3266
|
+
return readdirSync3(dir, { withFileTypes: true }).filter((e2) => e2.isDirectory() && !IGNORED_DIRS.has(e2.name)).map((e2) => join4(dir, e2.name));
|
|
2986
3267
|
} catch {
|
|
2987
3268
|
return [];
|
|
2988
3269
|
}
|
|
@@ -2992,8 +3273,8 @@ function detectPackageManifests(projectRoot) {
|
|
|
2992
3273
|
const manifests = [];
|
|
2993
3274
|
manifests.push(...detectManifestsInDir(projectRoot, projectRoot, warnings));
|
|
2994
3275
|
for (const ws of WORKSPACE_DIRS) {
|
|
2995
|
-
const wsRoot =
|
|
2996
|
-
if (!
|
|
3276
|
+
const wsRoot = join4(projectRoot, ws);
|
|
3277
|
+
if (!existsSync6(wsRoot)) continue;
|
|
2997
3278
|
for (const sub of listSubdirs(wsRoot)) {
|
|
2998
3279
|
manifests.push(...detectManifestsInDir(sub, projectRoot, warnings));
|
|
2999
3280
|
for (const sub2 of listSubdirs(sub)) {
|
|
@@ -7764,41 +8045,41 @@ var require_queue = __commonJS({
|
|
|
7764
8045
|
queue.drained = drained;
|
|
7765
8046
|
return queue;
|
|
7766
8047
|
function push(value) {
|
|
7767
|
-
var p19 = new Promise(function(
|
|
8048
|
+
var p19 = new Promise(function(resolve38, reject) {
|
|
7768
8049
|
pushCb(value, function(err, result) {
|
|
7769
8050
|
if (err) {
|
|
7770
8051
|
reject(err);
|
|
7771
8052
|
return;
|
|
7772
8053
|
}
|
|
7773
|
-
|
|
8054
|
+
resolve38(result);
|
|
7774
8055
|
});
|
|
7775
8056
|
});
|
|
7776
8057
|
p19.catch(noop);
|
|
7777
8058
|
return p19;
|
|
7778
8059
|
}
|
|
7779
8060
|
function unshift(value) {
|
|
7780
|
-
var p19 = new Promise(function(
|
|
8061
|
+
var p19 = new Promise(function(resolve38, reject) {
|
|
7781
8062
|
unshiftCb(value, function(err, result) {
|
|
7782
8063
|
if (err) {
|
|
7783
8064
|
reject(err);
|
|
7784
8065
|
return;
|
|
7785
8066
|
}
|
|
7786
|
-
|
|
8067
|
+
resolve38(result);
|
|
7787
8068
|
});
|
|
7788
8069
|
});
|
|
7789
8070
|
p19.catch(noop);
|
|
7790
8071
|
return p19;
|
|
7791
8072
|
}
|
|
7792
8073
|
function drained() {
|
|
7793
|
-
var p19 = new Promise(function(
|
|
8074
|
+
var p19 = new Promise(function(resolve38) {
|
|
7794
8075
|
process.nextTick(function() {
|
|
7795
8076
|
if (queue.idle()) {
|
|
7796
|
-
|
|
8077
|
+
resolve38();
|
|
7797
8078
|
} else {
|
|
7798
8079
|
var previousDrain = queue.drain;
|
|
7799
8080
|
queue.drain = function() {
|
|
7800
8081
|
if (typeof previousDrain === "function") previousDrain();
|
|
7801
|
-
|
|
8082
|
+
resolve38();
|
|
7802
8083
|
queue.drain = previousDrain;
|
|
7803
8084
|
};
|
|
7804
8085
|
}
|
|
@@ -8284,9 +8565,9 @@ var require_stream3 = __commonJS({
|
|
|
8284
8565
|
});
|
|
8285
8566
|
}
|
|
8286
8567
|
_getStat(filepath) {
|
|
8287
|
-
return new Promise((
|
|
8568
|
+
return new Promise((resolve38, reject) => {
|
|
8288
8569
|
this._stat(filepath, this._fsStatSettings, (error, stats) => {
|
|
8289
|
-
return error === null ?
|
|
8570
|
+
return error === null ? resolve38(stats) : reject(error);
|
|
8290
8571
|
});
|
|
8291
8572
|
});
|
|
8292
8573
|
}
|
|
@@ -8310,10 +8591,10 @@ var require_async5 = __commonJS({
|
|
|
8310
8591
|
this._readerStream = new stream_1.default(this._settings);
|
|
8311
8592
|
}
|
|
8312
8593
|
dynamic(root, options) {
|
|
8313
|
-
return new Promise((
|
|
8594
|
+
return new Promise((resolve38, reject) => {
|
|
8314
8595
|
this._walkAsync(root, options, (error, entries) => {
|
|
8315
8596
|
if (error === null) {
|
|
8316
|
-
|
|
8597
|
+
resolve38(entries);
|
|
8317
8598
|
} else {
|
|
8318
8599
|
reject(error);
|
|
8319
8600
|
}
|
|
@@ -8323,10 +8604,10 @@ var require_async5 = __commonJS({
|
|
|
8323
8604
|
async static(patterns, options) {
|
|
8324
8605
|
const entries = [];
|
|
8325
8606
|
const stream = this._readerStream.static(patterns, options);
|
|
8326
|
-
return new Promise((
|
|
8607
|
+
return new Promise((resolve38, reject) => {
|
|
8327
8608
|
stream.once("error", reject);
|
|
8328
8609
|
stream.on("data", (entry) => entries.push(entry));
|
|
8329
|
-
stream.once("end", () =>
|
|
8610
|
+
stream.once("end", () => resolve38(entries));
|
|
8330
8611
|
});
|
|
8331
8612
|
}
|
|
8332
8613
|
};
|
|
@@ -8985,7 +9266,7 @@ var require_out4 = __commonJS({
|
|
|
8985
9266
|
|
|
8986
9267
|
// src/detect/source-dir-detector.ts
|
|
8987
9268
|
import { realpathSync } from "fs";
|
|
8988
|
-
import { resolve as
|
|
9269
|
+
import { resolve as resolve5 } from "path";
|
|
8989
9270
|
function extsFor(language) {
|
|
8990
9271
|
return EXTENSIONS[language] ?? [];
|
|
8991
9272
|
}
|
|
@@ -9011,7 +9292,7 @@ function topSegment(rel) {
|
|
|
9011
9292
|
function isInsideRoot(root, candidate) {
|
|
9012
9293
|
try {
|
|
9013
9294
|
const realRoot = realpathSync(root);
|
|
9014
|
-
const realCand = realpathSync(
|
|
9295
|
+
const realCand = realpathSync(resolve5(root, candidate));
|
|
9015
9296
|
return realCand === realRoot || realCand.startsWith(realRoot + "/");
|
|
9016
9297
|
} catch {
|
|
9017
9298
|
return false;
|
|
@@ -9179,38 +9460,38 @@ var init_source_dir_detector = __esm({
|
|
|
9179
9460
|
});
|
|
9180
9461
|
|
|
9181
9462
|
// src/detect/monorepo-detector.ts
|
|
9182
|
-
import { readFileSync as
|
|
9183
|
-
import { join as
|
|
9463
|
+
import { readFileSync as readFileSync6, existsSync as existsSync7, statSync as statSync3, lstatSync as lstatSync2, readdirSync as readdirSync4 } from "fs";
|
|
9464
|
+
import { join as join5, relative as relative3 } from "path";
|
|
9184
9465
|
import { parse as parseYaml3 } from "yaml";
|
|
9185
9466
|
import { parse as parseToml2 } from "smol-toml";
|
|
9186
9467
|
function safeReadText(path) {
|
|
9187
9468
|
try {
|
|
9188
|
-
if (!
|
|
9469
|
+
if (!existsSync7(path)) return null;
|
|
9189
9470
|
const ls = lstatSync2(path);
|
|
9190
9471
|
if (ls.isSymbolicLink()) return null;
|
|
9191
9472
|
const st = statSync3(path);
|
|
9192
9473
|
if (!st.isFile()) return null;
|
|
9193
|
-
return
|
|
9474
|
+
return readFileSync6(path, "utf-8");
|
|
9194
9475
|
} catch {
|
|
9195
9476
|
return null;
|
|
9196
9477
|
}
|
|
9197
9478
|
}
|
|
9198
9479
|
function firstManifestIn(dir) {
|
|
9199
9480
|
for (const m3 of MANIFEST_PRIORITY) {
|
|
9200
|
-
if (
|
|
9481
|
+
if (existsSync7(join5(dir, m3))) return m3;
|
|
9201
9482
|
}
|
|
9202
9483
|
return null;
|
|
9203
9484
|
}
|
|
9204
9485
|
function manifestName(dir, manifest) {
|
|
9205
9486
|
try {
|
|
9206
9487
|
if (manifest === "package.json") {
|
|
9207
|
-
const raw = safeReadText(
|
|
9488
|
+
const raw = safeReadText(join5(dir, "package.json"));
|
|
9208
9489
|
if (!raw) return null;
|
|
9209
9490
|
const pkg = JSON.parse(raw);
|
|
9210
9491
|
return typeof pkg.name === "string" ? pkg.name : null;
|
|
9211
9492
|
}
|
|
9212
9493
|
if (manifest === "pyproject.toml") {
|
|
9213
|
-
const raw = safeReadText(
|
|
9494
|
+
const raw = safeReadText(join5(dir, "pyproject.toml"));
|
|
9214
9495
|
if (!raw) return null;
|
|
9215
9496
|
const toml = parseToml2(raw);
|
|
9216
9497
|
const project = toml.project;
|
|
@@ -9221,7 +9502,7 @@ function manifestName(dir, manifest) {
|
|
|
9221
9502
|
return null;
|
|
9222
9503
|
}
|
|
9223
9504
|
if (manifest === "Cargo.toml") {
|
|
9224
|
-
const raw = safeReadText(
|
|
9505
|
+
const raw = safeReadText(join5(dir, "Cargo.toml"));
|
|
9225
9506
|
if (!raw) return null;
|
|
9226
9507
|
const toml = parseToml2(raw);
|
|
9227
9508
|
const pkg = toml.package;
|
|
@@ -9229,7 +9510,7 @@ function manifestName(dir, manifest) {
|
|
|
9229
9510
|
return null;
|
|
9230
9511
|
}
|
|
9231
9512
|
if (manifest === "go.mod") {
|
|
9232
|
-
const raw = safeReadText(
|
|
9513
|
+
const raw = safeReadText(join5(dir, "go.mod"));
|
|
9233
9514
|
if (!raw) return null;
|
|
9234
9515
|
for (const line of raw.split(/\r?\n/)) {
|
|
9235
9516
|
const trimmed = line.trim();
|
|
@@ -9253,7 +9534,7 @@ function pkgFromDir(root, dir) {
|
|
|
9253
9534
|
}
|
|
9254
9535
|
function listSubdirs2(dir) {
|
|
9255
9536
|
try {
|
|
9256
|
-
return readdirSync4(dir, { withFileTypes: true }).filter((e2) => e2.isDirectory() && !IGNORED_DIRS2.has(e2.name)).map((e2) =>
|
|
9537
|
+
return readdirSync4(dir, { withFileTypes: true }).filter((e2) => e2.isDirectory() && !IGNORED_DIRS2.has(e2.name)).map((e2) => join5(dir, e2.name));
|
|
9257
9538
|
} catch {
|
|
9258
9539
|
return [];
|
|
9259
9540
|
}
|
|
@@ -9261,8 +9542,8 @@ function listSubdirs2(dir) {
|
|
|
9261
9542
|
function genericWorkspaces(root) {
|
|
9262
9543
|
const out = [];
|
|
9263
9544
|
for (const parent of CONVENTIONAL_WORKSPACE_PARENTS) {
|
|
9264
|
-
const p19 =
|
|
9265
|
-
if (!
|
|
9545
|
+
const p19 = join5(root, parent);
|
|
9546
|
+
if (!existsSync7(p19)) continue;
|
|
9266
9547
|
for (const sub of listSubdirs2(p19)) {
|
|
9267
9548
|
const pkg = pkgFromDir(root, sub);
|
|
9268
9549
|
if (pkg) out.push(pkg);
|
|
@@ -9271,7 +9552,7 @@ function genericWorkspaces(root) {
|
|
|
9271
9552
|
return out;
|
|
9272
9553
|
}
|
|
9273
9554
|
function detectYarnWorkspaces(root) {
|
|
9274
|
-
const raw = safeReadText(
|
|
9555
|
+
const raw = safeReadText(join5(root, "package.json"));
|
|
9275
9556
|
if (!raw) return null;
|
|
9276
9557
|
let pkg;
|
|
9277
9558
|
try {
|
|
@@ -9286,7 +9567,7 @@ function detectYarnWorkspaces(root) {
|
|
|
9286
9567
|
return expandWorkspaceGlobs(root, globs);
|
|
9287
9568
|
}
|
|
9288
9569
|
function detectPnpmWorkspaces(root) {
|
|
9289
|
-
const raw = safeReadText(
|
|
9570
|
+
const raw = safeReadText(join5(root, "pnpm-workspace.yaml"));
|
|
9290
9571
|
if (!raw) return null;
|
|
9291
9572
|
try {
|
|
9292
9573
|
const parsed = parseYaml3(raw);
|
|
@@ -9302,8 +9583,8 @@ function expandWorkspaceGlobs(root, globs) {
|
|
|
9302
9583
|
for (const pattern of globs) {
|
|
9303
9584
|
const parts = pattern.split("/");
|
|
9304
9585
|
if (parts.length === 2 && (parts[1] === "*" || parts[1] === "**")) {
|
|
9305
|
-
const parent =
|
|
9306
|
-
if (!
|
|
9586
|
+
const parent = join5(root, parts[0]);
|
|
9587
|
+
if (!existsSync7(parent)) continue;
|
|
9307
9588
|
for (const sub of listSubdirs2(parent)) {
|
|
9308
9589
|
const pkg = pkgFromDir(root, sub);
|
|
9309
9590
|
if (pkg && !seen.has(pkg.path)) {
|
|
@@ -9313,8 +9594,8 @@ function expandWorkspaceGlobs(root, globs) {
|
|
|
9313
9594
|
}
|
|
9314
9595
|
continue;
|
|
9315
9596
|
}
|
|
9316
|
-
const direct =
|
|
9317
|
-
if (
|
|
9597
|
+
const direct = join5(root, pattern);
|
|
9598
|
+
if (existsSync7(direct)) {
|
|
9318
9599
|
const pkg = pkgFromDir(root, direct);
|
|
9319
9600
|
if (pkg && !seen.has(pkg.path)) {
|
|
9320
9601
|
seen.add(pkg.path);
|
|
@@ -9325,16 +9606,16 @@ function expandWorkspaceGlobs(root, globs) {
|
|
|
9325
9606
|
return out;
|
|
9326
9607
|
}
|
|
9327
9608
|
function hasTurbo(root) {
|
|
9328
|
-
return
|
|
9609
|
+
return existsSync7(join5(root, "turbo.json"));
|
|
9329
9610
|
}
|
|
9330
9611
|
function hasNx(root) {
|
|
9331
|
-
return
|
|
9612
|
+
return existsSync7(join5(root, "nx.json"));
|
|
9332
9613
|
}
|
|
9333
9614
|
function hasLerna(root) {
|
|
9334
|
-
return
|
|
9615
|
+
return existsSync7(join5(root, "lerna.json"));
|
|
9335
9616
|
}
|
|
9336
9617
|
function hasBazel(root) {
|
|
9337
|
-
return
|
|
9618
|
+
return existsSync7(join5(root, "WORKSPACE")) || existsSync7(join5(root, "WORKSPACE.bazel")) || existsSync7(join5(root, "MODULE.bazel"));
|
|
9338
9619
|
}
|
|
9339
9620
|
function detectMonorepo(projectRoot) {
|
|
9340
9621
|
const nested = [];
|
|
@@ -9525,8 +9806,8 @@ var init_vr_command_map = __esm({
|
|
|
9525
9806
|
});
|
|
9526
9807
|
|
|
9527
9808
|
// src/detect/domain-inferrer.ts
|
|
9528
|
-
import { existsSync as
|
|
9529
|
-
import { join as
|
|
9809
|
+
import { existsSync as existsSync8, readdirSync as readdirSync5 } from "fs";
|
|
9810
|
+
import { join as join6 } from "path";
|
|
9530
9811
|
function titleCase(s) {
|
|
9531
9812
|
if (!s) return s;
|
|
9532
9813
|
return s.split(/[-_\s]+/).filter(Boolean).map((p19) => p19.charAt(0).toUpperCase() + p19.slice(1)).join(" ");
|
|
@@ -9542,14 +9823,33 @@ function domainFromWorkspace(pkg) {
|
|
|
9542
9823
|
allowedImportsFrom: []
|
|
9543
9824
|
};
|
|
9544
9825
|
}
|
|
9545
|
-
function topLevelSrcSubdirs(root) {
|
|
9546
|
-
const
|
|
9547
|
-
|
|
9548
|
-
|
|
9549
|
-
|
|
9550
|
-
|
|
9551
|
-
|
|
9826
|
+
function topLevelSrcSubdirs(root, sourceDirs) {
|
|
9827
|
+
const effective = sourceDirs.length > 0 ? sourceDirs : ["src"];
|
|
9828
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9829
|
+
for (const rel of effective) {
|
|
9830
|
+
const abs = join6(root, rel);
|
|
9831
|
+
if (!existsSync8(abs)) continue;
|
|
9832
|
+
try {
|
|
9833
|
+
for (const e2 of readdirSync5(abs, { withFileTypes: true })) {
|
|
9834
|
+
if (!e2.isDirectory()) continue;
|
|
9835
|
+
if (IGNORED_SUBDIRS.has(e2.name)) continue;
|
|
9836
|
+
seen.add(e2.name);
|
|
9837
|
+
}
|
|
9838
|
+
} catch {
|
|
9839
|
+
}
|
|
9552
9840
|
}
|
|
9841
|
+
return Array.from(seen).sort();
|
|
9842
|
+
}
|
|
9843
|
+
function flattenSourceDirs(sourceDirs) {
|
|
9844
|
+
const flat = /* @__PURE__ */ new Set();
|
|
9845
|
+
for (const entry of Object.values(sourceDirs)) {
|
|
9846
|
+
if (!entry) continue;
|
|
9847
|
+
for (const dir of entry.source_dirs) {
|
|
9848
|
+
if (dir === "." || dir === "") continue;
|
|
9849
|
+
flat.add(dir);
|
|
9850
|
+
}
|
|
9851
|
+
}
|
|
9852
|
+
return Array.from(flat);
|
|
9553
9853
|
}
|
|
9554
9854
|
function inferDomains(projectRoot, monorepo, sourceDirs) {
|
|
9555
9855
|
const domains = [];
|
|
@@ -9558,7 +9858,8 @@ function inferDomains(projectRoot, monorepo, sourceDirs) {
|
|
|
9558
9858
|
domains.push(domainFromWorkspace(pkg));
|
|
9559
9859
|
}
|
|
9560
9860
|
} else {
|
|
9561
|
-
const
|
|
9861
|
+
const flat = flattenSourceDirs(sourceDirs);
|
|
9862
|
+
const subdirs = topLevelSrcSubdirs(projectRoot, flat);
|
|
9562
9863
|
for (const s of subdirs) {
|
|
9563
9864
|
domains.push({
|
|
9564
9865
|
name: titleCase(s),
|
|
@@ -9614,8 +9915,8 @@ var init_domain_inferrer = __esm({
|
|
|
9614
9915
|
});
|
|
9615
9916
|
|
|
9616
9917
|
// src/detect/regex-fallback.ts
|
|
9617
|
-
import { existsSync as
|
|
9618
|
-
import { resolve as
|
|
9918
|
+
import { existsSync as existsSync9, readdirSync as readdirSync6, readFileSync as readFileSync7, statSync as statSync4 } from "fs";
|
|
9919
|
+
import { resolve as resolve6, join as join7, basename as basename2 } from "path";
|
|
9619
9920
|
function introspectPython(detection, projectRoot) {
|
|
9620
9921
|
const sourceDir = resolveSourceDir(detection, "python", projectRoot);
|
|
9621
9922
|
if (!sourceDir) return null;
|
|
@@ -9781,10 +10082,10 @@ function resolveSourceDir(detection, lang, projectRoot) {
|
|
|
9781
10082
|
const list = info?.source_dirs ?? [];
|
|
9782
10083
|
if (list.length > 0) {
|
|
9783
10084
|
const first = list[0];
|
|
9784
|
-
const abs =
|
|
9785
|
-
return
|
|
10085
|
+
const abs = resolve6(projectRoot, first);
|
|
10086
|
+
return existsSync9(abs) ? abs : null;
|
|
9786
10087
|
}
|
|
9787
|
-
return
|
|
10088
|
+
return existsSync9(projectRoot) ? projectRoot : null;
|
|
9788
10089
|
}
|
|
9789
10090
|
function sampleFiles(dir, nameRegex, pathFilter) {
|
|
9790
10091
|
const out = [];
|
|
@@ -9804,7 +10105,7 @@ function sampleFiles(dir, nameRegex, pathFilter) {
|
|
|
9804
10105
|
if (entry === "__pycache__") continue;
|
|
9805
10106
|
if (entry === "venv" || entry === ".venv") continue;
|
|
9806
10107
|
if (entry === "dist" || entry === "build") continue;
|
|
9807
|
-
const child =
|
|
10108
|
+
const child = join7(path, entry);
|
|
9808
10109
|
let st;
|
|
9809
10110
|
try {
|
|
9810
10111
|
st = statSync4(child);
|
|
@@ -9829,7 +10130,7 @@ function readSafe(path) {
|
|
|
9829
10130
|
try {
|
|
9830
10131
|
const st = statSync4(path);
|
|
9831
10132
|
if (st.size > MAX_FILE_BYTES) return null;
|
|
9832
|
-
return
|
|
10133
|
+
return readFileSync7(path, "utf-8");
|
|
9833
10134
|
} catch {
|
|
9834
10135
|
return null;
|
|
9835
10136
|
}
|
|
@@ -9910,8 +10211,8 @@ var init_parse_guard = __esm({
|
|
|
9910
10211
|
|
|
9911
10212
|
// src/detect/adapters/runner.ts
|
|
9912
10213
|
import { basename as basename3, relative as relative4 } from "path";
|
|
9913
|
-
import { existsSync as
|
|
9914
|
-
import { join as
|
|
10214
|
+
import { existsSync as existsSync10, readdirSync as readdirSync7, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
|
|
10215
|
+
import { join as join8 } from "path";
|
|
9915
10216
|
async function runAdapters(adapters, rootDir, signals, options = {}) {
|
|
9916
10217
|
const out = {
|
|
9917
10218
|
byAdapter: {},
|
|
@@ -10008,7 +10309,7 @@ function buildDetectionSignals(rootDir) {
|
|
|
10008
10309
|
for (const entry of readdirSync7(rootDir)) {
|
|
10009
10310
|
if (entry.startsWith(".")) continue;
|
|
10010
10311
|
try {
|
|
10011
|
-
const st = statSync5(
|
|
10312
|
+
const st = statSync5(join8(rootDir, entry));
|
|
10012
10313
|
if (st.isDirectory()) presentDirs.add(entry);
|
|
10013
10314
|
else if (st.isFile()) presentFiles.add(entry);
|
|
10014
10315
|
} catch {
|
|
@@ -10017,14 +10318,14 @@ function buildDetectionSignals(rootDir) {
|
|
|
10017
10318
|
} catch {
|
|
10018
10319
|
}
|
|
10019
10320
|
return {
|
|
10020
|
-
packageJson: tryReadJson(
|
|
10021
|
-
pyprojectToml: tryReadToml(
|
|
10022
|
-
gemfile: tryReadString(
|
|
10023
|
-
cargoToml: tryReadToml(
|
|
10024
|
-
goMod: tryReadString(
|
|
10025
|
-
mixExs: tryReadString(
|
|
10321
|
+
packageJson: tryReadJson(join8(rootDir, "package.json")),
|
|
10322
|
+
pyprojectToml: tryReadToml(join8(rootDir, "pyproject.toml")),
|
|
10323
|
+
gemfile: tryReadString(join8(rootDir, "Gemfile")),
|
|
10324
|
+
cargoToml: tryReadToml(join8(rootDir, "Cargo.toml")),
|
|
10325
|
+
goMod: tryReadString(join8(rootDir, "go.mod")),
|
|
10326
|
+
mixExs: tryReadString(join8(rootDir, "mix.exs")),
|
|
10026
10327
|
csproj: tryReadFirstCsproj(rootDir, presentFiles),
|
|
10027
|
-
pomXml: tryReadString(
|
|
10328
|
+
pomXml: tryReadString(join8(rootDir, "pom.xml")),
|
|
10028
10329
|
gradleBuild: tryReadGradleBuild(rootDir, presentFiles),
|
|
10029
10330
|
presentDirs,
|
|
10030
10331
|
presentFiles
|
|
@@ -10033,21 +10334,21 @@ function buildDetectionSignals(rootDir) {
|
|
|
10033
10334
|
function tryReadFirstCsproj(rootDir, presentFiles) {
|
|
10034
10335
|
const csprojNames = [...presentFiles].filter((f2) => f2.endsWith(".csproj")).sort();
|
|
10035
10336
|
if (csprojNames.length === 0) return void 0;
|
|
10036
|
-
return tryReadString(
|
|
10337
|
+
return tryReadString(join8(rootDir, csprojNames[0]));
|
|
10037
10338
|
}
|
|
10038
10339
|
function tryReadGradleBuild(rootDir, presentFiles) {
|
|
10039
10340
|
if (presentFiles.has("build.gradle.kts")) {
|
|
10040
|
-
return tryReadString(
|
|
10341
|
+
return tryReadString(join8(rootDir, "build.gradle.kts"));
|
|
10041
10342
|
}
|
|
10042
10343
|
if (presentFiles.has("build.gradle")) {
|
|
10043
|
-
return tryReadString(
|
|
10344
|
+
return tryReadString(join8(rootDir, "build.gradle"));
|
|
10044
10345
|
}
|
|
10045
10346
|
return void 0;
|
|
10046
10347
|
}
|
|
10047
10348
|
function tryReadString(path) {
|
|
10048
|
-
if (!
|
|
10349
|
+
if (!existsSync10(path)) return void 0;
|
|
10049
10350
|
try {
|
|
10050
|
-
return
|
|
10351
|
+
return readFileSync8(path, "utf-8");
|
|
10051
10352
|
} catch {
|
|
10052
10353
|
return void 0;
|
|
10053
10354
|
}
|
|
@@ -10162,11 +10463,11 @@ ${querySource}`
|
|
|
10162
10463
|
});
|
|
10163
10464
|
|
|
10164
10465
|
// src/detect/adapters/tree-sitter-loader.ts
|
|
10165
|
-
import { createHash as
|
|
10466
|
+
import { createHash as createHash3 } from "crypto";
|
|
10166
10467
|
import {
|
|
10167
|
-
mkdirSync as
|
|
10468
|
+
mkdirSync as mkdirSync4,
|
|
10168
10469
|
readdirSync as readdirSync8,
|
|
10169
|
-
readFileSync as
|
|
10470
|
+
readFileSync as readFileSync9,
|
|
10170
10471
|
writeFileSync,
|
|
10171
10472
|
renameSync as renameSync2,
|
|
10172
10473
|
unlinkSync,
|
|
@@ -10174,14 +10475,14 @@ import {
|
|
|
10174
10475
|
chmodSync,
|
|
10175
10476
|
utimesSync
|
|
10176
10477
|
} from "fs";
|
|
10177
|
-
import { homedir as
|
|
10178
|
-
import { dirname as
|
|
10478
|
+
import { homedir as homedir3 } from "os";
|
|
10479
|
+
import { dirname as dirname5, join as join9 } from "path";
|
|
10179
10480
|
import { Language, Parser } from "web-tree-sitter";
|
|
10180
10481
|
function getCacheDir() {
|
|
10181
|
-
return process.env.MASSU_WASM_CACHE_DIR ??
|
|
10482
|
+
return process.env.MASSU_WASM_CACHE_DIR ?? join9(homedir3(), ".massu", "wasm-cache");
|
|
10182
10483
|
}
|
|
10183
10484
|
function getCachedPath(language, sha) {
|
|
10184
|
-
return
|
|
10485
|
+
return join9(getCacheDir(), `${language}-${sha}.wasm`);
|
|
10185
10486
|
}
|
|
10186
10487
|
function getCacheRetainCount() {
|
|
10187
10488
|
const env = process.env.MASSU_WASM_CACHE_RETAIN;
|
|
@@ -10209,7 +10510,7 @@ function evictBeyondRetainCount(retain = getCacheRetainCount()) {
|
|
|
10209
10510
|
const candidates = [];
|
|
10210
10511
|
for (const name of entries) {
|
|
10211
10512
|
if (!name.endsWith(".wasm")) continue;
|
|
10212
|
-
const path =
|
|
10513
|
+
const path = join9(dir, name);
|
|
10213
10514
|
let stat;
|
|
10214
10515
|
try {
|
|
10215
10516
|
stat = lstatSync3(path);
|
|
@@ -10234,7 +10535,7 @@ function evictBeyondRetainCount(retain = getCacheRetainCount()) {
|
|
|
10234
10535
|
}
|
|
10235
10536
|
}
|
|
10236
10537
|
function sha256(bytes) {
|
|
10237
|
-
return
|
|
10538
|
+
return createHash3("sha256").update(bytes).digest("hex");
|
|
10238
10539
|
}
|
|
10239
10540
|
async function ensureParserInitialized() {
|
|
10240
10541
|
if (parserInitPromise) return parserInitPromise;
|
|
@@ -10265,7 +10566,7 @@ async function loadGrammar(language, options = {}) {
|
|
|
10265
10566
|
}
|
|
10266
10567
|
let bytes;
|
|
10267
10568
|
try {
|
|
10268
|
-
bytes =
|
|
10569
|
+
bytes = readFileSync9(cachePath);
|
|
10269
10570
|
} catch (e2) {
|
|
10270
10571
|
bytes = new Uint8Array(0);
|
|
10271
10572
|
}
|
|
@@ -10305,9 +10606,9 @@ async function loadGrammar(language, options = {}) {
|
|
|
10305
10606
|
throw new GrammarSHAMismatchError(language, manifest.sha256, downloadedSha);
|
|
10306
10607
|
}
|
|
10307
10608
|
try {
|
|
10308
|
-
|
|
10609
|
+
mkdirSync4(dirname5(cachePath), { recursive: true, mode: 448 });
|
|
10309
10610
|
try {
|
|
10310
|
-
chmodSync(
|
|
10611
|
+
chmodSync(dirname5(cachePath), 448);
|
|
10311
10612
|
} catch {
|
|
10312
10613
|
}
|
|
10313
10614
|
const tmpPath = `${cachePath}.tmp.${process.pid}`;
|
|
@@ -11206,8 +11507,8 @@ __export(file_sampler_exports, {
|
|
|
11206
11507
|
SAMPLE_TEST_FILE_PATTERNS: () => SAMPLE_TEST_FILE_PATTERNS,
|
|
11207
11508
|
sampleFilesForAdapter: () => sampleFilesForAdapter
|
|
11208
11509
|
});
|
|
11209
|
-
import { readdirSync as readdirSync9, readFileSync as
|
|
11210
|
-
import { join as
|
|
11510
|
+
import { readdirSync as readdirSync9, readFileSync as readFileSync10, lstatSync as lstatSync4 } from "node:fs";
|
|
11511
|
+
import { join as join10, extname } from "node:path";
|
|
11211
11512
|
function sampleFilesForAdapter(adapter, projectRoot, detection, options = {}) {
|
|
11212
11513
|
const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
11213
11514
|
const maxFiles = options.maxFilesPerAdapter ?? DEFAULT_MAX_FILES;
|
|
@@ -11222,7 +11523,7 @@ function sampleFilesForAdapter(adapter, projectRoot, detection, options = {}) {
|
|
|
11222
11523
|
const langDetection = detection.sourceDirs[langKey];
|
|
11223
11524
|
const candidateDirs = [];
|
|
11224
11525
|
if (langDetection?.source_dirs && langDetection.source_dirs.length > 0) {
|
|
11225
|
-
candidateDirs.push(...langDetection.source_dirs.map((d2) =>
|
|
11526
|
+
candidateDirs.push(...langDetection.source_dirs.map((d2) => join10(projectRoot, d2)));
|
|
11226
11527
|
} else {
|
|
11227
11528
|
candidateDirs.push(projectRoot);
|
|
11228
11529
|
}
|
|
@@ -11246,7 +11547,7 @@ function walkDir(dir, exts, testPatterns, lang, maxDepth, curDepth, out, seen, m
|
|
|
11246
11547
|
if (out.length >= maxFiles) return;
|
|
11247
11548
|
if (entry.startsWith(".")) continue;
|
|
11248
11549
|
if (IGNORED_DIRS3.has(entry)) continue;
|
|
11249
|
-
const fullPath =
|
|
11550
|
+
const fullPath = join10(dir, entry);
|
|
11250
11551
|
let st;
|
|
11251
11552
|
try {
|
|
11252
11553
|
st = lstatSync4(fullPath);
|
|
@@ -11267,7 +11568,7 @@ function walkDir(dir, exts, testPatterns, lang, maxDepth, curDepth, out, seen, m
|
|
|
11267
11568
|
seen.add(fullPath);
|
|
11268
11569
|
let content;
|
|
11269
11570
|
try {
|
|
11270
|
-
content =
|
|
11571
|
+
content = readFileSync10(fullPath, "utf-8");
|
|
11271
11572
|
} catch {
|
|
11272
11573
|
continue;
|
|
11273
11574
|
}
|
|
@@ -11505,7 +11806,7 @@ var init_detect = __esm({
|
|
|
11505
11806
|
});
|
|
11506
11807
|
|
|
11507
11808
|
// src/detect/drift.ts
|
|
11508
|
-
import { createHash as
|
|
11809
|
+
import { createHash as createHash4 } from "crypto";
|
|
11509
11810
|
function summarizeDetection(det) {
|
|
11510
11811
|
const languages = Array.from(new Set(det.manifests.map((m3) => m3.language))).sort();
|
|
11511
11812
|
const frameworks = {};
|
|
@@ -11536,7 +11837,7 @@ function summarizeDetection(det) {
|
|
|
11536
11837
|
function computeFingerprint(det) {
|
|
11537
11838
|
const data = summarizeDetection(det);
|
|
11538
11839
|
const stable = JSON.stringify(data, Object.keys(data).sort());
|
|
11539
|
-
return
|
|
11840
|
+
return createHash4("sha256").update(stable).digest("hex");
|
|
11540
11841
|
}
|
|
11541
11842
|
function stringOf(v3) {
|
|
11542
11843
|
if (typeof v3 === "string") return v3;
|
|
@@ -12695,10 +12996,10 @@ __export(init_exports, {
|
|
|
12695
12996
|
validateWrittenConfig: () => validateWrittenConfig,
|
|
12696
12997
|
writeConfigAtomic: () => writeConfigAtomic
|
|
12697
12998
|
});
|
|
12698
|
-
import { closeSync as closeSync2, existsSync as
|
|
12699
|
-
import { resolve as
|
|
12999
|
+
import { closeSync as closeSync2, existsSync as existsSync11, fsyncSync as fsyncSync2, openSync as openSync2, readFileSync as readFileSync11, writeFileSync as writeFileSync2, writeSync as writeSync2, mkdirSync as mkdirSync5, readdirSync as readdirSync10, renameSync as renameSync3, rmSync as rmSync2, statSync as statSync7, chmodSync as chmodSync2 } from "fs";
|
|
13000
|
+
import { resolve as resolve7, basename as basename4, dirname as dirname6 } from "path";
|
|
12700
13001
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
12701
|
-
import { homedir as
|
|
13002
|
+
import { homedir as homedir4 } from "os";
|
|
12702
13003
|
import { stringify as yamlStringify, parse as yamlParse } from "yaml";
|
|
12703
13004
|
function detectFramework(projectRoot) {
|
|
12704
13005
|
const result = {
|
|
@@ -12707,10 +13008,10 @@ function detectFramework(projectRoot) {
|
|
|
12707
13008
|
orm: "none",
|
|
12708
13009
|
ui: "none"
|
|
12709
13010
|
};
|
|
12710
|
-
const pkgPath =
|
|
12711
|
-
if (!
|
|
13011
|
+
const pkgPath = resolve7(projectRoot, "package.json");
|
|
13012
|
+
if (!existsSync11(pkgPath)) return result;
|
|
12712
13013
|
try {
|
|
12713
|
-
const pkg = JSON.parse(
|
|
13014
|
+
const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
|
|
12714
13015
|
const allDeps = {
|
|
12715
13016
|
...pkg.dependencies,
|
|
12716
13017
|
...pkg.devDependencies
|
|
@@ -12744,7 +13045,7 @@ function detectPython(projectRoot) {
|
|
|
12744
13045
|
alembicDir: null
|
|
12745
13046
|
};
|
|
12746
13047
|
const markers = ["pyproject.toml", "setup.py", "requirements.txt", "Pipfile"];
|
|
12747
|
-
const hasMarker = markers.some((m3) =>
|
|
13048
|
+
const hasMarker = markers.some((m3) => existsSync11(resolve7(projectRoot, m3)));
|
|
12748
13049
|
if (!hasMarker) return result;
|
|
12749
13050
|
result.detected = true;
|
|
12750
13051
|
const depFiles = [
|
|
@@ -12754,33 +13055,33 @@ function detectPython(projectRoot) {
|
|
|
12754
13055
|
{ file: "Pipfile" }
|
|
12755
13056
|
];
|
|
12756
13057
|
for (const { file } of depFiles) {
|
|
12757
|
-
const filePath =
|
|
12758
|
-
if (
|
|
13058
|
+
const filePath = resolve7(projectRoot, file);
|
|
13059
|
+
if (existsSync11(filePath)) {
|
|
12759
13060
|
try {
|
|
12760
|
-
const content =
|
|
13061
|
+
const content = readFileSync11(filePath, "utf-8").toLowerCase();
|
|
12761
13062
|
if (content.includes("fastapi")) result.hasFastapi = true;
|
|
12762
13063
|
if (content.includes("sqlalchemy")) result.hasSqlalchemy = true;
|
|
12763
13064
|
} catch {
|
|
12764
13065
|
}
|
|
12765
13066
|
}
|
|
12766
13067
|
}
|
|
12767
|
-
if (
|
|
13068
|
+
if (existsSync11(resolve7(projectRoot, "alembic.ini"))) {
|
|
12768
13069
|
result.hasAlembic = true;
|
|
12769
|
-
if (
|
|
13070
|
+
if (existsSync11(resolve7(projectRoot, "alembic"))) {
|
|
12770
13071
|
result.alembicDir = "alembic";
|
|
12771
13072
|
}
|
|
12772
|
-
} else if (
|
|
13073
|
+
} else if (existsSync11(resolve7(projectRoot, "alembic"))) {
|
|
12773
13074
|
result.hasAlembic = true;
|
|
12774
13075
|
result.alembicDir = "alembic";
|
|
12775
13076
|
}
|
|
12776
13077
|
const candidateRoots = ["app", "src", "backend", "api"];
|
|
12777
13078
|
for (const candidate of candidateRoots) {
|
|
12778
|
-
const candidatePath =
|
|
12779
|
-
if (
|
|
13079
|
+
const candidatePath = resolve7(projectRoot, candidate);
|
|
13080
|
+
if (existsSync11(candidatePath) && existsSync11(resolve7(candidatePath, "__init__.py"))) {
|
|
12780
13081
|
result.root = candidate;
|
|
12781
13082
|
break;
|
|
12782
13083
|
}
|
|
12783
|
-
if (
|
|
13084
|
+
if (existsSync11(candidatePath)) {
|
|
12784
13085
|
try {
|
|
12785
13086
|
const files = readdirSync10(candidatePath);
|
|
12786
13087
|
if (files.some((f2) => f2.endsWith(".py"))) {
|
|
@@ -12800,8 +13101,8 @@ function generateConfig(projectRoot, framework) {
|
|
|
12800
13101
|
console.warn(
|
|
12801
13102
|
"[@massu/core] generateConfig() is deprecated since 1.2.1 \u2014 use buildConfigFromDetection instead. It cannot produce valid configs for monorepos."
|
|
12802
13103
|
);
|
|
12803
|
-
const configPath =
|
|
12804
|
-
if (
|
|
13104
|
+
const configPath = resolve7(projectRoot, "massu.config.yaml");
|
|
13105
|
+
if (existsSync11(configPath)) {
|
|
12805
13106
|
return false;
|
|
12806
13107
|
}
|
|
12807
13108
|
const projectName = basename4(projectRoot);
|
|
@@ -12990,7 +13291,7 @@ function buildConfigFromDetection(opts) {
|
|
|
12990
13291
|
};
|
|
12991
13292
|
if (pyFw?.framework) pythonBlock.framework = pyFw.framework;
|
|
12992
13293
|
if (pyFw?.orm) pythonBlock.orm = pyFw.orm;
|
|
12993
|
-
if (
|
|
13294
|
+
if (existsSync11(resolve7(projectRoot, "alembic.ini")) || existsSync11(resolve7(projectRoot, "alembic"))) {
|
|
12994
13295
|
pythonBlock.alembic_dir = "alembic";
|
|
12995
13296
|
}
|
|
12996
13297
|
config.python = pythonBlock;
|
|
@@ -13014,11 +13315,11 @@ function applyVariantTemplate(config, templatesDir) {
|
|
|
13014
13315
|
}
|
|
13015
13316
|
}
|
|
13016
13317
|
if (templateId === null) return config;
|
|
13017
|
-
const templatePath =
|
|
13018
|
-
if (!
|
|
13318
|
+
const templatePath = resolve7(templatesDir, templateId, "massu.config.yaml");
|
|
13319
|
+
if (!existsSync11(templatePath)) return config;
|
|
13019
13320
|
let template;
|
|
13020
13321
|
try {
|
|
13021
|
-
template = yamlParse(
|
|
13322
|
+
template = yamlParse(readFileSync11(templatePath, "utf-8"));
|
|
13022
13323
|
} catch {
|
|
13023
13324
|
return config;
|
|
13024
13325
|
}
|
|
@@ -13077,7 +13378,7 @@ ${yamlStringify(config)}`;
|
|
|
13077
13378
|
function writeConfigAtomic(configPath, content) {
|
|
13078
13379
|
const tmpPath = `${configPath}.tmp`;
|
|
13079
13380
|
let existingMode;
|
|
13080
|
-
if (
|
|
13381
|
+
if (existsSync11(configPath)) {
|
|
13081
13382
|
try {
|
|
13082
13383
|
existingMode = statSync7(configPath).mode;
|
|
13083
13384
|
} catch {
|
|
@@ -13085,7 +13386,7 @@ function writeConfigAtomic(configPath, content) {
|
|
|
13085
13386
|
}
|
|
13086
13387
|
}
|
|
13087
13388
|
try {
|
|
13088
|
-
|
|
13389
|
+
mkdirSync5(dirname6(configPath), { recursive: true });
|
|
13089
13390
|
const fd = openSync2(tmpPath, "w", 420);
|
|
13090
13391
|
try {
|
|
13091
13392
|
const buf = Buffer.from(content, "utf-8");
|
|
@@ -13107,7 +13408,7 @@ function writeConfigAtomic(configPath, content) {
|
|
|
13107
13408
|
}
|
|
13108
13409
|
return { validated: true };
|
|
13109
13410
|
} catch (err) {
|
|
13110
|
-
if (
|
|
13411
|
+
if (existsSync11(tmpPath)) {
|
|
13111
13412
|
try {
|
|
13112
13413
|
rmSync2(tmpPath, { force: true });
|
|
13113
13414
|
} catch {
|
|
@@ -13118,8 +13419,8 @@ function writeConfigAtomic(configPath, content) {
|
|
|
13118
13419
|
}
|
|
13119
13420
|
function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
13120
13421
|
try {
|
|
13121
|
-
if (!
|
|
13122
|
-
const content =
|
|
13422
|
+
if (!existsSync11(configPath)) return "Config file does not exist after write";
|
|
13423
|
+
const content = readFileSync11(configPath, "utf-8");
|
|
13123
13424
|
const parsed = yamlParse(content);
|
|
13124
13425
|
if (parsed === null || typeof parsed !== "object") {
|
|
13125
13426
|
return "Config is not a valid YAML object";
|
|
@@ -13139,8 +13440,8 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
|
13139
13440
|
if (checkPaths) {
|
|
13140
13441
|
const src = cfg.paths.source;
|
|
13141
13442
|
if (src && src !== ".") {
|
|
13142
|
-
const srcAbs =
|
|
13143
|
-
if (!
|
|
13443
|
+
const srcAbs = resolve7(projectRoot, src);
|
|
13444
|
+
if (!existsSync11(srcAbs)) {
|
|
13144
13445
|
return `paths.source '${src}' does not exist on disk`;
|
|
13145
13446
|
}
|
|
13146
13447
|
}
|
|
@@ -13150,8 +13451,8 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
|
13150
13451
|
if (!Array.isArray(rawDirs)) continue;
|
|
13151
13452
|
for (const d2 of rawDirs) {
|
|
13152
13453
|
if (typeof d2 !== "string" || d2 === ".") continue;
|
|
13153
|
-
const abs =
|
|
13154
|
-
if (!
|
|
13454
|
+
const abs = resolve7(projectRoot, d2);
|
|
13455
|
+
if (!existsSync11(abs)) {
|
|
13155
13456
|
return `framework.languages.${lang}.source_dirs '${d2}' does not exist on disk`;
|
|
13156
13457
|
}
|
|
13157
13458
|
}
|
|
@@ -13160,7 +13461,7 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
|
13160
13461
|
if (Array.isArray(mRoots)) {
|
|
13161
13462
|
for (const r2 of mRoots) {
|
|
13162
13463
|
if (typeof r2 !== "string" || r2 === ".") continue;
|
|
13163
|
-
if (!
|
|
13464
|
+
if (!existsSync11(resolve7(projectRoot, r2))) {
|
|
13164
13465
|
return `paths.monorepo_roots '${r2}' does not exist on disk`;
|
|
13165
13466
|
}
|
|
13166
13467
|
}
|
|
@@ -13192,21 +13493,21 @@ function resolveTemplatesDir() {
|
|
|
13192
13493
|
const cwd = process.cwd();
|
|
13193
13494
|
const candidates = [
|
|
13194
13495
|
// Project-local install: `<project>/node_modules/@massu/core/templates`.
|
|
13195
|
-
|
|
13496
|
+
resolve7(cwd, "node_modules/@massu/core/templates"),
|
|
13196
13497
|
// Bundled cli.js layout: cli.js sits at `<package>/dist/cli.js`, so
|
|
13197
13498
|
// templates live one level up at `<package>/templates`. (Plan 1.5.1
|
|
13198
13499
|
// bug discovery: pre-existing layout assumed `dist/commands/init.js`
|
|
13199
13500
|
// depth which never matched the bundled cli, so resolveTemplatesDir
|
|
13200
13501
|
// returned null in production for both `--template` mode AND the
|
|
13201
13502
|
// applyVariantTemplate path.)
|
|
13202
|
-
|
|
13503
|
+
resolve7(__dirname2, "../templates"),
|
|
13203
13504
|
// Legacy nested layouts retained as fallbacks (in case a future
|
|
13204
13505
|
// build moves cli.js back into a subdirectory).
|
|
13205
|
-
|
|
13206
|
-
|
|
13506
|
+
resolve7(__dirname2, "../../templates"),
|
|
13507
|
+
resolve7(__dirname2, "../../../templates")
|
|
13207
13508
|
];
|
|
13208
13509
|
for (const c2 of candidates) {
|
|
13209
|
-
if (
|
|
13510
|
+
if (existsSync11(c2)) return c2;
|
|
13210
13511
|
}
|
|
13211
13512
|
return null;
|
|
13212
13513
|
}
|
|
@@ -13215,12 +13516,12 @@ function copyTemplateConfig(templateName, targetPath, projectName) {
|
|
|
13215
13516
|
if (!templatesDir) {
|
|
13216
13517
|
return { success: false, error: `Templates directory not found (looked in node_modules and dist/src)` };
|
|
13217
13518
|
}
|
|
13218
|
-
const srcPath =
|
|
13219
|
-
if (!
|
|
13519
|
+
const srcPath = resolve7(templatesDir, templateName, "massu.config.yaml");
|
|
13520
|
+
if (!existsSync11(srcPath)) {
|
|
13220
13521
|
return { success: false, error: `Template '${templateName}' not found at ${srcPath}` };
|
|
13221
13522
|
}
|
|
13222
13523
|
try {
|
|
13223
|
-
let content =
|
|
13524
|
+
let content = readFileSync11(srcPath, "utf-8");
|
|
13224
13525
|
content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectName);
|
|
13225
13526
|
writeFileSync2(targetPath, content, "utf-8");
|
|
13226
13527
|
return { success: true };
|
|
@@ -13229,11 +13530,11 @@ function copyTemplateConfig(templateName, targetPath, projectName) {
|
|
|
13229
13530
|
}
|
|
13230
13531
|
}
|
|
13231
13532
|
function registerMcpServer(projectRoot) {
|
|
13232
|
-
const mcpPath =
|
|
13533
|
+
const mcpPath = resolve7(projectRoot, ".mcp.json");
|
|
13233
13534
|
let existing = {};
|
|
13234
|
-
if (
|
|
13535
|
+
if (existsSync11(mcpPath)) {
|
|
13235
13536
|
try {
|
|
13236
|
-
existing = JSON.parse(
|
|
13537
|
+
existing = JSON.parse(readFileSync11(mcpPath, "utf-8"));
|
|
13237
13538
|
} catch {
|
|
13238
13539
|
existing = {};
|
|
13239
13540
|
}
|
|
@@ -13253,12 +13554,12 @@ function registerMcpServer(projectRoot) {
|
|
|
13253
13554
|
}
|
|
13254
13555
|
function resolveHooksDir() {
|
|
13255
13556
|
const cwd = process.cwd();
|
|
13256
|
-
const nodeModulesPath =
|
|
13257
|
-
if (
|
|
13557
|
+
const nodeModulesPath = resolve7(cwd, "node_modules/@massu/core/dist/hooks");
|
|
13558
|
+
if (existsSync11(nodeModulesPath)) {
|
|
13258
13559
|
return "node_modules/@massu/core/dist/hooks";
|
|
13259
13560
|
}
|
|
13260
|
-
const localPath =
|
|
13261
|
-
if (
|
|
13561
|
+
const localPath = resolve7(__dirname2, "../dist/hooks");
|
|
13562
|
+
if (existsSync11(localPath)) {
|
|
13262
13563
|
return localPath;
|
|
13263
13564
|
}
|
|
13264
13565
|
return "node_modules/@massu/core/dist/hooks";
|
|
@@ -13350,19 +13651,11 @@ function installHooks(projectRoot) {
|
|
|
13350
13651
|
} catch {
|
|
13351
13652
|
claudeDirName = ".claude";
|
|
13352
13653
|
}
|
|
13353
|
-
const claudeDir =
|
|
13354
|
-
|
|
13355
|
-
|
|
13356
|
-
mkdirSync4(claudeDir, { recursive: true });
|
|
13357
|
-
}
|
|
13358
|
-
let settings = {};
|
|
13359
|
-
if (existsSync10(settingsPath)) {
|
|
13360
|
-
try {
|
|
13361
|
-
settings = JSON.parse(readFileSync10(settingsPath, "utf-8"));
|
|
13362
|
-
} catch {
|
|
13363
|
-
settings = {};
|
|
13364
|
-
}
|
|
13654
|
+
const claudeDir = resolve7(projectRoot, claudeDirName);
|
|
13655
|
+
if (!existsSync11(claudeDir)) {
|
|
13656
|
+
mkdirSync5(claudeDir, { recursive: true });
|
|
13365
13657
|
}
|
|
13658
|
+
const settings = readSettingsLocal(claudeDir);
|
|
13366
13659
|
const hooksDir = resolveHooksDir();
|
|
13367
13660
|
const hooksConfig = buildHooksConfig(hooksDir);
|
|
13368
13661
|
let hookCount = 0;
|
|
@@ -13372,20 +13665,20 @@ function installHooks(projectRoot) {
|
|
|
13372
13665
|
}
|
|
13373
13666
|
}
|
|
13374
13667
|
settings.hooks = hooksConfig;
|
|
13375
|
-
|
|
13668
|
+
writeSettingsLocalAtomic(claudeDir, settings);
|
|
13376
13669
|
return { installed: true, count: hookCount };
|
|
13377
13670
|
}
|
|
13378
13671
|
function initMemoryDir(projectRoot) {
|
|
13379
13672
|
const encodedRoot = "-" + projectRoot.replace(/\//g, "-");
|
|
13380
|
-
const memoryDir =
|
|
13673
|
+
const memoryDir = resolve7(homedir4(), `.claude/projects/${encodedRoot}/memory`);
|
|
13381
13674
|
let created = false;
|
|
13382
|
-
if (!
|
|
13383
|
-
|
|
13675
|
+
if (!existsSync11(memoryDir)) {
|
|
13676
|
+
mkdirSync5(memoryDir, { recursive: true });
|
|
13384
13677
|
created = true;
|
|
13385
13678
|
}
|
|
13386
|
-
const memoryMdPath =
|
|
13679
|
+
const memoryMdPath = resolve7(memoryDir, "MEMORY.md");
|
|
13387
13680
|
let memoryMdCreated = false;
|
|
13388
|
-
if (!
|
|
13681
|
+
if (!existsSync11(memoryMdPath)) {
|
|
13389
13682
|
const projectName = basename4(projectRoot);
|
|
13390
13683
|
const memoryContent = `# ${projectName} - Massu Memory
|
|
13391
13684
|
|
|
@@ -13491,8 +13784,8 @@ async function runInit(argv, overrides) {
|
|
|
13491
13784
|
log("Massu AI - Project Setup");
|
|
13492
13785
|
log("========================");
|
|
13493
13786
|
log("");
|
|
13494
|
-
const configPath =
|
|
13495
|
-
if (
|
|
13787
|
+
const configPath = resolve7(projectRoot, "massu.config.yaml");
|
|
13788
|
+
if (existsSync11(configPath)) {
|
|
13496
13789
|
if (opts.ci && !opts.force) {
|
|
13497
13790
|
errLog(`error: massu.config.yaml already exists at ${configPath}`);
|
|
13498
13791
|
errLog(" rerun with --force to overwrite, or remove the file first");
|
|
@@ -13631,8 +13924,8 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
|
|
|
13631
13924
|
const commandStats = cmdResult.assets.commands;
|
|
13632
13925
|
const stackResolved = !emptyStack && commandStats && (commandStats.installed > 0 || commandStats.updated > 0 || commandStats.kept > 0);
|
|
13633
13926
|
if (!stackResolved) {
|
|
13634
|
-
const placeholderPath =
|
|
13635
|
-
if (!
|
|
13927
|
+
const placeholderPath = resolve7(cmdResult.claudeDir, "commands", "_massu-needs-stack.md");
|
|
13928
|
+
if (!existsSync11(placeholderPath)) {
|
|
13636
13929
|
const placeholderBody = [
|
|
13637
13930
|
"# Massu \u2014 stack not yet detected",
|
|
13638
13931
|
"",
|
|
@@ -13653,7 +13946,7 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
|
|
|
13653
13946
|
"\u2014 Massu"
|
|
13654
13947
|
].join("\n");
|
|
13655
13948
|
try {
|
|
13656
|
-
|
|
13949
|
+
mkdirSync5(resolve7(cmdResult.claudeDir, "commands"), { recursive: true });
|
|
13657
13950
|
writeFileSync2(placeholderPath, placeholderBody, "utf-8");
|
|
13658
13951
|
log(" Wrote _massu-needs-stack.md placeholder (no stack detected yet)");
|
|
13659
13952
|
} catch {
|
|
@@ -13673,8 +13966,8 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
|
|
|
13673
13966
|
(async () => {
|
|
13674
13967
|
try {
|
|
13675
13968
|
const encodedRoot = projectRoot.replace(/\//g, "-");
|
|
13676
|
-
const memoryDir =
|
|
13677
|
-
const memFiles =
|
|
13969
|
+
const memoryDir = resolve7(homedir4(), ".claude", "projects", encodedRoot, "memory");
|
|
13970
|
+
const memFiles = existsSync11(memoryDir) ? readdirSync10(memoryDir).filter((f2) => f2.endsWith(".md") && f2 !== "MEMORY.md") : [];
|
|
13678
13971
|
if (memFiles.length > 0) {
|
|
13679
13972
|
const { getMemoryDb: getMemoryDb2 } = await Promise.resolve().then(() => (init_memory_db(), memory_db_exports));
|
|
13680
13973
|
const db = getMemoryDb2();
|
|
@@ -13731,10 +14024,11 @@ var init_init = __esm({
|
|
|
13731
14024
|
init_memory_file_ingest();
|
|
13732
14025
|
init_config();
|
|
13733
14026
|
init_install_commands();
|
|
14027
|
+
init_settings_local();
|
|
13734
14028
|
init_detect();
|
|
13735
14029
|
init_drift();
|
|
13736
14030
|
__filename2 = fileURLToPath2(import.meta.url);
|
|
13737
|
-
__dirname2 =
|
|
14031
|
+
__dirname2 = dirname6(__filename2);
|
|
13738
14032
|
FRAMEWORK_TO_TEMPLATE_ID = {
|
|
13739
14033
|
rails: "rails",
|
|
13740
14034
|
phoenix: "phoenix",
|
|
@@ -13756,7 +14050,7 @@ var init_init = __esm({
|
|
|
13756
14050
|
});
|
|
13757
14051
|
|
|
13758
14052
|
// src/license.ts
|
|
13759
|
-
import { createHash as
|
|
14053
|
+
import { createHash as createHash5 } from "crypto";
|
|
13760
14054
|
function tierLevel(tier) {
|
|
13761
14055
|
return TIER_LEVELS[tier] ?? 0;
|
|
13762
14056
|
}
|
|
@@ -13780,8 +14074,11 @@ function annotateToolDefinitions(defs) {
|
|
|
13780
14074
|
};
|
|
13781
14075
|
});
|
|
13782
14076
|
}
|
|
14077
|
+
function isCloudFeatureAvailable() {
|
|
14078
|
+
return getConfig().cloud?.enabled === true;
|
|
14079
|
+
}
|
|
13783
14080
|
async function validateLicense(apiKey) {
|
|
13784
|
-
const keyHash =
|
|
14081
|
+
const keyHash = createHash5("sha256").update(apiKey).digest("hex");
|
|
13785
14082
|
const memDb = getMemoryDb();
|
|
13786
14083
|
try {
|
|
13787
14084
|
const cached = memDb.prepare(
|
|
@@ -13842,7 +14139,7 @@ async function validateLicense(apiKey) {
|
|
|
13842
14139
|
}
|
|
13843
14140
|
}
|
|
13844
14141
|
function updateLicenseCache(apiKey, tier, validUntil, features = []) {
|
|
13845
|
-
const keyHash =
|
|
14142
|
+
const keyHash = createHash5("sha256").update(apiKey).digest("hex");
|
|
13846
14143
|
const memDb = getMemoryDb();
|
|
13847
14144
|
try {
|
|
13848
14145
|
memDb.prepare(`
|
|
@@ -14050,17 +14347,17 @@ __export(doctor_exports, {
|
|
|
14050
14347
|
runDoctor: () => runDoctor,
|
|
14051
14348
|
runValidateConfig: () => runValidateConfig
|
|
14052
14349
|
});
|
|
14053
|
-
import { existsSync as
|
|
14054
|
-
import { resolve as
|
|
14350
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12, readdirSync as readdirSync11 } from "fs";
|
|
14351
|
+
import { resolve as resolve8, dirname as dirname7 } from "path";
|
|
14055
14352
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
14056
14353
|
import { parse as parseYaml4 } from "yaml";
|
|
14057
14354
|
function checkConfig(projectRoot) {
|
|
14058
|
-
const configPath =
|
|
14059
|
-
if (!
|
|
14355
|
+
const configPath = resolve8(projectRoot, "massu.config.yaml");
|
|
14356
|
+
if (!existsSync12(configPath)) {
|
|
14060
14357
|
return { name: "Configuration", status: "fail", detail: "massu.config.yaml not found. Run: npx massu init" };
|
|
14061
14358
|
}
|
|
14062
14359
|
try {
|
|
14063
|
-
const content =
|
|
14360
|
+
const content = readFileSync12(configPath, "utf-8");
|
|
14064
14361
|
const parsed = parseYaml4(content);
|
|
14065
14362
|
if (!parsed || typeof parsed !== "object") {
|
|
14066
14363
|
return { name: "Configuration", status: "fail", detail: "massu.config.yaml is empty or invalid YAML" };
|
|
@@ -14072,11 +14369,11 @@ function checkConfig(projectRoot) {
|
|
|
14072
14369
|
}
|
|
14073
14370
|
function checkMcpServer(projectRoot) {
|
|
14074
14371
|
const mcpPath = getResolvedPaths().mcpJsonPath;
|
|
14075
|
-
if (!
|
|
14372
|
+
if (!existsSync12(mcpPath)) {
|
|
14076
14373
|
return { name: "MCP Server", status: "fail", detail: ".mcp.json not found. Run: npx massu init" };
|
|
14077
14374
|
}
|
|
14078
14375
|
try {
|
|
14079
|
-
const content = JSON.parse(
|
|
14376
|
+
const content = JSON.parse(readFileSync12(mcpPath, "utf-8"));
|
|
14080
14377
|
const servers = content.mcpServers ?? {};
|
|
14081
14378
|
if (!servers.massu) {
|
|
14082
14379
|
return { name: "MCP Server", status: "fail", detail: "massu not registered in .mcp.json. Run: npx massu init" };
|
|
@@ -14088,11 +14385,11 @@ function checkMcpServer(projectRoot) {
|
|
|
14088
14385
|
}
|
|
14089
14386
|
function checkHooksConfig(projectRoot) {
|
|
14090
14387
|
const settingsPath = getResolvedPaths().settingsLocalPath;
|
|
14091
|
-
if (!
|
|
14388
|
+
if (!existsSync12(settingsPath)) {
|
|
14092
14389
|
return { name: "Hooks Config", status: "fail", detail: ".claude/settings.local.json not found. Run: npx massu init" };
|
|
14093
14390
|
}
|
|
14094
14391
|
try {
|
|
14095
|
-
const content =
|
|
14392
|
+
const content = readSettingsAtPath(settingsPath);
|
|
14096
14393
|
if (!content.hooks) {
|
|
14097
14394
|
return { name: "Hooks Config", status: "fail", detail: "No hooks configured. Run: npx massu install-hooks" };
|
|
14098
14395
|
}
|
|
@@ -14116,11 +14413,11 @@ function checkHooksConfig(projectRoot) {
|
|
|
14116
14413
|
}
|
|
14117
14414
|
}
|
|
14118
14415
|
function checkHookFiles(projectRoot) {
|
|
14119
|
-
const nodeModulesHooksDir =
|
|
14416
|
+
const nodeModulesHooksDir = resolve8(projectRoot, "node_modules/@massu/core/dist/hooks");
|
|
14120
14417
|
let hooksDir = nodeModulesHooksDir;
|
|
14121
|
-
if (!
|
|
14122
|
-
const devHooksDir =
|
|
14123
|
-
if (
|
|
14418
|
+
if (!existsSync12(nodeModulesHooksDir)) {
|
|
14419
|
+
const devHooksDir = resolve8(__dirname3, "../../dist/hooks");
|
|
14420
|
+
if (existsSync12(devHooksDir)) {
|
|
14124
14421
|
hooksDir = devHooksDir;
|
|
14125
14422
|
} else {
|
|
14126
14423
|
return { name: "Hook Files", status: "fail", detail: "Compiled hooks not found. Run: npm install @massu/core" };
|
|
@@ -14128,7 +14425,7 @@ function checkHookFiles(projectRoot) {
|
|
|
14128
14425
|
}
|
|
14129
14426
|
const missing = [];
|
|
14130
14427
|
for (const hookFile of EXPECTED_HOOKS) {
|
|
14131
|
-
if (!
|
|
14428
|
+
if (!existsSync12(resolve8(hooksDir, hookFile))) {
|
|
14132
14429
|
missing.push(hookFile);
|
|
14133
14430
|
}
|
|
14134
14431
|
}
|
|
@@ -14154,8 +14451,8 @@ function checkNodeVersion() {
|
|
|
14154
14451
|
return { name: "Node.js", status: "fail", detail: `v${version} \u2014 Node.js 18+ is required` };
|
|
14155
14452
|
}
|
|
14156
14453
|
async function checkGitRepo(projectRoot) {
|
|
14157
|
-
const gitDir =
|
|
14158
|
-
if (!
|
|
14454
|
+
const gitDir = resolve8(projectRoot, ".git");
|
|
14455
|
+
if (!existsSync12(gitDir)) {
|
|
14159
14456
|
return { name: "Git Repository", status: "warn", detail: "Not a git repository (optional but recommended)" };
|
|
14160
14457
|
}
|
|
14161
14458
|
try {
|
|
@@ -14173,7 +14470,7 @@ async function checkGitRepo(projectRoot) {
|
|
|
14173
14470
|
}
|
|
14174
14471
|
function checkKnowledgeDb(projectRoot) {
|
|
14175
14472
|
const knowledgeDbPath = getResolvedPaths().memoryDbPath;
|
|
14176
|
-
if (!
|
|
14473
|
+
if (!existsSync12(knowledgeDbPath)) {
|
|
14177
14474
|
return {
|
|
14178
14475
|
name: "Knowledge DB",
|
|
14179
14476
|
status: "warn",
|
|
@@ -14184,7 +14481,7 @@ function checkKnowledgeDb(projectRoot) {
|
|
|
14184
14481
|
}
|
|
14185
14482
|
function checkMemoryDir(_projectRoot2) {
|
|
14186
14483
|
const memoryDir = getResolvedPaths().memoryDir;
|
|
14187
|
-
if (!
|
|
14484
|
+
if (!existsSync12(memoryDir)) {
|
|
14188
14485
|
return {
|
|
14189
14486
|
name: "Memory Directory",
|
|
14190
14487
|
status: "warn",
|
|
@@ -14195,7 +14492,7 @@ function checkMemoryDir(_projectRoot2) {
|
|
|
14195
14492
|
}
|
|
14196
14493
|
function checkShellHooksWired(_projectRoot2) {
|
|
14197
14494
|
const settingsPath = getResolvedPaths().settingsLocalPath;
|
|
14198
|
-
if (!
|
|
14495
|
+
if (!existsSync12(settingsPath)) {
|
|
14199
14496
|
return {
|
|
14200
14497
|
name: "Shell Hooks",
|
|
14201
14498
|
status: "fail",
|
|
@@ -14203,7 +14500,7 @@ function checkShellHooksWired(_projectRoot2) {
|
|
|
14203
14500
|
};
|
|
14204
14501
|
}
|
|
14205
14502
|
try {
|
|
14206
|
-
const content =
|
|
14503
|
+
const content = readSettingsAtPath(settingsPath);
|
|
14207
14504
|
const hooks = content.hooks ?? {};
|
|
14208
14505
|
const hasSessionStart = Array.isArray(hooks.SessionStart) && hooks.SessionStart.length > 0;
|
|
14209
14506
|
const hasPreToolUse = Array.isArray(hooks.PreToolUse) && hooks.PreToolUse.length > 0;
|
|
@@ -14254,8 +14551,8 @@ async function checkLicenseStatus() {
|
|
|
14254
14551
|
function checkPythonHealth(projectRoot) {
|
|
14255
14552
|
const config = getConfig();
|
|
14256
14553
|
if (!config.python?.root) return null;
|
|
14257
|
-
const pythonRoot =
|
|
14258
|
-
if (!
|
|
14554
|
+
const pythonRoot = resolve8(projectRoot, config.python.root);
|
|
14555
|
+
if (!existsSync12(pythonRoot)) {
|
|
14259
14556
|
return {
|
|
14260
14557
|
name: "Python",
|
|
14261
14558
|
status: "fail",
|
|
@@ -14274,8 +14571,8 @@ function checkPythonHealth(projectRoot) {
|
|
|
14274
14571
|
if (entry.isDirectory()) {
|
|
14275
14572
|
const excludeDirs = config.python?.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
|
|
14276
14573
|
if (!excludeDirs.includes(entry.name)) {
|
|
14277
|
-
const subdir =
|
|
14278
|
-
if (depth <= 2 && !
|
|
14574
|
+
const subdir = resolve8(dir, entry.name);
|
|
14575
|
+
if (depth <= 2 && !existsSync12(resolve8(subdir, "__init__.py"))) {
|
|
14279
14576
|
try {
|
|
14280
14577
|
const subEntries = readdirSync11(subdir);
|
|
14281
14578
|
if (subEntries.some((f2) => f2.endsWith(".py") && f2 !== "__init__.py")) {
|
|
@@ -14370,15 +14667,15 @@ async function runDoctor() {
|
|
|
14370
14667
|
}
|
|
14371
14668
|
async function runValidateConfig() {
|
|
14372
14669
|
const projectRoot = process.cwd();
|
|
14373
|
-
const configPath =
|
|
14374
|
-
if (!
|
|
14670
|
+
const configPath = resolve8(projectRoot, "massu.config.yaml");
|
|
14671
|
+
if (!existsSync12(configPath)) {
|
|
14375
14672
|
console.error("Error: massu.config.yaml not found in current directory");
|
|
14376
14673
|
console.error("Run: npx massu init");
|
|
14377
14674
|
process.exit(1);
|
|
14378
14675
|
return;
|
|
14379
14676
|
}
|
|
14380
14677
|
try {
|
|
14381
|
-
const content =
|
|
14678
|
+
const content = readFileSync12(configPath, "utf-8");
|
|
14382
14679
|
const parsed = parseYaml4(content);
|
|
14383
14680
|
if (!parsed || typeof parsed !== "object") {
|
|
14384
14681
|
console.error("Error: massu.config.yaml is empty or not a valid YAML object");
|
|
@@ -14416,8 +14713,9 @@ var init_doctor = __esm({
|
|
|
14416
14713
|
"use strict";
|
|
14417
14714
|
init_config();
|
|
14418
14715
|
init_license();
|
|
14716
|
+
init_settings_local();
|
|
14419
14717
|
__filename3 = fileURLToPath3(import.meta.url);
|
|
14420
|
-
__dirname3 =
|
|
14718
|
+
__dirname3 = dirname7(__filename3);
|
|
14421
14719
|
EXPECTED_HOOKS = [
|
|
14422
14720
|
"session-start.js",
|
|
14423
14721
|
"session-end.js",
|
|
@@ -14458,13 +14756,140 @@ var init_install_hooks = __esm({
|
|
|
14458
14756
|
}
|
|
14459
14757
|
});
|
|
14460
14758
|
|
|
14759
|
+
// src/commands/permissions.ts
|
|
14760
|
+
var permissions_exports = {};
|
|
14761
|
+
__export(permissions_exports, {
|
|
14762
|
+
handlePermissionsSubcommand: () => handlePermissionsSubcommand,
|
|
14763
|
+
printPermissionsHelp: () => printPermissionsHelp
|
|
14764
|
+
});
|
|
14765
|
+
import { resolve as resolve9 } from "path";
|
|
14766
|
+
function resolveClaudeDir2() {
|
|
14767
|
+
let claudeDirName = ".claude";
|
|
14768
|
+
try {
|
|
14769
|
+
claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
|
|
14770
|
+
} catch {
|
|
14771
|
+
claudeDirName = ".claude";
|
|
14772
|
+
}
|
|
14773
|
+
return resolve9(process.cwd(), claudeDirName);
|
|
14774
|
+
}
|
|
14775
|
+
async function handlePermissionsSubcommand(args2) {
|
|
14776
|
+
const sub = args2[0];
|
|
14777
|
+
switch (sub) {
|
|
14778
|
+
case "install": {
|
|
14779
|
+
const claudeDir = resolveClaudeDir2();
|
|
14780
|
+
const result = runWithManifest(
|
|
14781
|
+
claudeDir,
|
|
14782
|
+
(manifest) => installPermissions(claudeDir, manifest, { silent: false })
|
|
14783
|
+
);
|
|
14784
|
+
if (result.installed > 0) {
|
|
14785
|
+
process.stdout.write(
|
|
14786
|
+
"Wrote merged permissions block to .claude/settings.local.json.\n"
|
|
14787
|
+
);
|
|
14788
|
+
} else if (result.skipped > 0) {
|
|
14789
|
+
process.stdout.write("Permissions already in sync \u2014 no changes.\n");
|
|
14790
|
+
} else if (result.kept > 0) {
|
|
14791
|
+
process.stdout.write(
|
|
14792
|
+
"Operator-edited permissions block preserved. Run `npx massu permissions check-drift` to inspect.\n"
|
|
14793
|
+
);
|
|
14794
|
+
}
|
|
14795
|
+
return { exitCode: 0 };
|
|
14796
|
+
}
|
|
14797
|
+
case "verify": {
|
|
14798
|
+
const claudeDir = resolveClaudeDir2();
|
|
14799
|
+
const { missing } = verifyPermissions(claudeDir);
|
|
14800
|
+
if (missing.length === 0) {
|
|
14801
|
+
process.stdout.write("All MCP allowlist entries present.\n");
|
|
14802
|
+
return { exitCode: 0 };
|
|
14803
|
+
}
|
|
14804
|
+
for (const entry of missing) {
|
|
14805
|
+
process.stderr.write(`missing: ${entry}
|
|
14806
|
+
`);
|
|
14807
|
+
}
|
|
14808
|
+
return { exitCode: 1 };
|
|
14809
|
+
}
|
|
14810
|
+
case "check-drift": {
|
|
14811
|
+
const claudeDir = resolveClaudeDir2();
|
|
14812
|
+
const { driftItems } = checkPermissionsDrift(claudeDir);
|
|
14813
|
+
if (driftItems.length === 0) {
|
|
14814
|
+
process.stdout.write("No permission drift detected.\n");
|
|
14815
|
+
return { exitCode: 0 };
|
|
14816
|
+
}
|
|
14817
|
+
let highest = 0;
|
|
14818
|
+
for (const item of driftItems) {
|
|
14819
|
+
const code = DRIFT_KIND_EXIT_CODE[item.kind];
|
|
14820
|
+
if (code > highest) highest = code;
|
|
14821
|
+
process.stderr.write(
|
|
14822
|
+
`drift[${item.kind}]: ${item.detail} \u2014 remediation: ${item.remediation}
|
|
14823
|
+
`
|
|
14824
|
+
);
|
|
14825
|
+
}
|
|
14826
|
+
return { exitCode: highest };
|
|
14827
|
+
}
|
|
14828
|
+
case "--help":
|
|
14829
|
+
case "-h":
|
|
14830
|
+
case void 0: {
|
|
14831
|
+
printPermissionsHelp();
|
|
14832
|
+
return { exitCode: 0 };
|
|
14833
|
+
}
|
|
14834
|
+
default: {
|
|
14835
|
+
process.stderr.write(`massu: unknown permissions subcommand: ${sub}
|
|
14836
|
+
`);
|
|
14837
|
+
printPermissionsHelp();
|
|
14838
|
+
return { exitCode: 1 };
|
|
14839
|
+
}
|
|
14840
|
+
}
|
|
14841
|
+
}
|
|
14842
|
+
function printPermissionsHelp() {
|
|
14843
|
+
process.stdout.write(`
|
|
14844
|
+
massu permissions <subcommand>
|
|
14845
|
+
|
|
14846
|
+
Subcommands:
|
|
14847
|
+
install Seed mcp__massu__* into .claude/settings.local.json's permissions.allow.
|
|
14848
|
+
Also propagates global defaultMode (from ~/.claude/settings.json) into
|
|
14849
|
+
the project-local file to prevent the merge-replacement trap (see
|
|
14850
|
+
https://massu.ai/docs/reference/cli-reference#permissions-trap).
|
|
14851
|
+
Idempotent. Preserves operator-edited values.
|
|
14852
|
+
|
|
14853
|
+
verify Read-only check that all canonical MCP allowlist entries are present.
|
|
14854
|
+
Exit 0 if clean, exit 1 with one diagnostic line per missing entry.
|
|
14855
|
+
|
|
14856
|
+
check-drift Extended diagnostic surfacing 4 drift kinds:
|
|
14857
|
+
- missing-allow (exit 1) \u2014 canonical entries missing
|
|
14858
|
+
- invalid-default-mode (exit 2) \u2014 defaultMode requires launch flag
|
|
14859
|
+
- unknown-key (exit 3) \u2014 undocumented top-level setting
|
|
14860
|
+
- strips-global-defaultmode (exit 4) \u2014 project-local would strip global value
|
|
14861
|
+
|
|
14862
|
+
Examples:
|
|
14863
|
+
npx massu permissions install
|
|
14864
|
+
npx massu permissions verify
|
|
14865
|
+
npx massu permissions check-drift
|
|
14866
|
+
|
|
14867
|
+
Documentation: https://massu.ai/docs/reference/cli-reference#massu-permissions
|
|
14868
|
+
`);
|
|
14869
|
+
}
|
|
14870
|
+
var DRIFT_KIND_EXIT_CODE;
|
|
14871
|
+
var init_permissions2 = __esm({
|
|
14872
|
+
"src/commands/permissions.ts"() {
|
|
14873
|
+
"use strict";
|
|
14874
|
+
init_config();
|
|
14875
|
+
init_permissions();
|
|
14876
|
+
init_install_commands();
|
|
14877
|
+
DRIFT_KIND_EXIT_CODE = {
|
|
14878
|
+
"missing-allow": 1,
|
|
14879
|
+
"invalid-default-mode": 2,
|
|
14880
|
+
"unknown-key": 3,
|
|
14881
|
+
"strips-global-defaultmode": 4
|
|
14882
|
+
};
|
|
14883
|
+
}
|
|
14884
|
+
});
|
|
14885
|
+
|
|
14461
14886
|
// src/commands/show-template.ts
|
|
14462
14887
|
var show_template_exports = {};
|
|
14463
14888
|
__export(show_template_exports, {
|
|
14464
14889
|
runShowTemplate: () => runShowTemplate
|
|
14465
14890
|
});
|
|
14466
|
-
import { existsSync as
|
|
14467
|
-
import { resolve as
|
|
14891
|
+
import { existsSync as existsSync13, readFileSync as readFileSync13 } from "fs";
|
|
14892
|
+
import { resolve as resolve10 } from "path";
|
|
14468
14893
|
function normalizeBaseName(input) {
|
|
14469
14894
|
return input.endsWith(".md") ? input.slice(0, -".md".length) : input;
|
|
14470
14895
|
}
|
|
@@ -14492,14 +14917,14 @@ async function runShowTemplate(args2) {
|
|
|
14492
14917
|
return;
|
|
14493
14918
|
}
|
|
14494
14919
|
const suffix = choice.kind === "hit" ? choice.suffix : "";
|
|
14495
|
-
const file = suffix === "" ?
|
|
14496
|
-
if (!
|
|
14920
|
+
const file = suffix === "" ? resolve10(sourceDir, `${baseName}.md`) : resolve10(sourceDir, `${baseName}${suffix}.md`);
|
|
14921
|
+
if (!existsSync13(file)) {
|
|
14497
14922
|
process.stderr.write(`massu: resolved template "${file}" no longer exists
|
|
14498
14923
|
`);
|
|
14499
14924
|
process.exit(1);
|
|
14500
14925
|
return;
|
|
14501
14926
|
}
|
|
14502
|
-
process.stdout.write(
|
|
14927
|
+
process.stdout.write(readFileSync13(file, "utf-8"));
|
|
14503
14928
|
}
|
|
14504
14929
|
var init_show_template = __esm({
|
|
14505
14930
|
"src/commands/show-template.ts"() {
|
|
@@ -14552,12 +14977,12 @@ var init_passthrough = __esm({
|
|
|
14552
14977
|
});
|
|
14553
14978
|
|
|
14554
14979
|
// src/lib/fileLock.ts
|
|
14555
|
-
import { mkdirSync as
|
|
14556
|
-
import { dirname as
|
|
14980
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync14, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
14981
|
+
import { dirname as dirname8 } from "path";
|
|
14557
14982
|
import * as lockfile from "proper-lockfile";
|
|
14558
14983
|
function readLockHolderPid(lockPath) {
|
|
14559
14984
|
try {
|
|
14560
|
-
const raw =
|
|
14985
|
+
const raw = readFileSync14(`${lockPath}.pid`, "utf-8").trim();
|
|
14561
14986
|
const pid = Number.parseInt(raw, 10);
|
|
14562
14987
|
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
14563
14988
|
return pid;
|
|
@@ -14576,7 +15001,7 @@ function busyWaitSync(ms) {
|
|
|
14576
15001
|
Atomics.wait(view, 0, 0, ms);
|
|
14577
15002
|
}
|
|
14578
15003
|
function withFileLockSync(lockPath, fn, opts = {}) {
|
|
14579
|
-
|
|
15004
|
+
mkdirSync6(dirname8(lockPath), { recursive: true });
|
|
14580
15005
|
const staleMs = opts.staleMs ?? 3e4;
|
|
14581
15006
|
const blockMs = opts.retries === 0 ? 0 : opts.blockMs ?? 3e4;
|
|
14582
15007
|
const pollIntervalMs = opts.pollIntervalMs ?? 100;
|
|
@@ -14644,9 +15069,9 @@ var init_fileLock = __esm({
|
|
|
14644
15069
|
});
|
|
14645
15070
|
|
|
14646
15071
|
// src/lib/installLock.ts
|
|
14647
|
-
import { resolve as
|
|
15072
|
+
import { resolve as resolve11 } from "path";
|
|
14648
15073
|
function withInstallLock(projectRoot, fn, opts = {}) {
|
|
14649
|
-
const lockPath =
|
|
15074
|
+
const lockPath = resolve11(projectRoot, ".massu", "installAll.lock");
|
|
14650
15075
|
return withFileLockSync(
|
|
14651
15076
|
lockPath,
|
|
14652
15077
|
fn,
|
|
@@ -14682,8 +15107,8 @@ __export(config_refresh_exports, {
|
|
|
14682
15107
|
mergeRefresh: () => mergeRefresh,
|
|
14683
15108
|
runConfigRefresh: () => runConfigRefresh
|
|
14684
15109
|
});
|
|
14685
|
-
import { existsSync as
|
|
14686
|
-
import { resolve as
|
|
15110
|
+
import { existsSync as existsSync14, readFileSync as readFileSync15, rmSync as rmSync4 } from "fs";
|
|
15111
|
+
import { resolve as resolve12 } from "path";
|
|
14687
15112
|
import { parse as parseYaml5 } from "yaml";
|
|
14688
15113
|
function flatten(obj, prefix3 = "") {
|
|
14689
15114
|
const out = {};
|
|
@@ -14811,17 +15236,17 @@ function renderDiff(diff) {
|
|
|
14811
15236
|
}
|
|
14812
15237
|
async function runConfigRefresh(opts = {}) {
|
|
14813
15238
|
const cwd = opts.cwd ?? process.cwd();
|
|
14814
|
-
const configPath =
|
|
15239
|
+
const configPath = resolve12(cwd, "massu.config.yaml");
|
|
14815
15240
|
const log = opts.silent ? () => {
|
|
14816
15241
|
} : (s) => process.stdout.write(s);
|
|
14817
|
-
if (!
|
|
15242
|
+
if (!existsSync14(configPath)) {
|
|
14818
15243
|
const message = "massu.config.yaml not found. Run: npx massu init";
|
|
14819
15244
|
if (!opts.silent) process.stderr.write(message + "\n");
|
|
14820
15245
|
return { exitCode: 1, applied: false, dryRun: !!opts.dryRun, diff: [], message };
|
|
14821
15246
|
}
|
|
14822
15247
|
let existing;
|
|
14823
15248
|
try {
|
|
14824
|
-
const content =
|
|
15249
|
+
const content = readFileSync15(configPath, "utf-8");
|
|
14825
15250
|
const parsed = parseYaml5(content);
|
|
14826
15251
|
if (!parsed || typeof parsed !== "object") {
|
|
14827
15252
|
throw new Error("config is not a YAML object");
|
|
@@ -14894,8 +15319,8 @@ async function runConfigRefresh(opts = {}) {
|
|
|
14894
15319
|
`);
|
|
14895
15320
|
const stackResolved = installResult.totalInstalled > 0 || installResult.totalUpdated > 0;
|
|
14896
15321
|
if (stackResolved) {
|
|
14897
|
-
const placeholderPath =
|
|
14898
|
-
if (
|
|
15322
|
+
const placeholderPath = resolve12(installResult.claudeDir, "commands", "_massu-needs-stack.md");
|
|
15323
|
+
if (existsSync14(placeholderPath)) {
|
|
14899
15324
|
try {
|
|
14900
15325
|
rmSync4(placeholderPath, { force: true });
|
|
14901
15326
|
log("Removed _massu-needs-stack.md (stack now declared).\n");
|
|
@@ -15004,12 +15429,12 @@ var init_gitToplevel = __esm({
|
|
|
15004
15429
|
});
|
|
15005
15430
|
|
|
15006
15431
|
// src/watch/lockfile-detector.ts
|
|
15007
|
-
import { existsSync as
|
|
15008
|
-
import { resolve as
|
|
15432
|
+
import { existsSync as existsSync15, statSync as statSync8 } from "fs";
|
|
15433
|
+
import { resolve as resolve13 } from "path";
|
|
15009
15434
|
function lockfileMidWrite(projectRoot, now = Date.now(), windowMs = LOCKFILE_WINDOW_MS) {
|
|
15010
15435
|
for (const lf of KNOWN_LOCKFILES) {
|
|
15011
|
-
const p19 =
|
|
15012
|
-
if (!
|
|
15436
|
+
const p19 = resolve13(projectRoot, lf);
|
|
15437
|
+
if (!existsSync15(p19)) continue;
|
|
15013
15438
|
try {
|
|
15014
15439
|
const stat = statSync8(p19);
|
|
15015
15440
|
const delta = now - stat.mtimeMs;
|
|
@@ -15022,7 +15447,7 @@ function lockfileMidWrite(projectRoot, now = Date.now(), windowMs = LOCKFILE_WIN
|
|
|
15022
15447
|
function gitMidOperation(projectRoot) {
|
|
15023
15448
|
const sentinels = ["MERGE_HEAD", "REBASE_HEAD", "CHERRY_PICK_HEAD", "rebase-apply", "rebase-merge"];
|
|
15024
15449
|
for (const s of sentinels) {
|
|
15025
|
-
if (
|
|
15450
|
+
if (existsSync15(resolve13(projectRoot, ".git", s))) return true;
|
|
15026
15451
|
}
|
|
15027
15452
|
return false;
|
|
15028
15453
|
}
|
|
@@ -15219,18 +15644,18 @@ var init_paths = __esm({
|
|
|
15219
15644
|
});
|
|
15220
15645
|
|
|
15221
15646
|
// src/watch/state.ts
|
|
15222
|
-
import { closeSync as closeSync3, existsSync as
|
|
15223
|
-
import { dirname as
|
|
15647
|
+
import { closeSync as closeSync3, existsSync as existsSync16, fsyncSync as fsyncSync3, mkdirSync as mkdirSync7, openSync as openSync3, readFileSync as readFileSync16, renameSync as renameSync4, rmSync as rmSync5, writeFileSync as writeFileSync4, writeSync as writeSync3 } from "fs";
|
|
15648
|
+
import { dirname as dirname9, resolve as resolve14 } from "path";
|
|
15224
15649
|
function watchStatePath(projectRoot) {
|
|
15225
|
-
return
|
|
15650
|
+
return resolve14(projectRoot, ".massu", "watch-state.json");
|
|
15226
15651
|
}
|
|
15227
15652
|
function backupStatePath(projectRoot) {
|
|
15228
|
-
return
|
|
15653
|
+
return resolve14(projectRoot, ".massu", "watch-state.v0.bak.json");
|
|
15229
15654
|
}
|
|
15230
15655
|
function readState(projectRoot) {
|
|
15231
15656
|
const path = watchStatePath(projectRoot);
|
|
15232
|
-
if (!
|
|
15233
|
-
const content =
|
|
15657
|
+
if (!existsSync16(path)) return { ...DEFAULT_STATE };
|
|
15658
|
+
const content = readFileSync16(path, "utf-8");
|
|
15234
15659
|
let raw;
|
|
15235
15660
|
try {
|
|
15236
15661
|
raw = JSON.parse(content);
|
|
@@ -15272,14 +15697,14 @@ function readState(projectRoot) {
|
|
|
15272
15697
|
}
|
|
15273
15698
|
function archiveCorrupt(projectRoot, content) {
|
|
15274
15699
|
const bak = backupStatePath(projectRoot);
|
|
15275
|
-
|
|
15700
|
+
mkdirSync7(dirname9(bak), { recursive: true });
|
|
15276
15701
|
writeFileSync4(bak, content, "utf-8");
|
|
15277
15702
|
}
|
|
15278
15703
|
function writeStateAtomic(projectRoot, state) {
|
|
15279
15704
|
const path = watchStatePath(projectRoot);
|
|
15280
15705
|
writeStateAtomicCounter = writeStateAtomicCounter + 1 >>> 0;
|
|
15281
15706
|
const tmp = `${path}.${process.pid}.${writeStateAtomicCounter}.tmp`;
|
|
15282
|
-
|
|
15707
|
+
mkdirSync7(dirname9(path), { recursive: true });
|
|
15283
15708
|
let renamed = false;
|
|
15284
15709
|
try {
|
|
15285
15710
|
const fd = openSync3(tmp, "w");
|
|
@@ -15293,7 +15718,7 @@ function writeStateAtomic(projectRoot, state) {
|
|
|
15293
15718
|
renameSync4(tmp, path);
|
|
15294
15719
|
renamed = true;
|
|
15295
15720
|
} finally {
|
|
15296
|
-
if (!renamed &&
|
|
15721
|
+
if (!renamed && existsSync16(tmp)) {
|
|
15297
15722
|
try {
|
|
15298
15723
|
rmSync5(tmp, { force: true });
|
|
15299
15724
|
} catch {
|
|
@@ -15573,8 +15998,8 @@ __export(watch_exports, {
|
|
|
15573
15998
|
runWatch: () => runWatch
|
|
15574
15999
|
});
|
|
15575
16000
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
15576
|
-
import { basename as basename5, dirname as
|
|
15577
|
-
import { appendFileSync, existsSync as
|
|
16001
|
+
import { basename as basename5, dirname as dirname10, resolve as resolve15 } from "path";
|
|
16002
|
+
import { appendFileSync, existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync17 } from "fs";
|
|
15578
16003
|
function parseFlags(args2) {
|
|
15579
16004
|
const out = {
|
|
15580
16005
|
foreground: false,
|
|
@@ -15599,8 +16024,8 @@ function parseFlags(args2) {
|
|
|
15599
16024
|
}
|
|
15600
16025
|
function findClaudeBg() {
|
|
15601
16026
|
const home = process.env.HOME ?? "";
|
|
15602
|
-
const fixed = home ?
|
|
15603
|
-
if (fixed &&
|
|
16027
|
+
const fixed = home ? resolve15(home, ".claude", "bin", "claude-bg") : null;
|
|
16028
|
+
if (fixed && existsSync17(fixed)) return fixed;
|
|
15604
16029
|
const which = spawnSync3("which", ["claude-bg"], { encoding: "utf-8" });
|
|
15605
16030
|
if (which.status === 0 && which.stdout) {
|
|
15606
16031
|
const p19 = which.stdout.trim();
|
|
@@ -15661,7 +16086,7 @@ async function runWatch(args2) {
|
|
|
15661
16086
|
}
|
|
15662
16087
|
function runStatus(root) {
|
|
15663
16088
|
const path = watchStatePath(root);
|
|
15664
|
-
if (!
|
|
16089
|
+
if (!existsSync17(path)) {
|
|
15665
16090
|
process.stdout.write("massu watch: not running (no state file)\n");
|
|
15666
16091
|
return { exitCode: 0 };
|
|
15667
16092
|
}
|
|
@@ -15743,7 +16168,7 @@ async function runForeground(root) {
|
|
|
15743
16168
|
}
|
|
15744
16169
|
throw err;
|
|
15745
16170
|
}
|
|
15746
|
-
return new Promise((
|
|
16171
|
+
return new Promise((resolve38) => {
|
|
15747
16172
|
const shutdown = async () => {
|
|
15748
16173
|
if (stopped) return;
|
|
15749
16174
|
stopped = true;
|
|
@@ -15753,7 +16178,7 @@ async function runForeground(root) {
|
|
|
15753
16178
|
process.chdir(priorCwd);
|
|
15754
16179
|
} catch {
|
|
15755
16180
|
}
|
|
15756
|
-
|
|
16181
|
+
resolve38({ exitCode: 0 });
|
|
15757
16182
|
};
|
|
15758
16183
|
process.on("SIGINT", () => {
|
|
15759
16184
|
void shutdown();
|
|
@@ -15814,23 +16239,23 @@ async function runOnQuiescent(projectRoot) {
|
|
|
15814
16239
|
);
|
|
15815
16240
|
}
|
|
15816
16241
|
function refreshLogPath(projectRoot) {
|
|
15817
|
-
return
|
|
16242
|
+
return resolve15(projectRoot, ".massu", "refresh-log.jsonl");
|
|
15818
16243
|
}
|
|
15819
16244
|
function appendRefreshLog(projectRoot, event) {
|
|
15820
16245
|
const path = refreshLogPath(projectRoot);
|
|
15821
16246
|
try {
|
|
15822
|
-
|
|
16247
|
+
mkdirSync8(dirname10(path), { recursive: true });
|
|
15823
16248
|
appendFileSync(path, JSON.stringify(event) + "\n", "utf-8");
|
|
15824
16249
|
} catch {
|
|
15825
16250
|
}
|
|
15826
16251
|
}
|
|
15827
16252
|
function readRefreshLog(projectRoot, limit = 10, opts = {}) {
|
|
15828
16253
|
const path = refreshLogPath(projectRoot);
|
|
15829
|
-
if (!
|
|
16254
|
+
if (!existsSync17(path)) return [];
|
|
15830
16255
|
const warn = opts.warn ?? ((s) => {
|
|
15831
16256
|
process.stderr.write(s);
|
|
15832
16257
|
});
|
|
15833
|
-
const lines =
|
|
16258
|
+
const lines = readFileSync17(path, "utf-8").split("\n").filter(Boolean);
|
|
15834
16259
|
const tail = lines.slice(-limit);
|
|
15835
16260
|
const out = [];
|
|
15836
16261
|
let corrupt = 0;
|
|
@@ -15903,26 +16328,26 @@ var init_refresh_log = __esm({
|
|
|
15903
16328
|
import {
|
|
15904
16329
|
chmodSync as chmodSync3,
|
|
15905
16330
|
closeSync as closeSync4,
|
|
15906
|
-
existsSync as
|
|
16331
|
+
existsSync as existsSync18,
|
|
15907
16332
|
fsyncSync as fsyncSync4,
|
|
15908
|
-
mkdirSync as
|
|
16333
|
+
mkdirSync as mkdirSync9,
|
|
15909
16334
|
openSync as openSync4,
|
|
15910
16335
|
renameSync as renameSync5,
|
|
15911
16336
|
rmSync as rmSync6,
|
|
15912
16337
|
statSync as statSync9,
|
|
15913
16338
|
writeSync as writeSync4
|
|
15914
16339
|
} from "node:fs";
|
|
15915
|
-
import { dirname as
|
|
16340
|
+
import { dirname as dirname11 } from "node:path";
|
|
15916
16341
|
function atomicWrite(path, content, opts = {}) {
|
|
15917
16342
|
const tmpPath = `${path}.tmp`;
|
|
15918
|
-
const parentDir =
|
|
16343
|
+
const parentDir = dirname11(path);
|
|
15919
16344
|
try {
|
|
15920
|
-
if (!
|
|
16345
|
+
if (!existsSync18(parentDir)) {
|
|
15921
16346
|
const mkdirOpts = { recursive: true };
|
|
15922
16347
|
if (opts.ensureParentDirMode !== void 0) {
|
|
15923
16348
|
mkdirOpts.mode = opts.ensureParentDirMode;
|
|
15924
16349
|
}
|
|
15925
|
-
|
|
16350
|
+
mkdirSync9(parentDir, mkdirOpts);
|
|
15926
16351
|
}
|
|
15927
16352
|
const buf = typeof content === "string" ? Buffer.from(content, "utf-8") : content;
|
|
15928
16353
|
const openMode = opts.mode ?? 420;
|
|
@@ -15939,7 +16364,7 @@ function atomicWrite(path, content, opts = {}) {
|
|
|
15939
16364
|
renameSync5(tmpPath, path);
|
|
15940
16365
|
return { written: true };
|
|
15941
16366
|
} catch (err) {
|
|
15942
|
-
if (
|
|
16367
|
+
if (existsSync18(tmpPath)) {
|
|
15943
16368
|
try {
|
|
15944
16369
|
rmSync6(tmpPath, { force: true });
|
|
15945
16370
|
} catch {
|
|
@@ -16019,10 +16444,10 @@ var init_registry_pubkey_generated = __esm({
|
|
|
16019
16444
|
});
|
|
16020
16445
|
|
|
16021
16446
|
// src/security/adapter-verifier.ts
|
|
16022
|
-
import { createHash as
|
|
16447
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
16023
16448
|
import nacl from "tweetnacl";
|
|
16024
|
-
function
|
|
16025
|
-
return
|
|
16449
|
+
function sha256Hex2(bytes) {
|
|
16450
|
+
return createHash6("sha256").update(bytes).digest("hex");
|
|
16026
16451
|
}
|
|
16027
16452
|
function reviver(key, value) {
|
|
16028
16453
|
if (key === "__proto__" || key === "constructor" || key === "prototype") {
|
|
@@ -16065,7 +16490,7 @@ function verifyManifest(input) {
|
|
|
16065
16490
|
if (manifestBytes.length === 0) {
|
|
16066
16491
|
return { ok: false, reason: "manifest_b64 decoded to zero bytes" };
|
|
16067
16492
|
}
|
|
16068
|
-
const computedSha =
|
|
16493
|
+
const computedSha = sha256Hex2(manifestBytes);
|
|
16069
16494
|
if (computedSha !== envelope.manifest_sha256) {
|
|
16070
16495
|
return {
|
|
16071
16496
|
ok: false,
|
|
@@ -16119,10 +16544,10 @@ function verifyManifest(input) {
|
|
|
16119
16544
|
if (!sigOk) {
|
|
16120
16545
|
return {
|
|
16121
16546
|
ok: false,
|
|
16122
|
-
reason: `Ed25519 signature verification failed against bundled public key (fingerprint ${
|
|
16547
|
+
reason: `Ed25519 signature verification failed against bundled public key (fingerprint ${sha256Hex2(publicKey).slice(0, 16)}...). Manifest was either signed by a different key OR the signature is corrupt.`
|
|
16123
16548
|
};
|
|
16124
16549
|
}
|
|
16125
|
-
const expectedKeyId =
|
|
16550
|
+
const expectedKeyId = sha256Hex2(publicKey);
|
|
16126
16551
|
if (envelope.signing_key_id !== expectedKeyId) {
|
|
16127
16552
|
return {
|
|
16128
16553
|
ok: false,
|
|
@@ -16282,24 +16707,24 @@ var init_fetcher = __esm({
|
|
|
16282
16707
|
});
|
|
16283
16708
|
|
|
16284
16709
|
// src/security/manifest-cache.ts
|
|
16285
|
-
import { existsSync as
|
|
16286
|
-
import { homedir as
|
|
16287
|
-
import { resolve as
|
|
16710
|
+
import { existsSync as existsSync19, readFileSync as readFileSync18, statSync as statSync10 } from "node:fs";
|
|
16711
|
+
import { homedir as homedir5 } from "node:os";
|
|
16712
|
+
import { resolve as resolve16 } from "node:path";
|
|
16288
16713
|
import { z as z4 } from "zod";
|
|
16289
16714
|
function defaultCachePaths() {
|
|
16290
|
-
const dir =
|
|
16715
|
+
const dir = resolve16(homedir5(), ".massu");
|
|
16291
16716
|
return {
|
|
16292
|
-
cachePath:
|
|
16293
|
-
lockPath:
|
|
16717
|
+
cachePath: resolve16(dir, "adapter-manifest.json"),
|
|
16718
|
+
lockPath: resolve16(dir, ".adapter-manifest.lock")
|
|
16294
16719
|
};
|
|
16295
16720
|
}
|
|
16296
16721
|
function loadCachedManifest(paths = defaultCachePaths()) {
|
|
16297
|
-
if (!
|
|
16722
|
+
if (!existsSync19(paths.cachePath)) {
|
|
16298
16723
|
return { kind: "absent" };
|
|
16299
16724
|
}
|
|
16300
16725
|
let raw;
|
|
16301
16726
|
try {
|
|
16302
|
-
const content =
|
|
16727
|
+
const content = readFileSync18(paths.cachePath, "utf-8");
|
|
16303
16728
|
raw = JSON.parse(content);
|
|
16304
16729
|
} catch (err) {
|
|
16305
16730
|
return {
|
|
@@ -16488,15 +16913,15 @@ var init_adapter_origin = __esm({
|
|
|
16488
16913
|
});
|
|
16489
16914
|
|
|
16490
16915
|
// src/security/local-fingerprint.ts
|
|
16491
|
-
import { existsSync as
|
|
16492
|
-
import { homedir as
|
|
16493
|
-
import { resolve as
|
|
16494
|
-
import { createHash as
|
|
16916
|
+
import { existsSync as existsSync20, readFileSync as readFileSync19, lstatSync as lstatSync5 } from "node:fs";
|
|
16917
|
+
import { homedir as homedir6 } from "node:os";
|
|
16918
|
+
import { resolve as resolve17, isAbsolute } from "node:path";
|
|
16919
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
16495
16920
|
import { z as z5 } from "zod";
|
|
16496
16921
|
function computeLocalFingerprint(localPaths, projectRoot) {
|
|
16497
16922
|
const tuples = [];
|
|
16498
16923
|
for (const p19 of localPaths) {
|
|
16499
|
-
const abs = isAbsolute(p19) ? p19 :
|
|
16924
|
+
const abs = isAbsolute(p19) ? p19 : resolve17(projectRoot, p19);
|
|
16500
16925
|
let contentTag;
|
|
16501
16926
|
try {
|
|
16502
16927
|
const lst = lstatSync5(abs);
|
|
@@ -16505,7 +16930,7 @@ function computeLocalFingerprint(localPaths, projectRoot) {
|
|
|
16505
16930
|
} else if (!lst.isFile()) {
|
|
16506
16931
|
contentTag = "<not-a-file>";
|
|
16507
16932
|
} else {
|
|
16508
|
-
contentTag =
|
|
16933
|
+
contentTag = createHash7("sha256").update(readFileSync19(abs)).digest("hex");
|
|
16509
16934
|
}
|
|
16510
16935
|
} catch {
|
|
16511
16936
|
contentTag = "<missing>";
|
|
@@ -16514,13 +16939,13 @@ function computeLocalFingerprint(localPaths, projectRoot) {
|
|
|
16514
16939
|
}
|
|
16515
16940
|
tuples.sort((a2, b2) => a2.path < b2.path ? -1 : a2.path > b2.path ? 1 : 0);
|
|
16516
16941
|
const canonical = JSON.stringify(tuples);
|
|
16517
|
-
return
|
|
16942
|
+
return createHash7("sha256").update(canonical).digest("hex");
|
|
16518
16943
|
}
|
|
16519
16944
|
function readFingerprintSentinel(path = FINGERPRINT_PATH) {
|
|
16520
|
-
if (!
|
|
16945
|
+
if (!existsSync20(path)) return null;
|
|
16521
16946
|
let raw;
|
|
16522
16947
|
try {
|
|
16523
|
-
raw = JSON.parse(
|
|
16948
|
+
raw = JSON.parse(readFileSync19(path, "utf-8"));
|
|
16524
16949
|
} catch {
|
|
16525
16950
|
return null;
|
|
16526
16951
|
}
|
|
@@ -16568,7 +16993,7 @@ var init_local_fingerprint = __esm({
|
|
|
16568
16993
|
"use strict";
|
|
16569
16994
|
init_atomic_write();
|
|
16570
16995
|
init_manifest_schema();
|
|
16571
|
-
FINGERPRINT_PATH =
|
|
16996
|
+
FINGERPRINT_PATH = resolve17(homedir6(), ".massu", "adapters-local-fingerprint.json");
|
|
16572
16997
|
FingerprintSentinelSchema = z5.object({
|
|
16573
16998
|
fingerprint: z5.string().regex(/^[0-9a-f]{64}$/),
|
|
16574
16999
|
source: z5.enum(["cli", "cli-resync"]),
|
|
@@ -16584,15 +17009,15 @@ var init_local_fingerprint = __esm({
|
|
|
16584
17009
|
});
|
|
16585
17010
|
|
|
16586
17011
|
// src/security/install-tracking.ts
|
|
16587
|
-
import { readFileSync as
|
|
16588
|
-
import { join as
|
|
16589
|
-
import { homedir as
|
|
16590
|
-
import { resolve as
|
|
16591
|
-
import { createHash as
|
|
17012
|
+
import { readFileSync as readFileSync20, readdirSync as readdirSync12, lstatSync as lstatSync6, existsSync as existsSync21 } from "node:fs";
|
|
17013
|
+
import { join as join11, relative as relative5, sep } from "node:path";
|
|
17014
|
+
import { homedir as homedir7 } from "node:os";
|
|
17015
|
+
import { resolve as resolve18 } from "node:path";
|
|
17016
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
16592
17017
|
import { z as z6 } from "zod";
|
|
16593
17018
|
function containsHiddenDirs(packageDir) {
|
|
16594
17019
|
for (const hidden of EXCLUDED_DIR_NAMES) {
|
|
16595
|
-
if (
|
|
17020
|
+
if (existsSync21(`${packageDir}/${hidden}`)) {
|
|
16596
17021
|
return hidden;
|
|
16597
17022
|
}
|
|
16598
17023
|
}
|
|
@@ -16609,7 +17034,7 @@ function sha256OfDir(dir, opts = {}) {
|
|
|
16609
17034
|
return;
|
|
16610
17035
|
}
|
|
16611
17036
|
for (const entry of entries.sort()) {
|
|
16612
|
-
const absPath =
|
|
17037
|
+
const absPath = join11(currentDir, entry);
|
|
16613
17038
|
let lst;
|
|
16614
17039
|
try {
|
|
16615
17040
|
lst = lstatSync6(absPath);
|
|
@@ -16636,9 +17061,9 @@ function sha256OfDir(dir, opts = {}) {
|
|
|
16636
17061
|
}
|
|
16637
17062
|
walk(dir);
|
|
16638
17063
|
files.sort((a2, b2) => a2.relativePath < b2.relativePath ? -1 : a2.relativePath > b2.relativePath ? 1 : 0);
|
|
16639
|
-
const top =
|
|
17064
|
+
const top = createHash8("sha256");
|
|
16640
17065
|
for (const f2 of files) {
|
|
16641
|
-
const fileHash =
|
|
17066
|
+
const fileHash = createHash8("sha256").update(readFileSync20(f2.absPath)).digest("hex");
|
|
16642
17067
|
top.update(f2.relativePath, "utf-8");
|
|
16643
17068
|
top.update("\0", "utf-8");
|
|
16644
17069
|
top.update(fileHash, "utf-8");
|
|
@@ -16647,10 +17072,10 @@ function sha256OfDir(dir, opts = {}) {
|
|
|
16647
17072
|
return top.digest("hex");
|
|
16648
17073
|
}
|
|
16649
17074
|
function readInstalledManifest(path = INSTALLED_MANIFEST_PATH) {
|
|
16650
|
-
if (!
|
|
17075
|
+
if (!existsSync21(path)) return {};
|
|
16651
17076
|
let raw;
|
|
16652
17077
|
try {
|
|
16653
|
-
raw = JSON.parse(
|
|
17078
|
+
raw = JSON.parse(readFileSync20(path, "utf-8"));
|
|
16654
17079
|
} catch {
|
|
16655
17080
|
return {};
|
|
16656
17081
|
}
|
|
@@ -16714,7 +17139,7 @@ var init_install_tracking = __esm({
|
|
|
16714
17139
|
"src/security/install-tracking.ts"() {
|
|
16715
17140
|
"use strict";
|
|
16716
17141
|
init_atomic_write();
|
|
16717
|
-
INSTALLED_MANIFEST_PATH =
|
|
17142
|
+
INSTALLED_MANIFEST_PATH = resolve18(homedir7(), ".massu", "adapter-manifest-installed.json");
|
|
16718
17143
|
DEFAULT_MAX_FILE_BYTES = 64 * 1024 * 1024;
|
|
16719
17144
|
EXCLUDED_DIR_NAMES = /* @__PURE__ */ new Set([".git", "node_modules", ".cache", ".tmp"]);
|
|
16720
17145
|
InstallEntrySchema = z6.object({
|
|
@@ -16737,12 +17162,12 @@ var init_install_tracking = __esm({
|
|
|
16737
17162
|
});
|
|
16738
17163
|
|
|
16739
17164
|
// src/detect/adapters/discover.ts
|
|
16740
|
-
import { existsSync as
|
|
16741
|
-
import { resolve as
|
|
17165
|
+
import { existsSync as existsSync22, readdirSync as readdirSync13, readFileSync as readFileSync21, lstatSync as lstatSync7 } from "node:fs";
|
|
17166
|
+
import { resolve as resolve19, isAbsolute as isAbsolute2 } from "node:path";
|
|
16742
17167
|
import { z as z7 } from "zod";
|
|
16743
17168
|
function walkNodeModules(projectRoot, warnings) {
|
|
16744
|
-
const nodeModulesDir =
|
|
16745
|
-
if (!
|
|
17169
|
+
const nodeModulesDir = resolve19(projectRoot, "node_modules");
|
|
17170
|
+
if (!existsSync22(nodeModulesDir)) {
|
|
16746
17171
|
return [];
|
|
16747
17172
|
}
|
|
16748
17173
|
const candidates = [];
|
|
@@ -16755,7 +17180,7 @@ function walkNodeModules(projectRoot, warnings) {
|
|
|
16755
17180
|
}
|
|
16756
17181
|
for (const entry of topLevelEntries) {
|
|
16757
17182
|
if (entry.startsWith(".")) continue;
|
|
16758
|
-
const entryPath =
|
|
17183
|
+
const entryPath = resolve19(nodeModulesDir, entry);
|
|
16759
17184
|
let entryStat;
|
|
16760
17185
|
try {
|
|
16761
17186
|
entryStat = lstatSync7(entryPath);
|
|
@@ -16777,7 +17202,7 @@ function walkNodeModules(projectRoot, warnings) {
|
|
|
16777
17202
|
continue;
|
|
16778
17203
|
}
|
|
16779
17204
|
for (const sub of scopedEntries) {
|
|
16780
|
-
const subPath =
|
|
17205
|
+
const subPath = resolve19(entryPath, sub);
|
|
16781
17206
|
let subStat;
|
|
16782
17207
|
try {
|
|
16783
17208
|
subStat = lstatSync7(subPath);
|
|
@@ -16796,11 +17221,11 @@ function walkNodeModules(projectRoot, warnings) {
|
|
|
16796
17221
|
return candidates;
|
|
16797
17222
|
}
|
|
16798
17223
|
function tryReadAdapterPackage(packageDir, warnings) {
|
|
16799
|
-
const pkgJsonPath =
|
|
16800
|
-
if (!
|
|
17224
|
+
const pkgJsonPath = resolve19(packageDir, "package.json");
|
|
17225
|
+
if (!existsSync22(pkgJsonPath)) return null;
|
|
16801
17226
|
let raw;
|
|
16802
17227
|
try {
|
|
16803
|
-
raw = JSON.parse(
|
|
17228
|
+
raw = JSON.parse(readFileSync21(pkgJsonPath, "utf-8"));
|
|
16804
17229
|
} catch (err) {
|
|
16805
17230
|
warnings.push(
|
|
16806
17231
|
`skipping ${packageDir}: package.json parse failed (${err instanceof Error ? err.message : String(err)})`
|
|
@@ -16934,8 +17359,8 @@ function discoverAdapters(opts) {
|
|
|
16934
17359
|
const localSet = new Set(opts.configLocalPaths);
|
|
16935
17360
|
for (const localPath of opts.configLocalPaths) {
|
|
16936
17361
|
if (seenIds.has(localPath)) continue;
|
|
16937
|
-
const absPath = isAbsolute2(localPath) ? localPath :
|
|
16938
|
-
if (!
|
|
17362
|
+
const absPath = isAbsolute2(localPath) ? localPath : resolve19(opts.projectRoot, localPath);
|
|
17363
|
+
if (!existsSync22(absPath)) {
|
|
16939
17364
|
warnings.push(
|
|
16940
17365
|
`local adapter file not found: ${localPath} (resolved to ${absPath}). Remove via: massu adapters remove-local ${localPath}`
|
|
16941
17366
|
);
|
|
@@ -17016,8 +17441,8 @@ __export(adapters_exports, {
|
|
|
17016
17441
|
runAdaptersResyncLocalFingerprint: () => runAdaptersResyncLocalFingerprint,
|
|
17017
17442
|
runAdaptersSearch: () => runAdaptersSearch
|
|
17018
17443
|
});
|
|
17019
|
-
import { existsSync as
|
|
17020
|
-
import { resolve as
|
|
17444
|
+
import { existsSync as existsSync23, readFileSync as readFileSync22 } from "node:fs";
|
|
17445
|
+
import { resolve as resolve20 } from "node:path";
|
|
17021
17446
|
import { parseDocument } from "yaml";
|
|
17022
17447
|
async function handleAdaptersSubcommand(args2) {
|
|
17023
17448
|
const sub = args2[0];
|
|
@@ -17284,8 +17709,8 @@ function mutateLocalArray(mutator, command) {
|
|
|
17284
17709
|
);
|
|
17285
17710
|
return { exitCode: 2 };
|
|
17286
17711
|
}
|
|
17287
|
-
const yamlPath =
|
|
17288
|
-
if (!
|
|
17712
|
+
const yamlPath = resolve20(projectRoot, "massu.config.yaml");
|
|
17713
|
+
if (!existsSync23(yamlPath)) {
|
|
17289
17714
|
process.stderr.write(
|
|
17290
17715
|
`${command}: massu.config.yaml not found at ${yamlPath}. Run \`massu init\` first.
|
|
17291
17716
|
`
|
|
@@ -17294,7 +17719,7 @@ function mutateLocalArray(mutator, command) {
|
|
|
17294
17719
|
}
|
|
17295
17720
|
let yamlText;
|
|
17296
17721
|
try {
|
|
17297
|
-
yamlText =
|
|
17722
|
+
yamlText = readFileSync22(yamlPath, "utf-8");
|
|
17298
17723
|
} catch (err) {
|
|
17299
17724
|
process.stderr.write(`${command}: failed to read ${yamlPath}: ${err instanceof Error ? err.message : String(err)}
|
|
17300
17725
|
`);
|
|
@@ -17315,7 +17740,7 @@ function mutateLocalArray(mutator, command) {
|
|
|
17315
17740
|
if (next === null) {
|
|
17316
17741
|
return { exitCode: 0 };
|
|
17317
17742
|
}
|
|
17318
|
-
const lockPath =
|
|
17743
|
+
const lockPath = resolve20(projectRoot, ".massu", "adapters-local-mutate.lock");
|
|
17319
17744
|
return withFileLockSync(lockPath, () => {
|
|
17320
17745
|
doc.setIn(["adapters", "local"], next);
|
|
17321
17746
|
const newYaml = doc.toString();
|
|
@@ -17384,23 +17809,23 @@ async function runAdaptersInstall(args2) {
|
|
|
17384
17809
|
`);
|
|
17385
17810
|
return { exitCode: 1 };
|
|
17386
17811
|
}
|
|
17387
|
-
const packageDir =
|
|
17388
|
-
if (!
|
|
17812
|
+
const packageDir = resolve20(projectRoot, "node_modules", ...packageName.split("/"));
|
|
17813
|
+
if (!existsSync23(packageDir)) {
|
|
17389
17814
|
process.stderr.write(
|
|
17390
17815
|
`install: ${packageName} is not installed in node_modules. Run \`npm install ${packageName}\` first.
|
|
17391
17816
|
`
|
|
17392
17817
|
);
|
|
17393
17818
|
return { exitCode: 1 };
|
|
17394
17819
|
}
|
|
17395
|
-
const pkgJsonPath =
|
|
17396
|
-
if (!
|
|
17820
|
+
const pkgJsonPath = resolve20(packageDir, "package.json");
|
|
17821
|
+
if (!existsSync23(pkgJsonPath)) {
|
|
17397
17822
|
process.stderr.write(`install: ${packageName} has no package.json at ${pkgJsonPath}
|
|
17398
17823
|
`);
|
|
17399
17824
|
return { exitCode: 1 };
|
|
17400
17825
|
}
|
|
17401
17826
|
let pkgJson;
|
|
17402
17827
|
try {
|
|
17403
|
-
pkgJson = JSON.parse(
|
|
17828
|
+
pkgJson = JSON.parse(readFileSync22(pkgJsonPath, "utf-8"));
|
|
17404
17829
|
} catch (err) {
|
|
17405
17830
|
process.stderr.write(`install: ${packageName} has malformed package.json: ${err instanceof Error ? err.message : String(err)}
|
|
17406
17831
|
`);
|
|
@@ -17519,8 +17944,8 @@ async function runAdaptersResign(_args) {
|
|
|
17519
17944
|
removeInstalledManifestEntry(name);
|
|
17520
17945
|
continue;
|
|
17521
17946
|
}
|
|
17522
|
-
const packageDir =
|
|
17523
|
-
if (!
|
|
17947
|
+
const packageDir = resolve20(projectRoot, "node_modules", ...name.split("/"));
|
|
17948
|
+
if (!existsSync23(packageDir)) {
|
|
17524
17949
|
removed++;
|
|
17525
17950
|
warnings.push(`${name}@${entry.version}: not present in node_modules \u2014 REMOVED from sidecar`);
|
|
17526
17951
|
removeInstalledManifestEntry(name);
|
|
@@ -17607,11 +18032,11 @@ var init_adapters2 = __esm({
|
|
|
17607
18032
|
|
|
17608
18033
|
// src/db.ts
|
|
17609
18034
|
import Database2 from "better-sqlite3";
|
|
17610
|
-
import { dirname as
|
|
17611
|
-
import { existsSync as
|
|
18035
|
+
import { dirname as dirname12, join as join12 } from "path";
|
|
18036
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync10, readdirSync as readdirSync14, statSync as statSync11 } from "fs";
|
|
17612
18037
|
function getCodeGraphDb() {
|
|
17613
18038
|
const dbPath = getResolvedPaths().codegraphDbPath;
|
|
17614
|
-
if (!
|
|
18039
|
+
if (!existsSync24(dbPath)) {
|
|
17615
18040
|
throw new CodegraphDbNotInitializedError(dbPath);
|
|
17616
18041
|
}
|
|
17617
18042
|
const db = new Database2(dbPath, { readonly: true });
|
|
@@ -17620,9 +18045,9 @@ function getCodeGraphDb() {
|
|
|
17620
18045
|
}
|
|
17621
18046
|
function getDataDb() {
|
|
17622
18047
|
const dbPath = getResolvedPaths().dataDbPath;
|
|
17623
|
-
const dir =
|
|
17624
|
-
if (!
|
|
17625
|
-
|
|
18048
|
+
const dir = dirname12(dbPath);
|
|
18049
|
+
if (!existsSync24(dir)) {
|
|
18050
|
+
mkdirSync10(dir, { recursive: true });
|
|
17626
18051
|
}
|
|
17627
18052
|
const db = new Database2(dbPath);
|
|
17628
18053
|
db.pragma("journal_mode = WAL");
|
|
@@ -17894,7 +18319,7 @@ function isPythonDataStale(dataDb, pythonRoot) {
|
|
|
17894
18319
|
try {
|
|
17895
18320
|
const entries = readdirSync14(dir, { withFileTypes: true });
|
|
17896
18321
|
for (const entry of entries) {
|
|
17897
|
-
const fullPath =
|
|
18322
|
+
const fullPath = join12(dir, entry.name);
|
|
17898
18323
|
if (entry.isDirectory()) {
|
|
17899
18324
|
if (["__pycache__", ".venv", "venv", "node_modules", ".mypy_cache", ".pytest_cache"].includes(entry.name)) continue;
|
|
17900
18325
|
if (checkDir(fullPath)) return true;
|
|
@@ -17928,10 +18353,10 @@ var init_db = __esm({
|
|
|
17928
18353
|
});
|
|
17929
18354
|
|
|
17930
18355
|
// src/security-utils.ts
|
|
17931
|
-
import { resolve as
|
|
18356
|
+
import { resolve as resolve21, normalize } from "path";
|
|
17932
18357
|
function ensureWithinRoot(filePath, projectRoot) {
|
|
17933
|
-
const resolvedRoot =
|
|
17934
|
-
const resolvedPath =
|
|
18358
|
+
const resolvedRoot = resolve21(projectRoot);
|
|
18359
|
+
const resolvedPath = resolve21(resolvedRoot, filePath);
|
|
17935
18360
|
const normalizedPath = normalize(resolvedPath);
|
|
17936
18361
|
const normalizedRoot = normalize(resolvedRoot);
|
|
17937
18362
|
if (!normalizedPath.startsWith(normalizedRoot + "/") && normalizedPath !== normalizedRoot) {
|
|
@@ -18004,8 +18429,8 @@ var init_rules = __esm({
|
|
|
18004
18429
|
});
|
|
18005
18430
|
|
|
18006
18431
|
// src/import-resolver.ts
|
|
18007
|
-
import { readFileSync as
|
|
18008
|
-
import { resolve as
|
|
18432
|
+
import { readFileSync as readFileSync23, existsSync as existsSync25, statSync as statSync12 } from "fs";
|
|
18433
|
+
import { resolve as resolve22, dirname as dirname13, join as join13 } from "path";
|
|
18009
18434
|
function parseImports(source) {
|
|
18010
18435
|
const imports = [];
|
|
18011
18436
|
const lines = source.split("\n");
|
|
@@ -18061,23 +18486,23 @@ function resolveImportPath(specifier, fromFile) {
|
|
|
18061
18486
|
let basePath;
|
|
18062
18487
|
if (specifier.startsWith("@/")) {
|
|
18063
18488
|
const paths = getResolvedPaths();
|
|
18064
|
-
basePath =
|
|
18489
|
+
basePath = resolve22(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
|
|
18065
18490
|
} else {
|
|
18066
|
-
basePath =
|
|
18491
|
+
basePath = resolve22(dirname13(fromFile), specifier);
|
|
18067
18492
|
}
|
|
18068
|
-
if (
|
|
18493
|
+
if (existsSync25(basePath) && !isDirectory(basePath)) {
|
|
18069
18494
|
return toRelative(basePath);
|
|
18070
18495
|
}
|
|
18071
18496
|
const resolvedPaths = getResolvedPaths();
|
|
18072
18497
|
for (const ext of resolvedPaths.extensions) {
|
|
18073
18498
|
const withExt = basePath + ext;
|
|
18074
|
-
if (
|
|
18499
|
+
if (existsSync25(withExt)) {
|
|
18075
18500
|
return toRelative(withExt);
|
|
18076
18501
|
}
|
|
18077
18502
|
}
|
|
18078
18503
|
for (const indexFile of resolvedPaths.indexFiles) {
|
|
18079
|
-
const indexPath =
|
|
18080
|
-
if (
|
|
18504
|
+
const indexPath = join13(basePath, indexFile);
|
|
18505
|
+
if (existsSync25(indexPath)) {
|
|
18081
18506
|
return toRelative(indexPath);
|
|
18082
18507
|
}
|
|
18083
18508
|
}
|
|
@@ -18113,11 +18538,11 @@ function buildImportIndex(dataDb, codegraphDb) {
|
|
|
18113
18538
|
const batchSize = 500;
|
|
18114
18539
|
let batch = [];
|
|
18115
18540
|
for (const file of files) {
|
|
18116
|
-
const absPath = ensureWithinRoot(
|
|
18117
|
-
if (!
|
|
18541
|
+
const absPath = ensureWithinRoot(resolve22(projectRoot, file.path), projectRoot);
|
|
18542
|
+
if (!existsSync25(absPath)) continue;
|
|
18118
18543
|
let source;
|
|
18119
18544
|
try {
|
|
18120
|
-
source =
|
|
18545
|
+
source = readFileSync23(absPath, "utf-8");
|
|
18121
18546
|
} catch {
|
|
18122
18547
|
continue;
|
|
18123
18548
|
}
|
|
@@ -18153,15 +18578,15 @@ var init_import_resolver = __esm({
|
|
|
18153
18578
|
});
|
|
18154
18579
|
|
|
18155
18580
|
// src/trpc-index.ts
|
|
18156
|
-
import { readFileSync as
|
|
18157
|
-
import { resolve as
|
|
18581
|
+
import { readFileSync as readFileSync24, existsSync as existsSync26, readdirSync as readdirSync15 } from "fs";
|
|
18582
|
+
import { resolve as resolve23, join as join14 } from "path";
|
|
18158
18583
|
function parseRootRouter() {
|
|
18159
18584
|
const paths = getResolvedPaths();
|
|
18160
18585
|
const rootPath = paths.rootRouterPath;
|
|
18161
|
-
if (!
|
|
18586
|
+
if (!existsSync26(rootPath)) {
|
|
18162
18587
|
throw new Error(`Root router not found at ${rootPath}`);
|
|
18163
18588
|
}
|
|
18164
|
-
const source =
|
|
18589
|
+
const source = readFileSync24(rootPath, "utf-8");
|
|
18165
18590
|
const mappings = [];
|
|
18166
18591
|
const importMap = /* @__PURE__ */ new Map();
|
|
18167
18592
|
const importRegex = /import\s+\{[^}]*?(\w+Router)[^}]*\}\s+from\s+['"]\.\/routers\/([^'"]+)['"]/g;
|
|
@@ -18169,16 +18594,16 @@ function parseRootRouter() {
|
|
|
18169
18594
|
while ((match = importRegex.exec(source)) !== null) {
|
|
18170
18595
|
const variable = match[1];
|
|
18171
18596
|
let filePath = match[2];
|
|
18172
|
-
const fullPath =
|
|
18597
|
+
const fullPath = resolve23(paths.routersDir, filePath);
|
|
18173
18598
|
for (const ext of [".ts", ".tsx", ""]) {
|
|
18174
18599
|
const candidate = fullPath + ext;
|
|
18175
18600
|
const routersRelPath = getConfig().paths.routers ?? "src/server/api/routers";
|
|
18176
|
-
if (
|
|
18601
|
+
if (existsSync26(candidate)) {
|
|
18177
18602
|
filePath = routersRelPath + "/" + filePath + ext;
|
|
18178
18603
|
break;
|
|
18179
18604
|
}
|
|
18180
|
-
const indexCandidate =
|
|
18181
|
-
if (
|
|
18605
|
+
const indexCandidate = join14(fullPath, "index.ts");
|
|
18606
|
+
if (existsSync26(indexCandidate)) {
|
|
18182
18607
|
filePath = routersRelPath + "/" + filePath + "/index.ts";
|
|
18183
18608
|
break;
|
|
18184
18609
|
}
|
|
@@ -18197,9 +18622,9 @@ function parseRootRouter() {
|
|
|
18197
18622
|
return mappings;
|
|
18198
18623
|
}
|
|
18199
18624
|
function extractProcedures(routerFilePath) {
|
|
18200
|
-
const absPath =
|
|
18201
|
-
if (!
|
|
18202
|
-
const source =
|
|
18625
|
+
const absPath = resolve23(getProjectRoot(), routerFilePath);
|
|
18626
|
+
if (!existsSync26(absPath)) return [];
|
|
18627
|
+
const source = readFileSync24(absPath, "utf-8");
|
|
18203
18628
|
const procedures = [];
|
|
18204
18629
|
const seen = /* @__PURE__ */ new Set();
|
|
18205
18630
|
const procRegex = /(\w+)\s*:\s*(protected|public)Procedure/g;
|
|
@@ -18222,13 +18647,13 @@ function findUICallSites(routerKey, procedureName) {
|
|
|
18222
18647
|
const root = getProjectRoot();
|
|
18223
18648
|
const src = config.paths.source;
|
|
18224
18649
|
const searchDirs = [
|
|
18225
|
-
|
|
18226
|
-
|
|
18227
|
-
|
|
18650
|
+
resolve23(root, config.paths.pages ?? src + "/app"),
|
|
18651
|
+
resolve23(root, config.paths.components ?? src + "/components"),
|
|
18652
|
+
resolve23(root, config.paths.hooks ?? src + "/hooks")
|
|
18228
18653
|
];
|
|
18229
18654
|
const searchPattern = `api.${routerKey}.${procedureName}`;
|
|
18230
18655
|
for (const dir of searchDirs) {
|
|
18231
|
-
if (!
|
|
18656
|
+
if (!existsSync26(dir)) continue;
|
|
18232
18657
|
searchDirectory(dir, searchPattern, callSites);
|
|
18233
18658
|
}
|
|
18234
18659
|
return callSites;
|
|
@@ -18236,13 +18661,13 @@ function findUICallSites(routerKey, procedureName) {
|
|
|
18236
18661
|
function searchDirectory(dir, pattern, results) {
|
|
18237
18662
|
const entries = readdirSync15(dir, { withFileTypes: true });
|
|
18238
18663
|
for (const entry of entries) {
|
|
18239
|
-
const fullPath =
|
|
18664
|
+
const fullPath = join14(dir, entry.name);
|
|
18240
18665
|
if (entry.isDirectory()) {
|
|
18241
18666
|
if (entry.name === "node_modules" || entry.name === ".next") continue;
|
|
18242
18667
|
searchDirectory(fullPath, pattern, results);
|
|
18243
18668
|
} else if (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx")) {
|
|
18244
18669
|
try {
|
|
18245
|
-
const source =
|
|
18670
|
+
const source = readFileSync24(fullPath, "utf-8");
|
|
18246
18671
|
const lines = source.split("\n");
|
|
18247
18672
|
for (let i = 0; i < lines.length; i++) {
|
|
18248
18673
|
if (lines[i].includes(pattern)) {
|
|
@@ -18305,8 +18730,8 @@ var init_trpc_index = __esm({
|
|
|
18305
18730
|
});
|
|
18306
18731
|
|
|
18307
18732
|
// src/page-deps.ts
|
|
18308
|
-
import { readFileSync as
|
|
18309
|
-
import { resolve as
|
|
18733
|
+
import { readFileSync as readFileSync25, existsSync as existsSync27 } from "fs";
|
|
18734
|
+
import { resolve as resolve24 } from "path";
|
|
18310
18735
|
function deriveRoute(pageFile) {
|
|
18311
18736
|
let route = pageFile.replace(/^src\/app/, "").replace(/\/page\.tsx?$/, "").replace(/\/page\.jsx?$/, "");
|
|
18312
18737
|
return route || "/";
|
|
@@ -18344,10 +18769,10 @@ function findRouterCalls(files) {
|
|
|
18344
18769
|
const routers = /* @__PURE__ */ new Set();
|
|
18345
18770
|
const projectRoot = getProjectRoot();
|
|
18346
18771
|
for (const file of files) {
|
|
18347
|
-
const absPath = ensureWithinRoot(
|
|
18348
|
-
if (!
|
|
18772
|
+
const absPath = ensureWithinRoot(resolve24(projectRoot, file), projectRoot);
|
|
18773
|
+
if (!existsSync27(absPath)) continue;
|
|
18349
18774
|
try {
|
|
18350
|
-
const source =
|
|
18775
|
+
const source = readFileSync25(absPath, "utf-8");
|
|
18351
18776
|
const apiCallRegex = /api\.(\w+)\.\w+/g;
|
|
18352
18777
|
let match;
|
|
18353
18778
|
while ((match = apiCallRegex.exec(source)) !== null) {
|
|
@@ -18365,10 +18790,10 @@ function findTablesFromRouters(routerNames, dataDb) {
|
|
|
18365
18790
|
"SELECT DISTINCT router_file FROM massu_trpc_procedures WHERE router_name = ?"
|
|
18366
18791
|
).all(routerName);
|
|
18367
18792
|
for (const proc of procs) {
|
|
18368
|
-
const absPath = ensureWithinRoot(
|
|
18369
|
-
if (!
|
|
18793
|
+
const absPath = ensureWithinRoot(resolve24(getProjectRoot(), proc.router_file), getProjectRoot());
|
|
18794
|
+
if (!existsSync27(absPath)) continue;
|
|
18370
18795
|
try {
|
|
18371
|
-
const source =
|
|
18796
|
+
const source = readFileSync25(absPath, "utf-8");
|
|
18372
18797
|
const dbPattern = getConfig().dbAccessPattern ?? "ctx.db.{table}";
|
|
18373
18798
|
const regexStr = dbPattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace("\\{table\\}", "(\\w+)");
|
|
18374
18799
|
const tableRegex = new RegExp(regexStr + "\\.", "g");
|
|
@@ -18637,14 +19062,14 @@ var init_domains = __esm({
|
|
|
18637
19062
|
});
|
|
18638
19063
|
|
|
18639
19064
|
// src/schema-mapper.ts
|
|
18640
|
-
import { readFileSync as
|
|
18641
|
-
import { join as
|
|
19065
|
+
import { readFileSync as readFileSync26, existsSync as existsSync28, readdirSync as readdirSync16 } from "fs";
|
|
19066
|
+
import { join as join15 } from "path";
|
|
18642
19067
|
function parsePrismaSchema() {
|
|
18643
19068
|
const schemaPath = getResolvedPaths().prismaSchemaPath;
|
|
18644
|
-
if (!
|
|
19069
|
+
if (!existsSync28(schemaPath)) {
|
|
18645
19070
|
throw new Error(`Prisma schema not found at ${schemaPath}`);
|
|
18646
19071
|
}
|
|
18647
|
-
const source =
|
|
19072
|
+
const source = readFileSync26(schemaPath, "utf-8");
|
|
18648
19073
|
const models = [];
|
|
18649
19074
|
const sourceLines = source.split("\n");
|
|
18650
19075
|
let i = 0;
|
|
@@ -18702,14 +19127,14 @@ function toSnakeCase(str) {
|
|
|
18702
19127
|
function findColumnUsageInRouters(tableName) {
|
|
18703
19128
|
const usage = /* @__PURE__ */ new Map();
|
|
18704
19129
|
const routersDir = getResolvedPaths().routersDir;
|
|
18705
|
-
if (!
|
|
19130
|
+
if (!existsSync28(routersDir)) return usage;
|
|
18706
19131
|
scanDirectory(routersDir, tableName, usage);
|
|
18707
19132
|
return usage;
|
|
18708
19133
|
}
|
|
18709
19134
|
function scanDirectory(dir, tableName, usage) {
|
|
18710
19135
|
const entries = readdirSync16(dir, { withFileTypes: true });
|
|
18711
19136
|
for (const entry of entries) {
|
|
18712
|
-
const fullPath =
|
|
19137
|
+
const fullPath = join15(dir, entry.name);
|
|
18713
19138
|
if (entry.isDirectory()) {
|
|
18714
19139
|
scanDirectory(fullPath, tableName, usage);
|
|
18715
19140
|
} else if (entry.name.endsWith(".ts")) {
|
|
@@ -18719,7 +19144,7 @@ function scanDirectory(dir, tableName, usage) {
|
|
|
18719
19144
|
}
|
|
18720
19145
|
function scanFile(absPath, tableName, usage) {
|
|
18721
19146
|
try {
|
|
18722
|
-
const source =
|
|
19147
|
+
const source = readFileSync26(absPath, "utf-8");
|
|
18723
19148
|
if (!source.includes(tableName)) return;
|
|
18724
19149
|
const relPath = absPath.slice(getProjectRoot().length + 1);
|
|
18725
19150
|
const lines = source.split("\n");
|
|
@@ -18764,15 +19189,15 @@ function detectMismatches(models) {
|
|
|
18764
19189
|
}
|
|
18765
19190
|
function findFilesUsingColumn(dir, column, tableName) {
|
|
18766
19191
|
const result = [];
|
|
18767
|
-
if (!
|
|
19192
|
+
if (!existsSync28(dir)) return result;
|
|
18768
19193
|
const entries = readdirSync16(dir, { withFileTypes: true });
|
|
18769
19194
|
for (const entry of entries) {
|
|
18770
|
-
const fullPath =
|
|
19195
|
+
const fullPath = join15(dir, entry.name);
|
|
18771
19196
|
if (entry.isDirectory()) {
|
|
18772
19197
|
result.push(...findFilesUsingColumn(fullPath, column, tableName));
|
|
18773
19198
|
} else if (entry.name.endsWith(".ts")) {
|
|
18774
19199
|
try {
|
|
18775
|
-
const source =
|
|
19200
|
+
const source = readFileSync26(fullPath, "utf-8");
|
|
18776
19201
|
if (source.includes(tableName) && source.includes(column)) {
|
|
18777
19202
|
result.push(fullPath.slice(getProjectRoot().length + 1));
|
|
18778
19203
|
}
|
|
@@ -18917,34 +19342,34 @@ var init_import_parser = __esm({
|
|
|
18917
19342
|
});
|
|
18918
19343
|
|
|
18919
19344
|
// src/python/import-resolver.ts
|
|
18920
|
-
import { readFileSync as
|
|
18921
|
-
import { resolve as
|
|
19345
|
+
import { readFileSync as readFileSync27, existsSync as existsSync29, readdirSync as readdirSync17 } from "fs";
|
|
19346
|
+
import { resolve as resolve26, join as join16, relative as relative6, dirname as dirname14 } from "path";
|
|
18922
19347
|
function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
|
|
18923
19348
|
const projectRoot = getProjectRoot();
|
|
18924
19349
|
if (level > 0) {
|
|
18925
|
-
let baseDir =
|
|
19350
|
+
let baseDir = dirname14(resolve26(projectRoot, fromFile));
|
|
18926
19351
|
for (let i = 1; i < level; i++) {
|
|
18927
|
-
baseDir =
|
|
19352
|
+
baseDir = dirname14(baseDir);
|
|
18928
19353
|
}
|
|
18929
19354
|
const modulePart = module.replace(/^\.+/, "");
|
|
18930
19355
|
if (modulePart) {
|
|
18931
19356
|
const parts2 = modulePart.split(".");
|
|
18932
|
-
return tryResolvePythonPath(
|
|
19357
|
+
return tryResolvePythonPath(join16(baseDir, ...parts2), projectRoot);
|
|
18933
19358
|
}
|
|
18934
19359
|
return tryResolvePythonPath(baseDir, projectRoot);
|
|
18935
19360
|
}
|
|
18936
19361
|
const parts = module.split(".");
|
|
18937
|
-
const candidate =
|
|
19362
|
+
const candidate = join16(resolve26(projectRoot, pythonRoot), ...parts);
|
|
18938
19363
|
return tryResolvePythonPath(candidate, projectRoot);
|
|
18939
19364
|
}
|
|
18940
19365
|
function tryResolvePythonPath(basePath, projectRoot) {
|
|
18941
|
-
if (
|
|
19366
|
+
if (existsSync29(basePath + ".py")) {
|
|
18942
19367
|
return relative6(projectRoot, basePath + ".py");
|
|
18943
19368
|
}
|
|
18944
|
-
if (
|
|
18945
|
-
return relative6(projectRoot,
|
|
19369
|
+
if (existsSync29(join16(basePath, "__init__.py"))) {
|
|
19370
|
+
return relative6(projectRoot, join16(basePath, "__init__.py"));
|
|
18946
19371
|
}
|
|
18947
|
-
if (basePath.endsWith(".py") &&
|
|
19372
|
+
if (basePath.endsWith(".py") && existsSync29(basePath)) {
|
|
18948
19373
|
return relative6(projectRoot, basePath);
|
|
18949
19374
|
}
|
|
18950
19375
|
return null;
|
|
@@ -18956,9 +19381,9 @@ function walkPythonFiles(dir, excludeDirs) {
|
|
|
18956
19381
|
for (const entry of entries) {
|
|
18957
19382
|
if (entry.isDirectory()) {
|
|
18958
19383
|
if (excludeDirs.includes(entry.name)) continue;
|
|
18959
|
-
files.push(...walkPythonFiles(
|
|
19384
|
+
files.push(...walkPythonFiles(join16(dir, entry.name), excludeDirs));
|
|
18960
19385
|
} else if (entry.name.endsWith(".py")) {
|
|
18961
|
-
files.push(
|
|
19386
|
+
files.push(join16(dir, entry.name));
|
|
18962
19387
|
}
|
|
18963
19388
|
}
|
|
18964
19389
|
} catch {
|
|
@@ -18967,7 +19392,7 @@ function walkPythonFiles(dir, excludeDirs) {
|
|
|
18967
19392
|
}
|
|
18968
19393
|
function buildPythonImportIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
|
|
18969
19394
|
const projectRoot = getProjectRoot();
|
|
18970
|
-
const absRoot =
|
|
19395
|
+
const absRoot = resolve26(projectRoot, pythonRoot);
|
|
18971
19396
|
dataDb.exec("DELETE FROM massu_py_imports");
|
|
18972
19397
|
const insertStmt = dataDb.prepare(
|
|
18973
19398
|
"INSERT INTO massu_py_imports (source_file, target_file, import_type, imported_names, line) VALUES (?, ?, ?, ?, ?)"
|
|
@@ -18984,7 +19409,7 @@ function buildPythonImportIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__"
|
|
|
18984
19409
|
const relFile = relative6(projectRoot, absFile);
|
|
18985
19410
|
let source;
|
|
18986
19411
|
try {
|
|
18987
|
-
source =
|
|
19412
|
+
source = readFileSync27(absFile, "utf-8");
|
|
18988
19413
|
} catch {
|
|
18989
19414
|
continue;
|
|
18990
19415
|
}
|
|
@@ -19250,8 +19675,8 @@ var init_route_parser = __esm({
|
|
|
19250
19675
|
});
|
|
19251
19676
|
|
|
19252
19677
|
// src/python/route-indexer.ts
|
|
19253
|
-
import { readFileSync as
|
|
19254
|
-
import { join as
|
|
19678
|
+
import { readFileSync as readFileSync28, readdirSync as readdirSync18 } from "fs";
|
|
19679
|
+
import { join as join17, relative as relative7 } from "path";
|
|
19255
19680
|
function walkPyFiles(dir, excludeDirs) {
|
|
19256
19681
|
const files = [];
|
|
19257
19682
|
try {
|
|
@@ -19259,9 +19684,9 @@ function walkPyFiles(dir, excludeDirs) {
|
|
|
19259
19684
|
for (const entry of entries) {
|
|
19260
19685
|
if (entry.isDirectory()) {
|
|
19261
19686
|
if (excludeDirs.includes(entry.name)) continue;
|
|
19262
|
-
files.push(...walkPyFiles(
|
|
19687
|
+
files.push(...walkPyFiles(join17(dir, entry.name), excludeDirs));
|
|
19263
19688
|
} else if (entry.name.endsWith(".py")) {
|
|
19264
|
-
files.push(
|
|
19689
|
+
files.push(join17(dir, entry.name));
|
|
19265
19690
|
}
|
|
19266
19691
|
}
|
|
19267
19692
|
} catch {
|
|
@@ -19270,7 +19695,7 @@ function walkPyFiles(dir, excludeDirs) {
|
|
|
19270
19695
|
}
|
|
19271
19696
|
function buildPythonRouteIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
|
|
19272
19697
|
const projectRoot = getProjectRoot();
|
|
19273
|
-
const absRoot =
|
|
19698
|
+
const absRoot = join17(projectRoot, pythonRoot);
|
|
19274
19699
|
dataDb.exec("DELETE FROM massu_py_routes");
|
|
19275
19700
|
dataDb.exec("DELETE FROM massu_py_route_callers");
|
|
19276
19701
|
const insertStmt = dataDb.prepare(
|
|
@@ -19283,7 +19708,7 @@ function buildPythonRouteIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__",
|
|
|
19283
19708
|
const relFile = relative7(projectRoot, absFile);
|
|
19284
19709
|
let source;
|
|
19285
19710
|
try {
|
|
19286
|
-
source =
|
|
19711
|
+
source = readFileSync28(absFile, "utf-8");
|
|
19287
19712
|
} catch {
|
|
19288
19713
|
continue;
|
|
19289
19714
|
}
|
|
@@ -19494,8 +19919,8 @@ var init_model_parser = __esm({
|
|
|
19494
19919
|
});
|
|
19495
19920
|
|
|
19496
19921
|
// src/python/model-indexer.ts
|
|
19497
|
-
import { readFileSync as
|
|
19498
|
-
import { join as
|
|
19922
|
+
import { readFileSync as readFileSync29, readdirSync as readdirSync19 } from "fs";
|
|
19923
|
+
import { join as join18, relative as relative8 } from "path";
|
|
19499
19924
|
function walkPyFiles2(dir, excludeDirs) {
|
|
19500
19925
|
const files = [];
|
|
19501
19926
|
try {
|
|
@@ -19503,9 +19928,9 @@ function walkPyFiles2(dir, excludeDirs) {
|
|
|
19503
19928
|
for (const entry of entries) {
|
|
19504
19929
|
if (entry.isDirectory()) {
|
|
19505
19930
|
if (excludeDirs.includes(entry.name)) continue;
|
|
19506
|
-
files.push(...walkPyFiles2(
|
|
19931
|
+
files.push(...walkPyFiles2(join18(dir, entry.name), excludeDirs));
|
|
19507
19932
|
} else if (entry.name.endsWith(".py")) {
|
|
19508
|
-
files.push(
|
|
19933
|
+
files.push(join18(dir, entry.name));
|
|
19509
19934
|
}
|
|
19510
19935
|
}
|
|
19511
19936
|
} catch {
|
|
@@ -19514,7 +19939,7 @@ function walkPyFiles2(dir, excludeDirs) {
|
|
|
19514
19939
|
}
|
|
19515
19940
|
function buildPythonModelIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
|
|
19516
19941
|
const projectRoot = getProjectRoot();
|
|
19517
|
-
const absRoot =
|
|
19942
|
+
const absRoot = join18(projectRoot, pythonRoot);
|
|
19518
19943
|
dataDb.exec("DELETE FROM massu_py_models");
|
|
19519
19944
|
dataDb.exec("DELETE FROM massu_py_fk_edges");
|
|
19520
19945
|
const insertModel = dataDb.prepare(
|
|
@@ -19530,7 +19955,7 @@ function buildPythonModelIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__",
|
|
|
19530
19955
|
const relFile = relative8(projectRoot, absFile);
|
|
19531
19956
|
let source;
|
|
19532
19957
|
try {
|
|
19533
|
-
source =
|
|
19958
|
+
source = readFileSync29(absFile, "utf-8");
|
|
19534
19959
|
} catch {
|
|
19535
19960
|
continue;
|
|
19536
19961
|
}
|
|
@@ -19790,19 +20215,19 @@ var init_migration_parser = __esm({
|
|
|
19790
20215
|
});
|
|
19791
20216
|
|
|
19792
20217
|
// src/python/migration-indexer.ts
|
|
19793
|
-
import { readFileSync as
|
|
19794
|
-
import { join as
|
|
20218
|
+
import { readFileSync as readFileSync30, readdirSync as readdirSync20 } from "fs";
|
|
20219
|
+
import { join as join19, relative as relative9 } from "path";
|
|
19795
20220
|
function buildPythonMigrationIndex(dataDb, alembicDir) {
|
|
19796
20221
|
const projectRoot = getProjectRoot();
|
|
19797
|
-
const absDir =
|
|
20222
|
+
const absDir = join19(projectRoot, alembicDir);
|
|
19798
20223
|
dataDb.exec("DELETE FROM massu_py_migrations");
|
|
19799
|
-
const versionsDir =
|
|
20224
|
+
const versionsDir = join19(absDir, "versions");
|
|
19800
20225
|
let files = [];
|
|
19801
20226
|
try {
|
|
19802
|
-
files = readdirSync20(versionsDir).filter((f2) => f2.endsWith(".py")).map((f2) =>
|
|
20227
|
+
files = readdirSync20(versionsDir).filter((f2) => f2.endsWith(".py")).map((f2) => join19(versionsDir, f2));
|
|
19803
20228
|
} catch {
|
|
19804
20229
|
try {
|
|
19805
|
-
files = readdirSync20(absDir).filter((f2) => f2.endsWith(".py") && f2 !== "env.py").map((f2) =>
|
|
20230
|
+
files = readdirSync20(absDir).filter((f2) => f2.endsWith(".py") && f2 !== "env.py").map((f2) => join19(absDir, f2));
|
|
19806
20231
|
} catch {
|
|
19807
20232
|
}
|
|
19808
20233
|
}
|
|
@@ -19816,7 +20241,7 @@ function buildPythonMigrationIndex(dataDb, alembicDir) {
|
|
|
19816
20241
|
for (const absFile of files) {
|
|
19817
20242
|
let source;
|
|
19818
20243
|
try {
|
|
19819
|
-
source =
|
|
20244
|
+
source = readFileSync30(absFile, "utf-8");
|
|
19820
20245
|
} catch {
|
|
19821
20246
|
continue;
|
|
19822
20247
|
}
|
|
@@ -19850,12 +20275,12 @@ var init_migration_indexer = __esm({
|
|
|
19850
20275
|
});
|
|
19851
20276
|
|
|
19852
20277
|
// src/python/coupling-detector.ts
|
|
19853
|
-
import { readFileSync as
|
|
19854
|
-
import { join as
|
|
20278
|
+
import { readFileSync as readFileSync31, readdirSync as readdirSync21 } from "fs";
|
|
20279
|
+
import { join as join20, relative as relative10 } from "path";
|
|
19855
20280
|
function buildPythonCouplingIndex(dataDb) {
|
|
19856
20281
|
const projectRoot = getProjectRoot();
|
|
19857
20282
|
const config = getConfig();
|
|
19858
|
-
const srcDir =
|
|
20283
|
+
const srcDir = join20(projectRoot, config.paths.source);
|
|
19859
20284
|
const routes = dataDb.prepare("SELECT id, method, path FROM massu_py_routes").all();
|
|
19860
20285
|
if (routes.length === 0) return 0;
|
|
19861
20286
|
dataDb.exec("DELETE FROM massu_py_route_callers");
|
|
@@ -19887,7 +20312,7 @@ function buildPythonCouplingIndex(dataDb) {
|
|
|
19887
20312
|
const relFile = relative10(projectRoot, absFile);
|
|
19888
20313
|
let source;
|
|
19889
20314
|
try {
|
|
19890
|
-
source =
|
|
20315
|
+
source = readFileSync31(absFile, "utf-8");
|
|
19891
20316
|
} catch {
|
|
19892
20317
|
continue;
|
|
19893
20318
|
}
|
|
@@ -19919,9 +20344,9 @@ function walkFrontendFiles(dir) {
|
|
|
19919
20344
|
for (const entry of entries) {
|
|
19920
20345
|
if (entry.isDirectory()) {
|
|
19921
20346
|
if (exclude.includes(entry.name)) continue;
|
|
19922
|
-
files.push(...walkFrontendFiles(
|
|
20347
|
+
files.push(...walkFrontendFiles(join20(dir, entry.name)));
|
|
19923
20348
|
} else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
|
|
19924
|
-
files.push(
|
|
20349
|
+
files.push(join20(dir, entry.name));
|
|
19925
20350
|
}
|
|
19926
20351
|
}
|
|
19927
20352
|
} catch {
|
|
@@ -20292,8 +20717,8 @@ var init_memory_tools = __esm({
|
|
|
20292
20717
|
});
|
|
20293
20718
|
|
|
20294
20719
|
// src/docs-tools.ts
|
|
20295
|
-
import { readFileSync as
|
|
20296
|
-
import { resolve as
|
|
20720
|
+
import { readFileSync as readFileSync32, existsSync as existsSync30 } from "fs";
|
|
20721
|
+
import { resolve as resolve27, basename as basename6 } from "path";
|
|
20297
20722
|
function p3(baseName) {
|
|
20298
20723
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
20299
20724
|
}
|
|
@@ -20348,10 +20773,10 @@ function handleDocsToolCall(name, args2) {
|
|
|
20348
20773
|
}
|
|
20349
20774
|
function loadDocsMap() {
|
|
20350
20775
|
const mapPath = getResolvedPaths().docsMapPath;
|
|
20351
|
-
if (!
|
|
20776
|
+
if (!existsSync30(mapPath)) {
|
|
20352
20777
|
throw new Error(`docs-map.json not found at ${mapPath}`);
|
|
20353
20778
|
}
|
|
20354
|
-
return JSON.parse(
|
|
20779
|
+
return JSON.parse(readFileSync32(mapPath, "utf-8"));
|
|
20355
20780
|
}
|
|
20356
20781
|
function matchesPattern(filePath, pattern) {
|
|
20357
20782
|
const regexStr = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
@@ -20421,13 +20846,13 @@ function extractFrontmatter(content) {
|
|
|
20421
20846
|
}
|
|
20422
20847
|
function extractProcedureNames(routerPath) {
|
|
20423
20848
|
const root = getProjectRoot();
|
|
20424
|
-
const absPath = ensureWithinRoot(
|
|
20425
|
-
if (!
|
|
20426
|
-
const altPath = ensureWithinRoot(
|
|
20427
|
-
if (!
|
|
20428
|
-
return extractProcedureNamesFromContent(
|
|
20849
|
+
const absPath = ensureWithinRoot(resolve27(getResolvedPaths().srcDir, "..", routerPath), root);
|
|
20850
|
+
if (!existsSync30(absPath)) {
|
|
20851
|
+
const altPath = ensureWithinRoot(resolve27(getResolvedPaths().srcDir, "../server/api/routers", basename6(routerPath)), root);
|
|
20852
|
+
if (!existsSync30(altPath)) return [];
|
|
20853
|
+
return extractProcedureNamesFromContent(readFileSync32(altPath, "utf-8"));
|
|
20429
20854
|
}
|
|
20430
|
-
return extractProcedureNamesFromContent(
|
|
20855
|
+
return extractProcedureNamesFromContent(readFileSync32(absPath, "utf-8"));
|
|
20431
20856
|
}
|
|
20432
20857
|
function extractProcedureNamesFromContent(content) {
|
|
20433
20858
|
const procRegex = /\.(?:query|mutation)\s*\(/g;
|
|
@@ -20467,8 +20892,8 @@ function handleDocsAudit(args2) {
|
|
|
20467
20892
|
for (const [mappingId, triggeringFiles] of affectedMappings) {
|
|
20468
20893
|
const mapping = docsMap.mappings.find((m3) => m3.id === mappingId);
|
|
20469
20894
|
if (!mapping) continue;
|
|
20470
|
-
const helpPagePath = ensureWithinRoot(
|
|
20471
|
-
if (!
|
|
20895
|
+
const helpPagePath = ensureWithinRoot(resolve27(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
|
|
20896
|
+
if (!existsSync30(helpPagePath)) {
|
|
20472
20897
|
results.push({
|
|
20473
20898
|
helpPage: mapping.helpPage,
|
|
20474
20899
|
mappingId,
|
|
@@ -20480,7 +20905,7 @@ function handleDocsAudit(args2) {
|
|
|
20480
20905
|
});
|
|
20481
20906
|
continue;
|
|
20482
20907
|
}
|
|
20483
|
-
const content =
|
|
20908
|
+
const content = readFileSync32(helpPagePath, "utf-8");
|
|
20484
20909
|
const sections = extractSections(content);
|
|
20485
20910
|
const frontmatter = extractFrontmatter(content);
|
|
20486
20911
|
const staleReasons = [];
|
|
@@ -20520,9 +20945,9 @@ function handleDocsAudit(args2) {
|
|
|
20520
20945
|
});
|
|
20521
20946
|
for (const [guideName, parentId] of Object.entries(docsMap.userGuideInheritance.examples)) {
|
|
20522
20947
|
if (parentId === mappingId) {
|
|
20523
|
-
const guidePath = ensureWithinRoot(
|
|
20524
|
-
if (
|
|
20525
|
-
const guideContent =
|
|
20948
|
+
const guidePath = ensureWithinRoot(resolve27(getResolvedPaths().helpSitePath, `pages/user-guides/${guideName}/index.mdx`), getProjectRoot());
|
|
20949
|
+
if (existsSync30(guidePath)) {
|
|
20950
|
+
const guideContent = readFileSync32(guidePath, "utf-8");
|
|
20526
20951
|
const guideFrontmatter = extractFrontmatter(guideContent);
|
|
20527
20952
|
if (!guideFrontmatter?.lastVerified || status === "STALE") {
|
|
20528
20953
|
results.push({
|
|
@@ -20555,14 +20980,14 @@ function handleDocsCoverage(args2) {
|
|
|
20555
20980
|
const gaps = [];
|
|
20556
20981
|
const mappings = filterDomain ? docsMap.mappings.filter((m3) => m3.id === filterDomain) : docsMap.mappings;
|
|
20557
20982
|
for (const mapping of mappings) {
|
|
20558
|
-
const helpPagePath = ensureWithinRoot(
|
|
20559
|
-
const exists =
|
|
20983
|
+
const helpPagePath = ensureWithinRoot(resolve27(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
|
|
20984
|
+
const exists = existsSync30(helpPagePath);
|
|
20560
20985
|
let hasContent = false;
|
|
20561
20986
|
let lineCount = 0;
|
|
20562
20987
|
let lastVerified = null;
|
|
20563
20988
|
let status = null;
|
|
20564
20989
|
if (exists) {
|
|
20565
|
-
const content =
|
|
20990
|
+
const content = readFileSync32(helpPagePath, "utf-8");
|
|
20566
20991
|
lineCount = content.split("\n").length;
|
|
20567
20992
|
hasContent = lineCount > 10;
|
|
20568
20993
|
const frontmatter = extractFrontmatter(content);
|
|
@@ -20903,8 +21328,8 @@ var init_observability_tools = __esm({
|
|
|
20903
21328
|
});
|
|
20904
21329
|
|
|
20905
21330
|
// src/sentinel-db.ts
|
|
20906
|
-
import { existsSync as
|
|
20907
|
-
import { resolve as
|
|
21331
|
+
import { existsSync as existsSync31 } from "fs";
|
|
21332
|
+
import { resolve as resolve28 } from "path";
|
|
20908
21333
|
function parsePortalScope(raw) {
|
|
20909
21334
|
if (!raw) return [];
|
|
20910
21335
|
try {
|
|
@@ -21140,23 +21565,23 @@ function validateFeatures(db, domainFilter) {
|
|
|
21140
21565
|
const missingProcedures = [];
|
|
21141
21566
|
const missingPages = [];
|
|
21142
21567
|
for (const comp of components) {
|
|
21143
|
-
const absPath =
|
|
21144
|
-
if (!
|
|
21568
|
+
const absPath = resolve28(PROJECT_ROOT, comp.component_file);
|
|
21569
|
+
if (!existsSync31(absPath)) {
|
|
21145
21570
|
missingComponents.push(comp.component_file);
|
|
21146
21571
|
}
|
|
21147
21572
|
}
|
|
21148
21573
|
for (const proc of procedures) {
|
|
21149
|
-
const routerPath =
|
|
21150
|
-
if (!
|
|
21574
|
+
const routerPath = resolve28(PROJECT_ROOT, `src/server/api/routers/${proc.router_name}.ts`);
|
|
21575
|
+
if (!existsSync31(routerPath)) {
|
|
21151
21576
|
missingProcedures.push({ router: proc.router_name, procedure: proc.procedure_name });
|
|
21152
21577
|
}
|
|
21153
21578
|
}
|
|
21154
21579
|
for (const page of pages) {
|
|
21155
21580
|
const routeToPath = page.page_route.replace(/^\/(portal-[^/]+\/)?/, "src/app/").replace(/\/$/, "") + "/page.tsx";
|
|
21156
|
-
const absPath =
|
|
21157
|
-
if (page.page_route.startsWith("/") && !
|
|
21158
|
-
const altPath =
|
|
21159
|
-
if (!
|
|
21581
|
+
const absPath = resolve28(PROJECT_ROOT, routeToPath);
|
|
21582
|
+
if (page.page_route.startsWith("/") && !existsSync31(absPath)) {
|
|
21583
|
+
const altPath = resolve28(PROJECT_ROOT, `src/app${page.page_route}/page.tsx`);
|
|
21584
|
+
if (!existsSync31(altPath)) {
|
|
21160
21585
|
missingPages.push(page.page_route);
|
|
21161
21586
|
}
|
|
21162
21587
|
}
|
|
@@ -21680,8 +22105,8 @@ var init_sentinel_tools = __esm({
|
|
|
21680
22105
|
});
|
|
21681
22106
|
|
|
21682
22107
|
// src/sentinel-scanner.ts
|
|
21683
|
-
import { readFileSync as
|
|
21684
|
-
import { resolve as
|
|
22108
|
+
import { readFileSync as readFileSync33, existsSync as existsSync32, readdirSync as readdirSync22, statSync as statSync13 } from "fs";
|
|
22109
|
+
import { resolve as resolve29, join as join21, basename as basename7, dirname as dirname15, relative as relative11 } from "path";
|
|
21685
22110
|
function inferDomain(filePath) {
|
|
21686
22111
|
const domains = getConfig().domains;
|
|
21687
22112
|
const path = filePath.toLowerCase();
|
|
@@ -21810,8 +22235,8 @@ function scanComponentExports(dataDb) {
|
|
|
21810
22235
|
const projectRoot = getProjectRoot();
|
|
21811
22236
|
const componentsBase = config.paths.components ?? config.paths.source + "/components";
|
|
21812
22237
|
const componentDirs = [];
|
|
21813
|
-
const basePath =
|
|
21814
|
-
if (
|
|
22238
|
+
const basePath = resolve29(projectRoot, componentsBase);
|
|
22239
|
+
if (existsSync32(basePath)) {
|
|
21815
22240
|
try {
|
|
21816
22241
|
const entries = readdirSync22(basePath, { withFileTypes: true });
|
|
21817
22242
|
for (const entry of entries) {
|
|
@@ -21823,12 +22248,12 @@ function scanComponentExports(dataDb) {
|
|
|
21823
22248
|
}
|
|
21824
22249
|
}
|
|
21825
22250
|
for (const dir of componentDirs) {
|
|
21826
|
-
const absDir =
|
|
21827
|
-
if (!
|
|
22251
|
+
const absDir = resolve29(projectRoot, dir);
|
|
22252
|
+
if (!existsSync32(absDir)) continue;
|
|
21828
22253
|
const files = walkDir2(absDir).filter((f2) => f2.endsWith(".tsx") || f2.endsWith(".ts"));
|
|
21829
22254
|
for (const file of files) {
|
|
21830
22255
|
const relPath = relative11(projectRoot, file);
|
|
21831
|
-
const source =
|
|
22256
|
+
const source = readFileSync33(file, "utf-8");
|
|
21832
22257
|
const annotations = parseFeatureAnnotations(source);
|
|
21833
22258
|
if (annotations.length > 0) {
|
|
21834
22259
|
for (const ann of annotations) {
|
|
@@ -21854,7 +22279,7 @@ function scanComponentExports(dataDb) {
|
|
|
21854
22279
|
if (hasHandlers && exportMatch) {
|
|
21855
22280
|
const componentName = exportMatch[1];
|
|
21856
22281
|
const domain = inferDomain(relPath);
|
|
21857
|
-
const subdomain = basename7(
|
|
22282
|
+
const subdomain = basename7(dirname15(relPath));
|
|
21858
22283
|
const featureKey = `component.${subdomain}.${componentName.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "")}`;
|
|
21859
22284
|
if (!annotations.some((a2) => a2.featureKey === featureKey)) {
|
|
21860
22285
|
features.push({
|
|
@@ -21879,7 +22304,7 @@ function walkDir2(dir) {
|
|
|
21879
22304
|
try {
|
|
21880
22305
|
const entries = readdirSync22(dir);
|
|
21881
22306
|
for (const entry of entries) {
|
|
21882
|
-
const fullPath =
|
|
22307
|
+
const fullPath = join21(dir, entry);
|
|
21883
22308
|
try {
|
|
21884
22309
|
const stat = statSync13(fullPath);
|
|
21885
22310
|
if (stat.isDirectory()) {
|
|
@@ -22867,7 +23292,7 @@ var init_audit_trail = __esm({
|
|
|
22867
23292
|
});
|
|
22868
23293
|
|
|
22869
23294
|
// src/validation-engine.ts
|
|
22870
|
-
import { existsSync as
|
|
23295
|
+
import { existsSync as existsSync33, readFileSync as readFileSync34 } from "fs";
|
|
22871
23296
|
function p10(baseName) {
|
|
22872
23297
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
22873
23298
|
}
|
|
@@ -22898,7 +23323,7 @@ function validateFile(filePath, projectRoot) {
|
|
|
22898
23323
|
});
|
|
22899
23324
|
return checks;
|
|
22900
23325
|
}
|
|
22901
|
-
if (!
|
|
23326
|
+
if (!existsSync33(absPath)) {
|
|
22902
23327
|
checks.push({
|
|
22903
23328
|
name: "file_exists",
|
|
22904
23329
|
severity: "error",
|
|
@@ -22907,7 +23332,7 @@ function validateFile(filePath, projectRoot) {
|
|
|
22907
23332
|
});
|
|
22908
23333
|
return checks;
|
|
22909
23334
|
}
|
|
22910
|
-
const source =
|
|
23335
|
+
const source = readFileSync34(absPath, "utf-8");
|
|
22911
23336
|
const lines = source.split("\n");
|
|
22912
23337
|
if (activeChecks.rule_compliance !== false) {
|
|
22913
23338
|
for (const ruleSet of config.rules) {
|
|
@@ -23352,7 +23777,7 @@ var init_adr_generator = __esm({
|
|
|
23352
23777
|
});
|
|
23353
23778
|
|
|
23354
23779
|
// src/security-scorer.ts
|
|
23355
|
-
import { existsSync as
|
|
23780
|
+
import { existsSync as existsSync34, readFileSync as readFileSync35 } from "fs";
|
|
23356
23781
|
function p12(baseName) {
|
|
23357
23782
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
23358
23783
|
}
|
|
@@ -23376,12 +23801,12 @@ function scoreFileSecurity(filePath, projectRoot) {
|
|
|
23376
23801
|
}]
|
|
23377
23802
|
};
|
|
23378
23803
|
}
|
|
23379
|
-
if (!
|
|
23804
|
+
if (!existsSync34(absPath)) {
|
|
23380
23805
|
return { riskScore: 0, findings: [] };
|
|
23381
23806
|
}
|
|
23382
23807
|
let source;
|
|
23383
23808
|
try {
|
|
23384
|
-
source =
|
|
23809
|
+
source = readFileSync35(absPath, "utf-8");
|
|
23385
23810
|
} catch {
|
|
23386
23811
|
return { riskScore: 0, findings: [] };
|
|
23387
23812
|
}
|
|
@@ -23670,8 +24095,8 @@ var init_security_scorer = __esm({
|
|
|
23670
24095
|
});
|
|
23671
24096
|
|
|
23672
24097
|
// src/dependency-scorer.ts
|
|
23673
|
-
import { existsSync as
|
|
23674
|
-
import { resolve as
|
|
24098
|
+
import { existsSync as existsSync35, readFileSync as readFileSync36 } from "fs";
|
|
24099
|
+
import { resolve as resolve30 } from "path";
|
|
23675
24100
|
function p13(baseName) {
|
|
23676
24101
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
23677
24102
|
}
|
|
@@ -23703,10 +24128,10 @@ function calculateDepRisk(factors) {
|
|
|
23703
24128
|
return Math.min(100, risk);
|
|
23704
24129
|
}
|
|
23705
24130
|
function getInstalledPackages(projectRoot) {
|
|
23706
|
-
const pkgPath =
|
|
23707
|
-
if (!
|
|
24131
|
+
const pkgPath = resolve30(projectRoot, "package.json");
|
|
24132
|
+
if (!existsSync35(pkgPath)) return /* @__PURE__ */ new Map();
|
|
23708
24133
|
try {
|
|
23709
|
-
const pkg = JSON.parse(
|
|
24134
|
+
const pkg = JSON.parse(readFileSync36(pkgPath, "utf-8"));
|
|
23710
24135
|
const packages = /* @__PURE__ */ new Map();
|
|
23711
24136
|
for (const [name, version] of Object.entries(pkg.dependencies ?? {})) {
|
|
23712
24137
|
packages.set(name, version);
|
|
@@ -24308,9 +24733,9 @@ var init_regression_detector = __esm({
|
|
|
24308
24733
|
});
|
|
24309
24734
|
|
|
24310
24735
|
// src/knowledge-indexer.ts
|
|
24311
|
-
import { createHash as
|
|
24312
|
-
import { readFileSync as
|
|
24313
|
-
import { resolve as
|
|
24736
|
+
import { createHash as createHash9 } from "crypto";
|
|
24737
|
+
import { readFileSync as readFileSync37, readdirSync as readdirSync23, statSync as statSync14, existsSync as existsSync36 } from "fs";
|
|
24738
|
+
import { resolve as resolve31, relative as relative12, basename as basename8, extname as extname2 } from "path";
|
|
24314
24739
|
function getKnowledgePaths() {
|
|
24315
24740
|
const resolved = getResolvedPaths();
|
|
24316
24741
|
const config = getConfig();
|
|
@@ -24338,7 +24763,7 @@ function discoverMarkdownFiles(baseDir) {
|
|
|
24338
24763
|
try {
|
|
24339
24764
|
const entries = readdirSync23(dir, { withFileTypes: true });
|
|
24340
24765
|
for (const entry of entries) {
|
|
24341
|
-
const fullPath =
|
|
24766
|
+
const fullPath = resolve31(dir, entry.name);
|
|
24342
24767
|
if (entry.isDirectory()) {
|
|
24343
24768
|
if (entry.name === "archive" && dir.includes("session-state")) continue;
|
|
24344
24769
|
if (entry.name === "archive" && dir.includes("status")) continue;
|
|
@@ -24396,7 +24821,7 @@ function categorizeFile(filePath) {
|
|
|
24396
24821
|
return "root";
|
|
24397
24822
|
}
|
|
24398
24823
|
function hashContent2(content) {
|
|
24399
|
-
return
|
|
24824
|
+
return createHash9("sha256").update(content).digest("hex");
|
|
24400
24825
|
}
|
|
24401
24826
|
function parseCRTable(content) {
|
|
24402
24827
|
const rules = [];
|
|
@@ -24616,11 +25041,11 @@ function indexAllKnowledge(db) {
|
|
|
24616
25041
|
files.push(...memFiles);
|
|
24617
25042
|
} catch {
|
|
24618
25043
|
}
|
|
24619
|
-
if (
|
|
25044
|
+
if (existsSync36(paths.plansDir)) {
|
|
24620
25045
|
const planFiles = discoverMarkdownFiles(paths.plansDir);
|
|
24621
25046
|
files.push(...planFiles);
|
|
24622
25047
|
}
|
|
24623
|
-
if (
|
|
25048
|
+
if (existsSync36(paths.docsDir)) {
|
|
24624
25049
|
const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
|
|
24625
25050
|
const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f2) => !f2.includes("/plans/") && !excludePatterns.some((p19) => f2.includes(p19)));
|
|
24626
25051
|
files.push(...docsFiles);
|
|
@@ -24663,8 +25088,8 @@ function indexAllKnowledge(db) {
|
|
|
24663
25088
|
} catch {
|
|
24664
25089
|
}
|
|
24665
25090
|
for (const filePath of files) {
|
|
24666
|
-
if (!
|
|
24667
|
-
const content =
|
|
25091
|
+
if (!existsSync36(filePath)) continue;
|
|
25092
|
+
const content = readFileSync37(filePath, "utf-8");
|
|
24668
25093
|
const hash = hashContent2(content);
|
|
24669
25094
|
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);
|
|
24670
25095
|
const category = categorizeFile(filePath);
|
|
@@ -24784,10 +25209,10 @@ function isKnowledgeStale(db) {
|
|
|
24784
25209
|
files.push(...discoverMarkdownFiles(paths.memoryDir));
|
|
24785
25210
|
} catch {
|
|
24786
25211
|
}
|
|
24787
|
-
if (
|
|
25212
|
+
if (existsSync36(paths.plansDir)) {
|
|
24788
25213
|
files.push(...discoverMarkdownFiles(paths.plansDir));
|
|
24789
25214
|
}
|
|
24790
|
-
if (
|
|
25215
|
+
if (existsSync36(paths.docsDir)) {
|
|
24791
25216
|
const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
|
|
24792
25217
|
const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f2) => !f2.includes("/plans/") && !excludePatterns.some((p19) => f2.includes(p19)));
|
|
24793
25218
|
files.push(...docsFiles);
|
|
@@ -24816,8 +25241,8 @@ var init_knowledge_indexer = __esm({
|
|
|
24816
25241
|
});
|
|
24817
25242
|
|
|
24818
25243
|
// src/knowledge-tools.ts
|
|
24819
|
-
import { readFileSync as
|
|
24820
|
-
import { resolve as
|
|
25244
|
+
import { readFileSync as readFileSync38, writeFileSync as writeFileSync5, appendFileSync as appendFileSync2, readdirSync as readdirSync24 } from "fs";
|
|
25245
|
+
import { resolve as resolve32, basename as basename9 } from "path";
|
|
24821
25246
|
function p16(baseName) {
|
|
24822
25247
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
24823
25248
|
}
|
|
@@ -25554,7 +25979,7 @@ function handleCorrect(db, args2) {
|
|
|
25554
25979
|
if (!wrong || !correction || !rule) {
|
|
25555
25980
|
return text15("Error: wrong, correction, and rule are all required.");
|
|
25556
25981
|
}
|
|
25557
|
-
const correctionsPath =
|
|
25982
|
+
const correctionsPath = resolve32(getResolvedPaths().memoryDir, "corrections.md");
|
|
25558
25983
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
25559
25984
|
const title = rule.slice(0, 60);
|
|
25560
25985
|
const entry = `
|
|
@@ -25567,7 +25992,7 @@ ${crRule ? `- **CR**: ${crRule}
|
|
|
25567
25992
|
`;
|
|
25568
25993
|
let existing = "";
|
|
25569
25994
|
try {
|
|
25570
|
-
existing =
|
|
25995
|
+
existing = readFileSync38(correctionsPath, "utf-8");
|
|
25571
25996
|
} catch {
|
|
25572
25997
|
}
|
|
25573
25998
|
const archiveIdx = existing.indexOf("## Archived");
|
|
@@ -25905,13 +26330,13 @@ var init_knowledge_tools = __esm({
|
|
|
25905
26330
|
|
|
25906
26331
|
// src/knowledge-db.ts
|
|
25907
26332
|
import Database3 from "better-sqlite3";
|
|
25908
|
-
import { dirname as
|
|
25909
|
-
import { existsSync as
|
|
26333
|
+
import { dirname as dirname16 } from "path";
|
|
26334
|
+
import { existsSync as existsSync38, mkdirSync as mkdirSync11 } from "fs";
|
|
25910
26335
|
function getKnowledgeDb() {
|
|
25911
26336
|
const dbPath = getResolvedPaths().knowledgeDbPath;
|
|
25912
|
-
const dir =
|
|
25913
|
-
if (!
|
|
25914
|
-
|
|
26337
|
+
const dir = dirname16(dbPath);
|
|
26338
|
+
if (!existsSync38(dir)) {
|
|
26339
|
+
mkdirSync11(dir, { recursive: true });
|
|
25915
26340
|
}
|
|
25916
26341
|
const db = new Database3(dbPath);
|
|
25917
26342
|
db.pragma("journal_mode = WAL");
|
|
@@ -26644,8 +27069,8 @@ var init_python_tools = __esm({
|
|
|
26644
27069
|
});
|
|
26645
27070
|
|
|
26646
27071
|
// src/tools.ts
|
|
26647
|
-
import { readFileSync as
|
|
26648
|
-
import { resolve as
|
|
27072
|
+
import { readFileSync as readFileSync39, existsSync as existsSync39 } from "fs";
|
|
27073
|
+
import { resolve as resolve33, basename as basename10 } from "path";
|
|
26649
27074
|
function prefix2() {
|
|
26650
27075
|
return getConfig().toolPrefix;
|
|
26651
27076
|
}
|
|
@@ -26680,7 +27105,7 @@ function ensureIndexes(dataDb, codegraphDb, force = false) {
|
|
|
26680
27105
|
if (config.python?.root) {
|
|
26681
27106
|
const pythonRoot = config.python.root;
|
|
26682
27107
|
const excludeDirs = config.python.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
|
|
26683
|
-
if (force || isPythonDataStale(dataDb,
|
|
27108
|
+
if (force || isPythonDataStale(dataDb, resolve33(getProjectRoot(), pythonRoot))) {
|
|
26684
27109
|
const pyImports = buildPythonImportIndex(dataDb, pythonRoot, excludeDirs);
|
|
26685
27110
|
results.push(`Python imports: ${pyImports}`);
|
|
26686
27111
|
const pyRoutes = buildPythonRouteIndex(dataDb, pythonRoot, excludeDirs);
|
|
@@ -26723,7 +27148,8 @@ function getToolDefinitions() {
|
|
|
26723
27148
|
...getSecurityToolDefinitions(),
|
|
26724
27149
|
...getDependencyToolDefinitions(),
|
|
26725
27150
|
// Enterprise layer (team knowledge, regression detection)
|
|
26726
|
-
|
|
27151
|
+
// P-A-003: team tools are cloud-gated — only listed when cloud.enabled is true.
|
|
27152
|
+
...isCloudFeatureAvailable() ? getTeamToolDefinitions() : [],
|
|
26727
27153
|
...getRegressionToolDefinitions(),
|
|
26728
27154
|
// Knowledge layer (indexed .claude/ knowledge — rules, patterns, incidents)
|
|
26729
27155
|
...getKnowledgeToolDefinitions(),
|
|
@@ -26927,7 +27353,7 @@ async function handleToolCall(name, args2, dataDb, codegraphDb) {
|
|
|
26927
27353
|
memDb.close();
|
|
26928
27354
|
}
|
|
26929
27355
|
}
|
|
26930
|
-
if (isTeamTool(name)) {
|
|
27356
|
+
if (isTeamTool(name) && isCloudFeatureAvailable()) {
|
|
26931
27357
|
const memDb = getMemoryDb();
|
|
26932
27358
|
try {
|
|
26933
27359
|
return handleTeamToolCall(name, args2, memDb);
|
|
@@ -27114,9 +27540,9 @@ function handleContext(file, dataDb, codegraphDb) {
|
|
|
27114
27540
|
try {
|
|
27115
27541
|
const resolvedPaths = getResolvedPaths();
|
|
27116
27542
|
const root = getProjectRoot();
|
|
27117
|
-
const absFilePath = ensureWithinRoot(
|
|
27118
|
-
if (
|
|
27119
|
-
const fileContent =
|
|
27543
|
+
const absFilePath = ensureWithinRoot(resolve33(resolvedPaths.srcDir, "..", file), root);
|
|
27544
|
+
if (existsSync39(absFilePath)) {
|
|
27545
|
+
const fileContent = readFileSync39(absFilePath, "utf-8").slice(0, 3e3);
|
|
27120
27546
|
const keywords = [];
|
|
27121
27547
|
if (fileContent.includes("ctx.db")) keywords.push("database", "schema");
|
|
27122
27548
|
if (fileContent.includes("BigInt") || fileContent.includes("Decimal")) keywords.push("BigInt", "serialization");
|
|
@@ -27540,11 +27966,11 @@ function handleSchema(args2) {
|
|
|
27540
27966
|
lines.push("Checking all column references against Prisma schema...");
|
|
27541
27967
|
lines.push("");
|
|
27542
27968
|
const projectRoot = getProjectRoot();
|
|
27543
|
-
const absPath = ensureWithinRoot(
|
|
27544
|
-
if (!
|
|
27969
|
+
const absPath = ensureWithinRoot(resolve33(projectRoot, file), projectRoot);
|
|
27970
|
+
if (!existsSync39(absPath)) {
|
|
27545
27971
|
return text17(`File not found: ${file}`);
|
|
27546
27972
|
}
|
|
27547
|
-
const source =
|
|
27973
|
+
const source = readFileSync39(absPath, "utf-8");
|
|
27548
27974
|
const config = getConfig();
|
|
27549
27975
|
const dbPattern = config.dbAccessPattern ?? "ctx.db.{table}";
|
|
27550
27976
|
const regexStr = dbPattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace("\\{table\\}", "(\\w+)");
|
|
@@ -27913,8 +28339,8 @@ var init_server_dispatch = __esm({
|
|
|
27913
28339
|
|
|
27914
28340
|
// src/server.ts
|
|
27915
28341
|
var server_exports = {};
|
|
27916
|
-
import { readFileSync as
|
|
27917
|
-
import { resolve as
|
|
28342
|
+
import { readFileSync as readFileSync40 } from "fs";
|
|
28343
|
+
import { resolve as resolve34, dirname as dirname17 } from "path";
|
|
27918
28344
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
27919
28345
|
function pruneMemoryOnStartup() {
|
|
27920
28346
|
try {
|
|
@@ -27946,10 +28372,10 @@ var init_server = __esm({
|
|
|
27946
28372
|
init_memory_db();
|
|
27947
28373
|
init_license();
|
|
27948
28374
|
init_server_dispatch();
|
|
27949
|
-
__dirname4 =
|
|
28375
|
+
__dirname4 = dirname17(fileURLToPath4(import.meta.url));
|
|
27950
28376
|
PKG_VERSION = (() => {
|
|
27951
28377
|
try {
|
|
27952
|
-
const pkg = JSON.parse(
|
|
28378
|
+
const pkg = JSON.parse(readFileSync40(resolve34(__dirname4, "..", "package.json"), "utf-8"));
|
|
27953
28379
|
return pkg.version ?? "0.0.0";
|
|
27954
28380
|
} catch {
|
|
27955
28381
|
return "0.0.0";
|
|
@@ -28235,19 +28661,19 @@ var config_upgrade_exports = {};
|
|
|
28235
28661
|
__export(config_upgrade_exports, {
|
|
28236
28662
|
runConfigUpgrade: () => runConfigUpgrade
|
|
28237
28663
|
});
|
|
28238
|
-
import { existsSync as
|
|
28239
|
-
import { resolve as
|
|
28664
|
+
import { existsSync as existsSync40, readFileSync as readFileSync41, writeFileSync as writeFileSync6, copyFileSync, unlinkSync as unlinkSync2 } from "fs";
|
|
28665
|
+
import { resolve as resolve35 } from "path";
|
|
28240
28666
|
import { parse as parseYaml6 } from "yaml";
|
|
28241
28667
|
async function runConfigUpgrade(opts = {}) {
|
|
28242
28668
|
const cwd = opts.cwd ?? process.cwd();
|
|
28243
|
-
const configPath =
|
|
28669
|
+
const configPath = resolve35(cwd, "massu.config.yaml");
|
|
28244
28670
|
const bakPath = `${configPath}.bak`;
|
|
28245
28671
|
const log = opts.silent ? () => {
|
|
28246
28672
|
} : (s) => process.stdout.write(s);
|
|
28247
28673
|
const err = opts.silent ? () => {
|
|
28248
28674
|
} : (s) => process.stderr.write(s);
|
|
28249
28675
|
if (opts.rollback) {
|
|
28250
|
-
if (!
|
|
28676
|
+
if (!existsSync40(bakPath)) {
|
|
28251
28677
|
const message = `No backup found at ${bakPath}`;
|
|
28252
28678
|
err(message + "\n");
|
|
28253
28679
|
return { exitCode: 1, action: "none", message };
|
|
@@ -28263,14 +28689,14 @@ async function runConfigUpgrade(opts = {}) {
|
|
|
28263
28689
|
return { exitCode: 2, action: "none", message };
|
|
28264
28690
|
}
|
|
28265
28691
|
}
|
|
28266
|
-
if (!
|
|
28692
|
+
if (!existsSync40(configPath)) {
|
|
28267
28693
|
const message = "massu.config.yaml not found. Run: npx massu init";
|
|
28268
28694
|
err(message + "\n");
|
|
28269
28695
|
return { exitCode: 1, action: "none", message };
|
|
28270
28696
|
}
|
|
28271
28697
|
let existing;
|
|
28272
28698
|
try {
|
|
28273
|
-
const content =
|
|
28699
|
+
const content = readFileSync41(configPath, "utf-8");
|
|
28274
28700
|
const parsed = parseYaml6(content);
|
|
28275
28701
|
if (!parsed || typeof parsed !== "object") {
|
|
28276
28702
|
throw new Error("config is not a YAML object");
|
|
@@ -28293,7 +28719,7 @@ async function runConfigUpgrade(opts = {}) {
|
|
|
28293
28719
|
fingerprint: computeFingerprint(detection)
|
|
28294
28720
|
};
|
|
28295
28721
|
try {
|
|
28296
|
-
const original =
|
|
28722
|
+
const original = readFileSync41(configPath, "utf-8");
|
|
28297
28723
|
writeFileSync6(bakPath, original, "utf-8");
|
|
28298
28724
|
} catch (e2) {
|
|
28299
28725
|
const message = `Failed to write backup: ${e2 instanceof Error ? e2.message : String(e2)}`;
|
|
@@ -28327,8 +28753,8 @@ var config_check_drift_exports = {};
|
|
|
28327
28753
|
__export(config_check_drift_exports, {
|
|
28328
28754
|
runConfigCheckDrift: () => runConfigCheckDrift
|
|
28329
28755
|
});
|
|
28330
|
-
import { existsSync as
|
|
28331
|
-
import { resolve as
|
|
28756
|
+
import { existsSync as existsSync41, readFileSync as readFileSync42 } from "fs";
|
|
28757
|
+
import { resolve as resolve36 } from "path";
|
|
28332
28758
|
import { parse as parseYaml7 } from "yaml";
|
|
28333
28759
|
function renderChanges(changes) {
|
|
28334
28760
|
if (changes.length === 0) return "(none)\n";
|
|
@@ -28336,12 +28762,12 @@ function renderChanges(changes) {
|
|
|
28336
28762
|
}
|
|
28337
28763
|
async function runConfigCheckDrift(opts = {}) {
|
|
28338
28764
|
const cwd = opts.cwd ?? process.cwd();
|
|
28339
|
-
const configPath =
|
|
28765
|
+
const configPath = resolve36(cwd, "massu.config.yaml");
|
|
28340
28766
|
const log = opts.silent ? () => {
|
|
28341
28767
|
} : (s) => process.stdout.write(s);
|
|
28342
28768
|
const err = opts.silent ? () => {
|
|
28343
28769
|
} : (s) => process.stderr.write(s);
|
|
28344
|
-
if (!
|
|
28770
|
+
if (!existsSync41(configPath)) {
|
|
28345
28771
|
const message = "massu.config.yaml not found. Run: npx massu init";
|
|
28346
28772
|
err(message + "\n");
|
|
28347
28773
|
return {
|
|
@@ -28355,7 +28781,7 @@ async function runConfigCheckDrift(opts = {}) {
|
|
|
28355
28781
|
}
|
|
28356
28782
|
let config;
|
|
28357
28783
|
try {
|
|
28358
|
-
const content =
|
|
28784
|
+
const content = readFileSync42(configPath, "utf-8");
|
|
28359
28785
|
const parsed = parseYaml7(content);
|
|
28360
28786
|
if (!parsed || typeof parsed !== "object") {
|
|
28361
28787
|
throw new Error("config is not a YAML object");
|
|
@@ -28422,11 +28848,11 @@ var init_config_check_drift = __esm({
|
|
|
28422
28848
|
});
|
|
28423
28849
|
|
|
28424
28850
|
// src/cli.ts
|
|
28425
|
-
import { readFileSync as
|
|
28426
|
-
import { resolve as
|
|
28851
|
+
import { readFileSync as readFileSync43 } from "fs";
|
|
28852
|
+
import { resolve as resolve37, dirname as dirname18 } from "path";
|
|
28427
28853
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
28428
28854
|
var __filename4 = fileURLToPath5(import.meta.url);
|
|
28429
|
-
var __dirname5 =
|
|
28855
|
+
var __dirname5 = dirname18(__filename4);
|
|
28430
28856
|
var args = process.argv.slice(2);
|
|
28431
28857
|
var subcommand = args[0];
|
|
28432
28858
|
async function main() {
|
|
@@ -28451,6 +28877,12 @@ async function main() {
|
|
|
28451
28877
|
await runInstallCommands2();
|
|
28452
28878
|
break;
|
|
28453
28879
|
}
|
|
28880
|
+
case "permissions": {
|
|
28881
|
+
const { handlePermissionsSubcommand: handlePermissionsSubcommand2 } = await Promise.resolve().then(() => (init_permissions2(), permissions_exports));
|
|
28882
|
+
const result = await handlePermissionsSubcommand2(args.slice(1));
|
|
28883
|
+
process.exit(result.exitCode);
|
|
28884
|
+
return;
|
|
28885
|
+
}
|
|
28454
28886
|
case "show-template": {
|
|
28455
28887
|
const { runShowTemplate: runShowTemplate2 } = await Promise.resolve().then(() => (init_show_template(), show_template_exports));
|
|
28456
28888
|
await runShowTemplate2(args.slice(1));
|
|
@@ -28563,12 +28995,13 @@ Commands:
|
|
|
28563
28995
|
init Set up Massu AI in your project (one command, full setup)
|
|
28564
28996
|
doctor Check installation health
|
|
28565
28997
|
install-hooks Install/update Claude Code hooks
|
|
28566
|
-
install-commands Install/update slash commands
|
|
28998
|
+
install-commands Install/update slash commands (use --skip-permissions to opt out of MCP allowlist seeding)
|
|
28567
28999
|
show-template Print the resolved variant of a bundled template (e.g. for diffs)
|
|
28568
29000
|
watch Run the file-watcher daemon (auto-refresh on stack changes)
|
|
28569
29001
|
refresh-log [N] Show the last N watcher auto-refresh events
|
|
28570
29002
|
validate-config Validate massu.config.yaml (alias: config validate)
|
|
28571
29003
|
config <sub> Config lifecycle: refresh | validate | upgrade | doctor | check-drift
|
|
29004
|
+
permissions <sub> MCP permission lifecycle: install | verify | check-drift
|
|
28572
29005
|
adapters <sub> Third-party adapter registry: list | refresh | search | add-local | remove-local | install | resign
|
|
28573
29006
|
|
|
28574
29007
|
Options:
|
|
@@ -28608,7 +29041,7 @@ Examples:
|
|
|
28608
29041
|
}
|
|
28609
29042
|
function printVersion() {
|
|
28610
29043
|
try {
|
|
28611
|
-
const pkg = JSON.parse(
|
|
29044
|
+
const pkg = JSON.parse(readFileSync43(resolve37(__dirname5, "../package.json"), "utf-8"));
|
|
28612
29045
|
console.log(`massu v${pkg.version}`);
|
|
28613
29046
|
} catch {
|
|
28614
29047
|
console.log("massu v0.1.0");
|