@massu/core 1.15.1 → 1.15.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/dist/cli.js +977 -572
- package/dist/hooks/auto-learning-pipeline.js +90 -19
- package/dist/hooks/classify-failure.js +112 -43
- package/dist/hooks/cost-tracker.js +108 -39
- package/dist/hooks/fix-detector.js +95 -24
- package/dist/hooks/incident-pipeline.js +114 -45
- package/dist/hooks/post-edit-context.js +105 -36
- package/dist/hooks/post-tool-use.js +137 -68
- package/dist/hooks/pre-compact.js +108 -39
- package/dist/hooks/pre-delete-check.js +107 -38
- package/dist/hooks/pre-tool-use-gate.js +113 -44
- package/dist/hooks/quality-event.js +108 -39
- package/dist/hooks/rule-enforcement-pipeline.js +92 -21
- package/dist/hooks/session-end.js +126 -57
- package/dist/hooks/session-start.js +165 -96
- package/dist/hooks/user-prompt.js +124 -55
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -58,17 +58,136 @@ var init_memory_path = __esm({
|
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
// src/
|
|
62
|
-
import { resolve, dirname } from "path";
|
|
63
|
-
import { existsSync, readFileSync } from "fs";
|
|
61
|
+
// src/credentials.ts
|
|
64
62
|
import { homedir } from "os";
|
|
63
|
+
import { resolve } from "path";
|
|
64
|
+
import {
|
|
65
|
+
existsSync,
|
|
66
|
+
readFileSync,
|
|
67
|
+
writeFileSync,
|
|
68
|
+
mkdirSync,
|
|
69
|
+
rmSync,
|
|
70
|
+
chmodSync
|
|
71
|
+
} from "fs";
|
|
72
|
+
function credentialsDir(home = homedir()) {
|
|
73
|
+
return resolve(home, ".massu");
|
|
74
|
+
}
|
|
75
|
+
function credentialsPath(home = homedir()) {
|
|
76
|
+
return resolve(credentialsDir(home), "credentials");
|
|
77
|
+
}
|
|
78
|
+
function readUserCredentials(home = homedir()) {
|
|
79
|
+
try {
|
|
80
|
+
const p19 = credentialsPath(home);
|
|
81
|
+
if (!existsSync(p19)) return void 0;
|
|
82
|
+
const parsed = JSON.parse(readFileSync(p19, "utf-8"));
|
|
83
|
+
const key = typeof parsed?.apiKey === "string" ? parsed.apiKey.trim() : "";
|
|
84
|
+
return key.length > 0 ? key : void 0;
|
|
85
|
+
} catch {
|
|
86
|
+
return void 0;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function writeUserCredentials(apiKey, home = homedir()) {
|
|
90
|
+
const dir = credentialsDir(home);
|
|
91
|
+
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
92
|
+
try {
|
|
93
|
+
chmodSync(dir, 448);
|
|
94
|
+
} catch {
|
|
95
|
+
}
|
|
96
|
+
const p19 = credentialsPath(home);
|
|
97
|
+
writeFileSync(p19, JSON.stringify({ apiKey: apiKey.trim() }, null, 2) + "\n", {
|
|
98
|
+
mode: 384
|
|
99
|
+
});
|
|
100
|
+
try {
|
|
101
|
+
chmodSync(p19, 384);
|
|
102
|
+
} catch {
|
|
103
|
+
}
|
|
104
|
+
return p19;
|
|
105
|
+
}
|
|
106
|
+
function removeUserCredentials(home = homedir()) {
|
|
107
|
+
const p19 = credentialsPath(home);
|
|
108
|
+
if (!existsSync(p19)) return false;
|
|
109
|
+
try {
|
|
110
|
+
rmSync(p19);
|
|
111
|
+
return true;
|
|
112
|
+
} catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function isUnresolvedLiteral(v3) {
|
|
117
|
+
return /^\$\{[^}]+\}$/.test(v3);
|
|
118
|
+
}
|
|
119
|
+
function resolveApiKey(opts = {}) {
|
|
120
|
+
const env = opts.env ?? process.env;
|
|
121
|
+
const home = opts.home ?? homedir();
|
|
122
|
+
const cfg = typeof opts.configApiKey === "string" ? opts.configApiKey.trim() : "";
|
|
123
|
+
if (cfg.length > 0 && !isUnresolvedLiteral(cfg)) {
|
|
124
|
+
return { apiKey: cfg, source: "config" };
|
|
125
|
+
}
|
|
126
|
+
const envRaw = env[MASSU_ENV_API_KEY];
|
|
127
|
+
const envKey = typeof envRaw === "string" ? envRaw.trim() : "";
|
|
128
|
+
if (envKey.length > 0) {
|
|
129
|
+
return { apiKey: envKey, source: "env" };
|
|
130
|
+
}
|
|
131
|
+
const fileKey = readUserCredentials(home);
|
|
132
|
+
if (fileKey) {
|
|
133
|
+
return { apiKey: fileKey, source: "user-file" };
|
|
134
|
+
}
|
|
135
|
+
return { source: "none" };
|
|
136
|
+
}
|
|
137
|
+
function resolveEndpoint(opts = {}) {
|
|
138
|
+
const env = opts.env ?? process.env;
|
|
139
|
+
const cfg = typeof opts.configEndpoint === "string" ? opts.configEndpoint.trim() : "";
|
|
140
|
+
if (cfg.length > 0) return cfg;
|
|
141
|
+
const envRaw = env[MASSU_ENV_CLOUD_ENDPOINT];
|
|
142
|
+
const envEp = typeof envRaw === "string" ? envRaw.trim() : "";
|
|
143
|
+
if (envEp.length > 0) return envEp;
|
|
144
|
+
return DEFAULT_CLOUD_ENDPOINT;
|
|
145
|
+
}
|
|
146
|
+
function apiKeySourceLabel(source) {
|
|
147
|
+
switch (source) {
|
|
148
|
+
case "config":
|
|
149
|
+
return "explicit cloud.apiKey";
|
|
150
|
+
case "env":
|
|
151
|
+
return "MASSU_API_KEY env";
|
|
152
|
+
case "user-file":
|
|
153
|
+
return "~/.massu/credentials";
|
|
154
|
+
case "none":
|
|
155
|
+
default:
|
|
156
|
+
return "no source";
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
var MASSU_ENV_API_KEY, MASSU_ENV_CLOUD_ENDPOINT, DEFAULT_CLOUD_ENDPOINT, API_KEY_PREFIX;
|
|
160
|
+
var init_credentials = __esm({
|
|
161
|
+
"src/credentials.ts"() {
|
|
162
|
+
"use strict";
|
|
163
|
+
MASSU_ENV_API_KEY = "MASSU_API_KEY";
|
|
164
|
+
MASSU_ENV_CLOUD_ENDPOINT = "MASSU_CLOUD_ENDPOINT";
|
|
165
|
+
DEFAULT_CLOUD_ENDPOINT = "https://api.massu.ai/v1";
|
|
166
|
+
API_KEY_PREFIX = "ms_live_";
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// src/config.ts
|
|
171
|
+
var config_exports = {};
|
|
172
|
+
__export(config_exports, {
|
|
173
|
+
AdapterLocalPathSchema: () => AdapterLocalPathSchema,
|
|
174
|
+
LSPConfigSchema: () => LSPConfigSchema,
|
|
175
|
+
getConfig: () => getConfig,
|
|
176
|
+
getProjectRoot: () => getProjectRoot,
|
|
177
|
+
getResolvedApiKeySource: () => getResolvedApiKeySource,
|
|
178
|
+
getResolvedPaths: () => getResolvedPaths,
|
|
179
|
+
resetConfig: () => resetConfig
|
|
180
|
+
});
|
|
181
|
+
import { resolve as resolve2, dirname } from "path";
|
|
182
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
183
|
+
import { homedir as homedir2 } from "os";
|
|
65
184
|
import { parse as parseYaml } from "yaml";
|
|
66
185
|
import { z } from "zod";
|
|
67
186
|
function findProjectRoot() {
|
|
68
187
|
const cwd = process.cwd();
|
|
69
188
|
let dir = cwd;
|
|
70
189
|
while (true) {
|
|
71
|
-
if (
|
|
190
|
+
if (existsSync2(resolve2(dir, "massu.config.yaml"))) {
|
|
72
191
|
return dir;
|
|
73
192
|
}
|
|
74
193
|
const parent = dirname(dir);
|
|
@@ -77,10 +196,10 @@ function findProjectRoot() {
|
|
|
77
196
|
}
|
|
78
197
|
dir = cwd;
|
|
79
198
|
while (true) {
|
|
80
|
-
if (
|
|
199
|
+
if (existsSync2(resolve2(dir, "package.json"))) {
|
|
81
200
|
return dir;
|
|
82
201
|
}
|
|
83
|
-
if (
|
|
202
|
+
if (existsSync2(resolve2(dir, ".git"))) {
|
|
84
203
|
return dir;
|
|
85
204
|
}
|
|
86
205
|
const parent = dirname(dir);
|
|
@@ -98,10 +217,10 @@ function getProjectRoot() {
|
|
|
98
217
|
function getConfig() {
|
|
99
218
|
if (_config) return _config;
|
|
100
219
|
const root = getProjectRoot();
|
|
101
|
-
const configPath =
|
|
220
|
+
const configPath = resolve2(root, "massu.config.yaml");
|
|
102
221
|
let rawYaml = {};
|
|
103
|
-
if (
|
|
104
|
-
const content =
|
|
222
|
+
if (existsSync2(configPath)) {
|
|
223
|
+
const content = readFileSync2(configPath, "utf-8");
|
|
105
224
|
rawYaml = parseYaml(content) ?? {};
|
|
106
225
|
}
|
|
107
226
|
const result = RawConfigSchema.safeParse(rawYaml);
|
|
@@ -118,7 +237,7 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
|
|
|
118
237
|
);
|
|
119
238
|
}
|
|
120
239
|
const parsed = result.data;
|
|
121
|
-
const projectRoot = parsed.project.root === "auto" || !parsed.project.root ? root :
|
|
240
|
+
const projectRoot = parsed.project.root === "auto" || !parsed.project.root ? root : resolve2(root, parsed.project.root);
|
|
122
241
|
const fw = parsed.framework;
|
|
123
242
|
let router = fw.router;
|
|
124
243
|
let orm = fw.orm;
|
|
@@ -176,60 +295,72 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
|
|
|
176
295
|
telemetry: parsed.telemetry,
|
|
177
296
|
lsp: parsed.lsp
|
|
178
297
|
};
|
|
179
|
-
|
|
298
|
+
const resolvedKey = resolveApiKey({ configApiKey: _config.cloud?.apiKey });
|
|
299
|
+
_resolvedApiKeySource = resolvedKey.source;
|
|
300
|
+
const resolvedEndpoint = resolveEndpoint({ configEndpoint: _config.cloud?.endpoint });
|
|
301
|
+
if (resolvedKey.apiKey) {
|
|
180
302
|
_config.cloud = {
|
|
181
303
|
enabled: true,
|
|
182
304
|
sync: { memory: true, analytics: true, audit: true },
|
|
183
305
|
..._config.cloud,
|
|
184
|
-
apiKey:
|
|
306
|
+
apiKey: resolvedKey.apiKey,
|
|
307
|
+
endpoint: resolvedEndpoint
|
|
185
308
|
};
|
|
309
|
+
} else if (_config.cloud) {
|
|
310
|
+
_config.cloud = { ..._config.cloud, endpoint: _config.cloud.endpoint ?? resolvedEndpoint };
|
|
186
311
|
}
|
|
187
312
|
return _config;
|
|
188
313
|
}
|
|
314
|
+
function getResolvedApiKeySource() {
|
|
315
|
+
getConfig();
|
|
316
|
+
return _resolvedApiKeySource;
|
|
317
|
+
}
|
|
189
318
|
function getResolvedPaths() {
|
|
190
319
|
const config = getConfig();
|
|
191
320
|
const root = getProjectRoot();
|
|
192
321
|
const claudeDirName = config.conventions?.claudeDirName ?? ".claude";
|
|
193
322
|
return {
|
|
194
|
-
codegraphDbPath:
|
|
195
|
-
dataDbPath:
|
|
196
|
-
prismaSchemaPath:
|
|
197
|
-
rootRouterPath:
|
|
198
|
-
routersDir:
|
|
199
|
-
srcDir:
|
|
323
|
+
codegraphDbPath: resolve2(root, ".codegraph/codegraph.db"),
|
|
324
|
+
dataDbPath: resolve2(root, ".massu/data.db"),
|
|
325
|
+
prismaSchemaPath: resolve2(root, config.paths.schema ?? "prisma/schema.prisma"),
|
|
326
|
+
rootRouterPath: resolve2(root, config.paths.routerRoot ?? "src/server/api/root.ts"),
|
|
327
|
+
routersDir: resolve2(root, config.paths.routers ?? "src/server/api/routers"),
|
|
328
|
+
srcDir: resolve2(root, config.paths.source),
|
|
200
329
|
pathAlias: Object.fromEntries(
|
|
201
330
|
Object.entries(config.paths.aliases).map(([alias, target]) => [
|
|
202
331
|
alias,
|
|
203
|
-
|
|
332
|
+
resolve2(root, target)
|
|
204
333
|
])
|
|
205
334
|
),
|
|
206
335
|
extensions: [".ts", ".tsx", ".js", ".jsx"],
|
|
207
336
|
indexFiles: ["index.ts", "index.tsx", "index.js", "index.jsx"],
|
|
208
|
-
patternsDir:
|
|
209
|
-
claudeMdPath:
|
|
210
|
-
docsMapPath:
|
|
211
|
-
helpSitePath:
|
|
212
|
-
memoryDbPath:
|
|
213
|
-
knowledgeDbPath:
|
|
214
|
-
plansDir:
|
|
215
|
-
docsDir:
|
|
216
|
-
claudeDir:
|
|
217
|
-
memoryDir:
|
|
218
|
-
sessionStatePath:
|
|
219
|
-
sessionArchivePath:
|
|
220
|
-
mcpJsonPath:
|
|
221
|
-
settingsLocalPath:
|
|
337
|
+
patternsDir: resolve2(root, claudeDirName, "patterns"),
|
|
338
|
+
claudeMdPath: resolve2(root, claudeDirName, "CLAUDE.md"),
|
|
339
|
+
docsMapPath: resolve2(root, ".massu/docs-map.json"),
|
|
340
|
+
helpSitePath: resolve2(root, "../" + config.project.name + "-help"),
|
|
341
|
+
memoryDbPath: resolve2(root, ".massu/memory.db"),
|
|
342
|
+
knowledgeDbPath: resolve2(root, ".massu/knowledge.db"),
|
|
343
|
+
plansDir: resolve2(root, "docs/plans"),
|
|
344
|
+
docsDir: resolve2(root, "docs"),
|
|
345
|
+
claudeDir: resolve2(root, claudeDirName),
|
|
346
|
+
memoryDir: resolve2(homedir2(), claudeDirName, "projects", encodeMemoryDirName(root), "memory"),
|
|
347
|
+
sessionStatePath: resolve2(root, config.conventions?.sessionStatePath ?? `${claudeDirName}/session-state/CURRENT.md`),
|
|
348
|
+
sessionArchivePath: resolve2(root, config.conventions?.sessionArchivePath ?? `${claudeDirName}/session-state/archive`),
|
|
349
|
+
mcpJsonPath: resolve2(root, ".mcp.json"),
|
|
350
|
+
settingsLocalPath: resolve2(root, claudeDirName, "settings.local.json")
|
|
222
351
|
};
|
|
223
352
|
}
|
|
224
353
|
function resetConfig() {
|
|
225
354
|
_config = null;
|
|
226
355
|
_projectRoot = null;
|
|
356
|
+
_resolvedApiKeySource = "none";
|
|
227
357
|
}
|
|
228
|
-
var DomainConfigSchema, PatternRuleConfigSchema, CostModelSchema, AnalyticsConfigSchema, CustomPatternSchema, GovernanceConfigSchema, SecurityPatternSchema, SecurityConfigSchema, TeamConfigSchema, RegressionConfigSchema, AutoLearningConfigSchema, CloudConfigSchema, ConventionsConfigSchema, PythonDomainConfigSchema, PythonConfigSchema, PathsConfigSchema, LanguageFrameworkEntrySchema, FrameworkConfigSchema, DetectedConfigSchema, VerificationEntrySchema, VerificationConfigSchema, CanonicalPathsSchema, VerificationTypesSchema, DetectionRuleEntrySchema, DetectionConfigSchema, WatchConfigSchema, AdapterLocalPathSchema, AdaptersConfigSchema, TelemetryConfigSchema, LSPConfigSchema, RawConfigSchema, _config, _projectRoot;
|
|
358
|
+
var DomainConfigSchema, PatternRuleConfigSchema, CostModelSchema, AnalyticsConfigSchema, CustomPatternSchema, GovernanceConfigSchema, SecurityPatternSchema, SecurityConfigSchema, TeamConfigSchema, RegressionConfigSchema, AutoLearningConfigSchema, CloudConfigSchema, ConventionsConfigSchema, PythonDomainConfigSchema, PythonConfigSchema, PathsConfigSchema, LanguageFrameworkEntrySchema, FrameworkConfigSchema, DetectedConfigSchema, VerificationEntrySchema, VerificationConfigSchema, CanonicalPathsSchema, VerificationTypesSchema, DetectionRuleEntrySchema, DetectionConfigSchema, WatchConfigSchema, AdapterLocalPathSchema, AdaptersConfigSchema, TelemetryConfigSchema, LSPConfigSchema, RawConfigSchema, _config, _resolvedApiKeySource, _projectRoot;
|
|
229
359
|
var init_config = __esm({
|
|
230
360
|
"src/config.ts"() {
|
|
231
361
|
"use strict";
|
|
232
362
|
init_memory_path();
|
|
363
|
+
init_credentials();
|
|
233
364
|
DomainConfigSchema = z.object({
|
|
234
365
|
name: z.string().default("Unknown"),
|
|
235
366
|
routers: z.array(z.string()).default([]),
|
|
@@ -611,6 +742,7 @@ var init_config = __esm({
|
|
|
611
742
|
lsp: LSPConfigSchema.optional()
|
|
612
743
|
}).passthrough();
|
|
613
744
|
_config = null;
|
|
745
|
+
_resolvedApiKeySource = "none";
|
|
614
746
|
_projectRoot = null;
|
|
615
747
|
}
|
|
616
748
|
});
|
|
@@ -673,7 +805,7 @@ __export(memory_db_exports, {
|
|
|
673
805
|
});
|
|
674
806
|
import Database from "better-sqlite3";
|
|
675
807
|
import { dirname as dirname2, basename } from "path";
|
|
676
|
-
import { existsSync as
|
|
808
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
677
809
|
function sanitizeFts5Query(raw) {
|
|
678
810
|
const trimmed = raw.trim();
|
|
679
811
|
if (!trimmed) return '""';
|
|
@@ -683,8 +815,8 @@ function sanitizeFts5Query(raw) {
|
|
|
683
815
|
function getMemoryDb() {
|
|
684
816
|
const dbPath = getResolvedPaths().memoryDbPath;
|
|
685
817
|
const dir = dirname2(dbPath);
|
|
686
|
-
if (!
|
|
687
|
-
|
|
818
|
+
if (!existsSync3(dir)) {
|
|
819
|
+
mkdirSync2(dir, { recursive: true });
|
|
688
820
|
}
|
|
689
821
|
const db = new Database(dbPath);
|
|
690
822
|
db.pragma("journal_mode = WAL");
|
|
@@ -2017,12 +2149,12 @@ var init_memory_db = __esm({
|
|
|
2017
2149
|
});
|
|
2018
2150
|
|
|
2019
2151
|
// src/memory-file-ingest.ts
|
|
2020
|
-
import { readFileSync as
|
|
2152
|
+
import { readFileSync as readFileSync3, existsSync as existsSync4, readdirSync } from "fs";
|
|
2021
2153
|
import { join } from "path";
|
|
2022
2154
|
import { parse as parseYaml2 } from "yaml";
|
|
2023
2155
|
function ingestMemoryFile(db, sessionId, filePath) {
|
|
2024
|
-
if (!
|
|
2025
|
-
const content =
|
|
2156
|
+
if (!existsSync4(filePath)) return "skipped";
|
|
2157
|
+
const content = readFileSync3(filePath, "utf-8");
|
|
2026
2158
|
const basename11 = (filePath.split("/").pop() ?? "").replace(".md", "");
|
|
2027
2159
|
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
2028
2160
|
let name = basename11;
|
|
@@ -2060,7 +2192,7 @@ ${body}` : body;
|
|
|
2060
2192
|
}
|
|
2061
2193
|
function backfillMemoryFiles(db, memoryDir, sessionId) {
|
|
2062
2194
|
const stats = { inserted: 0, updated: 0, skipped: 0, total: 0 };
|
|
2063
|
-
if (!
|
|
2195
|
+
if (!existsSync4(memoryDir)) return stats;
|
|
2064
2196
|
const files = readdirSync(memoryDir).filter(
|
|
2065
2197
|
(f2) => f2.endsWith(".md") && f2 !== "MEMORY.md"
|
|
2066
2198
|
);
|
|
@@ -2273,16 +2405,16 @@ var init_template_engine = __esm({
|
|
|
2273
2405
|
// src/lib/settings-local.ts
|
|
2274
2406
|
import {
|
|
2275
2407
|
closeSync,
|
|
2276
|
-
existsSync as
|
|
2408
|
+
existsSync as existsSync5,
|
|
2277
2409
|
fsyncSync,
|
|
2278
|
-
mkdirSync as
|
|
2410
|
+
mkdirSync as mkdirSync3,
|
|
2279
2411
|
openSync,
|
|
2280
|
-
readFileSync as
|
|
2412
|
+
readFileSync as readFileSync4,
|
|
2281
2413
|
renameSync,
|
|
2282
|
-
rmSync,
|
|
2414
|
+
rmSync as rmSync2,
|
|
2283
2415
|
writeSync
|
|
2284
2416
|
} from "fs";
|
|
2285
|
-
import { dirname as dirname3, resolve as
|
|
2417
|
+
import { dirname as dirname3, resolve as resolve4 } from "path";
|
|
2286
2418
|
function atomicWriteFile(targetPath, content, mode = 420) {
|
|
2287
2419
|
const tmpPath = `${targetPath}.${process.pid}.tmp`;
|
|
2288
2420
|
try {
|
|
@@ -2296,9 +2428,9 @@ function atomicWriteFile(targetPath, content, mode = 420) {
|
|
|
2296
2428
|
}
|
|
2297
2429
|
renameSync(tmpPath, targetPath);
|
|
2298
2430
|
} catch (err) {
|
|
2299
|
-
if (
|
|
2431
|
+
if (existsSync5(tmpPath)) {
|
|
2300
2432
|
try {
|
|
2301
|
-
|
|
2433
|
+
rmSync2(tmpPath, { force: true });
|
|
2302
2434
|
} catch {
|
|
2303
2435
|
}
|
|
2304
2436
|
}
|
|
@@ -2306,11 +2438,11 @@ function atomicWriteFile(targetPath, content, mode = 420) {
|
|
|
2306
2438
|
}
|
|
2307
2439
|
}
|
|
2308
2440
|
function readSettingsAtPath(absolutePath) {
|
|
2309
|
-
if (!
|
|
2441
|
+
if (!existsSync5(absolutePath)) {
|
|
2310
2442
|
return {};
|
|
2311
2443
|
}
|
|
2312
2444
|
try {
|
|
2313
|
-
const raw =
|
|
2445
|
+
const raw = readFileSync4(absolutePath, "utf-8");
|
|
2314
2446
|
const parsed = JSON.parse(raw);
|
|
2315
2447
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2316
2448
|
return {};
|
|
@@ -2321,13 +2453,13 @@ function readSettingsAtPath(absolutePath) {
|
|
|
2321
2453
|
}
|
|
2322
2454
|
}
|
|
2323
2455
|
function readSettingsLocal(claudeDir) {
|
|
2324
|
-
return readSettingsAtPath(
|
|
2456
|
+
return readSettingsAtPath(resolve4(claudeDir, "settings.local.json"));
|
|
2325
2457
|
}
|
|
2326
2458
|
function writeSettingsLocalAtomic(claudeDir, settings) {
|
|
2327
|
-
const targetPath =
|
|
2459
|
+
const targetPath = resolve4(claudeDir, "settings.local.json");
|
|
2328
2460
|
const dir = dirname3(targetPath);
|
|
2329
|
-
if (!
|
|
2330
|
-
|
|
2461
|
+
if (!existsSync5(dir)) {
|
|
2462
|
+
mkdirSync3(dir, { recursive: true });
|
|
2331
2463
|
}
|
|
2332
2464
|
const content = JSON.stringify(settings, null, 2) + "\n";
|
|
2333
2465
|
atomicWriteFile(targetPath, content);
|
|
@@ -2340,7 +2472,7 @@ var init_settings_local = __esm({
|
|
|
2340
2472
|
|
|
2341
2473
|
// src/permissions.ts
|
|
2342
2474
|
import { createHash } from "crypto";
|
|
2343
|
-
import { homedir as
|
|
2475
|
+
import { homedir as homedir3 } from "os";
|
|
2344
2476
|
import { join as join2 } from "path";
|
|
2345
2477
|
function canonicalJson(value) {
|
|
2346
2478
|
if (value === null || typeof value !== "object") {
|
|
@@ -2380,7 +2512,7 @@ function detectInvalidDefaultMode(settings) {
|
|
|
2380
2512
|
return { invalid: false };
|
|
2381
2513
|
}
|
|
2382
2514
|
function readGlobalSettings() {
|
|
2383
|
-
return readSettingsAtPath(join2(
|
|
2515
|
+
return readSettingsAtPath(join2(homedir3(), ".claude", "settings.json"));
|
|
2384
2516
|
}
|
|
2385
2517
|
function mergedPermissionState(global2, local, canonical) {
|
|
2386
2518
|
const globalPerm = global2.permissions ?? {};
|
|
@@ -2564,27 +2696,27 @@ __export(install_commands_exports, {
|
|
|
2564
2696
|
syncDirectory: () => syncDirectory
|
|
2565
2697
|
});
|
|
2566
2698
|
import {
|
|
2567
|
-
existsSync as
|
|
2568
|
-
readFileSync as
|
|
2569
|
-
mkdirSync as
|
|
2699
|
+
existsSync as existsSync6,
|
|
2700
|
+
readFileSync as readFileSync5,
|
|
2701
|
+
mkdirSync as mkdirSync4,
|
|
2570
2702
|
readdirSync as readdirSync2,
|
|
2571
2703
|
statSync
|
|
2572
2704
|
} from "fs";
|
|
2573
|
-
import { resolve as
|
|
2705
|
+
import { resolve as resolve5, dirname as dirname4, join as join3 } from "path";
|
|
2574
2706
|
import { fileURLToPath } from "url";
|
|
2575
2707
|
import { createHash as createHash2 } from "crypto";
|
|
2576
2708
|
function resolveAssetDir(assetName) {
|
|
2577
2709
|
const cwd = process.cwd();
|
|
2578
|
-
const nodeModulesPath =
|
|
2579
|
-
if (
|
|
2710
|
+
const nodeModulesPath = resolve5(cwd, "node_modules/@massu/core", assetName);
|
|
2711
|
+
if (existsSync6(nodeModulesPath)) {
|
|
2580
2712
|
return nodeModulesPath;
|
|
2581
2713
|
}
|
|
2582
|
-
const distRelPath =
|
|
2583
|
-
if (
|
|
2714
|
+
const distRelPath = resolve5(__dirname, "..", assetName);
|
|
2715
|
+
if (existsSync6(distRelPath)) {
|
|
2584
2716
|
return distRelPath;
|
|
2585
2717
|
}
|
|
2586
|
-
const srcRelPath =
|
|
2587
|
-
if (
|
|
2718
|
+
const srcRelPath = resolve5(__dirname, "../..", assetName);
|
|
2719
|
+
if (existsSync6(srcRelPath)) {
|
|
2588
2720
|
return srcRelPath;
|
|
2589
2721
|
}
|
|
2590
2722
|
return null;
|
|
@@ -2596,12 +2728,12 @@ function hashContent(content) {
|
|
|
2596
2728
|
return createHash2("sha256").update(content, "utf-8").digest("hex");
|
|
2597
2729
|
}
|
|
2598
2730
|
function loadManifest(claudeDir) {
|
|
2599
|
-
const path =
|
|
2600
|
-
if (!
|
|
2731
|
+
const path = resolve5(claudeDir, MANIFEST_RELPATH);
|
|
2732
|
+
if (!existsSync6(path)) {
|
|
2601
2733
|
return emptyManifest();
|
|
2602
2734
|
}
|
|
2603
2735
|
try {
|
|
2604
|
-
const raw =
|
|
2736
|
+
const raw = readFileSync5(path, "utf-8");
|
|
2605
2737
|
const parsed = JSON.parse(raw);
|
|
2606
2738
|
if (!parsed || typeof parsed !== "object" || !parsed.entries) {
|
|
2607
2739
|
return emptyManifest();
|
|
@@ -2612,11 +2744,11 @@ function loadManifest(claudeDir) {
|
|
|
2612
2744
|
}
|
|
2613
2745
|
}
|
|
2614
2746
|
function saveManifest(claudeDir, manifest) {
|
|
2615
|
-
const dir =
|
|
2616
|
-
if (!
|
|
2617
|
-
|
|
2747
|
+
const dir = resolve5(claudeDir, ".massu");
|
|
2748
|
+
if (!existsSync6(dir)) {
|
|
2749
|
+
mkdirSync4(dir, { recursive: true });
|
|
2618
2750
|
}
|
|
2619
|
-
const finalPath =
|
|
2751
|
+
const finalPath = resolve5(dir, "install-manifest.json");
|
|
2620
2752
|
manifest.generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2621
2753
|
atomicWriteFile(finalPath, JSON.stringify(manifest, null, 2));
|
|
2622
2754
|
}
|
|
@@ -2678,18 +2810,18 @@ function pickVariant(baseName, sourceDir, framework) {
|
|
|
2678
2810
|
}
|
|
2679
2811
|
for (const cand of candidates) {
|
|
2680
2812
|
if (cand.subFramework) {
|
|
2681
|
-
const subPath =
|
|
2682
|
-
if (
|
|
2813
|
+
const subPath = resolve5(sourceDir, `${baseName}.${cand.lang}-${cand.subFramework}.md`);
|
|
2814
|
+
if (existsSync6(subPath)) {
|
|
2683
2815
|
return { kind: "hit", suffix: `.${cand.lang}-${cand.subFramework}` };
|
|
2684
2816
|
}
|
|
2685
2817
|
}
|
|
2686
|
-
const langPath =
|
|
2687
|
-
if (
|
|
2818
|
+
const langPath = resolve5(sourceDir, `${baseName}.${cand.lang}.md`);
|
|
2819
|
+
if (existsSync6(langPath)) {
|
|
2688
2820
|
return { kind: "hit", suffix: `.${cand.lang}` };
|
|
2689
2821
|
}
|
|
2690
2822
|
}
|
|
2691
|
-
const defaultPath =
|
|
2692
|
-
if (
|
|
2823
|
+
const defaultPath = resolve5(sourceDir, `${baseName}.md`);
|
|
2824
|
+
if (existsSync6(defaultPath)) {
|
|
2693
2825
|
return { kind: "hit", suffix: "" };
|
|
2694
2826
|
}
|
|
2695
2827
|
if (framework.type === "multi" && !framework.primary) {
|
|
@@ -2705,15 +2837,15 @@ function isVariantFilename(entry) {
|
|
|
2705
2837
|
}
|
|
2706
2838
|
function syncDirectory(sourceDir, targetDir, framework, manifest, manifestKeyPrefix, topLevel = true, templateVars = {}) {
|
|
2707
2839
|
const stats = { installed: 0, updated: 0, skipped: 0, kept: 0 };
|
|
2708
|
-
if (!
|
|
2709
|
-
|
|
2840
|
+
if (!existsSync6(targetDir)) {
|
|
2841
|
+
mkdirSync4(targetDir, { recursive: true });
|
|
2710
2842
|
}
|
|
2711
2843
|
const entries = readdirSync2(sourceDir);
|
|
2712
2844
|
for (const entry of entries) {
|
|
2713
|
-
const sourcePath =
|
|
2845
|
+
const sourcePath = resolve5(sourceDir, entry);
|
|
2714
2846
|
const entryStat = statSync(sourcePath);
|
|
2715
2847
|
if (entryStat.isDirectory()) {
|
|
2716
|
-
const subTargetDir =
|
|
2848
|
+
const subTargetDir = resolve5(targetDir, entry);
|
|
2717
2849
|
const subPrefix = manifestKeyPrefix === "" ? entry : `${manifestKeyPrefix}/${entry}`;
|
|
2718
2850
|
const subStats = syncDirectory(
|
|
2719
2851
|
sourcePath,
|
|
@@ -2742,13 +2874,13 @@ function syncDirectory(sourceDir, targetDir, framework, manifest, manifestKeyPre
|
|
|
2742
2874
|
const suffix = choice.kind === "hit" ? choice.suffix : "";
|
|
2743
2875
|
sourceFilename = suffix === "" ? `${baseName}.md` : `${baseName}${suffix}.md`;
|
|
2744
2876
|
}
|
|
2745
|
-
const resolvedSourcePath =
|
|
2746
|
-
if (!
|
|
2877
|
+
const resolvedSourcePath = resolve5(sourceDir, sourceFilename);
|
|
2878
|
+
if (!existsSync6(resolvedSourcePath)) {
|
|
2747
2879
|
continue;
|
|
2748
2880
|
}
|
|
2749
2881
|
const targetFilename = topLevel ? `${baseName}.md` : entry;
|
|
2750
|
-
const targetPath =
|
|
2751
|
-
const rawContent =
|
|
2882
|
+
const targetPath = resolve5(targetDir, targetFilename);
|
|
2883
|
+
const rawContent = readFileSync5(resolvedSourcePath, "utf-8");
|
|
2752
2884
|
let sourceContent;
|
|
2753
2885
|
try {
|
|
2754
2886
|
sourceContent = renderTemplate(rawContent, templateVars);
|
|
@@ -2766,8 +2898,8 @@ function syncDirectory(sourceDir, targetDir, framework, manifest, manifestKeyPre
|
|
|
2766
2898
|
const sourceHash = hashContent(sourceContent);
|
|
2767
2899
|
const manifestKey = manifestKeyPrefix === "" ? targetFilename : `${manifestKeyPrefix}/${targetFilename}`;
|
|
2768
2900
|
const lastInstalledHash = manifest.entries[manifestKey];
|
|
2769
|
-
if (
|
|
2770
|
-
const existingContent =
|
|
2901
|
+
if (existsSync6(targetPath)) {
|
|
2902
|
+
const existingContent = readFileSync5(targetPath, "utf-8");
|
|
2771
2903
|
const existingHash = hashContent(existingContent);
|
|
2772
2904
|
if (existingHash === sourceHash) {
|
|
2773
2905
|
manifest.entries[manifestKey] = sourceHash;
|
|
@@ -2826,10 +2958,10 @@ function buildTemplateVars() {
|
|
|
2826
2958
|
}
|
|
2827
2959
|
function installCommands(projectRoot, opts = {}) {
|
|
2828
2960
|
const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
|
|
2829
|
-
const claudeDir =
|
|
2830
|
-
const targetDir =
|
|
2831
|
-
if (!
|
|
2832
|
-
|
|
2961
|
+
const claudeDir = resolve5(projectRoot, claudeDirName);
|
|
2962
|
+
const targetDir = resolve5(claudeDir, "commands");
|
|
2963
|
+
if (!existsSync6(targetDir)) {
|
|
2964
|
+
mkdirSync4(targetDir, { recursive: true });
|
|
2833
2965
|
}
|
|
2834
2966
|
const sourceDir = resolveAssetDir("commands");
|
|
2835
2967
|
if (!sourceDir) {
|
|
@@ -2858,7 +2990,7 @@ function installCommands(projectRoot, opts = {}) {
|
|
|
2858
2990
|
}
|
|
2859
2991
|
function installAll(projectRoot, opts = {}) {
|
|
2860
2992
|
const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
|
|
2861
|
-
const claudeDir =
|
|
2993
|
+
const claudeDir = resolve5(projectRoot, claudeDirName);
|
|
2862
2994
|
const assets = {};
|
|
2863
2995
|
let totalInstalled = 0;
|
|
2864
2996
|
let totalUpdated = 0;
|
|
@@ -2871,7 +3003,7 @@ function installAll(projectRoot, opts = {}) {
|
|
|
2871
3003
|
for (const assetType of ASSET_TYPES) {
|
|
2872
3004
|
const sourceDir = resolveAssetDir(assetType.name);
|
|
2873
3005
|
if (!sourceDir) continue;
|
|
2874
|
-
const targetDir =
|
|
3006
|
+
const targetDir = resolve5(claudeDir, assetType.targetSubdir);
|
|
2875
3007
|
const stats = syncDirectory(
|
|
2876
3008
|
sourceDir,
|
|
2877
3009
|
targetDir,
|
|
@@ -3175,17 +3307,17 @@ var init_manifest_registry = __esm({
|
|
|
3175
3307
|
});
|
|
3176
3308
|
|
|
3177
3309
|
// src/detect/package-detector.ts
|
|
3178
|
-
import { readFileSync as
|
|
3310
|
+
import { readFileSync as readFileSync6, existsSync as existsSync7, statSync as statSync2, lstatSync, readdirSync as readdirSync3 } from "fs";
|
|
3179
3311
|
import { join as join4, relative as relative2 } from "path";
|
|
3180
3312
|
import { parse as parseToml } from "smol-toml";
|
|
3181
3313
|
function safeRead(path) {
|
|
3182
3314
|
try {
|
|
3183
|
-
if (!
|
|
3315
|
+
if (!existsSync7(path)) return null;
|
|
3184
3316
|
const ls = lstatSync(path);
|
|
3185
3317
|
if (ls.isSymbolicLink()) return null;
|
|
3186
3318
|
const st = statSync2(path);
|
|
3187
3319
|
if (!st.isFile()) return null;
|
|
3188
|
-
return
|
|
3320
|
+
return readFileSync6(path, "utf-8");
|
|
3189
3321
|
} catch {
|
|
3190
3322
|
return null;
|
|
3191
3323
|
}
|
|
@@ -3216,7 +3348,7 @@ function parsePackageJson(path, directory, root, warnings) {
|
|
|
3216
3348
|
const peer = Object.keys(
|
|
3217
3349
|
pkg.peerDependencies ?? {}
|
|
3218
3350
|
);
|
|
3219
|
-
const hasTs = deps.includes("typescript") || devDeps.includes("typescript") ||
|
|
3351
|
+
const hasTs = deps.includes("typescript") || devDeps.includes("typescript") || existsSync7(join4(directory, "tsconfig.json"));
|
|
3220
3352
|
const language = hasTs ? "typescript" : "javascript";
|
|
3221
3353
|
const scripts = Object.keys(
|
|
3222
3354
|
pkg.scripts ?? {}
|
|
@@ -3643,7 +3775,7 @@ function detectManifestsInDir(dir, root, warnings) {
|
|
|
3643
3775
|
for (const entry of getManifestRegistry2()) {
|
|
3644
3776
|
if (!entry.pattern.startsWith("*")) {
|
|
3645
3777
|
const path = join4(dir, entry.pattern);
|
|
3646
|
-
if (!
|
|
3778
|
+
if (!existsSync7(path)) continue;
|
|
3647
3779
|
const m3 = entry.parse(path, dir, root, warnings);
|
|
3648
3780
|
if (m3 !== null) out.push(m3);
|
|
3649
3781
|
} else {
|
|
@@ -3657,7 +3789,7 @@ function detectManifestsInDir(dir, root, warnings) {
|
|
|
3657
3789
|
for (const fname of dirEntries) {
|
|
3658
3790
|
if (!matchManifestPattern2(fname, entry.pattern)) continue;
|
|
3659
3791
|
const path = join4(dir, fname);
|
|
3660
|
-
if (!
|
|
3792
|
+
if (!existsSync7(path)) continue;
|
|
3661
3793
|
const m3 = entry.parse(path, dir, root, warnings);
|
|
3662
3794
|
if (m3 !== null) out.push(m3);
|
|
3663
3795
|
}
|
|
@@ -3678,7 +3810,7 @@ function detectPackageManifests(projectRoot) {
|
|
|
3678
3810
|
manifests.push(...detectManifestsInDir(projectRoot, projectRoot, warnings));
|
|
3679
3811
|
for (const ws of WORKSPACE_DIRS) {
|
|
3680
3812
|
const wsRoot = join4(projectRoot, ws);
|
|
3681
|
-
if (!
|
|
3813
|
+
if (!existsSync7(wsRoot)) continue;
|
|
3682
3814
|
for (const sub of listSubdirs(wsRoot)) {
|
|
3683
3815
|
manifests.push(...detectManifestsInDir(sub, projectRoot, warnings));
|
|
3684
3816
|
for (const sub2 of listSubdirs(sub)) {
|
|
@@ -8449,41 +8581,41 @@ var require_queue = __commonJS({
|
|
|
8449
8581
|
queue.drained = drained;
|
|
8450
8582
|
return queue;
|
|
8451
8583
|
function push(value) {
|
|
8452
|
-
var p19 = new Promise(function(
|
|
8584
|
+
var p19 = new Promise(function(resolve42, reject) {
|
|
8453
8585
|
pushCb(value, function(err, result) {
|
|
8454
8586
|
if (err) {
|
|
8455
8587
|
reject(err);
|
|
8456
8588
|
return;
|
|
8457
8589
|
}
|
|
8458
|
-
|
|
8590
|
+
resolve42(result);
|
|
8459
8591
|
});
|
|
8460
8592
|
});
|
|
8461
8593
|
p19.catch(noop);
|
|
8462
8594
|
return p19;
|
|
8463
8595
|
}
|
|
8464
8596
|
function unshift(value) {
|
|
8465
|
-
var p19 = new Promise(function(
|
|
8597
|
+
var p19 = new Promise(function(resolve42, reject) {
|
|
8466
8598
|
unshiftCb(value, function(err, result) {
|
|
8467
8599
|
if (err) {
|
|
8468
8600
|
reject(err);
|
|
8469
8601
|
return;
|
|
8470
8602
|
}
|
|
8471
|
-
|
|
8603
|
+
resolve42(result);
|
|
8472
8604
|
});
|
|
8473
8605
|
});
|
|
8474
8606
|
p19.catch(noop);
|
|
8475
8607
|
return p19;
|
|
8476
8608
|
}
|
|
8477
8609
|
function drained() {
|
|
8478
|
-
var p19 = new Promise(function(
|
|
8610
|
+
var p19 = new Promise(function(resolve42) {
|
|
8479
8611
|
process.nextTick(function() {
|
|
8480
8612
|
if (queue.idle()) {
|
|
8481
|
-
|
|
8613
|
+
resolve42();
|
|
8482
8614
|
} else {
|
|
8483
8615
|
var previousDrain = queue.drain;
|
|
8484
8616
|
queue.drain = function() {
|
|
8485
8617
|
if (typeof previousDrain === "function") previousDrain();
|
|
8486
|
-
|
|
8618
|
+
resolve42();
|
|
8487
8619
|
queue.drain = previousDrain;
|
|
8488
8620
|
};
|
|
8489
8621
|
}
|
|
@@ -8969,9 +9101,9 @@ var require_stream3 = __commonJS({
|
|
|
8969
9101
|
});
|
|
8970
9102
|
}
|
|
8971
9103
|
_getStat(filepath) {
|
|
8972
|
-
return new Promise((
|
|
9104
|
+
return new Promise((resolve42, reject) => {
|
|
8973
9105
|
this._stat(filepath, this._fsStatSettings, (error, stats) => {
|
|
8974
|
-
return error === null ?
|
|
9106
|
+
return error === null ? resolve42(stats) : reject(error);
|
|
8975
9107
|
});
|
|
8976
9108
|
});
|
|
8977
9109
|
}
|
|
@@ -8995,10 +9127,10 @@ var require_async5 = __commonJS({
|
|
|
8995
9127
|
this._readerStream = new stream_1.default(this._settings);
|
|
8996
9128
|
}
|
|
8997
9129
|
dynamic(root, options) {
|
|
8998
|
-
return new Promise((
|
|
9130
|
+
return new Promise((resolve42, reject) => {
|
|
8999
9131
|
this._walkAsync(root, options, (error, entries) => {
|
|
9000
9132
|
if (error === null) {
|
|
9001
|
-
|
|
9133
|
+
resolve42(entries);
|
|
9002
9134
|
} else {
|
|
9003
9135
|
reject(error);
|
|
9004
9136
|
}
|
|
@@ -9008,10 +9140,10 @@ var require_async5 = __commonJS({
|
|
|
9008
9140
|
async static(patterns, options) {
|
|
9009
9141
|
const entries = [];
|
|
9010
9142
|
const stream = this._readerStream.static(patterns, options);
|
|
9011
|
-
return new Promise((
|
|
9143
|
+
return new Promise((resolve42, reject) => {
|
|
9012
9144
|
stream.once("error", reject);
|
|
9013
9145
|
stream.on("data", (entry) => entries.push(entry));
|
|
9014
|
-
stream.once("end", () =>
|
|
9146
|
+
stream.once("end", () => resolve42(entries));
|
|
9015
9147
|
});
|
|
9016
9148
|
}
|
|
9017
9149
|
};
|
|
@@ -9670,7 +9802,7 @@ var require_out4 = __commonJS({
|
|
|
9670
9802
|
|
|
9671
9803
|
// src/detect/source-dir-detector.ts
|
|
9672
9804
|
import { realpathSync } from "fs";
|
|
9673
|
-
import { resolve as
|
|
9805
|
+
import { resolve as resolve6 } from "path";
|
|
9674
9806
|
function extsFor(language) {
|
|
9675
9807
|
return EXTENSIONS[language] ?? [];
|
|
9676
9808
|
}
|
|
@@ -9696,7 +9828,7 @@ function topSegment(rel) {
|
|
|
9696
9828
|
function isInsideRoot(root, candidate) {
|
|
9697
9829
|
try {
|
|
9698
9830
|
const realRoot = realpathSync(root);
|
|
9699
|
-
const realCand = realpathSync(
|
|
9831
|
+
const realCand = realpathSync(resolve6(root, candidate));
|
|
9700
9832
|
return realCand === realRoot || realCand.startsWith(realRoot + "/");
|
|
9701
9833
|
} catch {
|
|
9702
9834
|
return false;
|
|
@@ -9864,25 +9996,25 @@ var init_source_dir_detector = __esm({
|
|
|
9864
9996
|
});
|
|
9865
9997
|
|
|
9866
9998
|
// src/detect/monorepo-detector.ts
|
|
9867
|
-
import { readFileSync as
|
|
9999
|
+
import { readFileSync as readFileSync7, existsSync as existsSync8, statSync as statSync3, lstatSync as lstatSync2, readdirSync as readdirSync4 } from "fs";
|
|
9868
10000
|
import { join as join5, relative as relative3 } from "path";
|
|
9869
10001
|
import { parse as parseYaml3 } from "yaml";
|
|
9870
10002
|
import { parse as parseToml2 } from "smol-toml";
|
|
9871
10003
|
function safeReadText(path) {
|
|
9872
10004
|
try {
|
|
9873
|
-
if (!
|
|
10005
|
+
if (!existsSync8(path)) return null;
|
|
9874
10006
|
const ls = lstatSync2(path);
|
|
9875
10007
|
if (ls.isSymbolicLink()) return null;
|
|
9876
10008
|
const st = statSync3(path);
|
|
9877
10009
|
if (!st.isFile()) return null;
|
|
9878
|
-
return
|
|
10010
|
+
return readFileSync7(path, "utf-8");
|
|
9879
10011
|
} catch {
|
|
9880
10012
|
return null;
|
|
9881
10013
|
}
|
|
9882
10014
|
}
|
|
9883
10015
|
function firstManifestIn(dir) {
|
|
9884
10016
|
for (const m3 of MANIFEST_PRIORITY) {
|
|
9885
|
-
if (
|
|
10017
|
+
if (existsSync8(join5(dir, m3))) return m3;
|
|
9886
10018
|
}
|
|
9887
10019
|
return null;
|
|
9888
10020
|
}
|
|
@@ -9947,7 +10079,7 @@ function genericWorkspaces(root) {
|
|
|
9947
10079
|
const out = [];
|
|
9948
10080
|
for (const parent of CONVENTIONAL_WORKSPACE_PARENTS) {
|
|
9949
10081
|
const p19 = join5(root, parent);
|
|
9950
|
-
if (!
|
|
10082
|
+
if (!existsSync8(p19)) continue;
|
|
9951
10083
|
for (const sub of listSubdirs2(p19)) {
|
|
9952
10084
|
const pkg = pkgFromDir(root, sub);
|
|
9953
10085
|
if (pkg) out.push(pkg);
|
|
@@ -9988,7 +10120,7 @@ function expandWorkspaceGlobs(root, globs) {
|
|
|
9988
10120
|
const parts = pattern.split("/");
|
|
9989
10121
|
if (parts.length === 2 && (parts[1] === "*" || parts[1] === "**")) {
|
|
9990
10122
|
const parent = join5(root, parts[0]);
|
|
9991
|
-
if (!
|
|
10123
|
+
if (!existsSync8(parent)) continue;
|
|
9992
10124
|
for (const sub of listSubdirs2(parent)) {
|
|
9993
10125
|
const pkg = pkgFromDir(root, sub);
|
|
9994
10126
|
if (pkg && !seen.has(pkg.path)) {
|
|
@@ -9999,7 +10131,7 @@ function expandWorkspaceGlobs(root, globs) {
|
|
|
9999
10131
|
continue;
|
|
10000
10132
|
}
|
|
10001
10133
|
const direct = join5(root, pattern);
|
|
10002
|
-
if (
|
|
10134
|
+
if (existsSync8(direct)) {
|
|
10003
10135
|
const pkg = pkgFromDir(root, direct);
|
|
10004
10136
|
if (pkg && !seen.has(pkg.path)) {
|
|
10005
10137
|
seen.add(pkg.path);
|
|
@@ -10010,16 +10142,16 @@ function expandWorkspaceGlobs(root, globs) {
|
|
|
10010
10142
|
return out;
|
|
10011
10143
|
}
|
|
10012
10144
|
function hasTurbo(root) {
|
|
10013
|
-
return
|
|
10145
|
+
return existsSync8(join5(root, "turbo.json"));
|
|
10014
10146
|
}
|
|
10015
10147
|
function hasNx(root) {
|
|
10016
|
-
return
|
|
10148
|
+
return existsSync8(join5(root, "nx.json"));
|
|
10017
10149
|
}
|
|
10018
10150
|
function hasLerna(root) {
|
|
10019
|
-
return
|
|
10151
|
+
return existsSync8(join5(root, "lerna.json"));
|
|
10020
10152
|
}
|
|
10021
10153
|
function hasBazel(root) {
|
|
10022
|
-
return
|
|
10154
|
+
return existsSync8(join5(root, "WORKSPACE")) || existsSync8(join5(root, "WORKSPACE.bazel")) || existsSync8(join5(root, "MODULE.bazel"));
|
|
10023
10155
|
}
|
|
10024
10156
|
function detectMonorepo(projectRoot) {
|
|
10025
10157
|
const nested = [];
|
|
@@ -10210,7 +10342,7 @@ var init_vr_command_map = __esm({
|
|
|
10210
10342
|
});
|
|
10211
10343
|
|
|
10212
10344
|
// src/detect/domain-inferrer.ts
|
|
10213
|
-
import { existsSync as
|
|
10345
|
+
import { existsSync as existsSync9, readdirSync as readdirSync5 } from "fs";
|
|
10214
10346
|
import { join as join6 } from "path";
|
|
10215
10347
|
function titleCase(s) {
|
|
10216
10348
|
if (!s) return s;
|
|
@@ -10232,7 +10364,7 @@ function topLevelSrcSubdirs(root, sourceDirs) {
|
|
|
10232
10364
|
const seen = /* @__PURE__ */ new Set();
|
|
10233
10365
|
for (const rel of effective) {
|
|
10234
10366
|
const abs = join6(root, rel);
|
|
10235
|
-
if (!
|
|
10367
|
+
if (!existsSync9(abs)) continue;
|
|
10236
10368
|
try {
|
|
10237
10369
|
for (const e2 of readdirSync5(abs, { withFileTypes: true })) {
|
|
10238
10370
|
if (!e2.isDirectory()) continue;
|
|
@@ -10319,8 +10451,8 @@ var init_domain_inferrer = __esm({
|
|
|
10319
10451
|
});
|
|
10320
10452
|
|
|
10321
10453
|
// src/detect/regex-fallback.ts
|
|
10322
|
-
import { existsSync as
|
|
10323
|
-
import { resolve as
|
|
10454
|
+
import { existsSync as existsSync10, readdirSync as readdirSync6, readFileSync as readFileSync8, statSync as statSync4 } from "fs";
|
|
10455
|
+
import { resolve as resolve7, join as join7, basename as basename2 } from "path";
|
|
10324
10456
|
function introspectPython(detection, projectRoot) {
|
|
10325
10457
|
const sourceDir = resolveSourceDir(detection, "python", projectRoot);
|
|
10326
10458
|
if (!sourceDir) return null;
|
|
@@ -10486,10 +10618,10 @@ function resolveSourceDir(detection, lang, projectRoot) {
|
|
|
10486
10618
|
const list = info?.source_dirs ?? [];
|
|
10487
10619
|
if (list.length > 0) {
|
|
10488
10620
|
const first = list[0];
|
|
10489
|
-
const abs =
|
|
10490
|
-
return
|
|
10621
|
+
const abs = resolve7(projectRoot, first);
|
|
10622
|
+
return existsSync10(abs) ? abs : null;
|
|
10491
10623
|
}
|
|
10492
|
-
return
|
|
10624
|
+
return existsSync10(projectRoot) ? projectRoot : null;
|
|
10493
10625
|
}
|
|
10494
10626
|
function sampleFiles(dir, nameRegex, pathFilter) {
|
|
10495
10627
|
const out = [];
|
|
@@ -10534,7 +10666,7 @@ function readSafe(path) {
|
|
|
10534
10666
|
try {
|
|
10535
10667
|
const st = statSync4(path);
|
|
10536
10668
|
if (st.size > MAX_FILE_BYTES) return null;
|
|
10537
|
-
return
|
|
10669
|
+
return readFileSync8(path, "utf-8");
|
|
10538
10670
|
} catch {
|
|
10539
10671
|
return null;
|
|
10540
10672
|
}
|
|
@@ -10615,7 +10747,7 @@ var init_parse_guard = __esm({
|
|
|
10615
10747
|
|
|
10616
10748
|
// src/detect/adapters/runner.ts
|
|
10617
10749
|
import { basename as basename3, relative as relative4 } from "path";
|
|
10618
|
-
import { existsSync as
|
|
10750
|
+
import { existsSync as existsSync11, readdirSync as readdirSync7, readFileSync as readFileSync9, statSync as statSync5 } from "fs";
|
|
10619
10751
|
import { join as join8 } from "path";
|
|
10620
10752
|
async function runAdapters(adapters, rootDir, signals, options = {}) {
|
|
10621
10753
|
const out = {
|
|
@@ -10750,9 +10882,9 @@ function tryReadGradleBuild(rootDir, presentFiles) {
|
|
|
10750
10882
|
return void 0;
|
|
10751
10883
|
}
|
|
10752
10884
|
function tryReadString(path) {
|
|
10753
|
-
if (!
|
|
10885
|
+
if (!existsSync11(path)) return void 0;
|
|
10754
10886
|
try {
|
|
10755
|
-
return
|
|
10887
|
+
return readFileSync9(path, "utf-8");
|
|
10756
10888
|
} catch {
|
|
10757
10889
|
return void 0;
|
|
10758
10890
|
}
|
|
@@ -10869,21 +11001,21 @@ ${querySource}`
|
|
|
10869
11001
|
// src/detect/adapters/tree-sitter-loader.ts
|
|
10870
11002
|
import { createHash as createHash3 } from "crypto";
|
|
10871
11003
|
import {
|
|
10872
|
-
mkdirSync as
|
|
11004
|
+
mkdirSync as mkdirSync5,
|
|
10873
11005
|
readdirSync as readdirSync8,
|
|
10874
|
-
readFileSync as
|
|
10875
|
-
writeFileSync,
|
|
11006
|
+
readFileSync as readFileSync10,
|
|
11007
|
+
writeFileSync as writeFileSync2,
|
|
10876
11008
|
renameSync as renameSync2,
|
|
10877
11009
|
unlinkSync,
|
|
10878
11010
|
lstatSync as lstatSync3,
|
|
10879
|
-
chmodSync,
|
|
11011
|
+
chmodSync as chmodSync2,
|
|
10880
11012
|
utimesSync
|
|
10881
11013
|
} from "fs";
|
|
10882
|
-
import { homedir as
|
|
11014
|
+
import { homedir as homedir4 } from "os";
|
|
10883
11015
|
import { dirname as dirname5, join as join9 } from "path";
|
|
10884
11016
|
import { Language, Parser } from "web-tree-sitter";
|
|
10885
11017
|
function getCacheDir() {
|
|
10886
|
-
return process.env.MASSU_WASM_CACHE_DIR ?? join9(
|
|
11018
|
+
return process.env.MASSU_WASM_CACHE_DIR ?? join9(homedir4(), ".massu", "wasm-cache");
|
|
10887
11019
|
}
|
|
10888
11020
|
function getCachedPath(language, sha) {
|
|
10889
11021
|
return join9(getCacheDir(), `${language}-${sha}.wasm`);
|
|
@@ -10971,7 +11103,7 @@ async function loadGrammar(language, options = {}) {
|
|
|
10971
11103
|
}
|
|
10972
11104
|
let bytes;
|
|
10973
11105
|
try {
|
|
10974
|
-
bytes =
|
|
11106
|
+
bytes = readFileSync10(cachePath);
|
|
10975
11107
|
} catch (e2) {
|
|
10976
11108
|
bytes = new Uint8Array(0);
|
|
10977
11109
|
}
|
|
@@ -11011,21 +11143,21 @@ async function loadGrammar(language, options = {}) {
|
|
|
11011
11143
|
throw new GrammarSHAMismatchError(language, manifest.sha256, downloadedSha);
|
|
11012
11144
|
}
|
|
11013
11145
|
try {
|
|
11014
|
-
|
|
11146
|
+
mkdirSync5(dirname5(cachePath), { recursive: true, mode: 448 });
|
|
11015
11147
|
try {
|
|
11016
|
-
|
|
11148
|
+
chmodSync2(dirname5(cachePath), 448);
|
|
11017
11149
|
} catch {
|
|
11018
11150
|
}
|
|
11019
11151
|
const tmpPath = `${cachePath}.tmp.${process.pid}`;
|
|
11020
|
-
|
|
11152
|
+
writeFileSync2(tmpPath, body, { mode: 384 });
|
|
11021
11153
|
try {
|
|
11022
|
-
|
|
11154
|
+
chmodSync2(tmpPath, 384);
|
|
11023
11155
|
} catch {
|
|
11024
11156
|
}
|
|
11025
11157
|
try {
|
|
11026
11158
|
renameSync2(tmpPath, cachePath);
|
|
11027
11159
|
try {
|
|
11028
|
-
|
|
11160
|
+
chmodSync2(cachePath, 384);
|
|
11029
11161
|
} catch {
|
|
11030
11162
|
}
|
|
11031
11163
|
} catch (e2) {
|
|
@@ -11889,7 +12021,7 @@ __export(file_sampler_exports, {
|
|
|
11889
12021
|
SAMPLE_TEST_FILE_PATTERNS: () => SAMPLE_TEST_FILE_PATTERNS,
|
|
11890
12022
|
sampleFilesForAdapter: () => sampleFilesForAdapter
|
|
11891
12023
|
});
|
|
11892
|
-
import { readdirSync as readdirSync9, readFileSync as
|
|
12024
|
+
import { readdirSync as readdirSync9, readFileSync as readFileSync11, lstatSync as lstatSync4 } from "node:fs";
|
|
11893
12025
|
import { join as join10, extname } from "node:path";
|
|
11894
12026
|
function sampleFilesForAdapter(adapter, projectRoot, detection, options = {}) {
|
|
11895
12027
|
const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
@@ -11950,7 +12082,7 @@ function walkDir(dir, exts, testPatterns, lang, maxDepth, curDepth, out, seen, m
|
|
|
11950
12082
|
seen.add(fullPath);
|
|
11951
12083
|
let content;
|
|
11952
12084
|
try {
|
|
11953
|
-
content =
|
|
12085
|
+
content = readFileSync11(fullPath, "utf-8");
|
|
11954
12086
|
} catch {
|
|
11955
12087
|
continue;
|
|
11956
12088
|
}
|
|
@@ -13374,10 +13506,10 @@ __export(init_exports, {
|
|
|
13374
13506
|
validateWrittenConfig: () => validateWrittenConfig,
|
|
13375
13507
|
writeConfigAtomic: () => writeConfigAtomic
|
|
13376
13508
|
});
|
|
13377
|
-
import { closeSync as closeSync2, existsSync as
|
|
13378
|
-
import { resolve as
|
|
13509
|
+
import { closeSync as closeSync2, existsSync as existsSync12, fsyncSync as fsyncSync2, openSync as openSync2, readFileSync as readFileSync12, writeFileSync as writeFileSync3, writeSync as writeSync2, mkdirSync as mkdirSync6, readdirSync as readdirSync10, renameSync as renameSync3, rmSync as rmSync3, statSync as statSync7, chmodSync as chmodSync3 } from "fs";
|
|
13510
|
+
import { resolve as resolve8, basename as basename4, dirname as dirname6 } from "path";
|
|
13379
13511
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
13380
|
-
import { homedir as
|
|
13512
|
+
import { homedir as homedir5 } from "os";
|
|
13381
13513
|
import { stringify as yamlStringify, parse as yamlParse } from "yaml";
|
|
13382
13514
|
function detectFramework(projectRoot) {
|
|
13383
13515
|
const result = {
|
|
@@ -13386,10 +13518,10 @@ function detectFramework(projectRoot) {
|
|
|
13386
13518
|
orm: "none",
|
|
13387
13519
|
ui: "none"
|
|
13388
13520
|
};
|
|
13389
|
-
const pkgPath =
|
|
13390
|
-
if (!
|
|
13521
|
+
const pkgPath = resolve8(projectRoot, "package.json");
|
|
13522
|
+
if (!existsSync12(pkgPath)) return result;
|
|
13391
13523
|
try {
|
|
13392
|
-
const pkg = JSON.parse(
|
|
13524
|
+
const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
13393
13525
|
const allDeps = {
|
|
13394
13526
|
...pkg.dependencies,
|
|
13395
13527
|
...pkg.devDependencies
|
|
@@ -13423,7 +13555,7 @@ function detectPython(projectRoot) {
|
|
|
13423
13555
|
alembicDir: null
|
|
13424
13556
|
};
|
|
13425
13557
|
const markers = ["pyproject.toml", "setup.py", "requirements.txt", "Pipfile"];
|
|
13426
|
-
const hasMarker = markers.some((m3) =>
|
|
13558
|
+
const hasMarker = markers.some((m3) => existsSync12(resolve8(projectRoot, m3)));
|
|
13427
13559
|
if (!hasMarker) return result;
|
|
13428
13560
|
result.detected = true;
|
|
13429
13561
|
const depFiles = [
|
|
@@ -13433,33 +13565,33 @@ function detectPython(projectRoot) {
|
|
|
13433
13565
|
{ file: "Pipfile" }
|
|
13434
13566
|
];
|
|
13435
13567
|
for (const { file } of depFiles) {
|
|
13436
|
-
const filePath =
|
|
13437
|
-
if (
|
|
13568
|
+
const filePath = resolve8(projectRoot, file);
|
|
13569
|
+
if (existsSync12(filePath)) {
|
|
13438
13570
|
try {
|
|
13439
|
-
const content =
|
|
13571
|
+
const content = readFileSync12(filePath, "utf-8").toLowerCase();
|
|
13440
13572
|
if (content.includes("fastapi")) result.hasFastapi = true;
|
|
13441
13573
|
if (content.includes("sqlalchemy")) result.hasSqlalchemy = true;
|
|
13442
13574
|
} catch {
|
|
13443
13575
|
}
|
|
13444
13576
|
}
|
|
13445
13577
|
}
|
|
13446
|
-
if (
|
|
13578
|
+
if (existsSync12(resolve8(projectRoot, "alembic.ini"))) {
|
|
13447
13579
|
result.hasAlembic = true;
|
|
13448
|
-
if (
|
|
13580
|
+
if (existsSync12(resolve8(projectRoot, "alembic"))) {
|
|
13449
13581
|
result.alembicDir = "alembic";
|
|
13450
13582
|
}
|
|
13451
|
-
} else if (
|
|
13583
|
+
} else if (existsSync12(resolve8(projectRoot, "alembic"))) {
|
|
13452
13584
|
result.hasAlembic = true;
|
|
13453
13585
|
result.alembicDir = "alembic";
|
|
13454
13586
|
}
|
|
13455
13587
|
const candidateRoots = ["app", "src", "backend", "api"];
|
|
13456
13588
|
for (const candidate of candidateRoots) {
|
|
13457
|
-
const candidatePath =
|
|
13458
|
-
if (
|
|
13589
|
+
const candidatePath = resolve8(projectRoot, candidate);
|
|
13590
|
+
if (existsSync12(candidatePath) && existsSync12(resolve8(candidatePath, "__init__.py"))) {
|
|
13459
13591
|
result.root = candidate;
|
|
13460
13592
|
break;
|
|
13461
13593
|
}
|
|
13462
|
-
if (
|
|
13594
|
+
if (existsSync12(candidatePath)) {
|
|
13463
13595
|
try {
|
|
13464
13596
|
const files = readdirSync10(candidatePath);
|
|
13465
13597
|
if (files.some((f2) => f2.endsWith(".py"))) {
|
|
@@ -13479,8 +13611,8 @@ function generateConfig(projectRoot, framework) {
|
|
|
13479
13611
|
console.warn(
|
|
13480
13612
|
"[@massu/core] generateConfig() is deprecated since 1.2.1 \u2014 use buildConfigFromDetection instead. It cannot produce valid configs for monorepos."
|
|
13481
13613
|
);
|
|
13482
|
-
const configPath =
|
|
13483
|
-
if (
|
|
13614
|
+
const configPath = resolve8(projectRoot, "massu.config.yaml");
|
|
13615
|
+
if (existsSync12(configPath)) {
|
|
13484
13616
|
return false;
|
|
13485
13617
|
}
|
|
13486
13618
|
const projectName = basename4(projectRoot);
|
|
@@ -13526,7 +13658,7 @@ function generateConfig(projectRoot, framework) {
|
|
|
13526
13658
|
# Documentation: https://massu.ai/docs/getting-started/configuration
|
|
13527
13659
|
|
|
13528
13660
|
${yamlStringify(config)}`;
|
|
13529
|
-
|
|
13661
|
+
writeFileSync3(configPath, yamlContent, "utf-8");
|
|
13530
13662
|
return true;
|
|
13531
13663
|
}
|
|
13532
13664
|
function monorepoCommonRoot(packages) {
|
|
@@ -13591,10 +13723,10 @@ function buildConfigFromDetection(opts) {
|
|
|
13591
13723
|
pathsSource = monorepoCommonRoot(detection.monorepo.packages);
|
|
13592
13724
|
}
|
|
13593
13725
|
}
|
|
13594
|
-
if (pathsSource === "src" && !
|
|
13726
|
+
if (pathsSource === "src" && !existsSync12(resolve8(projectRoot, "src"))) {
|
|
13595
13727
|
const fallbacks = ["app", "pages", "."];
|
|
13596
13728
|
for (const fallback of fallbacks) {
|
|
13597
|
-
if (fallback === "." ||
|
|
13729
|
+
if (fallback === "." || existsSync12(resolve8(projectRoot, fallback))) {
|
|
13598
13730
|
pathsSource = fallback;
|
|
13599
13731
|
break;
|
|
13600
13732
|
}
|
|
@@ -13678,7 +13810,7 @@ function buildConfigFromDetection(opts) {
|
|
|
13678
13810
|
};
|
|
13679
13811
|
if (pyFw?.framework) pythonBlock.framework = pyFw.framework;
|
|
13680
13812
|
if (pyFw?.orm) pythonBlock.orm = pyFw.orm;
|
|
13681
|
-
if (
|
|
13813
|
+
if (existsSync12(resolve8(projectRoot, "alembic.ini")) || existsSync12(resolve8(projectRoot, "alembic"))) {
|
|
13682
13814
|
pythonBlock.alembic_dir = "alembic";
|
|
13683
13815
|
}
|
|
13684
13816
|
config.python = pythonBlock;
|
|
@@ -13702,11 +13834,11 @@ function applyVariantTemplate(config, templatesDir) {
|
|
|
13702
13834
|
}
|
|
13703
13835
|
}
|
|
13704
13836
|
if (templateId === null) return config;
|
|
13705
|
-
const templatePath =
|
|
13706
|
-
if (!
|
|
13837
|
+
const templatePath = resolve8(templatesDir, templateId, "massu.config.yaml");
|
|
13838
|
+
if (!existsSync12(templatePath)) return config;
|
|
13707
13839
|
let template;
|
|
13708
13840
|
try {
|
|
13709
|
-
template = yamlParse(
|
|
13841
|
+
template = yamlParse(readFileSync12(templatePath, "utf-8"));
|
|
13710
13842
|
} catch {
|
|
13711
13843
|
return config;
|
|
13712
13844
|
}
|
|
@@ -13765,7 +13897,7 @@ ${yamlStringify(config)}`;
|
|
|
13765
13897
|
function writeConfigAtomic(configPath, content) {
|
|
13766
13898
|
const tmpPath = `${configPath}.tmp`;
|
|
13767
13899
|
let existingMode;
|
|
13768
|
-
if (
|
|
13900
|
+
if (existsSync12(configPath)) {
|
|
13769
13901
|
try {
|
|
13770
13902
|
existingMode = statSync7(configPath).mode;
|
|
13771
13903
|
} catch {
|
|
@@ -13773,7 +13905,7 @@ function writeConfigAtomic(configPath, content) {
|
|
|
13773
13905
|
}
|
|
13774
13906
|
}
|
|
13775
13907
|
try {
|
|
13776
|
-
|
|
13908
|
+
mkdirSync6(dirname6(configPath), { recursive: true });
|
|
13777
13909
|
const fd = openSync2(tmpPath, "w", 420);
|
|
13778
13910
|
try {
|
|
13779
13911
|
const buf = Buffer.from(content, "utf-8");
|
|
@@ -13789,15 +13921,15 @@ function writeConfigAtomic(configPath, content) {
|
|
|
13789
13921
|
renameSync3(tmpPath, configPath);
|
|
13790
13922
|
if (existingMode !== void 0) {
|
|
13791
13923
|
try {
|
|
13792
|
-
|
|
13924
|
+
chmodSync3(configPath, existingMode);
|
|
13793
13925
|
} catch {
|
|
13794
13926
|
}
|
|
13795
13927
|
}
|
|
13796
13928
|
return { validated: true };
|
|
13797
13929
|
} catch (err) {
|
|
13798
|
-
if (
|
|
13930
|
+
if (existsSync12(tmpPath)) {
|
|
13799
13931
|
try {
|
|
13800
|
-
|
|
13932
|
+
rmSync3(tmpPath, { force: true });
|
|
13801
13933
|
} catch {
|
|
13802
13934
|
}
|
|
13803
13935
|
}
|
|
@@ -13806,8 +13938,8 @@ function writeConfigAtomic(configPath, content) {
|
|
|
13806
13938
|
}
|
|
13807
13939
|
function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
13808
13940
|
try {
|
|
13809
|
-
if (!
|
|
13810
|
-
const content =
|
|
13941
|
+
if (!existsSync12(configPath)) return "Config file does not exist after write";
|
|
13942
|
+
const content = readFileSync12(configPath, "utf-8");
|
|
13811
13943
|
const parsed = yamlParse(content);
|
|
13812
13944
|
if (parsed === null || typeof parsed !== "object") {
|
|
13813
13945
|
return "Config is not a valid YAML object";
|
|
@@ -13827,8 +13959,8 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
|
13827
13959
|
if (checkPaths) {
|
|
13828
13960
|
const src = cfg.paths.source;
|
|
13829
13961
|
if (src && src !== ".") {
|
|
13830
|
-
const srcAbs =
|
|
13831
|
-
if (!
|
|
13962
|
+
const srcAbs = resolve8(projectRoot, src);
|
|
13963
|
+
if (!existsSync12(srcAbs)) {
|
|
13832
13964
|
return `paths.source '${src}' does not exist on disk`;
|
|
13833
13965
|
}
|
|
13834
13966
|
}
|
|
@@ -13838,8 +13970,8 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
|
13838
13970
|
if (!Array.isArray(rawDirs)) continue;
|
|
13839
13971
|
for (const d2 of rawDirs) {
|
|
13840
13972
|
if (typeof d2 !== "string" || d2 === ".") continue;
|
|
13841
|
-
const abs =
|
|
13842
|
-
if (!
|
|
13973
|
+
const abs = resolve8(projectRoot, d2);
|
|
13974
|
+
if (!existsSync12(abs)) {
|
|
13843
13975
|
return `framework.languages.${lang}.source_dirs '${d2}' does not exist on disk`;
|
|
13844
13976
|
}
|
|
13845
13977
|
}
|
|
@@ -13848,7 +13980,7 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
|
|
|
13848
13980
|
if (Array.isArray(mRoots)) {
|
|
13849
13981
|
for (const r2 of mRoots) {
|
|
13850
13982
|
if (typeof r2 !== "string" || r2 === ".") continue;
|
|
13851
|
-
if (!
|
|
13983
|
+
if (!existsSync12(resolve8(projectRoot, r2))) {
|
|
13852
13984
|
return `paths.monorepo_roots '${r2}' does not exist on disk`;
|
|
13853
13985
|
}
|
|
13854
13986
|
}
|
|
@@ -13880,21 +14012,21 @@ function resolveTemplatesDir() {
|
|
|
13880
14012
|
const cwd = process.cwd();
|
|
13881
14013
|
const candidates = [
|
|
13882
14014
|
// Project-local install: `<project>/node_modules/@massu/core/templates`.
|
|
13883
|
-
|
|
14015
|
+
resolve8(cwd, "node_modules/@massu/core/templates"),
|
|
13884
14016
|
// Bundled cli.js layout: cli.js sits at `<package>/dist/cli.js`, so
|
|
13885
14017
|
// templates live one level up at `<package>/templates`. (Plan 1.5.1
|
|
13886
14018
|
// bug discovery: pre-existing layout assumed `dist/commands/init.js`
|
|
13887
14019
|
// depth which never matched the bundled cli, so resolveTemplatesDir
|
|
13888
14020
|
// returned null in production for both `--template` mode AND the
|
|
13889
14021
|
// applyVariantTemplate path.)
|
|
13890
|
-
|
|
14022
|
+
resolve8(__dirname2, "../templates"),
|
|
13891
14023
|
// Legacy nested layouts retained as fallbacks (in case a future
|
|
13892
14024
|
// build moves cli.js back into a subdirectory).
|
|
13893
|
-
|
|
13894
|
-
|
|
14025
|
+
resolve8(__dirname2, "../../templates"),
|
|
14026
|
+
resolve8(__dirname2, "../../../templates")
|
|
13895
14027
|
];
|
|
13896
14028
|
for (const c2 of candidates) {
|
|
13897
|
-
if (
|
|
14029
|
+
if (existsSync12(c2)) return c2;
|
|
13898
14030
|
}
|
|
13899
14031
|
return null;
|
|
13900
14032
|
}
|
|
@@ -13903,14 +14035,14 @@ function copyTemplateConfig(templateName, targetPath, projectName) {
|
|
|
13903
14035
|
if (!templatesDir) {
|
|
13904
14036
|
return { success: false, error: `Templates directory not found (looked in node_modules and dist/src)` };
|
|
13905
14037
|
}
|
|
13906
|
-
const srcPath =
|
|
13907
|
-
if (!
|
|
14038
|
+
const srcPath = resolve8(templatesDir, templateName, "massu.config.yaml");
|
|
14039
|
+
if (!existsSync12(srcPath)) {
|
|
13908
14040
|
return { success: false, error: `Template '${templateName}' not found at ${srcPath}` };
|
|
13909
14041
|
}
|
|
13910
14042
|
try {
|
|
13911
|
-
let content =
|
|
14043
|
+
let content = readFileSync12(srcPath, "utf-8");
|
|
13912
14044
|
content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectName);
|
|
13913
|
-
|
|
14045
|
+
writeFileSync3(targetPath, content, "utf-8");
|
|
13914
14046
|
return { success: true };
|
|
13915
14047
|
} catch (err) {
|
|
13916
14048
|
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
@@ -13918,13 +14050,13 @@ function copyTemplateConfig(templateName, targetPath, projectName) {
|
|
|
13918
14050
|
}
|
|
13919
14051
|
function getInstallerVersion() {
|
|
13920
14052
|
const candidates = [
|
|
13921
|
-
|
|
13922
|
-
|
|
14053
|
+
resolve8(__dirname2, "../package.json"),
|
|
14054
|
+
resolve8(__dirname2, "../../package.json")
|
|
13923
14055
|
];
|
|
13924
14056
|
for (const candidate of candidates) {
|
|
13925
|
-
if (
|
|
14057
|
+
if (existsSync12(candidate)) {
|
|
13926
14058
|
try {
|
|
13927
|
-
const pkg = JSON.parse(
|
|
14059
|
+
const pkg = JSON.parse(readFileSync12(candidate, "utf-8"));
|
|
13928
14060
|
if (typeof pkg.version === "string" && pkg.version.length > 0 && pkg.name === "@massu/core") {
|
|
13929
14061
|
return pkg.version;
|
|
13930
14062
|
}
|
|
@@ -13937,11 +14069,11 @@ function getInstallerVersion() {
|
|
|
13937
14069
|
);
|
|
13938
14070
|
}
|
|
13939
14071
|
function registerMcpServer(projectRoot) {
|
|
13940
|
-
const mcpPath =
|
|
14072
|
+
const mcpPath = resolve8(projectRoot, ".mcp.json");
|
|
13941
14073
|
let existing = {};
|
|
13942
|
-
if (
|
|
14074
|
+
if (existsSync12(mcpPath)) {
|
|
13943
14075
|
try {
|
|
13944
|
-
existing = JSON.parse(
|
|
14076
|
+
existing = JSON.parse(readFileSync12(mcpPath, "utf-8"));
|
|
13945
14077
|
} catch {
|
|
13946
14078
|
existing = {};
|
|
13947
14079
|
}
|
|
@@ -13957,17 +14089,17 @@ function registerMcpServer(projectRoot) {
|
|
|
13957
14089
|
args: ["-y", `@massu/core@${version}`]
|
|
13958
14090
|
};
|
|
13959
14091
|
existing.mcpServers = servers;
|
|
13960
|
-
|
|
14092
|
+
writeFileSync3(mcpPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
|
|
13961
14093
|
return true;
|
|
13962
14094
|
}
|
|
13963
14095
|
function resolveHooksDir() {
|
|
13964
14096
|
const cwd = process.cwd();
|
|
13965
|
-
const nodeModulesPath =
|
|
13966
|
-
if (
|
|
14097
|
+
const nodeModulesPath = resolve8(cwd, "node_modules/@massu/core/dist/hooks");
|
|
14098
|
+
if (existsSync12(nodeModulesPath)) {
|
|
13967
14099
|
return "node_modules/@massu/core/dist/hooks";
|
|
13968
14100
|
}
|
|
13969
|
-
const localPath =
|
|
13970
|
-
if (
|
|
14101
|
+
const localPath = resolve8(__dirname2, "../dist/hooks");
|
|
14102
|
+
if (existsSync12(localPath)) {
|
|
13971
14103
|
return localPath;
|
|
13972
14104
|
}
|
|
13973
14105
|
return "node_modules/@massu/core/dist/hooks";
|
|
@@ -14113,9 +14245,9 @@ function installHooks(projectRoot) {
|
|
|
14113
14245
|
} catch {
|
|
14114
14246
|
claudeDirName = ".claude";
|
|
14115
14247
|
}
|
|
14116
|
-
const claudeDir =
|
|
14117
|
-
if (!
|
|
14118
|
-
|
|
14248
|
+
const claudeDir = resolve8(projectRoot, claudeDirName);
|
|
14249
|
+
if (!existsSync12(claudeDir)) {
|
|
14250
|
+
mkdirSync6(claudeDir, { recursive: true });
|
|
14119
14251
|
}
|
|
14120
14252
|
const settings = readSettingsLocal(claudeDir);
|
|
14121
14253
|
const hooksConfig = buildHooksConfig(resolveHooksDir());
|
|
@@ -14133,17 +14265,17 @@ function installHooks(projectRoot) {
|
|
|
14133
14265
|
}
|
|
14134
14266
|
function initMemoryDir(projectRoot) {
|
|
14135
14267
|
const encodedRoot = encodeMemoryDirName(projectRoot);
|
|
14136
|
-
const memoryDir =
|
|
14268
|
+
const memoryDir = resolve8(homedir5(), `.claude/projects/${encodedRoot}/memory`);
|
|
14137
14269
|
let migratedFromLegacy = false;
|
|
14138
|
-
const legacyDir =
|
|
14139
|
-
if (
|
|
14270
|
+
const legacyDir = resolve8(homedir5(), `.claude/projects/-${encodedRoot}/memory`);
|
|
14271
|
+
if (existsSync12(legacyDir) && !existsSync12(memoryDir)) {
|
|
14140
14272
|
try {
|
|
14141
|
-
|
|
14273
|
+
mkdirSync6(resolve8(memoryDir, ".."), { recursive: true });
|
|
14142
14274
|
renameSync3(legacyDir, memoryDir);
|
|
14143
14275
|
try {
|
|
14144
|
-
const legacyParent =
|
|
14145
|
-
if (
|
|
14146
|
-
|
|
14276
|
+
const legacyParent = resolve8(legacyDir, "..");
|
|
14277
|
+
if (existsSync12(legacyParent) && readdirSync10(legacyParent).length === 0) {
|
|
14278
|
+
rmSync3(legacyParent, { recursive: false });
|
|
14147
14279
|
}
|
|
14148
14280
|
} catch {
|
|
14149
14281
|
}
|
|
@@ -14152,13 +14284,13 @@ function initMemoryDir(projectRoot) {
|
|
|
14152
14284
|
}
|
|
14153
14285
|
}
|
|
14154
14286
|
let created = false;
|
|
14155
|
-
if (!
|
|
14156
|
-
|
|
14287
|
+
if (!existsSync12(memoryDir)) {
|
|
14288
|
+
mkdirSync6(memoryDir, { recursive: true });
|
|
14157
14289
|
created = true;
|
|
14158
14290
|
}
|
|
14159
|
-
const memoryMdPath =
|
|
14291
|
+
const memoryMdPath = resolve8(memoryDir, "MEMORY.md");
|
|
14160
14292
|
let memoryMdCreated = false;
|
|
14161
|
-
if (!
|
|
14293
|
+
if (!existsSync12(memoryMdPath)) {
|
|
14162
14294
|
const projectName = basename4(projectRoot);
|
|
14163
14295
|
const memoryContent = `# ${projectName} - Massu Memory
|
|
14164
14296
|
|
|
@@ -14174,7 +14306,7 @@ function initMemoryDir(projectRoot) {
|
|
|
14174
14306
|
## File Index
|
|
14175
14307
|
<!-- Significant files and directories -->
|
|
14176
14308
|
`;
|
|
14177
|
-
|
|
14309
|
+
writeFileSync3(memoryMdPath, memoryContent, "utf-8");
|
|
14178
14310
|
memoryMdCreated = true;
|
|
14179
14311
|
}
|
|
14180
14312
|
return { created, memoryMdCreated, migratedFromLegacy };
|
|
@@ -14272,8 +14404,8 @@ async function runInit(argv, overrides) {
|
|
|
14272
14404
|
log("Massu AI - Project Setup");
|
|
14273
14405
|
log("========================");
|
|
14274
14406
|
log("");
|
|
14275
|
-
const configPath =
|
|
14276
|
-
if (
|
|
14407
|
+
const configPath = resolve8(projectRoot, "massu.config.yaml");
|
|
14408
|
+
if (existsSync12(configPath)) {
|
|
14277
14409
|
if (opts.ci && !opts.force) {
|
|
14278
14410
|
errLog(`error: massu.config.yaml already exists at ${configPath}`);
|
|
14279
14411
|
errLog(" rerun with --force to overwrite, or remove the file first");
|
|
@@ -14301,7 +14433,7 @@ async function runInit(argv, overrides) {
|
|
|
14301
14433
|
const validation2 = validateWrittenConfig(configPath, projectRoot, false);
|
|
14302
14434
|
if (validation2 !== null) {
|
|
14303
14435
|
try {
|
|
14304
|
-
|
|
14436
|
+
rmSync3(configPath, { force: true });
|
|
14305
14437
|
} catch {
|
|
14306
14438
|
}
|
|
14307
14439
|
errLog(`error: template config failed validation: ${validation2}`);
|
|
@@ -14379,7 +14511,7 @@ async function runInit(argv, overrides) {
|
|
|
14379
14511
|
const validation = validateWrittenConfig(configPath, projectRoot, !emptyStack);
|
|
14380
14512
|
if (validation !== null) {
|
|
14381
14513
|
try {
|
|
14382
|
-
|
|
14514
|
+
rmSync3(configPath, { force: true });
|
|
14383
14515
|
} catch {
|
|
14384
14516
|
}
|
|
14385
14517
|
errLog(`error: generated config failed validation: ${validation}`);
|
|
@@ -14412,8 +14544,8 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
|
|
|
14412
14544
|
const commandStats = cmdResult.assets.commands;
|
|
14413
14545
|
const stackResolved = !emptyStack && commandStats && (commandStats.installed > 0 || commandStats.updated > 0 || commandStats.kept > 0);
|
|
14414
14546
|
if (!stackResolved) {
|
|
14415
|
-
const placeholderPath =
|
|
14416
|
-
if (!
|
|
14547
|
+
const placeholderPath = resolve8(cmdResult.claudeDir, "commands", "_massu-needs-stack.md");
|
|
14548
|
+
if (!existsSync12(placeholderPath)) {
|
|
14417
14549
|
const placeholderBody = [
|
|
14418
14550
|
"# Massu \u2014 stack not yet detected",
|
|
14419
14551
|
"",
|
|
@@ -14434,8 +14566,8 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
|
|
|
14434
14566
|
"\u2014 Massu"
|
|
14435
14567
|
].join("\n");
|
|
14436
14568
|
try {
|
|
14437
|
-
|
|
14438
|
-
|
|
14569
|
+
mkdirSync6(resolve8(cmdResult.claudeDir, "commands"), { recursive: true });
|
|
14570
|
+
writeFileSync3(placeholderPath, placeholderBody, "utf-8");
|
|
14439
14571
|
log(" Wrote _massu-needs-stack.md placeholder (no stack detected yet)");
|
|
14440
14572
|
} catch {
|
|
14441
14573
|
}
|
|
@@ -14457,8 +14589,8 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
|
|
|
14457
14589
|
(async () => {
|
|
14458
14590
|
try {
|
|
14459
14591
|
const encodedRoot = encodeMemoryDirName(projectRoot);
|
|
14460
|
-
const memoryDir =
|
|
14461
|
-
const memFiles =
|
|
14592
|
+
const memoryDir = resolve8(homedir5(), ".claude", "projects", encodedRoot, "memory");
|
|
14593
|
+
const memFiles = existsSync12(memoryDir) ? readdirSync10(memoryDir).filter((f2) => f2.endsWith(".md") && f2 !== "MEMORY.md") : [];
|
|
14462
14594
|
if (memFiles.length > 0) {
|
|
14463
14595
|
const { getMemoryDb: getMemoryDb2 } = await Promise.resolve().then(() => (init_memory_db(), memory_db_exports));
|
|
14464
14596
|
const db = getMemoryDb2();
|
|
@@ -14648,6 +14780,28 @@ var init_license_response_verifier = __esm({
|
|
|
14648
14780
|
});
|
|
14649
14781
|
|
|
14650
14782
|
// src/license.ts
|
|
14783
|
+
var license_exports = {};
|
|
14784
|
+
__export(license_exports, {
|
|
14785
|
+
PLAN_TO_TIER_MAP: () => PLAN_TO_TIER_MAP,
|
|
14786
|
+
TOOL_TIER_MAP: () => TOOL_TIER_MAP,
|
|
14787
|
+
_resetCachedTier: () => _resetCachedTier,
|
|
14788
|
+
_setCachedTierForTest: () => _setCachedTierForTest,
|
|
14789
|
+
annotateToolDefinitions: () => annotateToolDefinitions,
|
|
14790
|
+
daysUntilExpiry: () => daysUntilExpiry,
|
|
14791
|
+
getCachedOrgId: () => getCachedOrgId,
|
|
14792
|
+
getCachedTierReadOnly: () => getCachedTierReadOnly,
|
|
14793
|
+
getCurrentTier: () => getCurrentTier,
|
|
14794
|
+
getLicenseInfo: () => getLicenseInfo,
|
|
14795
|
+
getLicenseToolDefinitions: () => getLicenseToolDefinitions,
|
|
14796
|
+
getToolTier: () => getToolTier,
|
|
14797
|
+
handleLicenseToolCall: () => handleLicenseToolCall,
|
|
14798
|
+
isCloudFeatureAvailable: () => isCloudFeatureAvailable,
|
|
14799
|
+
isLicenseTool: () => isLicenseTool,
|
|
14800
|
+
isToolAllowed: () => isToolAllowed,
|
|
14801
|
+
tierLevel: () => tierLevel,
|
|
14802
|
+
updateLicenseCache: () => updateLicenseCache,
|
|
14803
|
+
validateLicense: () => validateLicense
|
|
14804
|
+
});
|
|
14651
14805
|
import { createHash as createHash5 } from "crypto";
|
|
14652
14806
|
function warnLicenseSigOnce(reason) {
|
|
14653
14807
|
if (_warnedLicenseSig) return;
|
|
@@ -14713,11 +14867,12 @@ async function validateLicense(apiKey) {
|
|
|
14713
14867
|
const lastValidated = new Date(cached.last_validated);
|
|
14714
14868
|
const hourAgo = new Date(Date.now() - 60 * 60 * 1e3);
|
|
14715
14869
|
if (lastValidated > hourAgo) {
|
|
14716
|
-
return trusted;
|
|
14870
|
+
return { ...trusted, outcome: "cache_fresh" };
|
|
14717
14871
|
}
|
|
14718
14872
|
}
|
|
14719
14873
|
const config = getConfig();
|
|
14720
14874
|
const endpoint = config.cloud?.endpoint;
|
|
14875
|
+
let fallbackOutcome = "no_endpoint";
|
|
14721
14876
|
if (endpoint && /^https?:\/\/.+/.test(endpoint)) {
|
|
14722
14877
|
try {
|
|
14723
14878
|
const response = await fetch(`${endpoint}/validate-key`, {
|
|
@@ -14749,21 +14904,23 @@ async function validateLicense(apiKey) {
|
|
|
14749
14904
|
features,
|
|
14750
14905
|
sigResult.kind === "valid" ? data : null
|
|
14751
14906
|
);
|
|
14752
|
-
return { tier, validUntil, features };
|
|
14907
|
+
return { tier, validUntil, features, outcome: "validated" };
|
|
14753
14908
|
}
|
|
14754
|
-
return { tier: "free", validUntil: "", features: [] };
|
|
14909
|
+
return { tier: "free", validUntil: "", features: [], outcome: "rejected" };
|
|
14755
14910
|
}
|
|
14911
|
+
fallbackOutcome = "server_error";
|
|
14756
14912
|
} catch {
|
|
14913
|
+
fallbackOutcome = "network_error";
|
|
14757
14914
|
}
|
|
14758
14915
|
}
|
|
14759
14916
|
if (cached && trusted) {
|
|
14760
14917
|
const lastValidated = new Date(cached.last_validated);
|
|
14761
14918
|
const sevenDaysAgo = new Date(Date.now() - GRACE_PERIOD_MS);
|
|
14762
14919
|
if (lastValidated > sevenDaysAgo) {
|
|
14763
|
-
return trusted;
|
|
14920
|
+
return { ...trusted, outcome: "grace" };
|
|
14764
14921
|
}
|
|
14765
14922
|
}
|
|
14766
|
-
return { tier: "free", validUntil: "", features: [] };
|
|
14923
|
+
return { tier: "free", validUntil: "", features: [], outcome: fallbackOutcome };
|
|
14767
14924
|
} finally {
|
|
14768
14925
|
memDb.close();
|
|
14769
14926
|
}
|
|
@@ -14819,7 +14976,7 @@ async function getCurrentTier() {
|
|
|
14819
14976
|
const config = getConfig();
|
|
14820
14977
|
const apiKey = config.cloud?.apiKey;
|
|
14821
14978
|
if (!apiKey) {
|
|
14822
|
-
cachedTier = { tier: "free", validUntil: "", features: [] };
|
|
14979
|
+
cachedTier = { tier: "free", validUntil: "", features: [], outcome: "no_key" };
|
|
14823
14980
|
cachedTierTimestamp = Date.now();
|
|
14824
14981
|
return "free";
|
|
14825
14982
|
}
|
|
@@ -14952,6 +15109,14 @@ async function handleLicenseToolCall(name, _args, _memDb) {
|
|
|
14952
15109
|
}
|
|
14953
15110
|
return { content: [{ type: "text", text: `Unknown license tool: ${name}` }] };
|
|
14954
15111
|
}
|
|
15112
|
+
function _resetCachedTier() {
|
|
15113
|
+
cachedTier = null;
|
|
15114
|
+
cachedTierTimestamp = 0;
|
|
15115
|
+
}
|
|
15116
|
+
function _setCachedTierForTest(tier) {
|
|
15117
|
+
cachedTier = { tier, validUntil: "", features: [] };
|
|
15118
|
+
cachedTierTimestamp = Date.now();
|
|
15119
|
+
}
|
|
14955
15120
|
var _warnedLicenseSig, TIER_LEVELS, TOOL_TIER_MAP, PLAN_TO_TIER_MAP, TIER_LABELS, cachedTier, cachedTierTimestamp, IN_MEMORY_CACHE_TTL_MS, GRACE_PERIOD_MS;
|
|
14956
15121
|
var init_license = __esm({
|
|
14957
15122
|
"src/license.ts"() {
|
|
@@ -15107,20 +15272,21 @@ var init_hook_registry = __esm({
|
|
|
15107
15272
|
// src/commands/doctor.ts
|
|
15108
15273
|
var doctor_exports = {};
|
|
15109
15274
|
__export(doctor_exports, {
|
|
15275
|
+
formatLicenseCheck: () => formatLicenseCheck,
|
|
15110
15276
|
runDoctor: () => runDoctor,
|
|
15111
15277
|
runValidateConfig: () => runValidateConfig
|
|
15112
15278
|
});
|
|
15113
|
-
import { existsSync as
|
|
15114
|
-
import { resolve as
|
|
15279
|
+
import { existsSync as existsSync13, readFileSync as readFileSync13, readdirSync as readdirSync11 } from "fs";
|
|
15280
|
+
import { resolve as resolve9, dirname as dirname7 } from "path";
|
|
15115
15281
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
15116
15282
|
import { parse as parseYaml4 } from "yaml";
|
|
15117
15283
|
function checkConfig(projectRoot) {
|
|
15118
|
-
const configPath =
|
|
15119
|
-
if (!
|
|
15284
|
+
const configPath = resolve9(projectRoot, "massu.config.yaml");
|
|
15285
|
+
if (!existsSync13(configPath)) {
|
|
15120
15286
|
return { name: "Configuration", status: "fail", detail: "massu.config.yaml not found. Run: npx massu init" };
|
|
15121
15287
|
}
|
|
15122
15288
|
try {
|
|
15123
|
-
const content =
|
|
15289
|
+
const content = readFileSync13(configPath, "utf-8");
|
|
15124
15290
|
const parsed = parseYaml4(content);
|
|
15125
15291
|
if (!parsed || typeof parsed !== "object") {
|
|
15126
15292
|
return { name: "Configuration", status: "fail", detail: "massu.config.yaml is empty or invalid YAML" };
|
|
@@ -15132,11 +15298,11 @@ function checkConfig(projectRoot) {
|
|
|
15132
15298
|
}
|
|
15133
15299
|
function checkMcpServer(projectRoot) {
|
|
15134
15300
|
const mcpPath = getResolvedPaths().mcpJsonPath;
|
|
15135
|
-
if (!
|
|
15301
|
+
if (!existsSync13(mcpPath)) {
|
|
15136
15302
|
return { name: "MCP Server", status: "fail", detail: ".mcp.json not found. Run: npx massu init" };
|
|
15137
15303
|
}
|
|
15138
15304
|
try {
|
|
15139
|
-
const content = JSON.parse(
|
|
15305
|
+
const content = JSON.parse(readFileSync13(mcpPath, "utf-8"));
|
|
15140
15306
|
const servers = content.mcpServers ?? {};
|
|
15141
15307
|
if (!servers.massu) {
|
|
15142
15308
|
return { name: "MCP Server", status: "fail", detail: "massu not registered in .mcp.json. Run: npx massu init" };
|
|
@@ -15148,7 +15314,7 @@ function checkMcpServer(projectRoot) {
|
|
|
15148
15314
|
}
|
|
15149
15315
|
function checkHooksConfig(projectRoot) {
|
|
15150
15316
|
const settingsPath = getResolvedPaths().settingsLocalPath;
|
|
15151
|
-
if (!
|
|
15317
|
+
if (!existsSync13(settingsPath)) {
|
|
15152
15318
|
return { name: "Hooks Config", status: "fail", detail: ".claude/settings.local.json not found. Run: npx massu init" };
|
|
15153
15319
|
}
|
|
15154
15320
|
try {
|
|
@@ -15176,11 +15342,11 @@ function checkHooksConfig(projectRoot) {
|
|
|
15176
15342
|
}
|
|
15177
15343
|
}
|
|
15178
15344
|
function checkHookFiles(projectRoot) {
|
|
15179
|
-
const nodeModulesHooksDir =
|
|
15345
|
+
const nodeModulesHooksDir = resolve9(projectRoot, "node_modules/@massu/core/dist/hooks");
|
|
15180
15346
|
let hooksDir = nodeModulesHooksDir;
|
|
15181
|
-
if (!
|
|
15182
|
-
const devHooksDir =
|
|
15183
|
-
if (
|
|
15347
|
+
if (!existsSync13(nodeModulesHooksDir)) {
|
|
15348
|
+
const devHooksDir = resolve9(__dirname3, "../../dist/hooks");
|
|
15349
|
+
if (existsSync13(devHooksDir)) {
|
|
15184
15350
|
hooksDir = devHooksDir;
|
|
15185
15351
|
} else {
|
|
15186
15352
|
return { name: "Hook Files", status: "fail", detail: "Compiled hooks not found. Run: npm install @massu/core" };
|
|
@@ -15188,7 +15354,7 @@ function checkHookFiles(projectRoot) {
|
|
|
15188
15354
|
}
|
|
15189
15355
|
const missing = [];
|
|
15190
15356
|
for (const hookFile of EXPECTED_HOOKS) {
|
|
15191
|
-
if (!
|
|
15357
|
+
if (!existsSync13(resolve9(hooksDir, hookFile))) {
|
|
15192
15358
|
missing.push(hookFile);
|
|
15193
15359
|
}
|
|
15194
15360
|
}
|
|
@@ -15214,8 +15380,8 @@ function checkNodeVersion() {
|
|
|
15214
15380
|
return { name: "Node.js", status: "fail", detail: `v${version} \u2014 Node.js 18+ is required` };
|
|
15215
15381
|
}
|
|
15216
15382
|
async function checkGitRepo(projectRoot) {
|
|
15217
|
-
const gitDir =
|
|
15218
|
-
if (!
|
|
15383
|
+
const gitDir = resolve9(projectRoot, ".git");
|
|
15384
|
+
if (!existsSync13(gitDir)) {
|
|
15219
15385
|
return { name: "Git Repository", status: "warn", detail: "Not a git repository (optional but recommended)" };
|
|
15220
15386
|
}
|
|
15221
15387
|
try {
|
|
@@ -15233,7 +15399,7 @@ async function checkGitRepo(projectRoot) {
|
|
|
15233
15399
|
}
|
|
15234
15400
|
function checkKnowledgeDb(projectRoot) {
|
|
15235
15401
|
const knowledgeDbPath = getResolvedPaths().memoryDbPath;
|
|
15236
|
-
if (!
|
|
15402
|
+
if (!existsSync13(knowledgeDbPath)) {
|
|
15237
15403
|
return {
|
|
15238
15404
|
name: "Knowledge DB",
|
|
15239
15405
|
status: "warn",
|
|
@@ -15244,7 +15410,7 @@ function checkKnowledgeDb(projectRoot) {
|
|
|
15244
15410
|
}
|
|
15245
15411
|
function checkMemoryDir(_projectRoot2) {
|
|
15246
15412
|
const memoryDir = getResolvedPaths().memoryDir;
|
|
15247
|
-
if (!
|
|
15413
|
+
if (!existsSync13(memoryDir)) {
|
|
15248
15414
|
return {
|
|
15249
15415
|
name: "Memory Directory",
|
|
15250
15416
|
status: "warn",
|
|
@@ -15255,7 +15421,7 @@ function checkMemoryDir(_projectRoot2) {
|
|
|
15255
15421
|
}
|
|
15256
15422
|
function checkShellHooksWired(_projectRoot2) {
|
|
15257
15423
|
const settingsPath = getResolvedPaths().settingsLocalPath;
|
|
15258
|
-
if (!
|
|
15424
|
+
if (!existsSync13(settingsPath)) {
|
|
15259
15425
|
return {
|
|
15260
15426
|
name: "Shell Hooks",
|
|
15261
15427
|
status: "fail",
|
|
@@ -15283,26 +15449,43 @@ function checkShellHooksWired(_projectRoot2) {
|
|
|
15283
15449
|
};
|
|
15284
15450
|
}
|
|
15285
15451
|
}
|
|
15452
|
+
function formatLicenseCheck(input) {
|
|
15453
|
+
const { source, tier, validUntil, outcome } = input;
|
|
15454
|
+
if (source === "none") {
|
|
15455
|
+
return { name: "License", status: "pass", detail: "Free (no API key configured)" };
|
|
15456
|
+
}
|
|
15457
|
+
const label = apiKeySourceLabel(source);
|
|
15458
|
+
if (tier !== "free") {
|
|
15459
|
+
const cap = tier.charAt(0).toUpperCase() + tier.slice(1);
|
|
15460
|
+
const until = validUntil ? `, valid until ${validUntil}` : "";
|
|
15461
|
+
return { name: "License", status: "pass", detail: `${cap} (via ${label}${until})` };
|
|
15462
|
+
}
|
|
15463
|
+
if (outcome === "server_error") {
|
|
15464
|
+
return {
|
|
15465
|
+
name: "License",
|
|
15466
|
+
status: "warn",
|
|
15467
|
+
detail: `Could not validate \u2014 license server error (tier UNKNOWN; key present via ${label}). Not a Free downgrade; retries automatically.`
|
|
15468
|
+
};
|
|
15469
|
+
}
|
|
15470
|
+
if (outcome === "network_error" || outcome === "no_endpoint") {
|
|
15471
|
+
return {
|
|
15472
|
+
name: "License",
|
|
15473
|
+
status: "warn",
|
|
15474
|
+
detail: `Could not reach license server (tier UNKNOWN; key present via ${label}). Not a Free downgrade; retries automatically.`
|
|
15475
|
+
};
|
|
15476
|
+
}
|
|
15477
|
+
return {
|
|
15478
|
+
name: "License",
|
|
15479
|
+
status: "warn",
|
|
15480
|
+
detail: `Free \u2014 key present via ${label} but validated to Free (check key / subscription)`
|
|
15481
|
+
};
|
|
15482
|
+
}
|
|
15286
15483
|
async function checkLicenseStatus() {
|
|
15287
15484
|
try {
|
|
15485
|
+
const source = getResolvedApiKeySource();
|
|
15288
15486
|
const tier = await getCurrentTier();
|
|
15289
15487
|
const info = await getLicenseInfo();
|
|
15290
|
-
|
|
15291
|
-
return { name: "License", status: "pass", detail: "Free (no API key configured)" };
|
|
15292
|
-
}
|
|
15293
|
-
const days = await daysUntilExpiry();
|
|
15294
|
-
if (days >= 0 && info.validUntil) {
|
|
15295
|
-
return {
|
|
15296
|
-
name: "License",
|
|
15297
|
-
status: "pass",
|
|
15298
|
-
detail: `${tier.charAt(0).toUpperCase() + tier.slice(1)} (valid until ${info.validUntil})`
|
|
15299
|
-
};
|
|
15300
|
-
}
|
|
15301
|
-
return {
|
|
15302
|
-
name: "License",
|
|
15303
|
-
status: "pass",
|
|
15304
|
-
detail: `${tier.charAt(0).toUpperCase() + tier.slice(1)} (valid)`
|
|
15305
|
-
};
|
|
15488
|
+
return formatLicenseCheck({ source, tier, validUntil: info.validUntil, outcome: info.outcome });
|
|
15306
15489
|
} catch (err) {
|
|
15307
15490
|
return {
|
|
15308
15491
|
name: "License",
|
|
@@ -15311,11 +15494,31 @@ async function checkLicenseStatus() {
|
|
|
15311
15494
|
};
|
|
15312
15495
|
}
|
|
15313
15496
|
}
|
|
15497
|
+
async function checkApiKeyHygiene(projectRoot) {
|
|
15498
|
+
if (getResolvedApiKeySource() !== "config") return null;
|
|
15499
|
+
try {
|
|
15500
|
+
const { spawnSync: spawnSync4 } = await import("child_process");
|
|
15501
|
+
const res = spawnSync4("git", ["ls-files", "--error-unmatch", "massu.config.yaml"], {
|
|
15502
|
+
cwd: projectRoot,
|
|
15503
|
+
timeout: 5e3,
|
|
15504
|
+
encoding: "utf-8"
|
|
15505
|
+
});
|
|
15506
|
+
if (res.status === 0) {
|
|
15507
|
+
return {
|
|
15508
|
+
name: "API Key Hygiene",
|
|
15509
|
+
status: "warn",
|
|
15510
|
+
detail: "API key is committed in massu.config.yaml \u2014 move it to the MASSU_API_KEY env var or 'massu login' (~/.massu/credentials)"
|
|
15511
|
+
};
|
|
15512
|
+
}
|
|
15513
|
+
} catch {
|
|
15514
|
+
}
|
|
15515
|
+
return null;
|
|
15516
|
+
}
|
|
15314
15517
|
function checkPythonHealth(projectRoot) {
|
|
15315
15518
|
const config = getConfig();
|
|
15316
15519
|
if (!config.python?.root) return null;
|
|
15317
|
-
const pythonRoot =
|
|
15318
|
-
if (!
|
|
15520
|
+
const pythonRoot = resolve9(projectRoot, config.python.root);
|
|
15521
|
+
if (!existsSync13(pythonRoot)) {
|
|
15319
15522
|
return {
|
|
15320
15523
|
name: "Python",
|
|
15321
15524
|
status: "fail",
|
|
@@ -15334,8 +15537,8 @@ function checkPythonHealth(projectRoot) {
|
|
|
15334
15537
|
if (entry.isDirectory()) {
|
|
15335
15538
|
const excludeDirs = config.python?.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
|
|
15336
15539
|
if (!excludeDirs.includes(entry.name)) {
|
|
15337
|
-
const subdir =
|
|
15338
|
-
if (depth <= 2 && !
|
|
15540
|
+
const subdir = resolve9(dir, entry.name);
|
|
15541
|
+
if (depth <= 2 && !existsSync13(resolve9(subdir, "__init__.py"))) {
|
|
15339
15542
|
try {
|
|
15340
15543
|
const subEntries = readdirSync11(subdir);
|
|
15341
15544
|
if (subEntries.some((f2) => f2.endsWith(".py") && f2 !== "__init__.py")) {
|
|
@@ -15405,6 +15608,8 @@ async function runDoctor() {
|
|
|
15405
15608
|
];
|
|
15406
15609
|
const pythonCheck = checkPythonHealth(projectRoot);
|
|
15407
15610
|
if (pythonCheck) checks.push(pythonCheck);
|
|
15611
|
+
const hygieneCheck = await checkApiKeyHygiene(projectRoot);
|
|
15612
|
+
if (hygieneCheck) checks.push(hygieneCheck);
|
|
15408
15613
|
let passed = 0;
|
|
15409
15614
|
let failed = 0;
|
|
15410
15615
|
let warned = 0;
|
|
@@ -15430,15 +15635,15 @@ async function runDoctor() {
|
|
|
15430
15635
|
}
|
|
15431
15636
|
async function runValidateConfig() {
|
|
15432
15637
|
const projectRoot = process.cwd();
|
|
15433
|
-
const configPath =
|
|
15434
|
-
if (!
|
|
15638
|
+
const configPath = resolve9(projectRoot, "massu.config.yaml");
|
|
15639
|
+
if (!existsSync13(configPath)) {
|
|
15435
15640
|
console.error("Error: massu.config.yaml not found in current directory");
|
|
15436
15641
|
console.error("Run: npx massu init");
|
|
15437
15642
|
process.exit(1);
|
|
15438
15643
|
return;
|
|
15439
15644
|
}
|
|
15440
15645
|
try {
|
|
15441
|
-
const content =
|
|
15646
|
+
const content = readFileSync13(configPath, "utf-8");
|
|
15442
15647
|
const parsed = parseYaml4(content);
|
|
15443
15648
|
if (!parsed || typeof parsed !== "object") {
|
|
15444
15649
|
console.error("Error: massu.config.yaml is empty or not a valid YAML object");
|
|
@@ -15476,6 +15681,7 @@ var init_doctor = __esm({
|
|
|
15476
15681
|
"use strict";
|
|
15477
15682
|
init_config();
|
|
15478
15683
|
init_license();
|
|
15684
|
+
init_credentials();
|
|
15479
15685
|
init_settings_local();
|
|
15480
15686
|
init_hook_registry();
|
|
15481
15687
|
__filename3 = fileURLToPath3(import.meta.url);
|
|
@@ -15484,6 +15690,189 @@ var init_doctor = __esm({
|
|
|
15484
15690
|
}
|
|
15485
15691
|
});
|
|
15486
15692
|
|
|
15693
|
+
// src/commands/login.ts
|
|
15694
|
+
var login_exports = {};
|
|
15695
|
+
__export(login_exports, {
|
|
15696
|
+
MASSU_ENV_STDIN_TIMEOUT: () => MASSU_ENV_STDIN_TIMEOUT,
|
|
15697
|
+
MAX_STDIN_BYTES: () => MAX_STDIN_BYTES,
|
|
15698
|
+
NO_KEY_MESSAGE: () => NO_KEY_MESSAGE,
|
|
15699
|
+
NoLoginKeyError: () => NoLoginKeyError,
|
|
15700
|
+
loginStdinTimeoutMs: () => loginStdinTimeoutMs,
|
|
15701
|
+
resolveLoginKey: () => resolveLoginKey,
|
|
15702
|
+
runLogin: () => runLogin,
|
|
15703
|
+
runLogout: () => runLogout
|
|
15704
|
+
});
|
|
15705
|
+
import { createInterface as createInterface2 } from "readline";
|
|
15706
|
+
function loginStdinTimeoutMs(env) {
|
|
15707
|
+
const raw = env[MASSU_ENV_STDIN_TIMEOUT];
|
|
15708
|
+
const n = raw != null && raw !== "" ? Number(raw) : NaN;
|
|
15709
|
+
return Number.isFinite(n) && n > 0 ? n : DEFAULT_STDIN_TIMEOUT_MS;
|
|
15710
|
+
}
|
|
15711
|
+
async function readStdinBounded(readStdin, timeoutMs) {
|
|
15712
|
+
let timer;
|
|
15713
|
+
const read = readStdin();
|
|
15714
|
+
read.catch(() => void 0);
|
|
15715
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
15716
|
+
timer = setTimeout(() => reject(new NoLoginKeyError()), timeoutMs);
|
|
15717
|
+
if (timer && typeof timer.unref === "function") timer.unref();
|
|
15718
|
+
});
|
|
15719
|
+
try {
|
|
15720
|
+
return await Promise.race([read, timeout]);
|
|
15721
|
+
} finally {
|
|
15722
|
+
if (timer) clearTimeout(timer);
|
|
15723
|
+
}
|
|
15724
|
+
}
|
|
15725
|
+
async function resolveLoginKey(deps) {
|
|
15726
|
+
const { argv, env, isTTY, readStdin, prompt, timeoutMs } = deps;
|
|
15727
|
+
const keyFlagIdx = argv.indexOf("--key");
|
|
15728
|
+
if (keyFlagIdx >= 0 && argv[keyFlagIdx + 1]) {
|
|
15729
|
+
return { key: argv[keyFlagIdx + 1], source: "flag" };
|
|
15730
|
+
}
|
|
15731
|
+
const envRaw = env[MASSU_ENV_API_KEY];
|
|
15732
|
+
const envKey = typeof envRaw === "string" ? envRaw.trim() : "";
|
|
15733
|
+
if (envKey.length > 0) {
|
|
15734
|
+
return { key: envKey, source: "env" };
|
|
15735
|
+
}
|
|
15736
|
+
if (isTTY) {
|
|
15737
|
+
return { key: await prompt("Massu API key: "), source: "prompt" };
|
|
15738
|
+
}
|
|
15739
|
+
return { key: await readStdinBounded(readStdin, timeoutMs), source: "stdin" };
|
|
15740
|
+
}
|
|
15741
|
+
function maskKey(k3) {
|
|
15742
|
+
return k3.length <= 12 ? "****" : `${k3.slice(0, API_KEY_PREFIX.length)}\u2026${k3.slice(-4)}`;
|
|
15743
|
+
}
|
|
15744
|
+
function promptHidden(question) {
|
|
15745
|
+
return new Promise((resolvePromise) => {
|
|
15746
|
+
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
15747
|
+
let promptWritten = false;
|
|
15748
|
+
rl._writeToOutput = (chunk) => {
|
|
15749
|
+
if (!promptWritten) {
|
|
15750
|
+
rl.output.write(question);
|
|
15751
|
+
promptWritten = true;
|
|
15752
|
+
}
|
|
15753
|
+
void chunk;
|
|
15754
|
+
};
|
|
15755
|
+
rl.question(question, (answer) => {
|
|
15756
|
+
rl.close();
|
|
15757
|
+
process.stdout.write("\n");
|
|
15758
|
+
resolvePromise(answer);
|
|
15759
|
+
});
|
|
15760
|
+
});
|
|
15761
|
+
}
|
|
15762
|
+
async function readStdinKey() {
|
|
15763
|
+
const chunks = [];
|
|
15764
|
+
let total = 0;
|
|
15765
|
+
for await (const chunk of process.stdin) {
|
|
15766
|
+
const buf = chunk;
|
|
15767
|
+
total += buf.length;
|
|
15768
|
+
if (total > MAX_STDIN_BYTES) {
|
|
15769
|
+
process.stdin.destroy?.();
|
|
15770
|
+
throw new NoLoginKeyError(
|
|
15771
|
+
`Piped input exceeds ${MAX_STDIN_BYTES} bytes \u2014 an API key is under 100 bytes. Aborting; nothing was written.`
|
|
15772
|
+
);
|
|
15773
|
+
}
|
|
15774
|
+
chunks.push(buf);
|
|
15775
|
+
}
|
|
15776
|
+
return Buffer.concat(chunks).toString("utf-8");
|
|
15777
|
+
}
|
|
15778
|
+
async function runLogin(argv) {
|
|
15779
|
+
const noVerify = argv.includes("--no-verify");
|
|
15780
|
+
let resolved;
|
|
15781
|
+
try {
|
|
15782
|
+
resolved = await resolveLoginKey({
|
|
15783
|
+
argv,
|
|
15784
|
+
env: process.env,
|
|
15785
|
+
isTTY: Boolean(process.stdin.isTTY),
|
|
15786
|
+
readStdin: readStdinKey,
|
|
15787
|
+
prompt: promptHidden,
|
|
15788
|
+
timeoutMs: loginStdinTimeoutMs(process.env)
|
|
15789
|
+
});
|
|
15790
|
+
} catch (err) {
|
|
15791
|
+
if (err instanceof NoLoginKeyError) {
|
|
15792
|
+
console.error(`Error: ${err.message}`);
|
|
15793
|
+
process.exit(1);
|
|
15794
|
+
return;
|
|
15795
|
+
}
|
|
15796
|
+
throw err;
|
|
15797
|
+
}
|
|
15798
|
+
if (resolved.source === "flag") {
|
|
15799
|
+
console.error(
|
|
15800
|
+
'Warning: --key exposes your API key to shell history. Prefer MASSU_API_KEY or a piped key (echo "$KEY" | massu login) for automation.'
|
|
15801
|
+
);
|
|
15802
|
+
}
|
|
15803
|
+
const key = (resolved.key ?? "").trim();
|
|
15804
|
+
if (!key.startsWith(API_KEY_PREFIX)) {
|
|
15805
|
+
console.error(`Error: API key must start with "${API_KEY_PREFIX}".`);
|
|
15806
|
+
process.exit(1);
|
|
15807
|
+
return;
|
|
15808
|
+
}
|
|
15809
|
+
const path = writeUserCredentials(key);
|
|
15810
|
+
console.log(`
|
|
15811
|
+
Saved API key ${maskKey(key)} to ${path} (permissions 0600).`);
|
|
15812
|
+
console.log("This file lives outside every repository \u2014 your key is never committed to git.");
|
|
15813
|
+
if (!noVerify) {
|
|
15814
|
+
try {
|
|
15815
|
+
const { resetConfig: resetConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
15816
|
+
resetConfig2();
|
|
15817
|
+
const { validateLicense: validateLicense2 } = await Promise.resolve().then(() => (init_license(), license_exports));
|
|
15818
|
+
const info = await validateLicense2(key);
|
|
15819
|
+
if (info.tier !== "free") {
|
|
15820
|
+
const cap = info.tier.charAt(0).toUpperCase() + info.tier.slice(1);
|
|
15821
|
+
console.log(
|
|
15822
|
+
`Verified: ${cap} tier${info.validUntil ? `, valid until ${info.validUntil}` : ""}.`
|
|
15823
|
+
);
|
|
15824
|
+
} else {
|
|
15825
|
+
switch (info.outcome) {
|
|
15826
|
+
case "server_error":
|
|
15827
|
+
console.log(
|
|
15828
|
+
"Saved, but could NOT verify your key: the license server returned an error."
|
|
15829
|
+
);
|
|
15830
|
+
console.log(
|
|
15831
|
+
"This is NOT a Free determination \u2014 your tier is unknown. It will re-validate automatically on next use; if this persists, the license server may be down."
|
|
15832
|
+
);
|
|
15833
|
+
break;
|
|
15834
|
+
case "network_error":
|
|
15835
|
+
case "no_endpoint":
|
|
15836
|
+
console.log(
|
|
15837
|
+
"Saved; could not reach the license server right now (offline). Your key will validate automatically on next use."
|
|
15838
|
+
);
|
|
15839
|
+
break;
|
|
15840
|
+
default:
|
|
15841
|
+
console.log(
|
|
15842
|
+
"Verified: this key is on the Free tier (check the key or your subscription at https://massu.ai/dashboard)."
|
|
15843
|
+
);
|
|
15844
|
+
}
|
|
15845
|
+
}
|
|
15846
|
+
} catch {
|
|
15847
|
+
console.log("Saved; could not verify online right now (it will validate on next use).");
|
|
15848
|
+
}
|
|
15849
|
+
}
|
|
15850
|
+
console.log("");
|
|
15851
|
+
}
|
|
15852
|
+
async function runLogout() {
|
|
15853
|
+
const removed = removeUserCredentials();
|
|
15854
|
+
console.log(
|
|
15855
|
+
removed ? `Removed stored credentials at ${credentialsPath()}.` : "No stored credentials to remove."
|
|
15856
|
+
);
|
|
15857
|
+
}
|
|
15858
|
+
var DEFAULT_STDIN_TIMEOUT_MS, MASSU_ENV_STDIN_TIMEOUT, NO_KEY_MESSAGE, NoLoginKeyError, MAX_STDIN_BYTES;
|
|
15859
|
+
var init_login = __esm({
|
|
15860
|
+
"src/commands/login.ts"() {
|
|
15861
|
+
"use strict";
|
|
15862
|
+
init_credentials();
|
|
15863
|
+
DEFAULT_STDIN_TIMEOUT_MS = 2e3;
|
|
15864
|
+
MASSU_ENV_STDIN_TIMEOUT = "MASSU_LOGIN_STDIN_TIMEOUT_MS";
|
|
15865
|
+
NO_KEY_MESSAGE = `No API key found. Set ${MASSU_ENV_API_KEY}, pipe the key (echo "$KEY" | massu login), or pass --key <k>; the interactive prompt needs a TTY.`;
|
|
15866
|
+
NoLoginKeyError = class extends Error {
|
|
15867
|
+
constructor(message = NO_KEY_MESSAGE) {
|
|
15868
|
+
super(message);
|
|
15869
|
+
this.name = "NoLoginKeyError";
|
|
15870
|
+
}
|
|
15871
|
+
};
|
|
15872
|
+
MAX_STDIN_BYTES = 64 * 1024;
|
|
15873
|
+
}
|
|
15874
|
+
});
|
|
15875
|
+
|
|
15487
15876
|
// src/commands/install-hooks.ts
|
|
15488
15877
|
var install_hooks_exports = {};
|
|
15489
15878
|
__export(install_hooks_exports, {
|
|
@@ -15515,8 +15904,8 @@ __export(hook_runner_exports, {
|
|
|
15515
15904
|
resolveHookFile: () => resolveHookFile,
|
|
15516
15905
|
runHookRunner: () => runHookRunner
|
|
15517
15906
|
});
|
|
15518
|
-
import { existsSync as
|
|
15519
|
-
import { resolve as
|
|
15907
|
+
import { existsSync as existsSync14 } from "fs";
|
|
15908
|
+
import { resolve as resolve10, dirname as dirname8 } from "path";
|
|
15520
15909
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
15521
15910
|
import { spawn } from "child_process";
|
|
15522
15911
|
function resolveHookFile(hookName) {
|
|
@@ -15528,14 +15917,14 @@ function resolveHookFile(hookName) {
|
|
|
15528
15917
|
}
|
|
15529
15918
|
const candidates = [
|
|
15530
15919
|
// Bundled compiled layout: dist/cli.js → ./hooks/<file>.js
|
|
15531
|
-
|
|
15920
|
+
resolve10(__dirname4, "hooks", file),
|
|
15532
15921
|
// TS-source dev / sibling layout: src/commands/ → ../hooks/<file>
|
|
15533
|
-
|
|
15922
|
+
resolve10(__dirname4, "../hooks", file),
|
|
15534
15923
|
// TS-source dev fallback: src/commands/ → ../../dist/hooks/<file>
|
|
15535
|
-
|
|
15924
|
+
resolve10(__dirname4, "../../dist/hooks", file)
|
|
15536
15925
|
];
|
|
15537
15926
|
for (const candidate of candidates) {
|
|
15538
|
-
if (
|
|
15927
|
+
if (existsSync14(candidate)) {
|
|
15539
15928
|
return candidate;
|
|
15540
15929
|
}
|
|
15541
15930
|
}
|
|
@@ -15600,7 +15989,7 @@ __export(permissions_exports, {
|
|
|
15600
15989
|
handlePermissionsSubcommand: () => handlePermissionsSubcommand,
|
|
15601
15990
|
printPermissionsHelp: () => printPermissionsHelp
|
|
15602
15991
|
});
|
|
15603
|
-
import { resolve as
|
|
15992
|
+
import { resolve as resolve11 } from "path";
|
|
15604
15993
|
function resolveClaudeDir2() {
|
|
15605
15994
|
let claudeDirName = ".claude";
|
|
15606
15995
|
try {
|
|
@@ -15608,7 +15997,7 @@ function resolveClaudeDir2() {
|
|
|
15608
15997
|
} catch {
|
|
15609
15998
|
claudeDirName = ".claude";
|
|
15610
15999
|
}
|
|
15611
|
-
return
|
|
16000
|
+
return resolve11(process.cwd(), claudeDirName);
|
|
15612
16001
|
}
|
|
15613
16002
|
async function handlePermissionsSubcommand(args2) {
|
|
15614
16003
|
const sub = args2[0];
|
|
@@ -15738,8 +16127,8 @@ var init_auto_learning_entitlement = __esm({
|
|
|
15738
16127
|
});
|
|
15739
16128
|
|
|
15740
16129
|
// src/commands/license.ts
|
|
15741
|
-
var
|
|
15742
|
-
__export(
|
|
16130
|
+
var license_exports2 = {};
|
|
16131
|
+
__export(license_exports2, {
|
|
15743
16132
|
handleLicenseSubcommand: () => handleLicenseSubcommand,
|
|
15744
16133
|
printLicenseHelp: () => printLicenseHelp
|
|
15745
16134
|
});
|
|
@@ -15827,8 +16216,8 @@ var init_license2 = __esm({
|
|
|
15827
16216
|
});
|
|
15828
16217
|
|
|
15829
16218
|
// src/changelog-generator.ts
|
|
15830
|
-
import { existsSync as
|
|
15831
|
-
import { resolve as
|
|
16219
|
+
import { existsSync as existsSync15, readFileSync as readFileSync14, readdirSync as readdirSync12 } from "fs";
|
|
16220
|
+
import { resolve as resolve12 } from "path";
|
|
15832
16221
|
function parseCommitsForPlanTokens(subjects) {
|
|
15833
16222
|
const tokens = /* @__PURE__ */ new Set();
|
|
15834
16223
|
const maintenance = [];
|
|
@@ -15845,7 +16234,7 @@ function parseCommitsForPlanTokens(subjects) {
|
|
|
15845
16234
|
function loadPlanSummaries(tokens, planDir) {
|
|
15846
16235
|
const result = /* @__PURE__ */ new Map();
|
|
15847
16236
|
if (tokens.size === 0) return result;
|
|
15848
|
-
if (!
|
|
16237
|
+
if (!existsSync15(planDir)) {
|
|
15849
16238
|
throw new Error(`Plan directory does not exist: ${planDir}`);
|
|
15850
16239
|
}
|
|
15851
16240
|
const files = readdirSync12(planDir).filter((f2) => f2.endsWith(".md"));
|
|
@@ -15853,8 +16242,8 @@ function loadPlanSummaries(tokens, planDir) {
|
|
|
15853
16242
|
let matchedFile = null;
|
|
15854
16243
|
let content = "";
|
|
15855
16244
|
for (const file of files) {
|
|
15856
|
-
const path =
|
|
15857
|
-
const text18 =
|
|
16245
|
+
const path = resolve12(planDir, file);
|
|
16246
|
+
const text18 = readFileSync14(path, "utf-8");
|
|
15858
16247
|
const tokenRe = new RegExp(
|
|
15859
16248
|
`^\\*\\*Plan Token\\*\\*:\\s*\`?${token.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}\`?(\\s|$)`,
|
|
15860
16249
|
"m"
|
|
@@ -15935,8 +16324,8 @@ __export(changelog_exports, {
|
|
|
15935
16324
|
printChangelogHelp: () => printChangelogHelp
|
|
15936
16325
|
});
|
|
15937
16326
|
import { execSync } from "child_process";
|
|
15938
|
-
import { existsSync as
|
|
15939
|
-
import { resolve as
|
|
16327
|
+
import { existsSync as existsSync16, readFileSync as readFileSync15 } from "fs";
|
|
16328
|
+
import { resolve as resolve13 } from "path";
|
|
15940
16329
|
function resolveRepoRoot() {
|
|
15941
16330
|
try {
|
|
15942
16331
|
return execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
|
|
@@ -15960,25 +16349,25 @@ function getCommitSubjects(range) {
|
|
|
15960
16349
|
}
|
|
15961
16350
|
}
|
|
15962
16351
|
function getCurrentVersion(repoRoot) {
|
|
15963
|
-
const pkgPath =
|
|
15964
|
-
if (!
|
|
15965
|
-
const pkg = JSON.parse(
|
|
16352
|
+
const pkgPath = resolve13(repoRoot, "packages/core/package.json");
|
|
16353
|
+
if (!existsSync16(pkgPath)) return "0.0.0";
|
|
16354
|
+
const pkg = JSON.parse(readFileSync15(pkgPath, "utf-8"));
|
|
15966
16355
|
return pkg.version || "0.0.0";
|
|
15967
16356
|
}
|
|
15968
16357
|
function todayDate() {
|
|
15969
16358
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
15970
16359
|
}
|
|
15971
16360
|
function getLatestChangelogEntryBody(repoRoot) {
|
|
15972
|
-
const path =
|
|
15973
|
-
if (!
|
|
15974
|
-
const content =
|
|
16361
|
+
const path = resolve13(repoRoot, "CHANGELOG.md");
|
|
16362
|
+
if (!existsSync16(path)) return "";
|
|
16363
|
+
const content = readFileSync15(path, "utf-8");
|
|
15975
16364
|
const m3 = content.match(/^## \[[\d.]+\][^\n]*\n([\s\S]*?)(?=\n## \[|$)/m);
|
|
15976
16365
|
return m3 ? m3[1] : "";
|
|
15977
16366
|
}
|
|
15978
16367
|
async function handleChangelogSubcommand(args2) {
|
|
15979
16368
|
const sub = args2[0];
|
|
15980
16369
|
const repoRoot = resolveRepoRoot();
|
|
15981
|
-
const planDir =
|
|
16370
|
+
const planDir = resolve13(repoRoot, "docs/plans");
|
|
15982
16371
|
switch (sub) {
|
|
15983
16372
|
case "generate": {
|
|
15984
16373
|
const lastTag = getLastTag();
|
|
@@ -16189,7 +16578,7 @@ var init_rule_pack_schema = __esm({
|
|
|
16189
16578
|
});
|
|
16190
16579
|
|
|
16191
16580
|
// src/rule-pack-sync.ts
|
|
16192
|
-
import { existsSync as
|
|
16581
|
+
import { existsSync as existsSync17, writeFileSync as writeFileSync4, mkdirSync as mkdirSync7 } from "fs";
|
|
16193
16582
|
import { join as join11, dirname as dirname9 } from "path";
|
|
16194
16583
|
async function pullInstalledPackRules(db, opts = {}) {
|
|
16195
16584
|
const config = getConfig();
|
|
@@ -16252,7 +16641,7 @@ async function pullInstalledPackRules(db, opts = {}) {
|
|
|
16252
16641
|
result.pulled += 1;
|
|
16253
16642
|
const promptHash = packRulePromptHash(pack.slug, rule);
|
|
16254
16643
|
const candidatePath = sidecarPath(projectRoot, promptHash);
|
|
16255
|
-
if (
|
|
16644
|
+
if (existsSync17(candidatePath) || alreadyApplied(db, promptHash)) {
|
|
16256
16645
|
result.skipped += 1;
|
|
16257
16646
|
continue;
|
|
16258
16647
|
}
|
|
@@ -16340,8 +16729,8 @@ function materializeCandidate(projectRoot, candidatePath, promptHash, rule, pack
|
|
|
16340
16729
|
draft_text: draftText
|
|
16341
16730
|
};
|
|
16342
16731
|
const dir = dirname9(candidatePath);
|
|
16343
|
-
if (!
|
|
16344
|
-
|
|
16732
|
+
if (!existsSync17(dir)) mkdirSync7(dir, { recursive: true });
|
|
16733
|
+
writeFileSync4(candidatePath, JSON.stringify(sidecar, null, 2), "utf-8");
|
|
16345
16734
|
}
|
|
16346
16735
|
function emitDropTelemetry(db, eventType, data) {
|
|
16347
16736
|
recordTelemetry(db, eventType, data);
|
|
@@ -16455,8 +16844,8 @@ var show_template_exports = {};
|
|
|
16455
16844
|
__export(show_template_exports, {
|
|
16456
16845
|
runShowTemplate: () => runShowTemplate
|
|
16457
16846
|
});
|
|
16458
|
-
import { existsSync as
|
|
16459
|
-
import { resolve as
|
|
16847
|
+
import { existsSync as existsSync18, readFileSync as readFileSync16 } from "fs";
|
|
16848
|
+
import { resolve as resolve14 } from "path";
|
|
16460
16849
|
function normalizeBaseName(input) {
|
|
16461
16850
|
return input.endsWith(".md") ? input.slice(0, -".md".length) : input;
|
|
16462
16851
|
}
|
|
@@ -16484,14 +16873,14 @@ async function runShowTemplate(args2) {
|
|
|
16484
16873
|
return;
|
|
16485
16874
|
}
|
|
16486
16875
|
const suffix = choice.kind === "hit" ? choice.suffix : "";
|
|
16487
|
-
const file = suffix === "" ?
|
|
16488
|
-
if (!
|
|
16876
|
+
const file = suffix === "" ? resolve14(sourceDir, `${baseName}.md`) : resolve14(sourceDir, `${baseName}${suffix}.md`);
|
|
16877
|
+
if (!existsSync18(file)) {
|
|
16489
16878
|
process.stderr.write(`massu: resolved template "${file}" no longer exists
|
|
16490
16879
|
`);
|
|
16491
16880
|
process.exit(1);
|
|
16492
16881
|
return;
|
|
16493
16882
|
}
|
|
16494
|
-
process.stdout.write(
|
|
16883
|
+
process.stdout.write(readFileSync16(file, "utf-8"));
|
|
16495
16884
|
}
|
|
16496
16885
|
var init_show_template = __esm({
|
|
16497
16886
|
"src/commands/show-template.ts"() {
|
|
@@ -16544,12 +16933,12 @@ var init_passthrough = __esm({
|
|
|
16544
16933
|
});
|
|
16545
16934
|
|
|
16546
16935
|
// src/lib/fileLock.ts
|
|
16547
|
-
import { mkdirSync as
|
|
16936
|
+
import { mkdirSync as mkdirSync8, readFileSync as readFileSync17, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
16548
16937
|
import { dirname as dirname10 } from "path";
|
|
16549
16938
|
import * as lockfile from "proper-lockfile";
|
|
16550
16939
|
function readLockHolderPid(lockPath) {
|
|
16551
16940
|
try {
|
|
16552
|
-
const raw =
|
|
16941
|
+
const raw = readFileSync17(`${lockPath}.pid`, "utf-8").trim();
|
|
16553
16942
|
const pid = Number.parseInt(raw, 10);
|
|
16554
16943
|
if (!Number.isFinite(pid) || pid <= 0) return null;
|
|
16555
16944
|
return pid;
|
|
@@ -16568,7 +16957,7 @@ function busyWaitSync(ms) {
|
|
|
16568
16957
|
Atomics.wait(view, 0, 0, ms);
|
|
16569
16958
|
}
|
|
16570
16959
|
function withFileLockSync(lockPath, fn, opts = {}) {
|
|
16571
|
-
|
|
16960
|
+
mkdirSync8(dirname10(lockPath), { recursive: true });
|
|
16572
16961
|
const staleMs = opts.staleMs ?? 3e4;
|
|
16573
16962
|
const blockMs = opts.retries === 0 ? 0 : opts.blockMs ?? 3e4;
|
|
16574
16963
|
const pollIntervalMs = opts.pollIntervalMs ?? 100;
|
|
@@ -16585,7 +16974,7 @@ function withFileLockSync(lockPath, fn, opts = {}) {
|
|
|
16585
16974
|
realpath: false
|
|
16586
16975
|
});
|
|
16587
16976
|
try {
|
|
16588
|
-
|
|
16977
|
+
writeFileSync5(`${lockPath}.pid`, String(process.pid), "utf-8");
|
|
16589
16978
|
} catch {
|
|
16590
16979
|
}
|
|
16591
16980
|
break;
|
|
@@ -16612,7 +17001,7 @@ function withFileLockSync(lockPath, fn, opts = {}) {
|
|
|
16612
17001
|
} catch {
|
|
16613
17002
|
}
|
|
16614
17003
|
try {
|
|
16615
|
-
|
|
17004
|
+
rmSync4(`${lockPath}.pid`, { force: true });
|
|
16616
17005
|
} catch {
|
|
16617
17006
|
}
|
|
16618
17007
|
}
|
|
@@ -16640,9 +17029,9 @@ var init_fileLock = __esm({
|
|
|
16640
17029
|
});
|
|
16641
17030
|
|
|
16642
17031
|
// src/lib/installLock.ts
|
|
16643
|
-
import { resolve as
|
|
17032
|
+
import { resolve as resolve15 } from "path";
|
|
16644
17033
|
function withInstallLock(projectRoot, fn, opts = {}) {
|
|
16645
|
-
const lockPath =
|
|
17034
|
+
const lockPath = resolve15(projectRoot, ".massu", "installAll.lock");
|
|
16646
17035
|
return withFileLockSync(
|
|
16647
17036
|
lockPath,
|
|
16648
17037
|
fn,
|
|
@@ -16682,8 +17071,8 @@ __export(config_refresh_exports, {
|
|
|
16682
17071
|
mergeRefresh: () => mergeRefresh,
|
|
16683
17072
|
runConfigRefresh: () => runConfigRefresh
|
|
16684
17073
|
});
|
|
16685
|
-
import { existsSync as
|
|
16686
|
-
import { resolve as
|
|
17074
|
+
import { existsSync as existsSync19, readFileSync as readFileSync18, rmSync as rmSync5 } from "fs";
|
|
17075
|
+
import { resolve as resolve16 } from "path";
|
|
16687
17076
|
import { parse as parseYaml5 } from "yaml";
|
|
16688
17077
|
function flatten(obj, prefix3 = "") {
|
|
16689
17078
|
const out = {};
|
|
@@ -16811,17 +17200,17 @@ function renderDiff(diff) {
|
|
|
16811
17200
|
}
|
|
16812
17201
|
async function runConfigRefresh(opts = {}) {
|
|
16813
17202
|
const cwd = opts.cwd ?? process.cwd();
|
|
16814
|
-
const configPath =
|
|
17203
|
+
const configPath = resolve16(cwd, "massu.config.yaml");
|
|
16815
17204
|
const log = opts.silent ? () => {
|
|
16816
17205
|
} : (s) => process.stdout.write(s);
|
|
16817
|
-
if (!
|
|
17206
|
+
if (!existsSync19(configPath)) {
|
|
16818
17207
|
const message = "massu.config.yaml not found. Run: npx massu init";
|
|
16819
17208
|
if (!opts.silent) process.stderr.write(message + "\n");
|
|
16820
17209
|
return { exitCode: 1, applied: false, dryRun: !!opts.dryRun, diff: [], message };
|
|
16821
17210
|
}
|
|
16822
17211
|
let existing;
|
|
16823
17212
|
try {
|
|
16824
|
-
const content =
|
|
17213
|
+
const content = readFileSync18(configPath, "utf-8");
|
|
16825
17214
|
const parsed = parseYaml5(content);
|
|
16826
17215
|
if (!parsed || typeof parsed !== "object") {
|
|
16827
17216
|
throw new Error("config is not a YAML object");
|
|
@@ -16894,10 +17283,10 @@ async function runConfigRefresh(opts = {}) {
|
|
|
16894
17283
|
`);
|
|
16895
17284
|
const stackResolved = installResult.totalInstalled > 0 || installResult.totalUpdated > 0;
|
|
16896
17285
|
if (stackResolved) {
|
|
16897
|
-
const placeholderPath =
|
|
16898
|
-
if (
|
|
17286
|
+
const placeholderPath = resolve16(installResult.claudeDir, "commands", "_massu-needs-stack.md");
|
|
17287
|
+
if (existsSync19(placeholderPath)) {
|
|
16899
17288
|
try {
|
|
16900
|
-
|
|
17289
|
+
rmSync5(placeholderPath, { force: true });
|
|
16901
17290
|
log("Removed _massu-needs-stack.md (stack now declared).\n");
|
|
16902
17291
|
} catch {
|
|
16903
17292
|
}
|
|
@@ -17004,12 +17393,12 @@ var init_gitToplevel = __esm({
|
|
|
17004
17393
|
});
|
|
17005
17394
|
|
|
17006
17395
|
// src/watch/lockfile-detector.ts
|
|
17007
|
-
import { existsSync as
|
|
17008
|
-
import { resolve as
|
|
17396
|
+
import { existsSync as existsSync20, statSync as statSync8 } from "fs";
|
|
17397
|
+
import { resolve as resolve17 } from "path";
|
|
17009
17398
|
function lockfileMidWrite(projectRoot, now = Date.now(), windowMs = LOCKFILE_WINDOW_MS) {
|
|
17010
17399
|
for (const lf of KNOWN_LOCKFILES) {
|
|
17011
|
-
const p19 =
|
|
17012
|
-
if (!
|
|
17400
|
+
const p19 = resolve17(projectRoot, lf);
|
|
17401
|
+
if (!existsSync20(p19)) continue;
|
|
17013
17402
|
try {
|
|
17014
17403
|
const stat = statSync8(p19);
|
|
17015
17404
|
const delta = now - stat.mtimeMs;
|
|
@@ -17022,7 +17411,7 @@ function lockfileMidWrite(projectRoot, now = Date.now(), windowMs = LOCKFILE_WIN
|
|
|
17022
17411
|
function gitMidOperation(projectRoot) {
|
|
17023
17412
|
const sentinels = ["MERGE_HEAD", "REBASE_HEAD", "CHERRY_PICK_HEAD", "rebase-apply", "rebase-merge"];
|
|
17024
17413
|
for (const s of sentinels) {
|
|
17025
|
-
if (
|
|
17414
|
+
if (existsSync20(resolve17(projectRoot, ".git", s))) return true;
|
|
17026
17415
|
}
|
|
17027
17416
|
return false;
|
|
17028
17417
|
}
|
|
@@ -17221,18 +17610,18 @@ var init_paths = __esm({
|
|
|
17221
17610
|
});
|
|
17222
17611
|
|
|
17223
17612
|
// src/watch/state.ts
|
|
17224
|
-
import { closeSync as closeSync3, existsSync as
|
|
17225
|
-
import { dirname as dirname11, resolve as
|
|
17613
|
+
import { closeSync as closeSync3, existsSync as existsSync21, fsyncSync as fsyncSync3, mkdirSync as mkdirSync9, openSync as openSync3, readFileSync as readFileSync19, renameSync as renameSync4, rmSync as rmSync6, writeFileSync as writeFileSync6, writeSync as writeSync3 } from "fs";
|
|
17614
|
+
import { dirname as dirname11, resolve as resolve18 } from "path";
|
|
17226
17615
|
function watchStatePath(projectRoot) {
|
|
17227
|
-
return
|
|
17616
|
+
return resolve18(projectRoot, ".massu", "watch-state.json");
|
|
17228
17617
|
}
|
|
17229
17618
|
function backupStatePath(projectRoot) {
|
|
17230
|
-
return
|
|
17619
|
+
return resolve18(projectRoot, ".massu", "watch-state.v0.bak.json");
|
|
17231
17620
|
}
|
|
17232
17621
|
function readState(projectRoot) {
|
|
17233
17622
|
const path = watchStatePath(projectRoot);
|
|
17234
|
-
if (!
|
|
17235
|
-
const content =
|
|
17623
|
+
if (!existsSync21(path)) return { ...DEFAULT_STATE };
|
|
17624
|
+
const content = readFileSync19(path, "utf-8");
|
|
17236
17625
|
let raw;
|
|
17237
17626
|
try {
|
|
17238
17627
|
raw = JSON.parse(content);
|
|
@@ -17274,14 +17663,14 @@ function readState(projectRoot) {
|
|
|
17274
17663
|
}
|
|
17275
17664
|
function archiveCorrupt(projectRoot, content) {
|
|
17276
17665
|
const bak = backupStatePath(projectRoot);
|
|
17277
|
-
|
|
17278
|
-
|
|
17666
|
+
mkdirSync9(dirname11(bak), { recursive: true });
|
|
17667
|
+
writeFileSync6(bak, content, "utf-8");
|
|
17279
17668
|
}
|
|
17280
17669
|
function writeStateAtomic(projectRoot, state) {
|
|
17281
17670
|
const path = watchStatePath(projectRoot);
|
|
17282
17671
|
writeStateAtomicCounter = writeStateAtomicCounter + 1 >>> 0;
|
|
17283
17672
|
const tmp = `${path}.${process.pid}.${writeStateAtomicCounter}.tmp`;
|
|
17284
|
-
|
|
17673
|
+
mkdirSync9(dirname11(path), { recursive: true });
|
|
17285
17674
|
let renamed = false;
|
|
17286
17675
|
try {
|
|
17287
17676
|
const fd = openSync3(tmp, "w");
|
|
@@ -17295,9 +17684,9 @@ function writeStateAtomic(projectRoot, state) {
|
|
|
17295
17684
|
renameSync4(tmp, path);
|
|
17296
17685
|
renamed = true;
|
|
17297
17686
|
} finally {
|
|
17298
|
-
if (!renamed &&
|
|
17687
|
+
if (!renamed && existsSync21(tmp)) {
|
|
17299
17688
|
try {
|
|
17300
|
-
|
|
17689
|
+
rmSync6(tmp, { force: true });
|
|
17301
17690
|
} catch {
|
|
17302
17691
|
}
|
|
17303
17692
|
}
|
|
@@ -17577,8 +17966,8 @@ __export(watch_exports, {
|
|
|
17577
17966
|
runWatch: () => runWatch
|
|
17578
17967
|
});
|
|
17579
17968
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
17580
|
-
import { basename as basename5, dirname as dirname12, resolve as
|
|
17581
|
-
import { appendFileSync, existsSync as
|
|
17969
|
+
import { basename as basename5, dirname as dirname12, resolve as resolve19 } from "path";
|
|
17970
|
+
import { appendFileSync, existsSync as existsSync22, mkdirSync as mkdirSync10, readFileSync as readFileSync20 } from "fs";
|
|
17582
17971
|
function parseFlags(args2) {
|
|
17583
17972
|
const out = {
|
|
17584
17973
|
foreground: false,
|
|
@@ -17603,8 +17992,8 @@ function parseFlags(args2) {
|
|
|
17603
17992
|
}
|
|
17604
17993
|
function findClaudeBg() {
|
|
17605
17994
|
const home = process.env.HOME ?? "";
|
|
17606
|
-
const fixed = home ?
|
|
17607
|
-
if (fixed &&
|
|
17995
|
+
const fixed = home ? resolve19(home, ".claude", "bin", "claude-bg") : null;
|
|
17996
|
+
if (fixed && existsSync22(fixed)) return fixed;
|
|
17608
17997
|
const which = spawnSync3("which", ["claude-bg"], { encoding: "utf-8" });
|
|
17609
17998
|
if (which.status === 0 && which.stdout) {
|
|
17610
17999
|
const p19 = which.stdout.trim();
|
|
@@ -17665,7 +18054,7 @@ async function runWatch(args2) {
|
|
|
17665
18054
|
}
|
|
17666
18055
|
function runStatus(root) {
|
|
17667
18056
|
const path = watchStatePath(root);
|
|
17668
|
-
if (!
|
|
18057
|
+
if (!existsSync22(path)) {
|
|
17669
18058
|
process.stdout.write("massu watch: not running (no state file)\n");
|
|
17670
18059
|
return { exitCode: 0 };
|
|
17671
18060
|
}
|
|
@@ -17747,7 +18136,7 @@ async function runForeground(root) {
|
|
|
17747
18136
|
}
|
|
17748
18137
|
throw err;
|
|
17749
18138
|
}
|
|
17750
|
-
return new Promise((
|
|
18139
|
+
return new Promise((resolve42) => {
|
|
17751
18140
|
const shutdown = async () => {
|
|
17752
18141
|
if (stopped) return;
|
|
17753
18142
|
stopped = true;
|
|
@@ -17757,7 +18146,7 @@ async function runForeground(root) {
|
|
|
17757
18146
|
process.chdir(priorCwd);
|
|
17758
18147
|
} catch {
|
|
17759
18148
|
}
|
|
17760
|
-
|
|
18149
|
+
resolve42({ exitCode: 0 });
|
|
17761
18150
|
};
|
|
17762
18151
|
process.on("SIGINT", () => {
|
|
17763
18152
|
void shutdown();
|
|
@@ -17818,23 +18207,23 @@ async function runOnQuiescent(projectRoot) {
|
|
|
17818
18207
|
);
|
|
17819
18208
|
}
|
|
17820
18209
|
function refreshLogPath(projectRoot) {
|
|
17821
|
-
return
|
|
18210
|
+
return resolve19(projectRoot, ".massu", "refresh-log.jsonl");
|
|
17822
18211
|
}
|
|
17823
18212
|
function appendRefreshLog(projectRoot, event) {
|
|
17824
18213
|
const path = refreshLogPath(projectRoot);
|
|
17825
18214
|
try {
|
|
17826
|
-
|
|
18215
|
+
mkdirSync10(dirname12(path), { recursive: true });
|
|
17827
18216
|
appendFileSync(path, JSON.stringify(event) + "\n", "utf-8");
|
|
17828
18217
|
} catch {
|
|
17829
18218
|
}
|
|
17830
18219
|
}
|
|
17831
18220
|
function readRefreshLog(projectRoot, limit = 10, opts = {}) {
|
|
17832
18221
|
const path = refreshLogPath(projectRoot);
|
|
17833
|
-
if (!
|
|
18222
|
+
if (!existsSync22(path)) return [];
|
|
17834
18223
|
const warn = opts.warn ?? ((s) => {
|
|
17835
18224
|
process.stderr.write(s);
|
|
17836
18225
|
});
|
|
17837
|
-
const lines =
|
|
18226
|
+
const lines = readFileSync20(path, "utf-8").split("\n").filter(Boolean);
|
|
17838
18227
|
const tail = lines.slice(-limit);
|
|
17839
18228
|
const out = [];
|
|
17840
18229
|
let corrupt = 0;
|
|
@@ -17905,14 +18294,14 @@ var init_refresh_log = __esm({
|
|
|
17905
18294
|
|
|
17906
18295
|
// src/security/atomic-write.ts
|
|
17907
18296
|
import {
|
|
17908
|
-
chmodSync as
|
|
18297
|
+
chmodSync as chmodSync4,
|
|
17909
18298
|
closeSync as closeSync4,
|
|
17910
|
-
existsSync as
|
|
18299
|
+
existsSync as existsSync23,
|
|
17911
18300
|
fsyncSync as fsyncSync4,
|
|
17912
|
-
mkdirSync as
|
|
18301
|
+
mkdirSync as mkdirSync11,
|
|
17913
18302
|
openSync as openSync4,
|
|
17914
18303
|
renameSync as renameSync5,
|
|
17915
|
-
rmSync as
|
|
18304
|
+
rmSync as rmSync7,
|
|
17916
18305
|
statSync as statSync9,
|
|
17917
18306
|
writeSync as writeSync4
|
|
17918
18307
|
} from "node:fs";
|
|
@@ -17921,12 +18310,12 @@ function atomicWrite(path, content, opts = {}) {
|
|
|
17921
18310
|
const tmpPath = `${path}.tmp`;
|
|
17922
18311
|
const parentDir = dirname13(path);
|
|
17923
18312
|
try {
|
|
17924
|
-
if (!
|
|
18313
|
+
if (!existsSync23(parentDir)) {
|
|
17925
18314
|
const mkdirOpts = { recursive: true };
|
|
17926
18315
|
if (opts.ensureParentDirMode !== void 0) {
|
|
17927
18316
|
mkdirOpts.mode = opts.ensureParentDirMode;
|
|
17928
18317
|
}
|
|
17929
|
-
|
|
18318
|
+
mkdirSync11(parentDir, mkdirOpts);
|
|
17930
18319
|
}
|
|
17931
18320
|
const buf = typeof content === "string" ? Buffer.from(content, "utf-8") : content;
|
|
17932
18321
|
const openMode = opts.mode ?? 420;
|
|
@@ -17938,14 +18327,14 @@ function atomicWrite(path, content, opts = {}) {
|
|
|
17938
18327
|
closeSync4(fd);
|
|
17939
18328
|
}
|
|
17940
18329
|
if (opts.mode !== void 0) {
|
|
17941
|
-
|
|
18330
|
+
chmodSync4(tmpPath, opts.mode);
|
|
17942
18331
|
}
|
|
17943
18332
|
renameSync5(tmpPath, path);
|
|
17944
18333
|
return { written: true };
|
|
17945
18334
|
} catch (err) {
|
|
17946
|
-
if (
|
|
18335
|
+
if (existsSync23(tmpPath)) {
|
|
17947
18336
|
try {
|
|
17948
|
-
|
|
18337
|
+
rmSync7(tmpPath, { force: true });
|
|
17949
18338
|
} catch {
|
|
17950
18339
|
}
|
|
17951
18340
|
}
|
|
@@ -18292,24 +18681,24 @@ var init_fetcher = __esm({
|
|
|
18292
18681
|
});
|
|
18293
18682
|
|
|
18294
18683
|
// src/security/manifest-cache.ts
|
|
18295
|
-
import { existsSync as
|
|
18296
|
-
import { homedir as
|
|
18297
|
-
import { resolve as
|
|
18684
|
+
import { existsSync as existsSync24, readFileSync as readFileSync21, statSync as statSync10 } from "node:fs";
|
|
18685
|
+
import { homedir as homedir6 } from "node:os";
|
|
18686
|
+
import { resolve as resolve20 } from "node:path";
|
|
18298
18687
|
import { z as z4 } from "zod";
|
|
18299
18688
|
function defaultCachePaths() {
|
|
18300
|
-
const dir =
|
|
18689
|
+
const dir = resolve20(homedir6(), ".massu");
|
|
18301
18690
|
return {
|
|
18302
|
-
cachePath:
|
|
18303
|
-
lockPath:
|
|
18691
|
+
cachePath: resolve20(dir, "adapter-manifest.json"),
|
|
18692
|
+
lockPath: resolve20(dir, ".adapter-manifest.lock")
|
|
18304
18693
|
};
|
|
18305
18694
|
}
|
|
18306
18695
|
function loadCachedManifest(paths = defaultCachePaths()) {
|
|
18307
|
-
if (!
|
|
18696
|
+
if (!existsSync24(paths.cachePath)) {
|
|
18308
18697
|
return { kind: "absent" };
|
|
18309
18698
|
}
|
|
18310
18699
|
let raw;
|
|
18311
18700
|
try {
|
|
18312
|
-
const content =
|
|
18701
|
+
const content = readFileSync21(paths.cachePath, "utf-8");
|
|
18313
18702
|
raw = JSON.parse(content);
|
|
18314
18703
|
} catch (err) {
|
|
18315
18704
|
return {
|
|
@@ -18498,15 +18887,15 @@ var init_adapter_origin = __esm({
|
|
|
18498
18887
|
});
|
|
18499
18888
|
|
|
18500
18889
|
// src/security/local-fingerprint.ts
|
|
18501
|
-
import { existsSync as
|
|
18502
|
-
import { homedir as
|
|
18503
|
-
import { resolve as
|
|
18890
|
+
import { existsSync as existsSync25, readFileSync as readFileSync22, lstatSync as lstatSync5 } from "node:fs";
|
|
18891
|
+
import { homedir as homedir7 } from "node:os";
|
|
18892
|
+
import { resolve as resolve21, isAbsolute } from "node:path";
|
|
18504
18893
|
import { createHash as createHash7 } from "node:crypto";
|
|
18505
18894
|
import { z as z5 } from "zod";
|
|
18506
18895
|
function computeLocalFingerprint(localPaths, projectRoot) {
|
|
18507
18896
|
const tuples = [];
|
|
18508
18897
|
for (const p19 of localPaths) {
|
|
18509
|
-
const abs = isAbsolute(p19) ? p19 :
|
|
18898
|
+
const abs = isAbsolute(p19) ? p19 : resolve21(projectRoot, p19);
|
|
18510
18899
|
let contentTag;
|
|
18511
18900
|
try {
|
|
18512
18901
|
const lst = lstatSync5(abs);
|
|
@@ -18515,7 +18904,7 @@ function computeLocalFingerprint(localPaths, projectRoot) {
|
|
|
18515
18904
|
} else if (!lst.isFile()) {
|
|
18516
18905
|
contentTag = "<not-a-file>";
|
|
18517
18906
|
} else {
|
|
18518
|
-
contentTag = createHash7("sha256").update(
|
|
18907
|
+
contentTag = createHash7("sha256").update(readFileSync22(abs)).digest("hex");
|
|
18519
18908
|
}
|
|
18520
18909
|
} catch {
|
|
18521
18910
|
contentTag = "<missing>";
|
|
@@ -18527,10 +18916,10 @@ function computeLocalFingerprint(localPaths, projectRoot) {
|
|
|
18527
18916
|
return createHash7("sha256").update(canonical).digest("hex");
|
|
18528
18917
|
}
|
|
18529
18918
|
function readFingerprintSentinel(path = FINGERPRINT_PATH) {
|
|
18530
|
-
if (!
|
|
18919
|
+
if (!existsSync25(path)) return null;
|
|
18531
18920
|
let raw;
|
|
18532
18921
|
try {
|
|
18533
|
-
raw = JSON.parse(
|
|
18922
|
+
raw = JSON.parse(readFileSync22(path, "utf-8"));
|
|
18534
18923
|
} catch {
|
|
18535
18924
|
return null;
|
|
18536
18925
|
}
|
|
@@ -18578,7 +18967,7 @@ var init_local_fingerprint = __esm({
|
|
|
18578
18967
|
"use strict";
|
|
18579
18968
|
init_atomic_write();
|
|
18580
18969
|
init_manifest_schema();
|
|
18581
|
-
FINGERPRINT_PATH =
|
|
18970
|
+
FINGERPRINT_PATH = resolve21(homedir7(), ".massu", "adapters-local-fingerprint.json");
|
|
18582
18971
|
FingerprintSentinelSchema = z5.object({
|
|
18583
18972
|
fingerprint: z5.string().regex(/^[0-9a-f]{64}$/),
|
|
18584
18973
|
source: z5.enum(["cli", "cli-resync"]),
|
|
@@ -18594,15 +18983,15 @@ var init_local_fingerprint = __esm({
|
|
|
18594
18983
|
});
|
|
18595
18984
|
|
|
18596
18985
|
// src/security/install-tracking.ts
|
|
18597
|
-
import { readFileSync as
|
|
18986
|
+
import { readFileSync as readFileSync23, readdirSync as readdirSync13, lstatSync as lstatSync6, existsSync as existsSync26 } from "node:fs";
|
|
18598
18987
|
import { join as join12, relative as relative5, sep } from "node:path";
|
|
18599
|
-
import { homedir as
|
|
18600
|
-
import { resolve as
|
|
18988
|
+
import { homedir as homedir8 } from "node:os";
|
|
18989
|
+
import { resolve as resolve22 } from "node:path";
|
|
18601
18990
|
import { createHash as createHash8 } from "node:crypto";
|
|
18602
18991
|
import { z as z6 } from "zod";
|
|
18603
18992
|
function containsHiddenDirs(packageDir) {
|
|
18604
18993
|
for (const hidden of EXCLUDED_DIR_NAMES) {
|
|
18605
|
-
if (
|
|
18994
|
+
if (existsSync26(`${packageDir}/${hidden}`)) {
|
|
18606
18995
|
return hidden;
|
|
18607
18996
|
}
|
|
18608
18997
|
}
|
|
@@ -18648,7 +19037,7 @@ function sha256OfDir(dir, opts = {}) {
|
|
|
18648
19037
|
files.sort((a2, b2) => a2.relativePath < b2.relativePath ? -1 : a2.relativePath > b2.relativePath ? 1 : 0);
|
|
18649
19038
|
const top = createHash8("sha256");
|
|
18650
19039
|
for (const f2 of files) {
|
|
18651
|
-
const fileHash = createHash8("sha256").update(
|
|
19040
|
+
const fileHash = createHash8("sha256").update(readFileSync23(f2.absPath)).digest("hex");
|
|
18652
19041
|
top.update(f2.relativePath, "utf-8");
|
|
18653
19042
|
top.update("\0", "utf-8");
|
|
18654
19043
|
top.update(fileHash, "utf-8");
|
|
@@ -18657,10 +19046,10 @@ function sha256OfDir(dir, opts = {}) {
|
|
|
18657
19046
|
return top.digest("hex");
|
|
18658
19047
|
}
|
|
18659
19048
|
function readInstalledManifest(path = INSTALLED_MANIFEST_PATH) {
|
|
18660
|
-
if (!
|
|
19049
|
+
if (!existsSync26(path)) return {};
|
|
18661
19050
|
let raw;
|
|
18662
19051
|
try {
|
|
18663
|
-
raw = JSON.parse(
|
|
19052
|
+
raw = JSON.parse(readFileSync23(path, "utf-8"));
|
|
18664
19053
|
} catch {
|
|
18665
19054
|
return {};
|
|
18666
19055
|
}
|
|
@@ -18724,7 +19113,7 @@ var init_install_tracking = __esm({
|
|
|
18724
19113
|
"src/security/install-tracking.ts"() {
|
|
18725
19114
|
"use strict";
|
|
18726
19115
|
init_atomic_write();
|
|
18727
|
-
INSTALLED_MANIFEST_PATH =
|
|
19116
|
+
INSTALLED_MANIFEST_PATH = resolve22(homedir8(), ".massu", "adapter-manifest-installed.json");
|
|
18728
19117
|
DEFAULT_MAX_FILE_BYTES = 64 * 1024 * 1024;
|
|
18729
19118
|
EXCLUDED_DIR_NAMES = /* @__PURE__ */ new Set([".git", "node_modules", ".cache", ".tmp"]);
|
|
18730
19119
|
InstallEntrySchema = z6.object({
|
|
@@ -18747,12 +19136,12 @@ var init_install_tracking = __esm({
|
|
|
18747
19136
|
});
|
|
18748
19137
|
|
|
18749
19138
|
// src/detect/adapters/discover.ts
|
|
18750
|
-
import { existsSync as
|
|
18751
|
-
import { resolve as
|
|
19139
|
+
import { existsSync as existsSync27, readdirSync as readdirSync14, readFileSync as readFileSync24, lstatSync as lstatSync7 } from "node:fs";
|
|
19140
|
+
import { resolve as resolve23, isAbsolute as isAbsolute2 } from "node:path";
|
|
18752
19141
|
import { z as z7 } from "zod";
|
|
18753
19142
|
function walkNodeModules(projectRoot, warnings) {
|
|
18754
|
-
const nodeModulesDir =
|
|
18755
|
-
if (!
|
|
19143
|
+
const nodeModulesDir = resolve23(projectRoot, "node_modules");
|
|
19144
|
+
if (!existsSync27(nodeModulesDir)) {
|
|
18756
19145
|
return [];
|
|
18757
19146
|
}
|
|
18758
19147
|
const candidates = [];
|
|
@@ -18765,7 +19154,7 @@ function walkNodeModules(projectRoot, warnings) {
|
|
|
18765
19154
|
}
|
|
18766
19155
|
for (const entry of topLevelEntries) {
|
|
18767
19156
|
if (entry.startsWith(".")) continue;
|
|
18768
|
-
const entryPath =
|
|
19157
|
+
const entryPath = resolve23(nodeModulesDir, entry);
|
|
18769
19158
|
let entryStat;
|
|
18770
19159
|
try {
|
|
18771
19160
|
entryStat = lstatSync7(entryPath);
|
|
@@ -18787,7 +19176,7 @@ function walkNodeModules(projectRoot, warnings) {
|
|
|
18787
19176
|
continue;
|
|
18788
19177
|
}
|
|
18789
19178
|
for (const sub of scopedEntries) {
|
|
18790
|
-
const subPath =
|
|
19179
|
+
const subPath = resolve23(entryPath, sub);
|
|
18791
19180
|
let subStat;
|
|
18792
19181
|
try {
|
|
18793
19182
|
subStat = lstatSync7(subPath);
|
|
@@ -18806,11 +19195,11 @@ function walkNodeModules(projectRoot, warnings) {
|
|
|
18806
19195
|
return candidates;
|
|
18807
19196
|
}
|
|
18808
19197
|
function tryReadAdapterPackage(packageDir, warnings) {
|
|
18809
|
-
const pkgJsonPath =
|
|
18810
|
-
if (!
|
|
19198
|
+
const pkgJsonPath = resolve23(packageDir, "package.json");
|
|
19199
|
+
if (!existsSync27(pkgJsonPath)) return null;
|
|
18811
19200
|
let raw;
|
|
18812
19201
|
try {
|
|
18813
|
-
raw = JSON.parse(
|
|
19202
|
+
raw = JSON.parse(readFileSync24(pkgJsonPath, "utf-8"));
|
|
18814
19203
|
} catch (err) {
|
|
18815
19204
|
warnings.push(
|
|
18816
19205
|
`skipping ${packageDir}: package.json parse failed (${err instanceof Error ? err.message : String(err)})`
|
|
@@ -18944,8 +19333,8 @@ function discoverAdapters(opts) {
|
|
|
18944
19333
|
const localSet = new Set(opts.configLocalPaths);
|
|
18945
19334
|
for (const localPath of opts.configLocalPaths) {
|
|
18946
19335
|
if (seenIds.has(localPath)) continue;
|
|
18947
|
-
const absPath = isAbsolute2(localPath) ? localPath :
|
|
18948
|
-
if (!
|
|
19336
|
+
const absPath = isAbsolute2(localPath) ? localPath : resolve23(opts.projectRoot, localPath);
|
|
19337
|
+
if (!existsSync27(absPath)) {
|
|
18949
19338
|
warnings.push(
|
|
18950
19339
|
`local adapter file not found: ${localPath} (resolved to ${absPath}). Remove via: massu adapters remove-local ${localPath}`
|
|
18951
19340
|
);
|
|
@@ -19027,8 +19416,8 @@ __export(adapters_exports, {
|
|
|
19027
19416
|
runAdaptersResyncLocalFingerprint: () => runAdaptersResyncLocalFingerprint,
|
|
19028
19417
|
runAdaptersSearch: () => runAdaptersSearch
|
|
19029
19418
|
});
|
|
19030
|
-
import { existsSync as
|
|
19031
|
-
import { resolve as
|
|
19419
|
+
import { existsSync as existsSync28, readFileSync as readFileSync25 } from "node:fs";
|
|
19420
|
+
import { resolve as resolve24 } from "node:path";
|
|
19032
19421
|
import { parseDocument } from "yaml";
|
|
19033
19422
|
async function handleAdaptersSubcommand(args2) {
|
|
19034
19423
|
const sub = args2[0];
|
|
@@ -19295,8 +19684,8 @@ function mutateLocalArray(mutator, command) {
|
|
|
19295
19684
|
);
|
|
19296
19685
|
return { exitCode: 2 };
|
|
19297
19686
|
}
|
|
19298
|
-
const yamlPath =
|
|
19299
|
-
if (!
|
|
19687
|
+
const yamlPath = resolve24(projectRoot, "massu.config.yaml");
|
|
19688
|
+
if (!existsSync28(yamlPath)) {
|
|
19300
19689
|
process.stderr.write(
|
|
19301
19690
|
`${command}: massu.config.yaml not found at ${yamlPath}. Run \`massu init\` first.
|
|
19302
19691
|
`
|
|
@@ -19305,7 +19694,7 @@ function mutateLocalArray(mutator, command) {
|
|
|
19305
19694
|
}
|
|
19306
19695
|
let yamlText;
|
|
19307
19696
|
try {
|
|
19308
|
-
yamlText =
|
|
19697
|
+
yamlText = readFileSync25(yamlPath, "utf-8");
|
|
19309
19698
|
} catch (err) {
|
|
19310
19699
|
process.stderr.write(`${command}: failed to read ${yamlPath}: ${err instanceof Error ? err.message : String(err)}
|
|
19311
19700
|
`);
|
|
@@ -19326,7 +19715,7 @@ function mutateLocalArray(mutator, command) {
|
|
|
19326
19715
|
if (next === null) {
|
|
19327
19716
|
return { exitCode: 0 };
|
|
19328
19717
|
}
|
|
19329
|
-
const lockPath =
|
|
19718
|
+
const lockPath = resolve24(projectRoot, ".massu", "adapters-local-mutate.lock");
|
|
19330
19719
|
return withFileLockSync(lockPath, () => {
|
|
19331
19720
|
doc.setIn(["adapters", "local"], next);
|
|
19332
19721
|
const newYaml = doc.toString();
|
|
@@ -19395,23 +19784,23 @@ async function runAdaptersInstall(args2) {
|
|
|
19395
19784
|
`);
|
|
19396
19785
|
return { exitCode: 1 };
|
|
19397
19786
|
}
|
|
19398
|
-
const packageDir =
|
|
19399
|
-
if (!
|
|
19787
|
+
const packageDir = resolve24(projectRoot, "node_modules", ...packageName.split("/"));
|
|
19788
|
+
if (!existsSync28(packageDir)) {
|
|
19400
19789
|
process.stderr.write(
|
|
19401
19790
|
`install: ${packageName} is not installed in node_modules. Run \`npm install ${packageName}\` first.
|
|
19402
19791
|
`
|
|
19403
19792
|
);
|
|
19404
19793
|
return { exitCode: 1 };
|
|
19405
19794
|
}
|
|
19406
|
-
const pkgJsonPath =
|
|
19407
|
-
if (!
|
|
19795
|
+
const pkgJsonPath = resolve24(packageDir, "package.json");
|
|
19796
|
+
if (!existsSync28(pkgJsonPath)) {
|
|
19408
19797
|
process.stderr.write(`install: ${packageName} has no package.json at ${pkgJsonPath}
|
|
19409
19798
|
`);
|
|
19410
19799
|
return { exitCode: 1 };
|
|
19411
19800
|
}
|
|
19412
19801
|
let pkgJson;
|
|
19413
19802
|
try {
|
|
19414
|
-
pkgJson = JSON.parse(
|
|
19803
|
+
pkgJson = JSON.parse(readFileSync25(pkgJsonPath, "utf-8"));
|
|
19415
19804
|
} catch (err) {
|
|
19416
19805
|
process.stderr.write(`install: ${packageName} has malformed package.json: ${err instanceof Error ? err.message : String(err)}
|
|
19417
19806
|
`);
|
|
@@ -19530,8 +19919,8 @@ async function runAdaptersResign(_args) {
|
|
|
19530
19919
|
removeInstalledManifestEntry(name);
|
|
19531
19920
|
continue;
|
|
19532
19921
|
}
|
|
19533
|
-
const packageDir =
|
|
19534
|
-
if (!
|
|
19922
|
+
const packageDir = resolve24(projectRoot, "node_modules", ...name.split("/"));
|
|
19923
|
+
if (!existsSync28(packageDir)) {
|
|
19535
19924
|
removed++;
|
|
19536
19925
|
warnings.push(`${name}@${entry.version}: not present in node_modules \u2014 REMOVED from sidecar`);
|
|
19537
19926
|
removeInstalledManifestEntry(name);
|
|
@@ -19630,10 +20019,10 @@ var init_sql_table_names = __esm({
|
|
|
19630
20019
|
// src/db.ts
|
|
19631
20020
|
import Database2 from "better-sqlite3";
|
|
19632
20021
|
import { dirname as dirname14, join as join13 } from "path";
|
|
19633
|
-
import { existsSync as
|
|
20022
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync12, readdirSync as readdirSync15, statSync as statSync11 } from "fs";
|
|
19634
20023
|
function getCodeGraphDb() {
|
|
19635
20024
|
const dbPath = getResolvedPaths().codegraphDbPath;
|
|
19636
|
-
if (!
|
|
20025
|
+
if (!existsSync29(dbPath)) {
|
|
19637
20026
|
throw new CodegraphDbNotInitializedError(dbPath);
|
|
19638
20027
|
}
|
|
19639
20028
|
const db = new Database2(dbPath, { readonly: true });
|
|
@@ -19643,8 +20032,8 @@ function getCodeGraphDb() {
|
|
|
19643
20032
|
function getDataDb() {
|
|
19644
20033
|
const dbPath = getResolvedPaths().dataDbPath;
|
|
19645
20034
|
const dir = dirname14(dbPath);
|
|
19646
|
-
if (!
|
|
19647
|
-
|
|
20035
|
+
if (!existsSync29(dir)) {
|
|
20036
|
+
mkdirSync12(dir, { recursive: true });
|
|
19648
20037
|
}
|
|
19649
20038
|
const db = new Database2(dbPath);
|
|
19650
20039
|
db.pragma("journal_mode = WAL");
|
|
@@ -19953,12 +20342,12 @@ var init_db = __esm({
|
|
|
19953
20342
|
// src/knowledge-db.ts
|
|
19954
20343
|
import Database3 from "better-sqlite3";
|
|
19955
20344
|
import { dirname as dirname15 } from "path";
|
|
19956
|
-
import { existsSync as
|
|
20345
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync13 } from "fs";
|
|
19957
20346
|
function getKnowledgeDb() {
|
|
19958
20347
|
const dbPath = getResolvedPaths().knowledgeDbPath;
|
|
19959
20348
|
const dir = dirname15(dbPath);
|
|
19960
|
-
if (!
|
|
19961
|
-
|
|
20349
|
+
if (!existsSync30(dir)) {
|
|
20350
|
+
mkdirSync13(dir, { recursive: true });
|
|
19962
20351
|
}
|
|
19963
20352
|
const db = new Database3(dbPath);
|
|
19964
20353
|
db.pragma("journal_mode = WAL");
|
|
@@ -20129,10 +20518,10 @@ var init_knowledge_db = __esm({
|
|
|
20129
20518
|
});
|
|
20130
20519
|
|
|
20131
20520
|
// src/security-utils.ts
|
|
20132
|
-
import { resolve as
|
|
20521
|
+
import { resolve as resolve25, normalize } from "path";
|
|
20133
20522
|
function ensureWithinRoot(filePath, projectRoot) {
|
|
20134
|
-
const resolvedRoot =
|
|
20135
|
-
const resolvedPath =
|
|
20523
|
+
const resolvedRoot = resolve25(projectRoot);
|
|
20524
|
+
const resolvedPath = resolve25(resolvedRoot, filePath);
|
|
20136
20525
|
const normalizedPath = normalize(resolvedPath);
|
|
20137
20526
|
const normalizedRoot = normalize(resolvedRoot);
|
|
20138
20527
|
if (!normalizedPath.startsWith(normalizedRoot + "/") && normalizedPath !== normalizedRoot) {
|
|
@@ -20205,8 +20594,8 @@ var init_rules = __esm({
|
|
|
20205
20594
|
});
|
|
20206
20595
|
|
|
20207
20596
|
// src/import-resolver.ts
|
|
20208
|
-
import { readFileSync as
|
|
20209
|
-
import { resolve as
|
|
20597
|
+
import { readFileSync as readFileSync26, existsSync as existsSync31, statSync as statSync12 } from "fs";
|
|
20598
|
+
import { resolve as resolve26, dirname as dirname16, join as join14 } from "path";
|
|
20210
20599
|
function parseImports(source) {
|
|
20211
20600
|
const imports = [];
|
|
20212
20601
|
const lines = source.split("\n");
|
|
@@ -20262,23 +20651,23 @@ function resolveImportPath(specifier, fromFile) {
|
|
|
20262
20651
|
let basePath;
|
|
20263
20652
|
if (specifier.startsWith("@/")) {
|
|
20264
20653
|
const paths = getResolvedPaths();
|
|
20265
|
-
basePath =
|
|
20654
|
+
basePath = resolve26(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
|
|
20266
20655
|
} else {
|
|
20267
|
-
basePath =
|
|
20656
|
+
basePath = resolve26(dirname16(fromFile), specifier);
|
|
20268
20657
|
}
|
|
20269
|
-
if (
|
|
20658
|
+
if (existsSync31(basePath) && !isDirectory(basePath)) {
|
|
20270
20659
|
return toRelative(basePath);
|
|
20271
20660
|
}
|
|
20272
20661
|
const resolvedPaths = getResolvedPaths();
|
|
20273
20662
|
for (const ext of resolvedPaths.extensions) {
|
|
20274
20663
|
const withExt = basePath + ext;
|
|
20275
|
-
if (
|
|
20664
|
+
if (existsSync31(withExt)) {
|
|
20276
20665
|
return toRelative(withExt);
|
|
20277
20666
|
}
|
|
20278
20667
|
}
|
|
20279
20668
|
for (const indexFile of resolvedPaths.indexFiles) {
|
|
20280
20669
|
const indexPath = join14(basePath, indexFile);
|
|
20281
|
-
if (
|
|
20670
|
+
if (existsSync31(indexPath)) {
|
|
20282
20671
|
return toRelative(indexPath);
|
|
20283
20672
|
}
|
|
20284
20673
|
}
|
|
@@ -20314,11 +20703,11 @@ function buildImportIndex(dataDb, codegraphDb) {
|
|
|
20314
20703
|
const batchSize = 500;
|
|
20315
20704
|
let batch = [];
|
|
20316
20705
|
for (const file of files) {
|
|
20317
|
-
const absPath = ensureWithinRoot(
|
|
20318
|
-
if (!
|
|
20706
|
+
const absPath = ensureWithinRoot(resolve26(projectRoot, file.path), projectRoot);
|
|
20707
|
+
if (!existsSync31(absPath)) continue;
|
|
20319
20708
|
let source;
|
|
20320
20709
|
try {
|
|
20321
|
-
source =
|
|
20710
|
+
source = readFileSync26(absPath, "utf-8");
|
|
20322
20711
|
} catch {
|
|
20323
20712
|
continue;
|
|
20324
20713
|
}
|
|
@@ -20355,15 +20744,15 @@ var init_import_resolver = __esm({
|
|
|
20355
20744
|
});
|
|
20356
20745
|
|
|
20357
20746
|
// src/trpc-index.ts
|
|
20358
|
-
import { readFileSync as
|
|
20359
|
-
import { resolve as
|
|
20747
|
+
import { readFileSync as readFileSync27, existsSync as existsSync32, readdirSync as readdirSync16 } from "fs";
|
|
20748
|
+
import { resolve as resolve27, join as join15 } from "path";
|
|
20360
20749
|
function parseRootRouter() {
|
|
20361
20750
|
const paths = getResolvedPaths();
|
|
20362
20751
|
const rootPath = paths.rootRouterPath;
|
|
20363
|
-
if (!
|
|
20752
|
+
if (!existsSync32(rootPath)) {
|
|
20364
20753
|
throw new Error(`Root router not found at ${rootPath}`);
|
|
20365
20754
|
}
|
|
20366
|
-
const source =
|
|
20755
|
+
const source = readFileSync27(rootPath, "utf-8");
|
|
20367
20756
|
const mappings = [];
|
|
20368
20757
|
const importMap = /* @__PURE__ */ new Map();
|
|
20369
20758
|
const importRegex = /import\s+\{[^}]*?(\w+Router)[^}]*\}\s+from\s+['"]\.\/routers\/([^'"]+)['"]/g;
|
|
@@ -20371,16 +20760,16 @@ function parseRootRouter() {
|
|
|
20371
20760
|
while ((match = importRegex.exec(source)) !== null) {
|
|
20372
20761
|
const variable = match[1];
|
|
20373
20762
|
let filePath = match[2];
|
|
20374
|
-
const fullPath =
|
|
20763
|
+
const fullPath = resolve27(paths.routersDir, filePath);
|
|
20375
20764
|
for (const ext of [".ts", ".tsx", ""]) {
|
|
20376
20765
|
const candidate = fullPath + ext;
|
|
20377
20766
|
const routersRelPath = getConfig().paths.routers ?? "src/server/api/routers";
|
|
20378
|
-
if (
|
|
20767
|
+
if (existsSync32(candidate)) {
|
|
20379
20768
|
filePath = routersRelPath + "/" + filePath + ext;
|
|
20380
20769
|
break;
|
|
20381
20770
|
}
|
|
20382
20771
|
const indexCandidate = join15(fullPath, "index.ts");
|
|
20383
|
-
if (
|
|
20772
|
+
if (existsSync32(indexCandidate)) {
|
|
20384
20773
|
filePath = routersRelPath + "/" + filePath + "/index.ts";
|
|
20385
20774
|
break;
|
|
20386
20775
|
}
|
|
@@ -20399,9 +20788,9 @@ function parseRootRouter() {
|
|
|
20399
20788
|
return mappings;
|
|
20400
20789
|
}
|
|
20401
20790
|
function extractProcedures(routerFilePath) {
|
|
20402
|
-
const absPath =
|
|
20403
|
-
if (!
|
|
20404
|
-
const source =
|
|
20791
|
+
const absPath = resolve27(getProjectRoot(), routerFilePath);
|
|
20792
|
+
if (!existsSync32(absPath)) return [];
|
|
20793
|
+
const source = readFileSync27(absPath, "utf-8");
|
|
20405
20794
|
const procedures = [];
|
|
20406
20795
|
const seen = /* @__PURE__ */ new Set();
|
|
20407
20796
|
const procRegex = /(\w+)\s*:\s*(protected|public)Procedure/g;
|
|
@@ -20424,13 +20813,13 @@ function findUICallSites(routerKey, procedureName) {
|
|
|
20424
20813
|
const root = getProjectRoot();
|
|
20425
20814
|
const src = config.paths.source;
|
|
20426
20815
|
const searchDirs = [
|
|
20427
|
-
|
|
20428
|
-
|
|
20429
|
-
|
|
20816
|
+
resolve27(root, config.paths.pages ?? src + "/app"),
|
|
20817
|
+
resolve27(root, config.paths.components ?? src + "/components"),
|
|
20818
|
+
resolve27(root, config.paths.hooks ?? src + "/hooks")
|
|
20430
20819
|
];
|
|
20431
20820
|
const searchPattern = `api.${routerKey}.${procedureName}`;
|
|
20432
20821
|
for (const dir of searchDirs) {
|
|
20433
|
-
if (!
|
|
20822
|
+
if (!existsSync32(dir)) continue;
|
|
20434
20823
|
searchDirectory(dir, searchPattern, callSites);
|
|
20435
20824
|
}
|
|
20436
20825
|
return callSites;
|
|
@@ -20444,7 +20833,7 @@ function searchDirectory(dir, pattern, results) {
|
|
|
20444
20833
|
searchDirectory(fullPath, pattern, results);
|
|
20445
20834
|
} else if (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx")) {
|
|
20446
20835
|
try {
|
|
20447
|
-
const source =
|
|
20836
|
+
const source = readFileSync27(fullPath, "utf-8");
|
|
20448
20837
|
const lines = source.split("\n");
|
|
20449
20838
|
for (let i = 0; i < lines.length; i++) {
|
|
20450
20839
|
if (lines[i].includes(pattern)) {
|
|
@@ -20508,8 +20897,8 @@ var init_trpc_index = __esm({
|
|
|
20508
20897
|
});
|
|
20509
20898
|
|
|
20510
20899
|
// src/page-deps.ts
|
|
20511
|
-
import { readFileSync as
|
|
20512
|
-
import { resolve as
|
|
20900
|
+
import { readFileSync as readFileSync28, existsSync as existsSync33 } from "fs";
|
|
20901
|
+
import { resolve as resolve28 } from "path";
|
|
20513
20902
|
function deriveRoute(pageFile) {
|
|
20514
20903
|
let route = pageFile.replace(/^src\/app/, "").replace(/\/page\.tsx?$/, "").replace(/\/page\.jsx?$/, "");
|
|
20515
20904
|
return route || "/";
|
|
@@ -20547,10 +20936,10 @@ function findRouterCalls(files) {
|
|
|
20547
20936
|
const routers = /* @__PURE__ */ new Set();
|
|
20548
20937
|
const projectRoot = getProjectRoot();
|
|
20549
20938
|
for (const file of files) {
|
|
20550
|
-
const absPath = ensureWithinRoot(
|
|
20551
|
-
if (!
|
|
20939
|
+
const absPath = ensureWithinRoot(resolve28(projectRoot, file), projectRoot);
|
|
20940
|
+
if (!existsSync33(absPath)) continue;
|
|
20552
20941
|
try {
|
|
20553
|
-
const source =
|
|
20942
|
+
const source = readFileSync28(absPath, "utf-8");
|
|
20554
20943
|
const apiCallRegex = /api\.(\w+)\.\w+/g;
|
|
20555
20944
|
let match;
|
|
20556
20945
|
while ((match = apiCallRegex.exec(source)) !== null) {
|
|
@@ -20568,10 +20957,10 @@ function findTablesFromRouters(routerNames, dataDb) {
|
|
|
20568
20957
|
`SELECT DISTINCT router_file FROM ${t("trpc_procedures")} WHERE router_name = ? LIMIT 10000`
|
|
20569
20958
|
).all(routerName);
|
|
20570
20959
|
for (const proc of procs) {
|
|
20571
|
-
const absPath = ensureWithinRoot(
|
|
20572
|
-
if (!
|
|
20960
|
+
const absPath = ensureWithinRoot(resolve28(getProjectRoot(), proc.router_file), getProjectRoot());
|
|
20961
|
+
if (!existsSync33(absPath)) continue;
|
|
20573
20962
|
try {
|
|
20574
|
-
const source =
|
|
20963
|
+
const source = readFileSync28(absPath, "utf-8");
|
|
20575
20964
|
const dbPattern = getConfig().dbAccessPattern ?? "ctx.db.{table}";
|
|
20576
20965
|
const regexStr = dbPattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace("\\{table\\}", "(\\w+)");
|
|
20577
20966
|
const tableRegex = new RegExp(regexStr + "\\.", "g");
|
|
@@ -20843,14 +21232,14 @@ var init_domains = __esm({
|
|
|
20843
21232
|
});
|
|
20844
21233
|
|
|
20845
21234
|
// src/schema-mapper.ts
|
|
20846
|
-
import { readFileSync as
|
|
21235
|
+
import { readFileSync as readFileSync29, existsSync as existsSync34, readdirSync as readdirSync17 } from "fs";
|
|
20847
21236
|
import { join as join16 } from "path";
|
|
20848
21237
|
function parsePrismaSchema() {
|
|
20849
21238
|
const schemaPath = getResolvedPaths().prismaSchemaPath;
|
|
20850
|
-
if (!
|
|
21239
|
+
if (!existsSync34(schemaPath)) {
|
|
20851
21240
|
throw new Error(`Prisma schema not found at ${schemaPath}`);
|
|
20852
21241
|
}
|
|
20853
|
-
const source =
|
|
21242
|
+
const source = readFileSync29(schemaPath, "utf-8");
|
|
20854
21243
|
const models = [];
|
|
20855
21244
|
const sourceLines = source.split("\n");
|
|
20856
21245
|
let i = 0;
|
|
@@ -20908,7 +21297,7 @@ function toSnakeCase(str) {
|
|
|
20908
21297
|
function findColumnUsageInRouters(tableName) {
|
|
20909
21298
|
const usage = /* @__PURE__ */ new Map();
|
|
20910
21299
|
const routersDir = getResolvedPaths().routersDir;
|
|
20911
|
-
if (!
|
|
21300
|
+
if (!existsSync34(routersDir)) return usage;
|
|
20912
21301
|
scanDirectory(routersDir, tableName, usage);
|
|
20913
21302
|
return usage;
|
|
20914
21303
|
}
|
|
@@ -20925,7 +21314,7 @@ function scanDirectory(dir, tableName, usage) {
|
|
|
20925
21314
|
}
|
|
20926
21315
|
function scanFile(absPath, tableName, usage) {
|
|
20927
21316
|
try {
|
|
20928
|
-
const source =
|
|
21317
|
+
const source = readFileSync29(absPath, "utf-8");
|
|
20929
21318
|
if (!source.includes(tableName)) return;
|
|
20930
21319
|
const relPath = absPath.slice(getProjectRoot().length + 1);
|
|
20931
21320
|
const lines = source.split("\n");
|
|
@@ -20970,7 +21359,7 @@ function detectMismatches(models) {
|
|
|
20970
21359
|
}
|
|
20971
21360
|
function findFilesUsingColumn(dir, column, tableName) {
|
|
20972
21361
|
const result = [];
|
|
20973
|
-
if (!
|
|
21362
|
+
if (!existsSync34(dir)) return result;
|
|
20974
21363
|
const entries = readdirSync17(dir, { withFileTypes: true });
|
|
20975
21364
|
for (const entry of entries) {
|
|
20976
21365
|
const fullPath = join16(dir, entry.name);
|
|
@@ -20978,7 +21367,7 @@ function findFilesUsingColumn(dir, column, tableName) {
|
|
|
20978
21367
|
result.push(...findFilesUsingColumn(fullPath, column, tableName));
|
|
20979
21368
|
} else if (entry.name.endsWith(".ts")) {
|
|
20980
21369
|
try {
|
|
20981
|
-
const source =
|
|
21370
|
+
const source = readFileSync29(fullPath, "utf-8");
|
|
20982
21371
|
if (source.includes(tableName) && source.includes(column)) {
|
|
20983
21372
|
result.push(fullPath.slice(getProjectRoot().length + 1));
|
|
20984
21373
|
}
|
|
@@ -21123,12 +21512,12 @@ var init_import_parser = __esm({
|
|
|
21123
21512
|
});
|
|
21124
21513
|
|
|
21125
21514
|
// src/python/import-resolver.ts
|
|
21126
|
-
import { readFileSync as
|
|
21127
|
-
import { resolve as
|
|
21515
|
+
import { readFileSync as readFileSync30, existsSync as existsSync35, readdirSync as readdirSync18 } from "fs";
|
|
21516
|
+
import { resolve as resolve30, join as join17, relative as relative6, dirname as dirname17 } from "path";
|
|
21128
21517
|
function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
|
|
21129
21518
|
const projectRoot = getProjectRoot();
|
|
21130
21519
|
if (level > 0) {
|
|
21131
|
-
let baseDir = dirname17(
|
|
21520
|
+
let baseDir = dirname17(resolve30(projectRoot, fromFile));
|
|
21132
21521
|
for (let i = 1; i < level; i++) {
|
|
21133
21522
|
baseDir = dirname17(baseDir);
|
|
21134
21523
|
}
|
|
@@ -21140,17 +21529,17 @@ function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
|
|
|
21140
21529
|
return tryResolvePythonPath(baseDir, projectRoot);
|
|
21141
21530
|
}
|
|
21142
21531
|
const parts = module.split(".");
|
|
21143
|
-
const candidate = join17(
|
|
21532
|
+
const candidate = join17(resolve30(projectRoot, pythonRoot), ...parts);
|
|
21144
21533
|
return tryResolvePythonPath(candidate, projectRoot);
|
|
21145
21534
|
}
|
|
21146
21535
|
function tryResolvePythonPath(basePath, projectRoot) {
|
|
21147
|
-
if (
|
|
21536
|
+
if (existsSync35(basePath + ".py")) {
|
|
21148
21537
|
return relative6(projectRoot, basePath + ".py");
|
|
21149
21538
|
}
|
|
21150
|
-
if (
|
|
21539
|
+
if (existsSync35(join17(basePath, "__init__.py"))) {
|
|
21151
21540
|
return relative6(projectRoot, join17(basePath, "__init__.py"));
|
|
21152
21541
|
}
|
|
21153
|
-
if (basePath.endsWith(".py") &&
|
|
21542
|
+
if (basePath.endsWith(".py") && existsSync35(basePath)) {
|
|
21154
21543
|
return relative6(projectRoot, basePath);
|
|
21155
21544
|
}
|
|
21156
21545
|
return null;
|
|
@@ -21173,7 +21562,7 @@ function walkPythonFiles(dir, excludeDirs) {
|
|
|
21173
21562
|
}
|
|
21174
21563
|
function buildPythonImportIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
|
|
21175
21564
|
const projectRoot = getProjectRoot();
|
|
21176
|
-
const absRoot =
|
|
21565
|
+
const absRoot = resolve30(projectRoot, pythonRoot);
|
|
21177
21566
|
dataDb.exec(`DELETE FROM ${t("py_imports")}`);
|
|
21178
21567
|
const insertStmt = dataDb.prepare(
|
|
21179
21568
|
`INSERT INTO ${t("py_imports")} (source_file, target_file, import_type, imported_names, line) VALUES (?, ?, ?, ?, ?)`
|
|
@@ -21190,7 +21579,7 @@ function buildPythonImportIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__"
|
|
|
21190
21579
|
const relFile = relative6(projectRoot, absFile);
|
|
21191
21580
|
let source;
|
|
21192
21581
|
try {
|
|
21193
|
-
source =
|
|
21582
|
+
source = readFileSync30(absFile, "utf-8");
|
|
21194
21583
|
} catch {
|
|
21195
21584
|
continue;
|
|
21196
21585
|
}
|
|
@@ -21457,7 +21846,7 @@ var init_route_parser = __esm({
|
|
|
21457
21846
|
});
|
|
21458
21847
|
|
|
21459
21848
|
// src/python/route-indexer.ts
|
|
21460
|
-
import { readFileSync as
|
|
21849
|
+
import { readFileSync as readFileSync31, readdirSync as readdirSync19 } from "fs";
|
|
21461
21850
|
import { join as join18, relative as relative7 } from "path";
|
|
21462
21851
|
function walkPyFiles(dir, excludeDirs) {
|
|
21463
21852
|
const files = [];
|
|
@@ -21490,7 +21879,7 @@ function buildPythonRouteIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__",
|
|
|
21490
21879
|
const relFile = relative7(projectRoot, absFile);
|
|
21491
21880
|
let source;
|
|
21492
21881
|
try {
|
|
21493
|
-
source =
|
|
21882
|
+
source = readFileSync31(absFile, "utf-8");
|
|
21494
21883
|
} catch {
|
|
21495
21884
|
continue;
|
|
21496
21885
|
}
|
|
@@ -21702,7 +22091,7 @@ var init_model_parser = __esm({
|
|
|
21702
22091
|
});
|
|
21703
22092
|
|
|
21704
22093
|
// src/python/model-indexer.ts
|
|
21705
|
-
import { readFileSync as
|
|
22094
|
+
import { readFileSync as readFileSync32, readdirSync as readdirSync20 } from "fs";
|
|
21706
22095
|
import { join as join19, relative as relative8 } from "path";
|
|
21707
22096
|
function walkPyFiles2(dir, excludeDirs) {
|
|
21708
22097
|
const files = [];
|
|
@@ -21738,7 +22127,7 @@ function buildPythonModelIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__",
|
|
|
21738
22127
|
const relFile = relative8(projectRoot, absFile);
|
|
21739
22128
|
let source;
|
|
21740
22129
|
try {
|
|
21741
|
-
source =
|
|
22130
|
+
source = readFileSync32(absFile, "utf-8");
|
|
21742
22131
|
} catch {
|
|
21743
22132
|
continue;
|
|
21744
22133
|
}
|
|
@@ -21999,7 +22388,7 @@ var init_migration_parser = __esm({
|
|
|
21999
22388
|
});
|
|
22000
22389
|
|
|
22001
22390
|
// src/python/migration-indexer.ts
|
|
22002
|
-
import { readFileSync as
|
|
22391
|
+
import { readFileSync as readFileSync33, readdirSync as readdirSync21 } from "fs";
|
|
22003
22392
|
import { join as join20, relative as relative9 } from "path";
|
|
22004
22393
|
function buildPythonMigrationIndex(dataDb, alembicDir) {
|
|
22005
22394
|
const projectRoot = getProjectRoot();
|
|
@@ -22025,7 +22414,7 @@ function buildPythonMigrationIndex(dataDb, alembicDir) {
|
|
|
22025
22414
|
for (const absFile of files) {
|
|
22026
22415
|
let source;
|
|
22027
22416
|
try {
|
|
22028
|
-
source =
|
|
22417
|
+
source = readFileSync33(absFile, "utf-8");
|
|
22029
22418
|
} catch {
|
|
22030
22419
|
continue;
|
|
22031
22420
|
}
|
|
@@ -22060,7 +22449,7 @@ var init_migration_indexer = __esm({
|
|
|
22060
22449
|
});
|
|
22061
22450
|
|
|
22062
22451
|
// src/python/coupling-detector.ts
|
|
22063
|
-
import { readFileSync as
|
|
22452
|
+
import { readFileSync as readFileSync34, readdirSync as readdirSync22 } from "fs";
|
|
22064
22453
|
import { join as join21, relative as relative10 } from "path";
|
|
22065
22454
|
function buildPythonCouplingIndex(dataDb) {
|
|
22066
22455
|
const projectRoot = getProjectRoot();
|
|
@@ -22097,7 +22486,7 @@ function buildPythonCouplingIndex(dataDb) {
|
|
|
22097
22486
|
const relFile = relative10(projectRoot, absFile);
|
|
22098
22487
|
let source;
|
|
22099
22488
|
try {
|
|
22100
|
-
source =
|
|
22489
|
+
source = readFileSync34(absFile, "utf-8");
|
|
22101
22490
|
} catch {
|
|
22102
22491
|
continue;
|
|
22103
22492
|
}
|
|
@@ -22503,8 +22892,8 @@ var init_memory_tools = __esm({
|
|
|
22503
22892
|
});
|
|
22504
22893
|
|
|
22505
22894
|
// src/docs-tools.ts
|
|
22506
|
-
import { readFileSync as
|
|
22507
|
-
import { resolve as
|
|
22895
|
+
import { readFileSync as readFileSync35, existsSync as existsSync36 } from "fs";
|
|
22896
|
+
import { resolve as resolve31, basename as basename6 } from "path";
|
|
22508
22897
|
function p3(baseName) {
|
|
22509
22898
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
22510
22899
|
}
|
|
@@ -22559,10 +22948,10 @@ function handleDocsToolCall(name, args2) {
|
|
|
22559
22948
|
}
|
|
22560
22949
|
function loadDocsMap() {
|
|
22561
22950
|
const mapPath = getResolvedPaths().docsMapPath;
|
|
22562
|
-
if (!
|
|
22951
|
+
if (!existsSync36(mapPath)) {
|
|
22563
22952
|
throw new Error(`docs-map.json not found at ${mapPath}`);
|
|
22564
22953
|
}
|
|
22565
|
-
return JSON.parse(
|
|
22954
|
+
return JSON.parse(readFileSync35(mapPath, "utf-8"));
|
|
22566
22955
|
}
|
|
22567
22956
|
function matchesPattern(filePath, pattern) {
|
|
22568
22957
|
const regexStr = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
@@ -22632,13 +23021,13 @@ function extractFrontmatter(content) {
|
|
|
22632
23021
|
}
|
|
22633
23022
|
function extractProcedureNames(routerPath) {
|
|
22634
23023
|
const root = getProjectRoot();
|
|
22635
|
-
const absPath = ensureWithinRoot(
|
|
22636
|
-
if (!
|
|
22637
|
-
const altPath = ensureWithinRoot(
|
|
22638
|
-
if (!
|
|
22639
|
-
return extractProcedureNamesFromContent(
|
|
23024
|
+
const absPath = ensureWithinRoot(resolve31(getResolvedPaths().srcDir, "..", routerPath), root);
|
|
23025
|
+
if (!existsSync36(absPath)) {
|
|
23026
|
+
const altPath = ensureWithinRoot(resolve31(getResolvedPaths().srcDir, "../server/api/routers", basename6(routerPath)), root);
|
|
23027
|
+
if (!existsSync36(altPath)) return [];
|
|
23028
|
+
return extractProcedureNamesFromContent(readFileSync35(altPath, "utf-8"));
|
|
22640
23029
|
}
|
|
22641
|
-
return extractProcedureNamesFromContent(
|
|
23030
|
+
return extractProcedureNamesFromContent(readFileSync35(absPath, "utf-8"));
|
|
22642
23031
|
}
|
|
22643
23032
|
function extractProcedureNamesFromContent(content) {
|
|
22644
23033
|
const procRegex = /\.(?:query|mutation)\s*\(/g;
|
|
@@ -22678,8 +23067,8 @@ function handleDocsAudit(args2) {
|
|
|
22678
23067
|
for (const [mappingId, triggeringFiles] of affectedMappings) {
|
|
22679
23068
|
const mapping = docsMap.mappings.find((m3) => m3.id === mappingId);
|
|
22680
23069
|
if (!mapping) continue;
|
|
22681
|
-
const helpPagePath = ensureWithinRoot(
|
|
22682
|
-
if (!
|
|
23070
|
+
const helpPagePath = ensureWithinRoot(resolve31(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
|
|
23071
|
+
if (!existsSync36(helpPagePath)) {
|
|
22683
23072
|
results.push({
|
|
22684
23073
|
helpPage: mapping.helpPage,
|
|
22685
23074
|
mappingId,
|
|
@@ -22691,7 +23080,7 @@ function handleDocsAudit(args2) {
|
|
|
22691
23080
|
});
|
|
22692
23081
|
continue;
|
|
22693
23082
|
}
|
|
22694
|
-
const content =
|
|
23083
|
+
const content = readFileSync35(helpPagePath, "utf-8");
|
|
22695
23084
|
const sections = extractSections(content);
|
|
22696
23085
|
const frontmatter = extractFrontmatter(content);
|
|
22697
23086
|
const staleReasons = [];
|
|
@@ -22731,9 +23120,9 @@ function handleDocsAudit(args2) {
|
|
|
22731
23120
|
});
|
|
22732
23121
|
for (const [guideName, parentId] of Object.entries(docsMap.userGuideInheritance.examples)) {
|
|
22733
23122
|
if (parentId === mappingId) {
|
|
22734
|
-
const guidePath = ensureWithinRoot(
|
|
22735
|
-
if (
|
|
22736
|
-
const guideContent =
|
|
23123
|
+
const guidePath = ensureWithinRoot(resolve31(getResolvedPaths().helpSitePath, `pages/user-guides/${guideName}/index.mdx`), getProjectRoot());
|
|
23124
|
+
if (existsSync36(guidePath)) {
|
|
23125
|
+
const guideContent = readFileSync35(guidePath, "utf-8");
|
|
22737
23126
|
const guideFrontmatter = extractFrontmatter(guideContent);
|
|
22738
23127
|
if (!guideFrontmatter?.lastVerified || status === "STALE") {
|
|
22739
23128
|
results.push({
|
|
@@ -22766,14 +23155,14 @@ function handleDocsCoverage(args2) {
|
|
|
22766
23155
|
const gaps = [];
|
|
22767
23156
|
const mappings = filterDomain ? docsMap.mappings.filter((m3) => m3.id === filterDomain) : docsMap.mappings;
|
|
22768
23157
|
for (const mapping of mappings) {
|
|
22769
|
-
const helpPagePath = ensureWithinRoot(
|
|
22770
|
-
const exists =
|
|
23158
|
+
const helpPagePath = ensureWithinRoot(resolve31(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
|
|
23159
|
+
const exists = existsSync36(helpPagePath);
|
|
22771
23160
|
let hasContent = false;
|
|
22772
23161
|
let lineCount = 0;
|
|
22773
23162
|
let lastVerified = null;
|
|
22774
23163
|
let status = null;
|
|
22775
23164
|
if (exists) {
|
|
22776
|
-
const content =
|
|
23165
|
+
const content = readFileSync35(helpPagePath, "utf-8");
|
|
22777
23166
|
lineCount = content.split("\n").length;
|
|
22778
23167
|
hasContent = lineCount > 10;
|
|
22779
23168
|
const frontmatter = extractFrontmatter(content);
|
|
@@ -23114,8 +23503,8 @@ var init_observability_tools = __esm({
|
|
|
23114
23503
|
});
|
|
23115
23504
|
|
|
23116
23505
|
// src/sentinel-db.ts
|
|
23117
|
-
import { existsSync as
|
|
23118
|
-
import { resolve as
|
|
23506
|
+
import { existsSync as existsSync37 } from "fs";
|
|
23507
|
+
import { resolve as resolve32 } from "path";
|
|
23119
23508
|
function parsePortalScope(raw) {
|
|
23120
23509
|
if (!raw) return [];
|
|
23121
23510
|
try {
|
|
@@ -23351,23 +23740,23 @@ function validateFeatures(db, domainFilter) {
|
|
|
23351
23740
|
const missingProcedures = [];
|
|
23352
23741
|
const missingPages = [];
|
|
23353
23742
|
for (const comp of components) {
|
|
23354
|
-
const absPath =
|
|
23355
|
-
if (!
|
|
23743
|
+
const absPath = resolve32(PROJECT_ROOT, comp.component_file);
|
|
23744
|
+
if (!existsSync37(absPath)) {
|
|
23356
23745
|
missingComponents.push(comp.component_file);
|
|
23357
23746
|
}
|
|
23358
23747
|
}
|
|
23359
23748
|
for (const proc of procedures) {
|
|
23360
|
-
const routerPath =
|
|
23361
|
-
if (!
|
|
23749
|
+
const routerPath = resolve32(PROJECT_ROOT, `src/server/api/routers/${proc.router_name}.ts`);
|
|
23750
|
+
if (!existsSync37(routerPath)) {
|
|
23362
23751
|
missingProcedures.push({ router: proc.router_name, procedure: proc.procedure_name });
|
|
23363
23752
|
}
|
|
23364
23753
|
}
|
|
23365
23754
|
for (const page of pages) {
|
|
23366
23755
|
const routeToPath = page.page_route.replace(/^\/(portal-[^/]+\/)?/, "src/app/").replace(/\/$/, "") + "/page.tsx";
|
|
23367
|
-
const absPath =
|
|
23368
|
-
if (page.page_route.startsWith("/") && !
|
|
23369
|
-
const altPath =
|
|
23370
|
-
if (!
|
|
23756
|
+
const absPath = resolve32(PROJECT_ROOT, routeToPath);
|
|
23757
|
+
if (page.page_route.startsWith("/") && !existsSync37(absPath)) {
|
|
23758
|
+
const altPath = resolve32(PROJECT_ROOT, `src/app${page.page_route}/page.tsx`);
|
|
23759
|
+
if (!existsSync37(altPath)) {
|
|
23371
23760
|
missingPages.push(page.page_route);
|
|
23372
23761
|
}
|
|
23373
23762
|
}
|
|
@@ -23893,8 +24282,8 @@ var init_sentinel_tools = __esm({
|
|
|
23893
24282
|
});
|
|
23894
24283
|
|
|
23895
24284
|
// src/sentinel-scanner.ts
|
|
23896
|
-
import { readFileSync as
|
|
23897
|
-
import { resolve as
|
|
24285
|
+
import { readFileSync as readFileSync36, existsSync as existsSync38, readdirSync as readdirSync23, statSync as statSync13 } from "fs";
|
|
24286
|
+
import { resolve as resolve33, join as join22, basename as basename7, dirname as dirname18, relative as relative11 } from "path";
|
|
23898
24287
|
function inferDomain(filePath) {
|
|
23899
24288
|
const domains = getConfig().domains;
|
|
23900
24289
|
const path = filePath.toLowerCase();
|
|
@@ -24025,8 +24414,8 @@ function scanComponentExports(dataDb) {
|
|
|
24025
24414
|
const projectRoot = getProjectRoot();
|
|
24026
24415
|
const componentsBase = config.paths.components ?? config.paths.source + "/components";
|
|
24027
24416
|
const componentDirs = [];
|
|
24028
|
-
const basePath =
|
|
24029
|
-
if (
|
|
24417
|
+
const basePath = resolve33(projectRoot, componentsBase);
|
|
24418
|
+
if (existsSync38(basePath)) {
|
|
24030
24419
|
try {
|
|
24031
24420
|
const entries = readdirSync23(basePath, { withFileTypes: true });
|
|
24032
24421
|
for (const entry of entries) {
|
|
@@ -24038,12 +24427,12 @@ function scanComponentExports(dataDb) {
|
|
|
24038
24427
|
}
|
|
24039
24428
|
}
|
|
24040
24429
|
for (const dir of componentDirs) {
|
|
24041
|
-
const absDir =
|
|
24042
|
-
if (!
|
|
24430
|
+
const absDir = resolve33(projectRoot, dir);
|
|
24431
|
+
if (!existsSync38(absDir)) continue;
|
|
24043
24432
|
const files = walkDir2(absDir).filter((f2) => f2.endsWith(".tsx") || f2.endsWith(".ts"));
|
|
24044
24433
|
for (const file of files) {
|
|
24045
24434
|
const relPath = relative11(projectRoot, file);
|
|
24046
|
-
const source =
|
|
24435
|
+
const source = readFileSync36(file, "utf-8");
|
|
24047
24436
|
const annotations = parseFeatureAnnotations(source);
|
|
24048
24437
|
if (annotations.length > 0) {
|
|
24049
24438
|
for (const ann of annotations) {
|
|
@@ -25086,7 +25475,7 @@ var init_audit_trail = __esm({
|
|
|
25086
25475
|
});
|
|
25087
25476
|
|
|
25088
25477
|
// src/validation-engine.ts
|
|
25089
|
-
import { existsSync as
|
|
25478
|
+
import { existsSync as existsSync39, readFileSync as readFileSync37 } from "fs";
|
|
25090
25479
|
function p10(baseName) {
|
|
25091
25480
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
25092
25481
|
}
|
|
@@ -25117,7 +25506,7 @@ function validateFile(filePath, projectRoot) {
|
|
|
25117
25506
|
});
|
|
25118
25507
|
return checks;
|
|
25119
25508
|
}
|
|
25120
|
-
if (!
|
|
25509
|
+
if (!existsSync39(absPath)) {
|
|
25121
25510
|
checks.push({
|
|
25122
25511
|
name: "file_exists",
|
|
25123
25512
|
severity: "error",
|
|
@@ -25126,7 +25515,7 @@ function validateFile(filePath, projectRoot) {
|
|
|
25126
25515
|
});
|
|
25127
25516
|
return checks;
|
|
25128
25517
|
}
|
|
25129
|
-
const source =
|
|
25518
|
+
const source = readFileSync37(absPath, "utf-8");
|
|
25130
25519
|
const lines = source.split("\n");
|
|
25131
25520
|
if (activeChecks.rule_compliance !== false) {
|
|
25132
25521
|
for (const ruleSet of config.rules) {
|
|
@@ -25572,7 +25961,7 @@ var init_adr_generator = __esm({
|
|
|
25572
25961
|
});
|
|
25573
25962
|
|
|
25574
25963
|
// src/security-scorer.ts
|
|
25575
|
-
import { existsSync as
|
|
25964
|
+
import { existsSync as existsSync40, readFileSync as readFileSync38 } from "fs";
|
|
25576
25965
|
function p12(baseName) {
|
|
25577
25966
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
25578
25967
|
}
|
|
@@ -25596,12 +25985,12 @@ function scoreFileSecurity(filePath, projectRoot) {
|
|
|
25596
25985
|
}]
|
|
25597
25986
|
};
|
|
25598
25987
|
}
|
|
25599
|
-
if (!
|
|
25988
|
+
if (!existsSync40(absPath)) {
|
|
25600
25989
|
return { riskScore: 0, findings: [] };
|
|
25601
25990
|
}
|
|
25602
25991
|
let source;
|
|
25603
25992
|
try {
|
|
25604
|
-
source =
|
|
25993
|
+
source = readFileSync38(absPath, "utf-8");
|
|
25605
25994
|
} catch {
|
|
25606
25995
|
return { riskScore: 0, findings: [] };
|
|
25607
25996
|
}
|
|
@@ -25891,8 +26280,8 @@ var init_security_scorer = __esm({
|
|
|
25891
26280
|
});
|
|
25892
26281
|
|
|
25893
26282
|
// src/dependency-scorer.ts
|
|
25894
|
-
import { existsSync as
|
|
25895
|
-
import { resolve as
|
|
26283
|
+
import { existsSync as existsSync41, readFileSync as readFileSync39 } from "fs";
|
|
26284
|
+
import { resolve as resolve34 } from "path";
|
|
25896
26285
|
function p13(baseName) {
|
|
25897
26286
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
25898
26287
|
}
|
|
@@ -25924,10 +26313,10 @@ function calculateDepRisk(factors) {
|
|
|
25924
26313
|
return Math.min(100, risk);
|
|
25925
26314
|
}
|
|
25926
26315
|
function getInstalledPackages(projectRoot) {
|
|
25927
|
-
const pkgPath =
|
|
25928
|
-
if (!
|
|
26316
|
+
const pkgPath = resolve34(projectRoot, "package.json");
|
|
26317
|
+
if (!existsSync41(pkgPath)) return /* @__PURE__ */ new Map();
|
|
25929
26318
|
try {
|
|
25930
|
-
const pkg = JSON.parse(
|
|
26319
|
+
const pkg = JSON.parse(readFileSync39(pkgPath, "utf-8"));
|
|
25931
26320
|
const packages = /* @__PURE__ */ new Map();
|
|
25932
26321
|
for (const [name, version] of Object.entries(pkg.dependencies ?? {})) {
|
|
25933
26322
|
packages.set(name, version);
|
|
@@ -26533,8 +26922,8 @@ var init_regression_detector = __esm({
|
|
|
26533
26922
|
|
|
26534
26923
|
// src/knowledge-indexer.ts
|
|
26535
26924
|
import { createHash as createHash9 } from "crypto";
|
|
26536
|
-
import { readFileSync as
|
|
26537
|
-
import { resolve as
|
|
26925
|
+
import { readFileSync as readFileSync40, readdirSync as readdirSync24, statSync as statSync14, existsSync as existsSync42 } from "fs";
|
|
26926
|
+
import { resolve as resolve35, relative as relative12, basename as basename8, extname as extname2 } from "path";
|
|
26538
26927
|
function getKnowledgePaths() {
|
|
26539
26928
|
const resolved = getResolvedPaths();
|
|
26540
26929
|
const config = getConfig();
|
|
@@ -26562,7 +26951,7 @@ function discoverMarkdownFiles(baseDir) {
|
|
|
26562
26951
|
try {
|
|
26563
26952
|
const entries = readdirSync24(dir, { withFileTypes: true });
|
|
26564
26953
|
for (const entry of entries) {
|
|
26565
|
-
const fullPath =
|
|
26954
|
+
const fullPath = resolve35(dir, entry.name);
|
|
26566
26955
|
if (entry.isDirectory()) {
|
|
26567
26956
|
if (entry.name === "archive" && dir.includes("session-state")) continue;
|
|
26568
26957
|
if (entry.name === "archive" && dir.includes("status")) continue;
|
|
@@ -26841,11 +27230,11 @@ function indexAllKnowledge(db) {
|
|
|
26841
27230
|
files.push(...memFiles);
|
|
26842
27231
|
} catch {
|
|
26843
27232
|
}
|
|
26844
|
-
if (
|
|
27233
|
+
if (existsSync42(paths.plansDir)) {
|
|
26845
27234
|
const planFiles = discoverMarkdownFiles(paths.plansDir);
|
|
26846
27235
|
files.push(...planFiles);
|
|
26847
27236
|
}
|
|
26848
|
-
if (
|
|
27237
|
+
if (existsSync42(paths.docsDir)) {
|
|
26849
27238
|
const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
|
|
26850
27239
|
const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f2) => !f2.includes("/plans/") && !excludePatterns.some((p19) => f2.includes(p19)));
|
|
26851
27240
|
files.push(...docsFiles);
|
|
@@ -26888,8 +27277,8 @@ function indexAllKnowledge(db) {
|
|
|
26888
27277
|
} catch {
|
|
26889
27278
|
}
|
|
26890
27279
|
for (const filePath of files) {
|
|
26891
|
-
if (!
|
|
26892
|
-
const content =
|
|
27280
|
+
if (!existsSync42(filePath)) continue;
|
|
27281
|
+
const content = readFileSync40(filePath, "utf-8");
|
|
26893
27282
|
const hash = hashContent2(content);
|
|
26894
27283
|
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);
|
|
26895
27284
|
const category = categorizeFile(filePath);
|
|
@@ -27024,10 +27413,10 @@ function isKnowledgeStale(db) {
|
|
|
27024
27413
|
files.push(...discoverMarkdownFiles(paths.memoryDir));
|
|
27025
27414
|
} catch {
|
|
27026
27415
|
}
|
|
27027
|
-
if (
|
|
27416
|
+
if (existsSync42(paths.plansDir)) {
|
|
27028
27417
|
files.push(...discoverMarkdownFiles(paths.plansDir));
|
|
27029
27418
|
}
|
|
27030
|
-
if (
|
|
27419
|
+
if (existsSync42(paths.docsDir)) {
|
|
27031
27420
|
const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
|
|
27032
27421
|
const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f2) => !f2.includes("/plans/") && !excludePatterns.some((p19) => f2.includes(p19)));
|
|
27033
27422
|
files.push(...docsFiles);
|
|
@@ -27056,8 +27445,8 @@ var init_knowledge_indexer = __esm({
|
|
|
27056
27445
|
});
|
|
27057
27446
|
|
|
27058
27447
|
// src/knowledge-tools.ts
|
|
27059
|
-
import { readFileSync as
|
|
27060
|
-
import { resolve as
|
|
27448
|
+
import { readFileSync as readFileSync41, writeFileSync as writeFileSync7, appendFileSync as appendFileSync2, readdirSync as readdirSync25 } from "fs";
|
|
27449
|
+
import { resolve as resolve36, basename as basename9 } from "path";
|
|
27061
27450
|
function p16(baseName) {
|
|
27062
27451
|
return `${getConfig().toolPrefix}_${baseName}`;
|
|
27063
27452
|
}
|
|
@@ -27796,7 +28185,7 @@ function handleCorrect(db, args2) {
|
|
|
27796
28185
|
if (!wrong || !correction || !rule) {
|
|
27797
28186
|
return text15("Error: wrong, correction, and rule are all required.");
|
|
27798
28187
|
}
|
|
27799
|
-
const correctionsPath =
|
|
28188
|
+
const correctionsPath = resolve36(getResolvedPaths().memoryDir, "corrections.md");
|
|
27800
28189
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
27801
28190
|
const title = rule.slice(0, 60);
|
|
27802
28191
|
const entry = `
|
|
@@ -27809,14 +28198,14 @@ ${crRule ? `- **CR**: ${crRule}
|
|
|
27809
28198
|
`;
|
|
27810
28199
|
let existing = "";
|
|
27811
28200
|
try {
|
|
27812
|
-
existing =
|
|
28201
|
+
existing = readFileSync41(correctionsPath, "utf-8");
|
|
27813
28202
|
} catch {
|
|
27814
28203
|
}
|
|
27815
28204
|
const archiveIdx = existing.indexOf("## Archived");
|
|
27816
28205
|
if (archiveIdx > 0) {
|
|
27817
28206
|
const before = existing.slice(0, archiveIdx);
|
|
27818
28207
|
const after = existing.slice(archiveIdx);
|
|
27819
|
-
|
|
28208
|
+
writeFileSync7(correctionsPath, before + entry + after);
|
|
27820
28209
|
} else {
|
|
27821
28210
|
appendFileSync2(correctionsPath, entry);
|
|
27822
28211
|
}
|
|
@@ -28764,8 +29153,8 @@ var init_framework_supports = __esm({
|
|
|
28764
29153
|
});
|
|
28765
29154
|
|
|
28766
29155
|
// src/tools.ts
|
|
28767
|
-
import { readFileSync as
|
|
28768
|
-
import { resolve as
|
|
29156
|
+
import { readFileSync as readFileSync42, existsSync as existsSync44 } from "fs";
|
|
29157
|
+
import { resolve as resolve37, basename as basename10 } from "path";
|
|
28769
29158
|
function prefix2() {
|
|
28770
29159
|
return getConfig().toolPrefix;
|
|
28771
29160
|
}
|
|
@@ -28800,7 +29189,7 @@ function ensureIndexes(dataDb, codegraphDb, force = false) {
|
|
|
28800
29189
|
if (config.python?.root) {
|
|
28801
29190
|
const pythonRoot = config.python.root;
|
|
28802
29191
|
const excludeDirs = config.python.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
|
|
28803
|
-
if (force || isPythonDataStale(dataDb,
|
|
29192
|
+
if (force || isPythonDataStale(dataDb, resolve37(getProjectRoot(), pythonRoot))) {
|
|
28804
29193
|
const pyImports = buildPythonImportIndex(dataDb, pythonRoot, excludeDirs);
|
|
28805
29194
|
results.push(`Python imports: ${pyImports}`);
|
|
28806
29195
|
const pyRoutes = buildPythonRouteIndex(dataDb, pythonRoot, excludeDirs);
|
|
@@ -29183,9 +29572,9 @@ function handleContext(file, dataDb, codegraphDb) {
|
|
|
29183
29572
|
try {
|
|
29184
29573
|
const resolvedPaths = getResolvedPaths();
|
|
29185
29574
|
const root = getProjectRoot();
|
|
29186
|
-
const absFilePath = ensureWithinRoot(
|
|
29187
|
-
if (
|
|
29188
|
-
const fileContent =
|
|
29575
|
+
const absFilePath = ensureWithinRoot(resolve37(resolvedPaths.srcDir, "..", file), root);
|
|
29576
|
+
if (existsSync44(absFilePath)) {
|
|
29577
|
+
const fileContent = readFileSync42(absFilePath, "utf-8").slice(0, 3e3);
|
|
29189
29578
|
const keywords = [];
|
|
29190
29579
|
if (fileContent.includes("ctx.db")) keywords.push("database", "schema");
|
|
29191
29580
|
if (fileContent.includes("BigInt") || fileContent.includes("Decimal")) keywords.push("BigInt", "serialization");
|
|
@@ -29622,11 +30011,11 @@ function handleSchema(args2) {
|
|
|
29622
30011
|
lines.push("Checking all column references against Prisma schema...");
|
|
29623
30012
|
lines.push("");
|
|
29624
30013
|
const projectRoot = getProjectRoot();
|
|
29625
|
-
const absPath = ensureWithinRoot(
|
|
29626
|
-
if (!
|
|
30014
|
+
const absPath = ensureWithinRoot(resolve37(projectRoot, file), projectRoot);
|
|
30015
|
+
if (!existsSync44(absPath)) {
|
|
29627
30016
|
return text17(`File not found: ${file}`);
|
|
29628
30017
|
}
|
|
29629
|
-
const source =
|
|
30018
|
+
const source = readFileSync42(absPath, "utf-8");
|
|
29630
30019
|
const config = getConfig();
|
|
29631
30020
|
const dbPattern = config.dbAccessPattern ?? "ctx.db.{table}";
|
|
29632
30021
|
const regexStr = dbPattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace("\\{table\\}", "(\\w+)");
|
|
@@ -30031,8 +30420,8 @@ var init_server_dispatch = __esm({
|
|
|
30031
30420
|
|
|
30032
30421
|
// src/server.ts
|
|
30033
30422
|
var server_exports = {};
|
|
30034
|
-
import { readFileSync as
|
|
30035
|
-
import { resolve as
|
|
30423
|
+
import { readFileSync as readFileSync43 } from "fs";
|
|
30424
|
+
import { resolve as resolve38, dirname as dirname19 } from "path";
|
|
30036
30425
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
30037
30426
|
function pruneMemoryOnStartup() {
|
|
30038
30427
|
try {
|
|
@@ -30067,7 +30456,7 @@ var init_server = __esm({
|
|
|
30067
30456
|
__dirname5 = dirname19(fileURLToPath5(import.meta.url));
|
|
30068
30457
|
PKG_VERSION = (() => {
|
|
30069
30458
|
try {
|
|
30070
|
-
const pkg = JSON.parse(
|
|
30459
|
+
const pkg = JSON.parse(readFileSync43(resolve38(__dirname5, "..", "package.json"), "utf-8"));
|
|
30071
30460
|
return pkg.version ?? "0.0.0";
|
|
30072
30461
|
} catch {
|
|
30073
30462
|
return "0.0.0";
|
|
@@ -30353,19 +30742,19 @@ var config_upgrade_exports = {};
|
|
|
30353
30742
|
__export(config_upgrade_exports, {
|
|
30354
30743
|
runConfigUpgrade: () => runConfigUpgrade
|
|
30355
30744
|
});
|
|
30356
|
-
import { existsSync as
|
|
30357
|
-
import { resolve as
|
|
30745
|
+
import { existsSync as existsSync45, readFileSync as readFileSync44, writeFileSync as writeFileSync8, copyFileSync, unlinkSync as unlinkSync2 } from "fs";
|
|
30746
|
+
import { resolve as resolve39 } from "path";
|
|
30358
30747
|
import { parse as parseYaml6 } from "yaml";
|
|
30359
30748
|
async function runConfigUpgrade(opts = {}) {
|
|
30360
30749
|
const cwd = opts.cwd ?? process.cwd();
|
|
30361
|
-
const configPath =
|
|
30750
|
+
const configPath = resolve39(cwd, "massu.config.yaml");
|
|
30362
30751
|
const bakPath = `${configPath}.bak`;
|
|
30363
30752
|
const log = opts.silent ? () => {
|
|
30364
30753
|
} : (s) => process.stdout.write(s);
|
|
30365
30754
|
const err = opts.silent ? () => {
|
|
30366
30755
|
} : (s) => process.stderr.write(s);
|
|
30367
30756
|
if (opts.rollback) {
|
|
30368
|
-
if (!
|
|
30757
|
+
if (!existsSync45(bakPath)) {
|
|
30369
30758
|
const message = `No backup found at ${bakPath}`;
|
|
30370
30759
|
err(message + "\n");
|
|
30371
30760
|
return { exitCode: 1, action: "none", message };
|
|
@@ -30381,14 +30770,14 @@ async function runConfigUpgrade(opts = {}) {
|
|
|
30381
30770
|
return { exitCode: 2, action: "none", message };
|
|
30382
30771
|
}
|
|
30383
30772
|
}
|
|
30384
|
-
if (!
|
|
30773
|
+
if (!existsSync45(configPath)) {
|
|
30385
30774
|
const message = "massu.config.yaml not found. Run: npx massu init";
|
|
30386
30775
|
err(message + "\n");
|
|
30387
30776
|
return { exitCode: 1, action: "none", message };
|
|
30388
30777
|
}
|
|
30389
30778
|
let existing;
|
|
30390
30779
|
try {
|
|
30391
|
-
const content =
|
|
30780
|
+
const content = readFileSync44(configPath, "utf-8");
|
|
30392
30781
|
const parsed = parseYaml6(content);
|
|
30393
30782
|
if (!parsed || typeof parsed !== "object") {
|
|
30394
30783
|
throw new Error("config is not a YAML object");
|
|
@@ -30411,8 +30800,8 @@ async function runConfigUpgrade(opts = {}) {
|
|
|
30411
30800
|
fingerprint: computeFingerprint(detection)
|
|
30412
30801
|
};
|
|
30413
30802
|
try {
|
|
30414
|
-
const original =
|
|
30415
|
-
|
|
30803
|
+
const original = readFileSync44(configPath, "utf-8");
|
|
30804
|
+
writeFileSync8(bakPath, original, "utf-8");
|
|
30416
30805
|
} catch (e2) {
|
|
30417
30806
|
const message = `Failed to write backup: ${e2 instanceof Error ? e2.message : String(e2)}`;
|
|
30418
30807
|
err(message + "\n");
|
|
@@ -30445,8 +30834,8 @@ var config_check_drift_exports = {};
|
|
|
30445
30834
|
__export(config_check_drift_exports, {
|
|
30446
30835
|
runConfigCheckDrift: () => runConfigCheckDrift
|
|
30447
30836
|
});
|
|
30448
|
-
import { existsSync as
|
|
30449
|
-
import { resolve as
|
|
30837
|
+
import { existsSync as existsSync46, readFileSync as readFileSync45 } from "fs";
|
|
30838
|
+
import { resolve as resolve40 } from "path";
|
|
30450
30839
|
import { parse as parseYaml7 } from "yaml";
|
|
30451
30840
|
function renderChanges(changes) {
|
|
30452
30841
|
if (changes.length === 0) return "(none)\n";
|
|
@@ -30454,12 +30843,12 @@ function renderChanges(changes) {
|
|
|
30454
30843
|
}
|
|
30455
30844
|
async function runConfigCheckDrift(opts = {}) {
|
|
30456
30845
|
const cwd = opts.cwd ?? process.cwd();
|
|
30457
|
-
const configPath =
|
|
30846
|
+
const configPath = resolve40(cwd, "massu.config.yaml");
|
|
30458
30847
|
const log = opts.silent ? () => {
|
|
30459
30848
|
} : (s) => process.stdout.write(s);
|
|
30460
30849
|
const err = opts.silent ? () => {
|
|
30461
30850
|
} : (s) => process.stderr.write(s);
|
|
30462
|
-
if (!
|
|
30851
|
+
if (!existsSync46(configPath)) {
|
|
30463
30852
|
const message = "massu.config.yaml not found. Run: npx massu init";
|
|
30464
30853
|
err(message + "\n");
|
|
30465
30854
|
return {
|
|
@@ -30473,7 +30862,7 @@ async function runConfigCheckDrift(opts = {}) {
|
|
|
30473
30862
|
}
|
|
30474
30863
|
let config;
|
|
30475
30864
|
try {
|
|
30476
|
-
const content =
|
|
30865
|
+
const content = readFileSync45(configPath, "utf-8");
|
|
30477
30866
|
const parsed = parseYaml7(content);
|
|
30478
30867
|
if (!parsed || typeof parsed !== "object") {
|
|
30479
30868
|
throw new Error("config is not a YAML object");
|
|
@@ -30540,8 +30929,8 @@ var init_config_check_drift = __esm({
|
|
|
30540
30929
|
});
|
|
30541
30930
|
|
|
30542
30931
|
// src/cli.ts
|
|
30543
|
-
import { readFileSync as
|
|
30544
|
-
import { resolve as
|
|
30932
|
+
import { readFileSync as readFileSync46 } from "fs";
|
|
30933
|
+
import { resolve as resolve41, dirname as dirname20 } from "path";
|
|
30545
30934
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
30546
30935
|
var __filename5 = fileURLToPath6(import.meta.url);
|
|
30547
30936
|
var __dirname6 = dirname20(__filename5);
|
|
@@ -30559,6 +30948,16 @@ async function main() {
|
|
|
30559
30948
|
await runDoctor2();
|
|
30560
30949
|
break;
|
|
30561
30950
|
}
|
|
30951
|
+
case "login": {
|
|
30952
|
+
const { runLogin: runLogin2 } = await Promise.resolve().then(() => (init_login(), login_exports));
|
|
30953
|
+
await runLogin2(args.slice(1));
|
|
30954
|
+
break;
|
|
30955
|
+
}
|
|
30956
|
+
case "logout": {
|
|
30957
|
+
const { runLogout: runLogout2 } = await Promise.resolve().then(() => (init_login(), login_exports));
|
|
30958
|
+
await runLogout2();
|
|
30959
|
+
break;
|
|
30960
|
+
}
|
|
30562
30961
|
case "install-hooks": {
|
|
30563
30962
|
const { runInstallHooks: runInstallHooks2 } = await Promise.resolve().then(() => (init_install_hooks(), install_hooks_exports));
|
|
30564
30963
|
await runInstallHooks2();
|
|
@@ -30582,7 +30981,7 @@ async function main() {
|
|
|
30582
30981
|
return;
|
|
30583
30982
|
}
|
|
30584
30983
|
case "license": {
|
|
30585
|
-
const { handleLicenseSubcommand: handleLicenseSubcommand2 } = await Promise.resolve().then(() => (init_license2(),
|
|
30984
|
+
const { handleLicenseSubcommand: handleLicenseSubcommand2 } = await Promise.resolve().then(() => (init_license2(), license_exports2));
|
|
30586
30985
|
const result = await handleLicenseSubcommand2(args.slice(1));
|
|
30587
30986
|
process.exit(result.exitCode);
|
|
30588
30987
|
return;
|
|
@@ -30719,6 +31118,12 @@ Usage:
|
|
|
30719
31118
|
Commands:
|
|
30720
31119
|
init Set up Massu AI in your project (one command, full setup)
|
|
30721
31120
|
doctor Check installation health
|
|
31121
|
+
login Store your API key user-level in ~/.massu/credentials (git-safe).
|
|
31122
|
+
Interactive: hidden prompt. Non-interactive (CI/agent): set
|
|
31123
|
+
MASSU_API_KEY (recommended), or pipe (echo "$KEY" | massu login);
|
|
31124
|
+
--key <k> works but exposes the key to shell history. Never hangs
|
|
31125
|
+
without a TTY. Use --no-verify to skip the online check.
|
|
31126
|
+
logout Remove the stored user-level API key
|
|
30722
31127
|
install-hooks Install/update Claude Code hooks
|
|
30723
31128
|
install-commands Install/update slash commands (use --skip-permissions to opt out of MCP allowlist seeding)
|
|
30724
31129
|
show-template Print the resolved variant of a bundled template (e.g. for diffs)
|
|
@@ -30769,7 +31174,7 @@ Examples:
|
|
|
30769
31174
|
}
|
|
30770
31175
|
function printVersion() {
|
|
30771
31176
|
try {
|
|
30772
|
-
const pkg = JSON.parse(
|
|
31177
|
+
const pkg = JSON.parse(readFileSync46(resolve41(__dirname6, "../package.json"), "utf-8"));
|
|
30773
31178
|
console.log(`massu v${pkg.version}`);
|
|
30774
31179
|
} catch {
|
|
30775
31180
|
console.log("massu v0.1.0");
|