@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/dist/cli.js CHANGED
@@ -58,17 +58,136 @@ var init_memory_path = __esm({
58
58
  }
59
59
  });
60
60
 
61
- // src/config.ts
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 (existsSync(resolve(dir, "massu.config.yaml"))) {
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 (existsSync(resolve(dir, "package.json"))) {
199
+ if (existsSync2(resolve2(dir, "package.json"))) {
81
200
  return dir;
82
201
  }
83
- if (existsSync(resolve(dir, ".git"))) {
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 = resolve(root, "massu.config.yaml");
220
+ const configPath = resolve2(root, "massu.config.yaml");
102
221
  let rawYaml = {};
103
- if (existsSync(configPath)) {
104
- const content = readFileSync(configPath, "utf-8");
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 : resolve(root, parsed.project.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
- if (!_config.cloud?.apiKey && process.env.MASSU_API_KEY) {
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: process.env.MASSU_API_KEY
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: resolve(root, ".codegraph/codegraph.db"),
195
- dataDbPath: resolve(root, ".massu/data.db"),
196
- prismaSchemaPath: resolve(root, config.paths.schema ?? "prisma/schema.prisma"),
197
- rootRouterPath: resolve(root, config.paths.routerRoot ?? "src/server/api/root.ts"),
198
- routersDir: resolve(root, config.paths.routers ?? "src/server/api/routers"),
199
- srcDir: resolve(root, config.paths.source),
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
- resolve(root, target)
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: resolve(root, claudeDirName, "patterns"),
209
- claudeMdPath: resolve(root, claudeDirName, "CLAUDE.md"),
210
- docsMapPath: resolve(root, ".massu/docs-map.json"),
211
- helpSitePath: resolve(root, "../" + config.project.name + "-help"),
212
- memoryDbPath: resolve(root, ".massu/memory.db"),
213
- knowledgeDbPath: resolve(root, ".massu/knowledge.db"),
214
- plansDir: resolve(root, "docs/plans"),
215
- docsDir: resolve(root, "docs"),
216
- claudeDir: resolve(root, claudeDirName),
217
- memoryDir: resolve(homedir(), claudeDirName, "projects", encodeMemoryDirName(root), "memory"),
218
- sessionStatePath: resolve(root, config.conventions?.sessionStatePath ?? `${claudeDirName}/session-state/CURRENT.md`),
219
- sessionArchivePath: resolve(root, config.conventions?.sessionArchivePath ?? `${claudeDirName}/session-state/archive`),
220
- mcpJsonPath: resolve(root, ".mcp.json"),
221
- settingsLocalPath: resolve(root, claudeDirName, "settings.local.json")
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 existsSync2, mkdirSync } from "fs";
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 (!existsSync2(dir)) {
687
- mkdirSync(dir, { recursive: true });
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 readFileSync2, existsSync as existsSync3, readdirSync } from "fs";
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 (!existsSync3(filePath)) return "skipped";
2025
- const content = readFileSync2(filePath, "utf-8");
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 (!existsSync3(memoryDir)) return stats;
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 existsSync4,
2408
+ existsSync as existsSync5,
2277
2409
  fsyncSync,
2278
- mkdirSync as mkdirSync2,
2410
+ mkdirSync as mkdirSync3,
2279
2411
  openSync,
2280
- readFileSync as readFileSync3,
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 resolve3 } from "path";
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 (existsSync4(tmpPath)) {
2431
+ if (existsSync5(tmpPath)) {
2300
2432
  try {
2301
- rmSync(tmpPath, { force: true });
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 (!existsSync4(absolutePath)) {
2441
+ if (!existsSync5(absolutePath)) {
2310
2442
  return {};
2311
2443
  }
2312
2444
  try {
2313
- const raw = readFileSync3(absolutePath, "utf-8");
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(resolve3(claudeDir, "settings.local.json"));
2456
+ return readSettingsAtPath(resolve4(claudeDir, "settings.local.json"));
2325
2457
  }
2326
2458
  function writeSettingsLocalAtomic(claudeDir, settings) {
2327
- const targetPath = resolve3(claudeDir, "settings.local.json");
2459
+ const targetPath = resolve4(claudeDir, "settings.local.json");
2328
2460
  const dir = dirname3(targetPath);
2329
- if (!existsSync4(dir)) {
2330
- mkdirSync2(dir, { recursive: true });
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 homedir2 } from "os";
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(homedir2(), ".claude", "settings.json"));
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 existsSync5,
2568
- readFileSync as readFileSync4,
2569
- mkdirSync as mkdirSync3,
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 resolve4, dirname as dirname4, join as join3 } from "path";
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 = resolve4(cwd, "node_modules/@massu/core", assetName);
2579
- if (existsSync5(nodeModulesPath)) {
2710
+ const nodeModulesPath = resolve5(cwd, "node_modules/@massu/core", assetName);
2711
+ if (existsSync6(nodeModulesPath)) {
2580
2712
  return nodeModulesPath;
2581
2713
  }
2582
- const distRelPath = resolve4(__dirname, "..", assetName);
2583
- if (existsSync5(distRelPath)) {
2714
+ const distRelPath = resolve5(__dirname, "..", assetName);
2715
+ if (existsSync6(distRelPath)) {
2584
2716
  return distRelPath;
2585
2717
  }
2586
- const srcRelPath = resolve4(__dirname, "../..", assetName);
2587
- if (existsSync5(srcRelPath)) {
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 = resolve4(claudeDir, MANIFEST_RELPATH);
2600
- if (!existsSync5(path)) {
2731
+ const path = resolve5(claudeDir, MANIFEST_RELPATH);
2732
+ if (!existsSync6(path)) {
2601
2733
  return emptyManifest();
2602
2734
  }
2603
2735
  try {
2604
- const raw = readFileSync4(path, "utf-8");
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 = resolve4(claudeDir, ".massu");
2616
- if (!existsSync5(dir)) {
2617
- mkdirSync3(dir, { recursive: true });
2747
+ const dir = resolve5(claudeDir, ".massu");
2748
+ if (!existsSync6(dir)) {
2749
+ mkdirSync4(dir, { recursive: true });
2618
2750
  }
2619
- const finalPath = resolve4(dir, "install-manifest.json");
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 = resolve4(sourceDir, `${baseName}.${cand.lang}-${cand.subFramework}.md`);
2682
- if (existsSync5(subPath)) {
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 = resolve4(sourceDir, `${baseName}.${cand.lang}.md`);
2687
- if (existsSync5(langPath)) {
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 = resolve4(sourceDir, `${baseName}.md`);
2692
- if (existsSync5(defaultPath)) {
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 (!existsSync5(targetDir)) {
2709
- mkdirSync3(targetDir, { recursive: true });
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 = resolve4(sourceDir, entry);
2845
+ const sourcePath = resolve5(sourceDir, entry);
2714
2846
  const entryStat = statSync(sourcePath);
2715
2847
  if (entryStat.isDirectory()) {
2716
- const subTargetDir = resolve4(targetDir, entry);
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 = resolve4(sourceDir, sourceFilename);
2746
- if (!existsSync5(resolvedSourcePath)) {
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 = resolve4(targetDir, targetFilename);
2751
- const rawContent = readFileSync4(resolvedSourcePath, "utf-8");
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 (existsSync5(targetPath)) {
2770
- const existingContent = readFileSync4(targetPath, "utf-8");
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 = resolve4(projectRoot, claudeDirName);
2830
- const targetDir = resolve4(claudeDir, "commands");
2831
- if (!existsSync5(targetDir)) {
2832
- mkdirSync3(targetDir, { recursive: true });
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 = resolve4(projectRoot, claudeDirName);
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 = resolve4(claudeDir, assetType.targetSubdir);
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 readFileSync5, existsSync as existsSync6, statSync as statSync2, lstatSync, readdirSync as readdirSync3 } from "fs";
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 (!existsSync6(path)) return null;
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 readFileSync5(path, "utf-8");
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") || existsSync6(join4(directory, "tsconfig.json"));
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 (!existsSync6(path)) continue;
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 (!existsSync6(path)) continue;
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 (!existsSync6(wsRoot)) continue;
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(resolve41, reject) {
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
- resolve41(result);
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(resolve41, reject) {
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
- resolve41(result);
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(resolve41) {
8610
+ var p19 = new Promise(function(resolve42) {
8479
8611
  process.nextTick(function() {
8480
8612
  if (queue.idle()) {
8481
- resolve41();
8613
+ resolve42();
8482
8614
  } else {
8483
8615
  var previousDrain = queue.drain;
8484
8616
  queue.drain = function() {
8485
8617
  if (typeof previousDrain === "function") previousDrain();
8486
- resolve41();
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((resolve41, reject) => {
9104
+ return new Promise((resolve42, reject) => {
8973
9105
  this._stat(filepath, this._fsStatSettings, (error, stats) => {
8974
- return error === null ? resolve41(stats) : reject(error);
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((resolve41, reject) => {
9130
+ return new Promise((resolve42, reject) => {
8999
9131
  this._walkAsync(root, options, (error, entries) => {
9000
9132
  if (error === null) {
9001
- resolve41(entries);
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((resolve41, reject) => {
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", () => resolve41(entries));
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 resolve5 } from "path";
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(resolve5(root, candidate));
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 readFileSync6, existsSync as existsSync7, statSync as statSync3, lstatSync as lstatSync2, readdirSync as readdirSync4 } from "fs";
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 (!existsSync7(path)) return null;
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 readFileSync6(path, "utf-8");
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 (existsSync7(join5(dir, m3))) return m3;
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 (!existsSync7(p19)) continue;
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 (!existsSync7(parent)) continue;
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 (existsSync7(direct)) {
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 existsSync7(join5(root, "turbo.json"));
10145
+ return existsSync8(join5(root, "turbo.json"));
10014
10146
  }
10015
10147
  function hasNx(root) {
10016
- return existsSync7(join5(root, "nx.json"));
10148
+ return existsSync8(join5(root, "nx.json"));
10017
10149
  }
10018
10150
  function hasLerna(root) {
10019
- return existsSync7(join5(root, "lerna.json"));
10151
+ return existsSync8(join5(root, "lerna.json"));
10020
10152
  }
10021
10153
  function hasBazel(root) {
10022
- return existsSync7(join5(root, "WORKSPACE")) || existsSync7(join5(root, "WORKSPACE.bazel")) || existsSync7(join5(root, "MODULE.bazel"));
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 existsSync8, readdirSync as readdirSync5 } from "fs";
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 (!existsSync8(abs)) continue;
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 existsSync9, readdirSync as readdirSync6, readFileSync as readFileSync7, statSync as statSync4 } from "fs";
10323
- import { resolve as resolve6, join as join7, basename as basename2 } from "path";
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 = resolve6(projectRoot, first);
10490
- return existsSync9(abs) ? abs : null;
10621
+ const abs = resolve7(projectRoot, first);
10622
+ return existsSync10(abs) ? abs : null;
10491
10623
  }
10492
- return existsSync9(projectRoot) ? projectRoot : null;
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 readFileSync7(path, "utf-8");
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 existsSync10, readdirSync as readdirSync7, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
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 (!existsSync10(path)) return void 0;
10885
+ if (!existsSync11(path)) return void 0;
10754
10886
  try {
10755
- return readFileSync8(path, "utf-8");
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 mkdirSync4,
11004
+ mkdirSync as mkdirSync5,
10873
11005
  readdirSync as readdirSync8,
10874
- readFileSync as readFileSync9,
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 homedir3 } from "os";
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(homedir3(), ".massu", "wasm-cache");
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 = readFileSync9(cachePath);
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
- mkdirSync4(dirname5(cachePath), { recursive: true, mode: 448 });
11146
+ mkdirSync5(dirname5(cachePath), { recursive: true, mode: 448 });
11015
11147
  try {
11016
- chmodSync(dirname5(cachePath), 448);
11148
+ chmodSync2(dirname5(cachePath), 448);
11017
11149
  } catch {
11018
11150
  }
11019
11151
  const tmpPath = `${cachePath}.tmp.${process.pid}`;
11020
- writeFileSync(tmpPath, body, { mode: 384 });
11152
+ writeFileSync2(tmpPath, body, { mode: 384 });
11021
11153
  try {
11022
- chmodSync(tmpPath, 384);
11154
+ chmodSync2(tmpPath, 384);
11023
11155
  } catch {
11024
11156
  }
11025
11157
  try {
11026
11158
  renameSync2(tmpPath, cachePath);
11027
11159
  try {
11028
- chmodSync(cachePath, 384);
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 readFileSync10, lstatSync as lstatSync4 } from "node:fs";
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 = readFileSync10(fullPath, "utf-8");
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 existsSync11, fsyncSync as fsyncSync2, openSync as openSync2, readFileSync as readFileSync11, writeFileSync as writeFileSync2, writeSync as writeSync2, mkdirSync as mkdirSync5, readdirSync as readdirSync10, renameSync as renameSync3, rmSync as rmSync2, statSync as statSync7, chmodSync as chmodSync2 } from "fs";
13378
- import { resolve as resolve7, basename as basename4, dirname as dirname6 } from "path";
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 homedir4 } from "os";
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 = resolve7(projectRoot, "package.json");
13390
- if (!existsSync11(pkgPath)) return result;
13521
+ const pkgPath = resolve8(projectRoot, "package.json");
13522
+ if (!existsSync12(pkgPath)) return result;
13391
13523
  try {
13392
- const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
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) => existsSync11(resolve7(projectRoot, 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 = resolve7(projectRoot, file);
13437
- if (existsSync11(filePath)) {
13568
+ const filePath = resolve8(projectRoot, file);
13569
+ if (existsSync12(filePath)) {
13438
13570
  try {
13439
- const content = readFileSync11(filePath, "utf-8").toLowerCase();
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 (existsSync11(resolve7(projectRoot, "alembic.ini"))) {
13578
+ if (existsSync12(resolve8(projectRoot, "alembic.ini"))) {
13447
13579
  result.hasAlembic = true;
13448
- if (existsSync11(resolve7(projectRoot, "alembic"))) {
13580
+ if (existsSync12(resolve8(projectRoot, "alembic"))) {
13449
13581
  result.alembicDir = "alembic";
13450
13582
  }
13451
- } else if (existsSync11(resolve7(projectRoot, "alembic"))) {
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 = resolve7(projectRoot, candidate);
13458
- if (existsSync11(candidatePath) && existsSync11(resolve7(candidatePath, "__init__.py"))) {
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 (existsSync11(candidatePath)) {
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 = resolve7(projectRoot, "massu.config.yaml");
13483
- if (existsSync11(configPath)) {
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
- writeFileSync2(configPath, yamlContent, "utf-8");
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" && !existsSync11(resolve7(projectRoot, "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 === "." || existsSync11(resolve7(projectRoot, 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 (existsSync11(resolve7(projectRoot, "alembic.ini")) || existsSync11(resolve7(projectRoot, "alembic"))) {
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 = resolve7(templatesDir, templateId, "massu.config.yaml");
13706
- if (!existsSync11(templatePath)) return config;
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(readFileSync11(templatePath, "utf-8"));
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 (existsSync11(configPath)) {
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
- mkdirSync5(dirname6(configPath), { recursive: true });
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
- chmodSync2(configPath, existingMode);
13924
+ chmodSync3(configPath, existingMode);
13793
13925
  } catch {
13794
13926
  }
13795
13927
  }
13796
13928
  return { validated: true };
13797
13929
  } catch (err) {
13798
- if (existsSync11(tmpPath)) {
13930
+ if (existsSync12(tmpPath)) {
13799
13931
  try {
13800
- rmSync2(tmpPath, { force: true });
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 (!existsSync11(configPath)) return "Config file does not exist after write";
13810
- const content = readFileSync11(configPath, "utf-8");
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 = resolve7(projectRoot, src);
13831
- if (!existsSync11(srcAbs)) {
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 = resolve7(projectRoot, d2);
13842
- if (!existsSync11(abs)) {
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 (!existsSync11(resolve7(projectRoot, r2))) {
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
- resolve7(cwd, "node_modules/@massu/core/templates"),
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
- resolve7(__dirname2, "../templates"),
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
- resolve7(__dirname2, "../../templates"),
13894
- resolve7(__dirname2, "../../../templates")
14025
+ resolve8(__dirname2, "../../templates"),
14026
+ resolve8(__dirname2, "../../../templates")
13895
14027
  ];
13896
14028
  for (const c2 of candidates) {
13897
- if (existsSync11(c2)) return c2;
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 = resolve7(templatesDir, templateName, "massu.config.yaml");
13907
- if (!existsSync11(srcPath)) {
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 = readFileSync11(srcPath, "utf-8");
14043
+ let content = readFileSync12(srcPath, "utf-8");
13912
14044
  content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectName);
13913
- writeFileSync2(targetPath, content, "utf-8");
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
- resolve7(__dirname2, "../package.json"),
13922
- resolve7(__dirname2, "../../package.json")
14053
+ resolve8(__dirname2, "../package.json"),
14054
+ resolve8(__dirname2, "../../package.json")
13923
14055
  ];
13924
14056
  for (const candidate of candidates) {
13925
- if (existsSync11(candidate)) {
14057
+ if (existsSync12(candidate)) {
13926
14058
  try {
13927
- const pkg = JSON.parse(readFileSync11(candidate, "utf-8"));
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 = resolve7(projectRoot, ".mcp.json");
14072
+ const mcpPath = resolve8(projectRoot, ".mcp.json");
13941
14073
  let existing = {};
13942
- if (existsSync11(mcpPath)) {
14074
+ if (existsSync12(mcpPath)) {
13943
14075
  try {
13944
- existing = JSON.parse(readFileSync11(mcpPath, "utf-8"));
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
- writeFileSync2(mcpPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
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 = resolve7(cwd, "node_modules/@massu/core/dist/hooks");
13966
- if (existsSync11(nodeModulesPath)) {
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 = resolve7(__dirname2, "../dist/hooks");
13970
- if (existsSync11(localPath)) {
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 = resolve7(projectRoot, claudeDirName);
14117
- if (!existsSync11(claudeDir)) {
14118
- mkdirSync5(claudeDir, { recursive: true });
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 = resolve7(homedir4(), `.claude/projects/${encodedRoot}/memory`);
14268
+ const memoryDir = resolve8(homedir5(), `.claude/projects/${encodedRoot}/memory`);
14137
14269
  let migratedFromLegacy = false;
14138
- const legacyDir = resolve7(homedir4(), `.claude/projects/-${encodedRoot}/memory`);
14139
- if (existsSync11(legacyDir) && !existsSync11(memoryDir)) {
14270
+ const legacyDir = resolve8(homedir5(), `.claude/projects/-${encodedRoot}/memory`);
14271
+ if (existsSync12(legacyDir) && !existsSync12(memoryDir)) {
14140
14272
  try {
14141
- mkdirSync5(resolve7(memoryDir, ".."), { recursive: true });
14273
+ mkdirSync6(resolve8(memoryDir, ".."), { recursive: true });
14142
14274
  renameSync3(legacyDir, memoryDir);
14143
14275
  try {
14144
- const legacyParent = resolve7(legacyDir, "..");
14145
- if (existsSync11(legacyParent) && readdirSync10(legacyParent).length === 0) {
14146
- rmSync2(legacyParent, { recursive: false });
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 (!existsSync11(memoryDir)) {
14156
- mkdirSync5(memoryDir, { recursive: true });
14287
+ if (!existsSync12(memoryDir)) {
14288
+ mkdirSync6(memoryDir, { recursive: true });
14157
14289
  created = true;
14158
14290
  }
14159
- const memoryMdPath = resolve7(memoryDir, "MEMORY.md");
14291
+ const memoryMdPath = resolve8(memoryDir, "MEMORY.md");
14160
14292
  let memoryMdCreated = false;
14161
- if (!existsSync11(memoryMdPath)) {
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
- writeFileSync2(memoryMdPath, memoryContent, "utf-8");
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 = resolve7(projectRoot, "massu.config.yaml");
14276
- if (existsSync11(configPath)) {
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
- rmSync2(configPath, { force: true });
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
- rmSync2(configPath, { force: true });
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 = resolve7(cmdResult.claudeDir, "commands", "_massu-needs-stack.md");
14416
- if (!existsSync11(placeholderPath)) {
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
- mkdirSync5(resolve7(cmdResult.claudeDir, "commands"), { recursive: true });
14438
- writeFileSync2(placeholderPath, placeholderBody, "utf-8");
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 = resolve7(homedir4(), ".claude", "projects", encodedRoot, "memory");
14461
- const memFiles = existsSync11(memoryDir) ? readdirSync10(memoryDir).filter((f2) => f2.endsWith(".md") && f2 !== "MEMORY.md") : [];
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 existsSync12, readFileSync as readFileSync12, readdirSync as readdirSync11 } from "fs";
15114
- import { resolve as resolve8, dirname as dirname7 } from "path";
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 = resolve8(projectRoot, "massu.config.yaml");
15119
- if (!existsSync12(configPath)) {
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 = readFileSync12(configPath, "utf-8");
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 (!existsSync12(mcpPath)) {
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(readFileSync12(mcpPath, "utf-8"));
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 (!existsSync12(settingsPath)) {
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 = resolve8(projectRoot, "node_modules/@massu/core/dist/hooks");
15345
+ const nodeModulesHooksDir = resolve9(projectRoot, "node_modules/@massu/core/dist/hooks");
15180
15346
  let hooksDir = nodeModulesHooksDir;
15181
- if (!existsSync12(nodeModulesHooksDir)) {
15182
- const devHooksDir = resolve8(__dirname3, "../../dist/hooks");
15183
- if (existsSync12(devHooksDir)) {
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 (!existsSync12(resolve8(hooksDir, hookFile))) {
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 = resolve8(projectRoot, ".git");
15218
- if (!existsSync12(gitDir)) {
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 (!existsSync12(knowledgeDbPath)) {
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 (!existsSync12(memoryDir)) {
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 (!existsSync12(settingsPath)) {
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
- if (tier === "free" && !info.validUntil) {
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 = resolve8(projectRoot, config.python.root);
15318
- if (!existsSync12(pythonRoot)) {
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 = resolve8(dir, entry.name);
15338
- if (depth <= 2 && !existsSync12(resolve8(subdir, "__init__.py"))) {
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 = resolve8(projectRoot, "massu.config.yaml");
15434
- if (!existsSync12(configPath)) {
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 = readFileSync12(configPath, "utf-8");
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 existsSync13 } from "fs";
15519
- import { resolve as resolve9, dirname as dirname8 } from "path";
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
- resolve9(__dirname4, "hooks", file),
15920
+ resolve10(__dirname4, "hooks", file),
15532
15921
  // TS-source dev / sibling layout: src/commands/ → ../hooks/<file>
15533
- resolve9(__dirname4, "../hooks", file),
15922
+ resolve10(__dirname4, "../hooks", file),
15534
15923
  // TS-source dev fallback: src/commands/ → ../../dist/hooks/<file>
15535
- resolve9(__dirname4, "../../dist/hooks", file)
15924
+ resolve10(__dirname4, "../../dist/hooks", file)
15536
15925
  ];
15537
15926
  for (const candidate of candidates) {
15538
- if (existsSync13(candidate)) {
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 resolve10 } from "path";
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 resolve10(process.cwd(), claudeDirName);
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 license_exports = {};
15742
- __export(license_exports, {
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 existsSync14, readFileSync as readFileSync13, readdirSync as readdirSync12 } from "fs";
15831
- import { resolve as resolve11 } from "path";
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 (!existsSync14(planDir)) {
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 = resolve11(planDir, file);
15857
- const text18 = readFileSync13(path, "utf-8");
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 existsSync15, readFileSync as readFileSync14 } from "fs";
15939
- import { resolve as resolve12 } from "path";
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 = resolve12(repoRoot, "packages/core/package.json");
15964
- if (!existsSync15(pkgPath)) return "0.0.0";
15965
- const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
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 = resolve12(repoRoot, "CHANGELOG.md");
15973
- if (!existsSync15(path)) return "";
15974
- const content = readFileSync14(path, "utf-8");
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 = resolve12(repoRoot, "docs/plans");
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 existsSync16, writeFileSync as writeFileSync3, mkdirSync as mkdirSync6 } from "fs";
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 (existsSync16(candidatePath) || alreadyApplied(db, promptHash)) {
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 (!existsSync16(dir)) mkdirSync6(dir, { recursive: true });
16344
- writeFileSync3(candidatePath, JSON.stringify(sidecar, null, 2), "utf-8");
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 existsSync17, readFileSync as readFileSync15 } from "fs";
16459
- import { resolve as resolve13 } from "path";
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 === "" ? resolve13(sourceDir, `${baseName}.md`) : resolve13(sourceDir, `${baseName}${suffix}.md`);
16488
- if (!existsSync17(file)) {
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(readFileSync15(file, "utf-8"));
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 mkdirSync7, readFileSync as readFileSync16, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
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 = readFileSync16(`${lockPath}.pid`, "utf-8").trim();
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
- mkdirSync7(dirname10(lockPath), { recursive: true });
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
- writeFileSync4(`${lockPath}.pid`, String(process.pid), "utf-8");
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
- rmSync3(`${lockPath}.pid`, { force: true });
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 resolve14 } from "path";
17032
+ import { resolve as resolve15 } from "path";
16644
17033
  function withInstallLock(projectRoot, fn, opts = {}) {
16645
- const lockPath = resolve14(projectRoot, ".massu", "installAll.lock");
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 existsSync18, readFileSync as readFileSync17, rmSync as rmSync4 } from "fs";
16686
- import { resolve as resolve15 } from "path";
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 = resolve15(cwd, "massu.config.yaml");
17203
+ const configPath = resolve16(cwd, "massu.config.yaml");
16815
17204
  const log = opts.silent ? () => {
16816
17205
  } : (s) => process.stdout.write(s);
16817
- if (!existsSync18(configPath)) {
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 = readFileSync17(configPath, "utf-8");
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 = resolve15(installResult.claudeDir, "commands", "_massu-needs-stack.md");
16898
- if (existsSync18(placeholderPath)) {
17286
+ const placeholderPath = resolve16(installResult.claudeDir, "commands", "_massu-needs-stack.md");
17287
+ if (existsSync19(placeholderPath)) {
16899
17288
  try {
16900
- rmSync4(placeholderPath, { force: true });
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 existsSync19, statSync as statSync8 } from "fs";
17008
- import { resolve as resolve16 } from "path";
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 = resolve16(projectRoot, lf);
17012
- if (!existsSync19(p19)) continue;
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 (existsSync19(resolve16(projectRoot, ".git", s))) return true;
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 existsSync20, fsyncSync as fsyncSync3, mkdirSync as mkdirSync8, openSync as openSync3, readFileSync as readFileSync18, renameSync as renameSync4, rmSync as rmSync5, writeFileSync as writeFileSync5, writeSync as writeSync3 } from "fs";
17225
- import { dirname as dirname11, resolve as resolve17 } from "path";
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 resolve17(projectRoot, ".massu", "watch-state.json");
17616
+ return resolve18(projectRoot, ".massu", "watch-state.json");
17228
17617
  }
17229
17618
  function backupStatePath(projectRoot) {
17230
- return resolve17(projectRoot, ".massu", "watch-state.v0.bak.json");
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 (!existsSync20(path)) return { ...DEFAULT_STATE };
17235
- const content = readFileSync18(path, "utf-8");
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
- mkdirSync8(dirname11(bak), { recursive: true });
17278
- writeFileSync5(bak, content, "utf-8");
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
- mkdirSync8(dirname11(path), { recursive: true });
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 && existsSync20(tmp)) {
17687
+ if (!renamed && existsSync21(tmp)) {
17299
17688
  try {
17300
- rmSync5(tmp, { force: true });
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 resolve18 } from "path";
17581
- import { appendFileSync, existsSync as existsSync21, mkdirSync as mkdirSync9, readFileSync as readFileSync19 } from "fs";
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 ? resolve18(home, ".claude", "bin", "claude-bg") : null;
17607
- if (fixed && existsSync21(fixed)) return 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 (!existsSync21(path)) {
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((resolve41) => {
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
- resolve41({ exitCode: 0 });
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 resolve18(projectRoot, ".massu", "refresh-log.jsonl");
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
- mkdirSync9(dirname12(path), { recursive: true });
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 (!existsSync21(path)) return [];
18222
+ if (!existsSync22(path)) return [];
17834
18223
  const warn = opts.warn ?? ((s) => {
17835
18224
  process.stderr.write(s);
17836
18225
  });
17837
- const lines = readFileSync19(path, "utf-8").split("\n").filter(Boolean);
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 chmodSync3,
18297
+ chmodSync as chmodSync4,
17909
18298
  closeSync as closeSync4,
17910
- existsSync as existsSync22,
18299
+ existsSync as existsSync23,
17911
18300
  fsyncSync as fsyncSync4,
17912
- mkdirSync as mkdirSync10,
18301
+ mkdirSync as mkdirSync11,
17913
18302
  openSync as openSync4,
17914
18303
  renameSync as renameSync5,
17915
- rmSync as rmSync6,
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 (!existsSync22(parentDir)) {
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
- mkdirSync10(parentDir, mkdirOpts);
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
- chmodSync3(tmpPath, opts.mode);
18330
+ chmodSync4(tmpPath, opts.mode);
17942
18331
  }
17943
18332
  renameSync5(tmpPath, path);
17944
18333
  return { written: true };
17945
18334
  } catch (err) {
17946
- if (existsSync22(tmpPath)) {
18335
+ if (existsSync23(tmpPath)) {
17947
18336
  try {
17948
- rmSync6(tmpPath, { force: true });
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 existsSync23, readFileSync as readFileSync20, statSync as statSync10 } from "node:fs";
18296
- import { homedir as homedir5 } from "node:os";
18297
- import { resolve as resolve19 } from "node:path";
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 = resolve19(homedir5(), ".massu");
18689
+ const dir = resolve20(homedir6(), ".massu");
18301
18690
  return {
18302
- cachePath: resolve19(dir, "adapter-manifest.json"),
18303
- lockPath: resolve19(dir, ".adapter-manifest.lock")
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 (!existsSync23(paths.cachePath)) {
18696
+ if (!existsSync24(paths.cachePath)) {
18308
18697
  return { kind: "absent" };
18309
18698
  }
18310
18699
  let raw;
18311
18700
  try {
18312
- const content = readFileSync20(paths.cachePath, "utf-8");
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 existsSync24, readFileSync as readFileSync21, lstatSync as lstatSync5 } from "node:fs";
18502
- import { homedir as homedir6 } from "node:os";
18503
- import { resolve as resolve20, isAbsolute } from "node:path";
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 : resolve20(projectRoot, 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(readFileSync21(abs)).digest("hex");
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 (!existsSync24(path)) return null;
18919
+ if (!existsSync25(path)) return null;
18531
18920
  let raw;
18532
18921
  try {
18533
- raw = JSON.parse(readFileSync21(path, "utf-8"));
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 = resolve20(homedir6(), ".massu", "adapters-local-fingerprint.json");
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 readFileSync22, readdirSync as readdirSync13, lstatSync as lstatSync6, existsSync as existsSync25 } from "node:fs";
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 homedir7 } from "node:os";
18600
- import { resolve as resolve21 } from "node:path";
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 (existsSync25(`${packageDir}/${hidden}`)) {
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(readFileSync22(f2.absPath)).digest("hex");
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 (!existsSync25(path)) return {};
19049
+ if (!existsSync26(path)) return {};
18661
19050
  let raw;
18662
19051
  try {
18663
- raw = JSON.parse(readFileSync22(path, "utf-8"));
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 = resolve21(homedir7(), ".massu", "adapter-manifest-installed.json");
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 existsSync26, readdirSync as readdirSync14, readFileSync as readFileSync23, lstatSync as lstatSync7 } from "node:fs";
18751
- import { resolve as resolve22, isAbsolute as isAbsolute2 } from "node:path";
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 = resolve22(projectRoot, "node_modules");
18755
- if (!existsSync26(nodeModulesDir)) {
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 = resolve22(nodeModulesDir, entry);
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 = resolve22(entryPath, sub);
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 = resolve22(packageDir, "package.json");
18810
- if (!existsSync26(pkgJsonPath)) return null;
19198
+ const pkgJsonPath = resolve23(packageDir, "package.json");
19199
+ if (!existsSync27(pkgJsonPath)) return null;
18811
19200
  let raw;
18812
19201
  try {
18813
- raw = JSON.parse(readFileSync23(pkgJsonPath, "utf-8"));
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 : resolve22(opts.projectRoot, localPath);
18948
- if (!existsSync26(absPath)) {
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 existsSync27, readFileSync as readFileSync24 } from "node:fs";
19031
- import { resolve as resolve23 } from "node:path";
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 = resolve23(projectRoot, "massu.config.yaml");
19299
- if (!existsSync27(yamlPath)) {
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 = readFileSync24(yamlPath, "utf-8");
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 = resolve23(projectRoot, ".massu", "adapters-local-mutate.lock");
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 = resolve23(projectRoot, "node_modules", ...packageName.split("/"));
19399
- if (!existsSync27(packageDir)) {
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 = resolve23(packageDir, "package.json");
19407
- if (!existsSync27(pkgJsonPath)) {
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(readFileSync24(pkgJsonPath, "utf-8"));
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 = resolve23(projectRoot, "node_modules", ...name.split("/"));
19534
- if (!existsSync27(packageDir)) {
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 existsSync28, mkdirSync as mkdirSync11, readdirSync as readdirSync15, statSync as statSync11 } from "fs";
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 (!existsSync28(dbPath)) {
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 (!existsSync28(dir)) {
19647
- mkdirSync11(dir, { recursive: true });
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 existsSync29, mkdirSync as mkdirSync12 } from "fs";
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 (!existsSync29(dir)) {
19961
- mkdirSync12(dir, { recursive: true });
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 resolve24, normalize } from "path";
20521
+ import { resolve as resolve25, normalize } from "path";
20133
20522
  function ensureWithinRoot(filePath, projectRoot) {
20134
- const resolvedRoot = resolve24(projectRoot);
20135
- const resolvedPath = resolve24(resolvedRoot, filePath);
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 readFileSync25, existsSync as existsSync30, statSync as statSync12 } from "fs";
20209
- import { resolve as resolve25, dirname as dirname16, join as join14 } from "path";
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 = resolve25(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
20654
+ basePath = resolve26(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
20266
20655
  } else {
20267
- basePath = resolve25(dirname16(fromFile), specifier);
20656
+ basePath = resolve26(dirname16(fromFile), specifier);
20268
20657
  }
20269
- if (existsSync30(basePath) && !isDirectory(basePath)) {
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 (existsSync30(withExt)) {
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 (existsSync30(indexPath)) {
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(resolve25(projectRoot, file.path), projectRoot);
20318
- if (!existsSync30(absPath)) continue;
20706
+ const absPath = ensureWithinRoot(resolve26(projectRoot, file.path), projectRoot);
20707
+ if (!existsSync31(absPath)) continue;
20319
20708
  let source;
20320
20709
  try {
20321
- source = readFileSync25(absPath, "utf-8");
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 readFileSync26, existsSync as existsSync31, readdirSync as readdirSync16 } from "fs";
20359
- import { resolve as resolve26, join as join15 } from "path";
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 (!existsSync31(rootPath)) {
20752
+ if (!existsSync32(rootPath)) {
20364
20753
  throw new Error(`Root router not found at ${rootPath}`);
20365
20754
  }
20366
- const source = readFileSync26(rootPath, "utf-8");
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 = resolve26(paths.routersDir, filePath);
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 (existsSync31(candidate)) {
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 (existsSync31(indexCandidate)) {
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 = resolve26(getProjectRoot(), routerFilePath);
20403
- if (!existsSync31(absPath)) return [];
20404
- const source = readFileSync26(absPath, "utf-8");
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
- resolve26(root, config.paths.pages ?? src + "/app"),
20428
- resolve26(root, config.paths.components ?? src + "/components"),
20429
- resolve26(root, config.paths.hooks ?? src + "/hooks")
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 (!existsSync31(dir)) continue;
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 = readFileSync26(fullPath, "utf-8");
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 readFileSync27, existsSync as existsSync32 } from "fs";
20512
- import { resolve as resolve27 } from "path";
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(resolve27(projectRoot, file), projectRoot);
20551
- if (!existsSync32(absPath)) continue;
20939
+ const absPath = ensureWithinRoot(resolve28(projectRoot, file), projectRoot);
20940
+ if (!existsSync33(absPath)) continue;
20552
20941
  try {
20553
- const source = readFileSync27(absPath, "utf-8");
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(resolve27(getProjectRoot(), proc.router_file), getProjectRoot());
20572
- if (!existsSync32(absPath)) continue;
20960
+ const absPath = ensureWithinRoot(resolve28(getProjectRoot(), proc.router_file), getProjectRoot());
20961
+ if (!existsSync33(absPath)) continue;
20573
20962
  try {
20574
- const source = readFileSync27(absPath, "utf-8");
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 readFileSync28, existsSync as existsSync33, readdirSync as readdirSync17 } from "fs";
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 (!existsSync33(schemaPath)) {
21239
+ if (!existsSync34(schemaPath)) {
20851
21240
  throw new Error(`Prisma schema not found at ${schemaPath}`);
20852
21241
  }
20853
- const source = readFileSync28(schemaPath, "utf-8");
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 (!existsSync33(routersDir)) return usage;
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 = readFileSync28(absPath, "utf-8");
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 (!existsSync33(dir)) return result;
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 = readFileSync28(fullPath, "utf-8");
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 readFileSync29, existsSync as existsSync34, readdirSync as readdirSync18 } from "fs";
21127
- import { resolve as resolve29, join as join17, relative as relative6, dirname as dirname17 } from "path";
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(resolve29(projectRoot, fromFile));
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(resolve29(projectRoot, pythonRoot), ...parts);
21532
+ const candidate = join17(resolve30(projectRoot, pythonRoot), ...parts);
21144
21533
  return tryResolvePythonPath(candidate, projectRoot);
21145
21534
  }
21146
21535
  function tryResolvePythonPath(basePath, projectRoot) {
21147
- if (existsSync34(basePath + ".py")) {
21536
+ if (existsSync35(basePath + ".py")) {
21148
21537
  return relative6(projectRoot, basePath + ".py");
21149
21538
  }
21150
- if (existsSync34(join17(basePath, "__init__.py"))) {
21539
+ if (existsSync35(join17(basePath, "__init__.py"))) {
21151
21540
  return relative6(projectRoot, join17(basePath, "__init__.py"));
21152
21541
  }
21153
- if (basePath.endsWith(".py") && existsSync34(basePath)) {
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 = resolve29(projectRoot, pythonRoot);
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 = readFileSync29(absFile, "utf-8");
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 readFileSync30, readdirSync as readdirSync19 } from "fs";
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 = readFileSync30(absFile, "utf-8");
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 readFileSync31, readdirSync as readdirSync20 } from "fs";
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 = readFileSync31(absFile, "utf-8");
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 readFileSync32, readdirSync as readdirSync21 } from "fs";
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 = readFileSync32(absFile, "utf-8");
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 readFileSync33, readdirSync as readdirSync22 } from "fs";
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 = readFileSync33(absFile, "utf-8");
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 readFileSync34, existsSync as existsSync35 } from "fs";
22507
- import { resolve as resolve30, basename as basename6 } from "path";
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 (!existsSync35(mapPath)) {
22951
+ if (!existsSync36(mapPath)) {
22563
22952
  throw new Error(`docs-map.json not found at ${mapPath}`);
22564
22953
  }
22565
- return JSON.parse(readFileSync34(mapPath, "utf-8"));
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(resolve30(getResolvedPaths().srcDir, "..", routerPath), root);
22636
- if (!existsSync35(absPath)) {
22637
- const altPath = ensureWithinRoot(resolve30(getResolvedPaths().srcDir, "../server/api/routers", basename6(routerPath)), root);
22638
- if (!existsSync35(altPath)) return [];
22639
- return extractProcedureNamesFromContent(readFileSync34(altPath, "utf-8"));
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(readFileSync34(absPath, "utf-8"));
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(resolve30(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
22682
- if (!existsSync35(helpPagePath)) {
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 = readFileSync34(helpPagePath, "utf-8");
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(resolve30(getResolvedPaths().helpSitePath, `pages/user-guides/${guideName}/index.mdx`), getProjectRoot());
22735
- if (existsSync35(guidePath)) {
22736
- const guideContent = readFileSync34(guidePath, "utf-8");
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(resolve30(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
22770
- const exists = existsSync35(helpPagePath);
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 = readFileSync34(helpPagePath, "utf-8");
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 existsSync36 } from "fs";
23118
- import { resolve as resolve31 } from "path";
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 = resolve31(PROJECT_ROOT, comp.component_file);
23355
- if (!existsSync36(absPath)) {
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 = resolve31(PROJECT_ROOT, `src/server/api/routers/${proc.router_name}.ts`);
23361
- if (!existsSync36(routerPath)) {
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 = resolve31(PROJECT_ROOT, routeToPath);
23368
- if (page.page_route.startsWith("/") && !existsSync36(absPath)) {
23369
- const altPath = resolve31(PROJECT_ROOT, `src/app${page.page_route}/page.tsx`);
23370
- if (!existsSync36(altPath)) {
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 readFileSync35, existsSync as existsSync37, readdirSync as readdirSync23, statSync as statSync13 } from "fs";
23897
- import { resolve as resolve32, join as join22, basename as basename7, dirname as dirname18, relative as relative11 } from "path";
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 = resolve32(projectRoot, componentsBase);
24029
- if (existsSync37(basePath)) {
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 = resolve32(projectRoot, dir);
24042
- if (!existsSync37(absDir)) continue;
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 = readFileSync35(file, "utf-8");
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 existsSync38, readFileSync as readFileSync36 } from "fs";
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 (!existsSync38(absPath)) {
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 = readFileSync36(absPath, "utf-8");
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 existsSync39, readFileSync as readFileSync37 } from "fs";
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 (!existsSync39(absPath)) {
25988
+ if (!existsSync40(absPath)) {
25600
25989
  return { riskScore: 0, findings: [] };
25601
25990
  }
25602
25991
  let source;
25603
25992
  try {
25604
- source = readFileSync37(absPath, "utf-8");
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 existsSync40, readFileSync as readFileSync38 } from "fs";
25895
- import { resolve as resolve33 } from "path";
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 = resolve33(projectRoot, "package.json");
25928
- if (!existsSync40(pkgPath)) return /* @__PURE__ */ new Map();
26316
+ const pkgPath = resolve34(projectRoot, "package.json");
26317
+ if (!existsSync41(pkgPath)) return /* @__PURE__ */ new Map();
25929
26318
  try {
25930
- const pkg = JSON.parse(readFileSync38(pkgPath, "utf-8"));
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 readFileSync39, readdirSync as readdirSync24, statSync as statSync14, existsSync as existsSync41 } from "fs";
26537
- import { resolve as resolve34, relative as relative12, basename as basename8, extname as extname2 } from "path";
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 = resolve34(dir, entry.name);
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 (existsSync41(paths.plansDir)) {
27233
+ if (existsSync42(paths.plansDir)) {
26845
27234
  const planFiles = discoverMarkdownFiles(paths.plansDir);
26846
27235
  files.push(...planFiles);
26847
27236
  }
26848
- if (existsSync41(paths.docsDir)) {
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 (!existsSync41(filePath)) continue;
26892
- const content = readFileSync39(filePath, "utf-8");
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 (existsSync41(paths.plansDir)) {
27416
+ if (existsSync42(paths.plansDir)) {
27028
27417
  files.push(...discoverMarkdownFiles(paths.plansDir));
27029
27418
  }
27030
- if (existsSync41(paths.docsDir)) {
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 readFileSync40, writeFileSync as writeFileSync6, appendFileSync as appendFileSync2, readdirSync as readdirSync25 } from "fs";
27060
- import { resolve as resolve35, basename as basename9 } from "path";
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 = resolve35(getResolvedPaths().memoryDir, "corrections.md");
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 = readFileSync40(correctionsPath, "utf-8");
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
- writeFileSync6(correctionsPath, before + entry + after);
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 readFileSync41, existsSync as existsSync43 } from "fs";
28768
- import { resolve as resolve36, basename as basename10 } from "path";
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, resolve36(getProjectRoot(), pythonRoot))) {
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(resolve36(resolvedPaths.srcDir, "..", file), root);
29187
- if (existsSync43(absFilePath)) {
29188
- const fileContent = readFileSync41(absFilePath, "utf-8").slice(0, 3e3);
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(resolve36(projectRoot, file), projectRoot);
29626
- if (!existsSync43(absPath)) {
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 = readFileSync41(absPath, "utf-8");
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 readFileSync42 } from "fs";
30035
- import { resolve as resolve37, dirname as dirname19 } from "path";
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(readFileSync42(resolve37(__dirname5, "..", "package.json"), "utf-8"));
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 existsSync44, readFileSync as readFileSync43, writeFileSync as writeFileSync7, copyFileSync, unlinkSync as unlinkSync2 } from "fs";
30357
- import { resolve as resolve38 } from "path";
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 = resolve38(cwd, "massu.config.yaml");
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 (!existsSync44(bakPath)) {
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 (!existsSync44(configPath)) {
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 = readFileSync43(configPath, "utf-8");
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 = readFileSync43(configPath, "utf-8");
30415
- writeFileSync7(bakPath, original, "utf-8");
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 existsSync45, readFileSync as readFileSync44 } from "fs";
30449
- import { resolve as resolve39 } from "path";
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 = resolve39(cwd, "massu.config.yaml");
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 (!existsSync45(configPath)) {
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 = readFileSync44(configPath, "utf-8");
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 readFileSync45 } from "fs";
30544
- import { resolve as resolve40, dirname as dirname20 } from "path";
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(), license_exports));
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(readFileSync45(resolve40(__dirname6, "../package.json"), "utf-8"));
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");