@massu/core 1.15.0 → 1.15.2

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
@@ -12,11 +12,20 @@ var __require = /* @__PURE__ */ ((x2) => typeof require !== "undefined" ? requir
12
12
  if (typeof require !== "undefined") return require.apply(this, arguments);
13
13
  throw Error('Dynamic require of "' + x2 + '" is not supported');
14
14
  });
15
- var __esm = (fn, res) => function __init() {
16
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
15
+ var __esm = (fn, res, err) => function __init() {
16
+ if (err) throw err[0];
17
+ try {
18
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
19
+ } catch (e2) {
20
+ throw err = [e2], e2;
21
+ }
17
22
  };
18
23
  var __commonJS = (cb, mod) => function __require2() {
19
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
24
+ try {
25
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
26
+ } catch (e2) {
27
+ throw mod = 0, e2;
28
+ }
20
29
  };
21
30
  var __export = (target, all) => {
22
31
  for (var name in all)
@@ -49,17 +58,136 @@ var init_memory_path = __esm({
49
58
  }
50
59
  });
51
60
 
52
- // src/config.ts
53
- import { resolve, dirname } from "path";
54
- import { existsSync, readFileSync } from "fs";
61
+ // src/credentials.ts
55
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";
56
184
  import { parse as parseYaml } from "yaml";
57
185
  import { z } from "zod";
58
186
  function findProjectRoot() {
59
187
  const cwd = process.cwd();
60
188
  let dir = cwd;
61
189
  while (true) {
62
- if (existsSync(resolve(dir, "massu.config.yaml"))) {
190
+ if (existsSync2(resolve2(dir, "massu.config.yaml"))) {
63
191
  return dir;
64
192
  }
65
193
  const parent = dirname(dir);
@@ -68,10 +196,10 @@ function findProjectRoot() {
68
196
  }
69
197
  dir = cwd;
70
198
  while (true) {
71
- if (existsSync(resolve(dir, "package.json"))) {
199
+ if (existsSync2(resolve2(dir, "package.json"))) {
72
200
  return dir;
73
201
  }
74
- if (existsSync(resolve(dir, ".git"))) {
202
+ if (existsSync2(resolve2(dir, ".git"))) {
75
203
  return dir;
76
204
  }
77
205
  const parent = dirname(dir);
@@ -89,10 +217,10 @@ function getProjectRoot() {
89
217
  function getConfig() {
90
218
  if (_config) return _config;
91
219
  const root = getProjectRoot();
92
- const configPath = resolve(root, "massu.config.yaml");
220
+ const configPath = resolve2(root, "massu.config.yaml");
93
221
  let rawYaml = {};
94
- if (existsSync(configPath)) {
95
- const content = readFileSync(configPath, "utf-8");
222
+ if (existsSync2(configPath)) {
223
+ const content = readFileSync2(configPath, "utf-8");
96
224
  rawYaml = parseYaml(content) ?? {};
97
225
  }
98
226
  const result = RawConfigSchema.safeParse(rawYaml);
@@ -109,7 +237,7 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
109
237
  );
110
238
  }
111
239
  const parsed = result.data;
112
- 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);
113
241
  const fw = parsed.framework;
114
242
  let router = fw.router;
115
243
  let orm = fw.orm;
@@ -167,60 +295,72 @@ Hint: run \`massu config refresh\` to regenerate a valid config or fix the liste
167
295
  telemetry: parsed.telemetry,
168
296
  lsp: parsed.lsp
169
297
  };
170
- 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) {
171
302
  _config.cloud = {
172
303
  enabled: true,
173
304
  sync: { memory: true, analytics: true, audit: true },
174
305
  ..._config.cloud,
175
- apiKey: process.env.MASSU_API_KEY
306
+ apiKey: resolvedKey.apiKey,
307
+ endpoint: resolvedEndpoint
176
308
  };
309
+ } else if (_config.cloud) {
310
+ _config.cloud = { ..._config.cloud, endpoint: _config.cloud.endpoint ?? resolvedEndpoint };
177
311
  }
178
312
  return _config;
179
313
  }
314
+ function getResolvedApiKeySource() {
315
+ getConfig();
316
+ return _resolvedApiKeySource;
317
+ }
180
318
  function getResolvedPaths() {
181
319
  const config = getConfig();
182
320
  const root = getProjectRoot();
183
321
  const claudeDirName = config.conventions?.claudeDirName ?? ".claude";
184
322
  return {
185
- codegraphDbPath: resolve(root, ".codegraph/codegraph.db"),
186
- dataDbPath: resolve(root, ".massu/data.db"),
187
- prismaSchemaPath: resolve(root, config.paths.schema ?? "prisma/schema.prisma"),
188
- rootRouterPath: resolve(root, config.paths.routerRoot ?? "src/server/api/root.ts"),
189
- routersDir: resolve(root, config.paths.routers ?? "src/server/api/routers"),
190
- 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),
191
329
  pathAlias: Object.fromEntries(
192
330
  Object.entries(config.paths.aliases).map(([alias, target]) => [
193
331
  alias,
194
- resolve(root, target)
332
+ resolve2(root, target)
195
333
  ])
196
334
  ),
197
335
  extensions: [".ts", ".tsx", ".js", ".jsx"],
198
336
  indexFiles: ["index.ts", "index.tsx", "index.js", "index.jsx"],
199
- patternsDir: resolve(root, claudeDirName, "patterns"),
200
- claudeMdPath: resolve(root, claudeDirName, "CLAUDE.md"),
201
- docsMapPath: resolve(root, ".massu/docs-map.json"),
202
- helpSitePath: resolve(root, "../" + config.project.name + "-help"),
203
- memoryDbPath: resolve(root, ".massu/memory.db"),
204
- knowledgeDbPath: resolve(root, ".massu/knowledge.db"),
205
- plansDir: resolve(root, "docs/plans"),
206
- docsDir: resolve(root, "docs"),
207
- claudeDir: resolve(root, claudeDirName),
208
- memoryDir: resolve(homedir(), claudeDirName, "projects", encodeMemoryDirName(root), "memory"),
209
- sessionStatePath: resolve(root, config.conventions?.sessionStatePath ?? `${claudeDirName}/session-state/CURRENT.md`),
210
- sessionArchivePath: resolve(root, config.conventions?.sessionArchivePath ?? `${claudeDirName}/session-state/archive`),
211
- mcpJsonPath: resolve(root, ".mcp.json"),
212
- 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")
213
351
  };
214
352
  }
215
353
  function resetConfig() {
216
354
  _config = null;
217
355
  _projectRoot = null;
356
+ _resolvedApiKeySource = "none";
218
357
  }
219
- 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;
220
359
  var init_config = __esm({
221
360
  "src/config.ts"() {
222
361
  "use strict";
223
362
  init_memory_path();
363
+ init_credentials();
224
364
  DomainConfigSchema = z.object({
225
365
  name: z.string().default("Unknown"),
226
366
  routers: z.array(z.string()).default([]),
@@ -602,6 +742,7 @@ var init_config = __esm({
602
742
  lsp: LSPConfigSchema.optional()
603
743
  }).passthrough();
604
744
  _config = null;
745
+ _resolvedApiKeySource = "none";
605
746
  _projectRoot = null;
606
747
  }
607
748
  });
@@ -664,7 +805,7 @@ __export(memory_db_exports, {
664
805
  });
665
806
  import Database from "better-sqlite3";
666
807
  import { dirname as dirname2, basename } from "path";
667
- import { existsSync as existsSync2, mkdirSync } from "fs";
808
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
668
809
  function sanitizeFts5Query(raw) {
669
810
  const trimmed = raw.trim();
670
811
  if (!trimmed) return '""';
@@ -674,8 +815,8 @@ function sanitizeFts5Query(raw) {
674
815
  function getMemoryDb() {
675
816
  const dbPath = getResolvedPaths().memoryDbPath;
676
817
  const dir = dirname2(dbPath);
677
- if (!existsSync2(dir)) {
678
- mkdirSync(dir, { recursive: true });
818
+ if (!existsSync3(dir)) {
819
+ mkdirSync2(dir, { recursive: true });
679
820
  }
680
821
  const db = new Database(dbPath);
681
822
  db.pragma("journal_mode = WAL");
@@ -2008,12 +2149,12 @@ var init_memory_db = __esm({
2008
2149
  });
2009
2150
 
2010
2151
  // src/memory-file-ingest.ts
2011
- import { readFileSync as readFileSync2, existsSync as existsSync3, readdirSync } from "fs";
2152
+ import { readFileSync as readFileSync3, existsSync as existsSync4, readdirSync } from "fs";
2012
2153
  import { join } from "path";
2013
2154
  import { parse as parseYaml2 } from "yaml";
2014
2155
  function ingestMemoryFile(db, sessionId, filePath) {
2015
- if (!existsSync3(filePath)) return "skipped";
2016
- const content = readFileSync2(filePath, "utf-8");
2156
+ if (!existsSync4(filePath)) return "skipped";
2157
+ const content = readFileSync3(filePath, "utf-8");
2017
2158
  const basename11 = (filePath.split("/").pop() ?? "").replace(".md", "");
2018
2159
  const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
2019
2160
  let name = basename11;
@@ -2051,7 +2192,7 @@ ${body}` : body;
2051
2192
  }
2052
2193
  function backfillMemoryFiles(db, memoryDir, sessionId) {
2053
2194
  const stats = { inserted: 0, updated: 0, skipped: 0, total: 0 };
2054
- if (!existsSync3(memoryDir)) return stats;
2195
+ if (!existsSync4(memoryDir)) return stats;
2055
2196
  const files = readdirSync(memoryDir).filter(
2056
2197
  (f2) => f2.endsWith(".md") && f2 !== "MEMORY.md"
2057
2198
  );
@@ -2264,16 +2405,16 @@ var init_template_engine = __esm({
2264
2405
  // src/lib/settings-local.ts
2265
2406
  import {
2266
2407
  closeSync,
2267
- existsSync as existsSync4,
2408
+ existsSync as existsSync5,
2268
2409
  fsyncSync,
2269
- mkdirSync as mkdirSync2,
2410
+ mkdirSync as mkdirSync3,
2270
2411
  openSync,
2271
- readFileSync as readFileSync3,
2412
+ readFileSync as readFileSync4,
2272
2413
  renameSync,
2273
- rmSync,
2414
+ rmSync as rmSync2,
2274
2415
  writeSync
2275
2416
  } from "fs";
2276
- import { dirname as dirname3, resolve as resolve3 } from "path";
2417
+ import { dirname as dirname3, resolve as resolve4 } from "path";
2277
2418
  function atomicWriteFile(targetPath, content, mode = 420) {
2278
2419
  const tmpPath = `${targetPath}.${process.pid}.tmp`;
2279
2420
  try {
@@ -2287,9 +2428,9 @@ function atomicWriteFile(targetPath, content, mode = 420) {
2287
2428
  }
2288
2429
  renameSync(tmpPath, targetPath);
2289
2430
  } catch (err) {
2290
- if (existsSync4(tmpPath)) {
2431
+ if (existsSync5(tmpPath)) {
2291
2432
  try {
2292
- rmSync(tmpPath, { force: true });
2433
+ rmSync2(tmpPath, { force: true });
2293
2434
  } catch {
2294
2435
  }
2295
2436
  }
@@ -2297,11 +2438,11 @@ function atomicWriteFile(targetPath, content, mode = 420) {
2297
2438
  }
2298
2439
  }
2299
2440
  function readSettingsAtPath(absolutePath) {
2300
- if (!existsSync4(absolutePath)) {
2441
+ if (!existsSync5(absolutePath)) {
2301
2442
  return {};
2302
2443
  }
2303
2444
  try {
2304
- const raw = readFileSync3(absolutePath, "utf-8");
2445
+ const raw = readFileSync4(absolutePath, "utf-8");
2305
2446
  const parsed = JSON.parse(raw);
2306
2447
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
2307
2448
  return {};
@@ -2312,13 +2453,13 @@ function readSettingsAtPath(absolutePath) {
2312
2453
  }
2313
2454
  }
2314
2455
  function readSettingsLocal(claudeDir) {
2315
- return readSettingsAtPath(resolve3(claudeDir, "settings.local.json"));
2456
+ return readSettingsAtPath(resolve4(claudeDir, "settings.local.json"));
2316
2457
  }
2317
2458
  function writeSettingsLocalAtomic(claudeDir, settings) {
2318
- const targetPath = resolve3(claudeDir, "settings.local.json");
2459
+ const targetPath = resolve4(claudeDir, "settings.local.json");
2319
2460
  const dir = dirname3(targetPath);
2320
- if (!existsSync4(dir)) {
2321
- mkdirSync2(dir, { recursive: true });
2461
+ if (!existsSync5(dir)) {
2462
+ mkdirSync3(dir, { recursive: true });
2322
2463
  }
2323
2464
  const content = JSON.stringify(settings, null, 2) + "\n";
2324
2465
  atomicWriteFile(targetPath, content);
@@ -2331,7 +2472,7 @@ var init_settings_local = __esm({
2331
2472
 
2332
2473
  // src/permissions.ts
2333
2474
  import { createHash } from "crypto";
2334
- import { homedir as homedir2 } from "os";
2475
+ import { homedir as homedir3 } from "os";
2335
2476
  import { join as join2 } from "path";
2336
2477
  function canonicalJson(value) {
2337
2478
  if (value === null || typeof value !== "object") {
@@ -2371,7 +2512,7 @@ function detectInvalidDefaultMode(settings) {
2371
2512
  return { invalid: false };
2372
2513
  }
2373
2514
  function readGlobalSettings() {
2374
- return readSettingsAtPath(join2(homedir2(), ".claude", "settings.json"));
2515
+ return readSettingsAtPath(join2(homedir3(), ".claude", "settings.json"));
2375
2516
  }
2376
2517
  function mergedPermissionState(global2, local, canonical) {
2377
2518
  const globalPerm = global2.permissions ?? {};
@@ -2555,27 +2696,27 @@ __export(install_commands_exports, {
2555
2696
  syncDirectory: () => syncDirectory
2556
2697
  });
2557
2698
  import {
2558
- existsSync as existsSync5,
2559
- readFileSync as readFileSync4,
2560
- mkdirSync as mkdirSync3,
2699
+ existsSync as existsSync6,
2700
+ readFileSync as readFileSync5,
2701
+ mkdirSync as mkdirSync4,
2561
2702
  readdirSync as readdirSync2,
2562
2703
  statSync
2563
2704
  } from "fs";
2564
- 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";
2565
2706
  import { fileURLToPath } from "url";
2566
2707
  import { createHash as createHash2 } from "crypto";
2567
2708
  function resolveAssetDir(assetName) {
2568
2709
  const cwd = process.cwd();
2569
- const nodeModulesPath = resolve4(cwd, "node_modules/@massu/core", assetName);
2570
- if (existsSync5(nodeModulesPath)) {
2710
+ const nodeModulesPath = resolve5(cwd, "node_modules/@massu/core", assetName);
2711
+ if (existsSync6(nodeModulesPath)) {
2571
2712
  return nodeModulesPath;
2572
2713
  }
2573
- const distRelPath = resolve4(__dirname, "..", assetName);
2574
- if (existsSync5(distRelPath)) {
2714
+ const distRelPath = resolve5(__dirname, "..", assetName);
2715
+ if (existsSync6(distRelPath)) {
2575
2716
  return distRelPath;
2576
2717
  }
2577
- const srcRelPath = resolve4(__dirname, "../..", assetName);
2578
- if (existsSync5(srcRelPath)) {
2718
+ const srcRelPath = resolve5(__dirname, "../..", assetName);
2719
+ if (existsSync6(srcRelPath)) {
2579
2720
  return srcRelPath;
2580
2721
  }
2581
2722
  return null;
@@ -2587,12 +2728,12 @@ function hashContent(content) {
2587
2728
  return createHash2("sha256").update(content, "utf-8").digest("hex");
2588
2729
  }
2589
2730
  function loadManifest(claudeDir) {
2590
- const path = resolve4(claudeDir, MANIFEST_RELPATH);
2591
- if (!existsSync5(path)) {
2731
+ const path = resolve5(claudeDir, MANIFEST_RELPATH);
2732
+ if (!existsSync6(path)) {
2592
2733
  return emptyManifest();
2593
2734
  }
2594
2735
  try {
2595
- const raw = readFileSync4(path, "utf-8");
2736
+ const raw = readFileSync5(path, "utf-8");
2596
2737
  const parsed = JSON.parse(raw);
2597
2738
  if (!parsed || typeof parsed !== "object" || !parsed.entries) {
2598
2739
  return emptyManifest();
@@ -2603,11 +2744,11 @@ function loadManifest(claudeDir) {
2603
2744
  }
2604
2745
  }
2605
2746
  function saveManifest(claudeDir, manifest) {
2606
- const dir = resolve4(claudeDir, ".massu");
2607
- if (!existsSync5(dir)) {
2608
- mkdirSync3(dir, { recursive: true });
2747
+ const dir = resolve5(claudeDir, ".massu");
2748
+ if (!existsSync6(dir)) {
2749
+ mkdirSync4(dir, { recursive: true });
2609
2750
  }
2610
- const finalPath = resolve4(dir, "install-manifest.json");
2751
+ const finalPath = resolve5(dir, "install-manifest.json");
2611
2752
  manifest.generatedAt = (/* @__PURE__ */ new Date()).toISOString();
2612
2753
  atomicWriteFile(finalPath, JSON.stringify(manifest, null, 2));
2613
2754
  }
@@ -2669,18 +2810,18 @@ function pickVariant(baseName, sourceDir, framework) {
2669
2810
  }
2670
2811
  for (const cand of candidates) {
2671
2812
  if (cand.subFramework) {
2672
- const subPath = resolve4(sourceDir, `${baseName}.${cand.lang}-${cand.subFramework}.md`);
2673
- if (existsSync5(subPath)) {
2813
+ const subPath = resolve5(sourceDir, `${baseName}.${cand.lang}-${cand.subFramework}.md`);
2814
+ if (existsSync6(subPath)) {
2674
2815
  return { kind: "hit", suffix: `.${cand.lang}-${cand.subFramework}` };
2675
2816
  }
2676
2817
  }
2677
- const langPath = resolve4(sourceDir, `${baseName}.${cand.lang}.md`);
2678
- if (existsSync5(langPath)) {
2818
+ const langPath = resolve5(sourceDir, `${baseName}.${cand.lang}.md`);
2819
+ if (existsSync6(langPath)) {
2679
2820
  return { kind: "hit", suffix: `.${cand.lang}` };
2680
2821
  }
2681
2822
  }
2682
- const defaultPath = resolve4(sourceDir, `${baseName}.md`);
2683
- if (existsSync5(defaultPath)) {
2823
+ const defaultPath = resolve5(sourceDir, `${baseName}.md`);
2824
+ if (existsSync6(defaultPath)) {
2684
2825
  return { kind: "hit", suffix: "" };
2685
2826
  }
2686
2827
  if (framework.type === "multi" && !framework.primary) {
@@ -2696,15 +2837,15 @@ function isVariantFilename(entry) {
2696
2837
  }
2697
2838
  function syncDirectory(sourceDir, targetDir, framework, manifest, manifestKeyPrefix, topLevel = true, templateVars = {}) {
2698
2839
  const stats = { installed: 0, updated: 0, skipped: 0, kept: 0 };
2699
- if (!existsSync5(targetDir)) {
2700
- mkdirSync3(targetDir, { recursive: true });
2840
+ if (!existsSync6(targetDir)) {
2841
+ mkdirSync4(targetDir, { recursive: true });
2701
2842
  }
2702
2843
  const entries = readdirSync2(sourceDir);
2703
2844
  for (const entry of entries) {
2704
- const sourcePath = resolve4(sourceDir, entry);
2845
+ const sourcePath = resolve5(sourceDir, entry);
2705
2846
  const entryStat = statSync(sourcePath);
2706
2847
  if (entryStat.isDirectory()) {
2707
- const subTargetDir = resolve4(targetDir, entry);
2848
+ const subTargetDir = resolve5(targetDir, entry);
2708
2849
  const subPrefix = manifestKeyPrefix === "" ? entry : `${manifestKeyPrefix}/${entry}`;
2709
2850
  const subStats = syncDirectory(
2710
2851
  sourcePath,
@@ -2733,13 +2874,13 @@ function syncDirectory(sourceDir, targetDir, framework, manifest, manifestKeyPre
2733
2874
  const suffix = choice.kind === "hit" ? choice.suffix : "";
2734
2875
  sourceFilename = suffix === "" ? `${baseName}.md` : `${baseName}${suffix}.md`;
2735
2876
  }
2736
- const resolvedSourcePath = resolve4(sourceDir, sourceFilename);
2737
- if (!existsSync5(resolvedSourcePath)) {
2877
+ const resolvedSourcePath = resolve5(sourceDir, sourceFilename);
2878
+ if (!existsSync6(resolvedSourcePath)) {
2738
2879
  continue;
2739
2880
  }
2740
2881
  const targetFilename = topLevel ? `${baseName}.md` : entry;
2741
- const targetPath = resolve4(targetDir, targetFilename);
2742
- const rawContent = readFileSync4(resolvedSourcePath, "utf-8");
2882
+ const targetPath = resolve5(targetDir, targetFilename);
2883
+ const rawContent = readFileSync5(resolvedSourcePath, "utf-8");
2743
2884
  let sourceContent;
2744
2885
  try {
2745
2886
  sourceContent = renderTemplate(rawContent, templateVars);
@@ -2757,8 +2898,8 @@ function syncDirectory(sourceDir, targetDir, framework, manifest, manifestKeyPre
2757
2898
  const sourceHash = hashContent(sourceContent);
2758
2899
  const manifestKey = manifestKeyPrefix === "" ? targetFilename : `${manifestKeyPrefix}/${targetFilename}`;
2759
2900
  const lastInstalledHash = manifest.entries[manifestKey];
2760
- if (existsSync5(targetPath)) {
2761
- const existingContent = readFileSync4(targetPath, "utf-8");
2901
+ if (existsSync6(targetPath)) {
2902
+ const existingContent = readFileSync5(targetPath, "utf-8");
2762
2903
  const existingHash = hashContent(existingContent);
2763
2904
  if (existingHash === sourceHash) {
2764
2905
  manifest.entries[manifestKey] = sourceHash;
@@ -2817,10 +2958,10 @@ function buildTemplateVars() {
2817
2958
  }
2818
2959
  function installCommands(projectRoot, opts = {}) {
2819
2960
  const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
2820
- const claudeDir = resolve4(projectRoot, claudeDirName);
2821
- const targetDir = resolve4(claudeDir, "commands");
2822
- if (!existsSync5(targetDir)) {
2823
- 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 });
2824
2965
  }
2825
2966
  const sourceDir = resolveAssetDir("commands");
2826
2967
  if (!sourceDir) {
@@ -2849,7 +2990,7 @@ function installCommands(projectRoot, opts = {}) {
2849
2990
  }
2850
2991
  function installAll(projectRoot, opts = {}) {
2851
2992
  const claudeDirName = getConfig().conventions?.claudeDirName ?? ".claude";
2852
- const claudeDir = resolve4(projectRoot, claudeDirName);
2993
+ const claudeDir = resolve5(projectRoot, claudeDirName);
2853
2994
  const assets = {};
2854
2995
  let totalInstalled = 0;
2855
2996
  let totalUpdated = 0;
@@ -2862,7 +3003,7 @@ function installAll(projectRoot, opts = {}) {
2862
3003
  for (const assetType of ASSET_TYPES) {
2863
3004
  const sourceDir = resolveAssetDir(assetType.name);
2864
3005
  if (!sourceDir) continue;
2865
- const targetDir = resolve4(claudeDir, assetType.targetSubdir);
3006
+ const targetDir = resolve5(claudeDir, assetType.targetSubdir);
2866
3007
  const stats = syncDirectory(
2867
3008
  sourceDir,
2868
3009
  targetDir,
@@ -3166,17 +3307,17 @@ var init_manifest_registry = __esm({
3166
3307
  });
3167
3308
 
3168
3309
  // src/detect/package-detector.ts
3169
- 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";
3170
3311
  import { join as join4, relative as relative2 } from "path";
3171
3312
  import { parse as parseToml } from "smol-toml";
3172
3313
  function safeRead(path) {
3173
3314
  try {
3174
- if (!existsSync6(path)) return null;
3315
+ if (!existsSync7(path)) return null;
3175
3316
  const ls = lstatSync(path);
3176
3317
  if (ls.isSymbolicLink()) return null;
3177
3318
  const st = statSync2(path);
3178
3319
  if (!st.isFile()) return null;
3179
- return readFileSync5(path, "utf-8");
3320
+ return readFileSync6(path, "utf-8");
3180
3321
  } catch {
3181
3322
  return null;
3182
3323
  }
@@ -3207,7 +3348,7 @@ function parsePackageJson(path, directory, root, warnings) {
3207
3348
  const peer = Object.keys(
3208
3349
  pkg.peerDependencies ?? {}
3209
3350
  );
3210
- 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"));
3211
3352
  const language = hasTs ? "typescript" : "javascript";
3212
3353
  const scripts = Object.keys(
3213
3354
  pkg.scripts ?? {}
@@ -3634,7 +3775,7 @@ function detectManifestsInDir(dir, root, warnings) {
3634
3775
  for (const entry of getManifestRegistry2()) {
3635
3776
  if (!entry.pattern.startsWith("*")) {
3636
3777
  const path = join4(dir, entry.pattern);
3637
- if (!existsSync6(path)) continue;
3778
+ if (!existsSync7(path)) continue;
3638
3779
  const m3 = entry.parse(path, dir, root, warnings);
3639
3780
  if (m3 !== null) out.push(m3);
3640
3781
  } else {
@@ -3648,7 +3789,7 @@ function detectManifestsInDir(dir, root, warnings) {
3648
3789
  for (const fname of dirEntries) {
3649
3790
  if (!matchManifestPattern2(fname, entry.pattern)) continue;
3650
3791
  const path = join4(dir, fname);
3651
- if (!existsSync6(path)) continue;
3792
+ if (!existsSync7(path)) continue;
3652
3793
  const m3 = entry.parse(path, dir, root, warnings);
3653
3794
  if (m3 !== null) out.push(m3);
3654
3795
  }
@@ -3669,7 +3810,7 @@ function detectPackageManifests(projectRoot) {
3669
3810
  manifests.push(...detectManifestsInDir(projectRoot, projectRoot, warnings));
3670
3811
  for (const ws of WORKSPACE_DIRS) {
3671
3812
  const wsRoot = join4(projectRoot, ws);
3672
- if (!existsSync6(wsRoot)) continue;
3813
+ if (!existsSync7(wsRoot)) continue;
3673
3814
  for (const sub of listSubdirs(wsRoot)) {
3674
3815
  manifests.push(...detectManifestsInDir(sub, projectRoot, warnings));
3675
3816
  for (const sub2 of listSubdirs(sub)) {
@@ -8440,41 +8581,41 @@ var require_queue = __commonJS({
8440
8581
  queue.drained = drained;
8441
8582
  return queue;
8442
8583
  function push(value) {
8443
- var p19 = new Promise(function(resolve41, reject) {
8584
+ var p19 = new Promise(function(resolve42, reject) {
8444
8585
  pushCb(value, function(err, result) {
8445
8586
  if (err) {
8446
8587
  reject(err);
8447
8588
  return;
8448
8589
  }
8449
- resolve41(result);
8590
+ resolve42(result);
8450
8591
  });
8451
8592
  });
8452
8593
  p19.catch(noop);
8453
8594
  return p19;
8454
8595
  }
8455
8596
  function unshift(value) {
8456
- var p19 = new Promise(function(resolve41, reject) {
8597
+ var p19 = new Promise(function(resolve42, reject) {
8457
8598
  unshiftCb(value, function(err, result) {
8458
8599
  if (err) {
8459
8600
  reject(err);
8460
8601
  return;
8461
8602
  }
8462
- resolve41(result);
8603
+ resolve42(result);
8463
8604
  });
8464
8605
  });
8465
8606
  p19.catch(noop);
8466
8607
  return p19;
8467
8608
  }
8468
8609
  function drained() {
8469
- var p19 = new Promise(function(resolve41) {
8610
+ var p19 = new Promise(function(resolve42) {
8470
8611
  process.nextTick(function() {
8471
8612
  if (queue.idle()) {
8472
- resolve41();
8613
+ resolve42();
8473
8614
  } else {
8474
8615
  var previousDrain = queue.drain;
8475
8616
  queue.drain = function() {
8476
8617
  if (typeof previousDrain === "function") previousDrain();
8477
- resolve41();
8618
+ resolve42();
8478
8619
  queue.drain = previousDrain;
8479
8620
  };
8480
8621
  }
@@ -8960,9 +9101,9 @@ var require_stream3 = __commonJS({
8960
9101
  });
8961
9102
  }
8962
9103
  _getStat(filepath) {
8963
- return new Promise((resolve41, reject) => {
9104
+ return new Promise((resolve42, reject) => {
8964
9105
  this._stat(filepath, this._fsStatSettings, (error, stats) => {
8965
- return error === null ? resolve41(stats) : reject(error);
9106
+ return error === null ? resolve42(stats) : reject(error);
8966
9107
  });
8967
9108
  });
8968
9109
  }
@@ -8986,10 +9127,10 @@ var require_async5 = __commonJS({
8986
9127
  this._readerStream = new stream_1.default(this._settings);
8987
9128
  }
8988
9129
  dynamic(root, options) {
8989
- return new Promise((resolve41, reject) => {
9130
+ return new Promise((resolve42, reject) => {
8990
9131
  this._walkAsync(root, options, (error, entries) => {
8991
9132
  if (error === null) {
8992
- resolve41(entries);
9133
+ resolve42(entries);
8993
9134
  } else {
8994
9135
  reject(error);
8995
9136
  }
@@ -8999,10 +9140,10 @@ var require_async5 = __commonJS({
8999
9140
  async static(patterns, options) {
9000
9141
  const entries = [];
9001
9142
  const stream = this._readerStream.static(patterns, options);
9002
- return new Promise((resolve41, reject) => {
9143
+ return new Promise((resolve42, reject) => {
9003
9144
  stream.once("error", reject);
9004
9145
  stream.on("data", (entry) => entries.push(entry));
9005
- stream.once("end", () => resolve41(entries));
9146
+ stream.once("end", () => resolve42(entries));
9006
9147
  });
9007
9148
  }
9008
9149
  };
@@ -9661,7 +9802,7 @@ var require_out4 = __commonJS({
9661
9802
 
9662
9803
  // src/detect/source-dir-detector.ts
9663
9804
  import { realpathSync } from "fs";
9664
- import { resolve as resolve5 } from "path";
9805
+ import { resolve as resolve6 } from "path";
9665
9806
  function extsFor(language) {
9666
9807
  return EXTENSIONS[language] ?? [];
9667
9808
  }
@@ -9687,7 +9828,7 @@ function topSegment(rel) {
9687
9828
  function isInsideRoot(root, candidate) {
9688
9829
  try {
9689
9830
  const realRoot = realpathSync(root);
9690
- const realCand = realpathSync(resolve5(root, candidate));
9831
+ const realCand = realpathSync(resolve6(root, candidate));
9691
9832
  return realCand === realRoot || realCand.startsWith(realRoot + "/");
9692
9833
  } catch {
9693
9834
  return false;
@@ -9855,25 +9996,25 @@ var init_source_dir_detector = __esm({
9855
9996
  });
9856
9997
 
9857
9998
  // src/detect/monorepo-detector.ts
9858
- 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";
9859
10000
  import { join as join5, relative as relative3 } from "path";
9860
10001
  import { parse as parseYaml3 } from "yaml";
9861
10002
  import { parse as parseToml2 } from "smol-toml";
9862
10003
  function safeReadText(path) {
9863
10004
  try {
9864
- if (!existsSync7(path)) return null;
10005
+ if (!existsSync8(path)) return null;
9865
10006
  const ls = lstatSync2(path);
9866
10007
  if (ls.isSymbolicLink()) return null;
9867
10008
  const st = statSync3(path);
9868
10009
  if (!st.isFile()) return null;
9869
- return readFileSync6(path, "utf-8");
10010
+ return readFileSync7(path, "utf-8");
9870
10011
  } catch {
9871
10012
  return null;
9872
10013
  }
9873
10014
  }
9874
10015
  function firstManifestIn(dir) {
9875
10016
  for (const m3 of MANIFEST_PRIORITY) {
9876
- if (existsSync7(join5(dir, m3))) return m3;
10017
+ if (existsSync8(join5(dir, m3))) return m3;
9877
10018
  }
9878
10019
  return null;
9879
10020
  }
@@ -9938,7 +10079,7 @@ function genericWorkspaces(root) {
9938
10079
  const out = [];
9939
10080
  for (const parent of CONVENTIONAL_WORKSPACE_PARENTS) {
9940
10081
  const p19 = join5(root, parent);
9941
- if (!existsSync7(p19)) continue;
10082
+ if (!existsSync8(p19)) continue;
9942
10083
  for (const sub of listSubdirs2(p19)) {
9943
10084
  const pkg = pkgFromDir(root, sub);
9944
10085
  if (pkg) out.push(pkg);
@@ -9979,7 +10120,7 @@ function expandWorkspaceGlobs(root, globs) {
9979
10120
  const parts = pattern.split("/");
9980
10121
  if (parts.length === 2 && (parts[1] === "*" || parts[1] === "**")) {
9981
10122
  const parent = join5(root, parts[0]);
9982
- if (!existsSync7(parent)) continue;
10123
+ if (!existsSync8(parent)) continue;
9983
10124
  for (const sub of listSubdirs2(parent)) {
9984
10125
  const pkg = pkgFromDir(root, sub);
9985
10126
  if (pkg && !seen.has(pkg.path)) {
@@ -9990,7 +10131,7 @@ function expandWorkspaceGlobs(root, globs) {
9990
10131
  continue;
9991
10132
  }
9992
10133
  const direct = join5(root, pattern);
9993
- if (existsSync7(direct)) {
10134
+ if (existsSync8(direct)) {
9994
10135
  const pkg = pkgFromDir(root, direct);
9995
10136
  if (pkg && !seen.has(pkg.path)) {
9996
10137
  seen.add(pkg.path);
@@ -10001,16 +10142,16 @@ function expandWorkspaceGlobs(root, globs) {
10001
10142
  return out;
10002
10143
  }
10003
10144
  function hasTurbo(root) {
10004
- return existsSync7(join5(root, "turbo.json"));
10145
+ return existsSync8(join5(root, "turbo.json"));
10005
10146
  }
10006
10147
  function hasNx(root) {
10007
- return existsSync7(join5(root, "nx.json"));
10148
+ return existsSync8(join5(root, "nx.json"));
10008
10149
  }
10009
10150
  function hasLerna(root) {
10010
- return existsSync7(join5(root, "lerna.json"));
10151
+ return existsSync8(join5(root, "lerna.json"));
10011
10152
  }
10012
10153
  function hasBazel(root) {
10013
- 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"));
10014
10155
  }
10015
10156
  function detectMonorepo(projectRoot) {
10016
10157
  const nested = [];
@@ -10201,7 +10342,7 @@ var init_vr_command_map = __esm({
10201
10342
  });
10202
10343
 
10203
10344
  // src/detect/domain-inferrer.ts
10204
- import { existsSync as existsSync8, readdirSync as readdirSync5 } from "fs";
10345
+ import { existsSync as existsSync9, readdirSync as readdirSync5 } from "fs";
10205
10346
  import { join as join6 } from "path";
10206
10347
  function titleCase(s) {
10207
10348
  if (!s) return s;
@@ -10223,7 +10364,7 @@ function topLevelSrcSubdirs(root, sourceDirs) {
10223
10364
  const seen = /* @__PURE__ */ new Set();
10224
10365
  for (const rel of effective) {
10225
10366
  const abs = join6(root, rel);
10226
- if (!existsSync8(abs)) continue;
10367
+ if (!existsSync9(abs)) continue;
10227
10368
  try {
10228
10369
  for (const e2 of readdirSync5(abs, { withFileTypes: true })) {
10229
10370
  if (!e2.isDirectory()) continue;
@@ -10310,8 +10451,8 @@ var init_domain_inferrer = __esm({
10310
10451
  });
10311
10452
 
10312
10453
  // src/detect/regex-fallback.ts
10313
- import { existsSync as existsSync9, readdirSync as readdirSync6, readFileSync as readFileSync7, statSync as statSync4 } from "fs";
10314
- 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";
10315
10456
  function introspectPython(detection, projectRoot) {
10316
10457
  const sourceDir = resolveSourceDir(detection, "python", projectRoot);
10317
10458
  if (!sourceDir) return null;
@@ -10477,10 +10618,10 @@ function resolveSourceDir(detection, lang, projectRoot) {
10477
10618
  const list = info?.source_dirs ?? [];
10478
10619
  if (list.length > 0) {
10479
10620
  const first = list[0];
10480
- const abs = resolve6(projectRoot, first);
10481
- return existsSync9(abs) ? abs : null;
10621
+ const abs = resolve7(projectRoot, first);
10622
+ return existsSync10(abs) ? abs : null;
10482
10623
  }
10483
- return existsSync9(projectRoot) ? projectRoot : null;
10624
+ return existsSync10(projectRoot) ? projectRoot : null;
10484
10625
  }
10485
10626
  function sampleFiles(dir, nameRegex, pathFilter) {
10486
10627
  const out = [];
@@ -10525,7 +10666,7 @@ function readSafe(path) {
10525
10666
  try {
10526
10667
  const st = statSync4(path);
10527
10668
  if (st.size > MAX_FILE_BYTES) return null;
10528
- return readFileSync7(path, "utf-8");
10669
+ return readFileSync8(path, "utf-8");
10529
10670
  } catch {
10530
10671
  return null;
10531
10672
  }
@@ -10606,7 +10747,7 @@ var init_parse_guard = __esm({
10606
10747
 
10607
10748
  // src/detect/adapters/runner.ts
10608
10749
  import { basename as basename3, relative as relative4 } from "path";
10609
- 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";
10610
10751
  import { join as join8 } from "path";
10611
10752
  async function runAdapters(adapters, rootDir, signals, options = {}) {
10612
10753
  const out = {
@@ -10741,9 +10882,9 @@ function tryReadGradleBuild(rootDir, presentFiles) {
10741
10882
  return void 0;
10742
10883
  }
10743
10884
  function tryReadString(path) {
10744
- if (!existsSync10(path)) return void 0;
10885
+ if (!existsSync11(path)) return void 0;
10745
10886
  try {
10746
- return readFileSync8(path, "utf-8");
10887
+ return readFileSync9(path, "utf-8");
10747
10888
  } catch {
10748
10889
  return void 0;
10749
10890
  }
@@ -10860,21 +11001,21 @@ ${querySource}`
10860
11001
  // src/detect/adapters/tree-sitter-loader.ts
10861
11002
  import { createHash as createHash3 } from "crypto";
10862
11003
  import {
10863
- mkdirSync as mkdirSync4,
11004
+ mkdirSync as mkdirSync5,
10864
11005
  readdirSync as readdirSync8,
10865
- readFileSync as readFileSync9,
10866
- writeFileSync,
11006
+ readFileSync as readFileSync10,
11007
+ writeFileSync as writeFileSync2,
10867
11008
  renameSync as renameSync2,
10868
11009
  unlinkSync,
10869
11010
  lstatSync as lstatSync3,
10870
- chmodSync,
11011
+ chmodSync as chmodSync2,
10871
11012
  utimesSync
10872
11013
  } from "fs";
10873
- import { homedir as homedir3 } from "os";
11014
+ import { homedir as homedir4 } from "os";
10874
11015
  import { dirname as dirname5, join as join9 } from "path";
10875
11016
  import { Language, Parser } from "web-tree-sitter";
10876
11017
  function getCacheDir() {
10877
- 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");
10878
11019
  }
10879
11020
  function getCachedPath(language, sha) {
10880
11021
  return join9(getCacheDir(), `${language}-${sha}.wasm`);
@@ -10962,7 +11103,7 @@ async function loadGrammar(language, options = {}) {
10962
11103
  }
10963
11104
  let bytes;
10964
11105
  try {
10965
- bytes = readFileSync9(cachePath);
11106
+ bytes = readFileSync10(cachePath);
10966
11107
  } catch (e2) {
10967
11108
  bytes = new Uint8Array(0);
10968
11109
  }
@@ -11002,21 +11143,21 @@ async function loadGrammar(language, options = {}) {
11002
11143
  throw new GrammarSHAMismatchError(language, manifest.sha256, downloadedSha);
11003
11144
  }
11004
11145
  try {
11005
- mkdirSync4(dirname5(cachePath), { recursive: true, mode: 448 });
11146
+ mkdirSync5(dirname5(cachePath), { recursive: true, mode: 448 });
11006
11147
  try {
11007
- chmodSync(dirname5(cachePath), 448);
11148
+ chmodSync2(dirname5(cachePath), 448);
11008
11149
  } catch {
11009
11150
  }
11010
11151
  const tmpPath = `${cachePath}.tmp.${process.pid}`;
11011
- writeFileSync(tmpPath, body, { mode: 384 });
11152
+ writeFileSync2(tmpPath, body, { mode: 384 });
11012
11153
  try {
11013
- chmodSync(tmpPath, 384);
11154
+ chmodSync2(tmpPath, 384);
11014
11155
  } catch {
11015
11156
  }
11016
11157
  try {
11017
11158
  renameSync2(tmpPath, cachePath);
11018
11159
  try {
11019
- chmodSync(cachePath, 384);
11160
+ chmodSync2(cachePath, 384);
11020
11161
  } catch {
11021
11162
  }
11022
11163
  } catch (e2) {
@@ -11880,7 +12021,7 @@ __export(file_sampler_exports, {
11880
12021
  SAMPLE_TEST_FILE_PATTERNS: () => SAMPLE_TEST_FILE_PATTERNS,
11881
12022
  sampleFilesForAdapter: () => sampleFilesForAdapter
11882
12023
  });
11883
- 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";
11884
12025
  import { join as join10, extname } from "node:path";
11885
12026
  function sampleFilesForAdapter(adapter, projectRoot, detection, options = {}) {
11886
12027
  const maxDepth = options.maxDepth ?? DEFAULT_MAX_DEPTH;
@@ -11941,7 +12082,7 @@ function walkDir(dir, exts, testPatterns, lang, maxDepth, curDepth, out, seen, m
11941
12082
  seen.add(fullPath);
11942
12083
  let content;
11943
12084
  try {
11944
- content = readFileSync10(fullPath, "utf-8");
12085
+ content = readFileSync11(fullPath, "utf-8");
11945
12086
  } catch {
11946
12087
  continue;
11947
12088
  }
@@ -13365,10 +13506,10 @@ __export(init_exports, {
13365
13506
  validateWrittenConfig: () => validateWrittenConfig,
13366
13507
  writeConfigAtomic: () => writeConfigAtomic
13367
13508
  });
13368
- 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";
13369
- 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";
13370
13511
  import { fileURLToPath as fileURLToPath2 } from "url";
13371
- import { homedir as homedir4 } from "os";
13512
+ import { homedir as homedir5 } from "os";
13372
13513
  import { stringify as yamlStringify, parse as yamlParse } from "yaml";
13373
13514
  function detectFramework(projectRoot) {
13374
13515
  const result = {
@@ -13377,10 +13518,10 @@ function detectFramework(projectRoot) {
13377
13518
  orm: "none",
13378
13519
  ui: "none"
13379
13520
  };
13380
- const pkgPath = resolve7(projectRoot, "package.json");
13381
- if (!existsSync11(pkgPath)) return result;
13521
+ const pkgPath = resolve8(projectRoot, "package.json");
13522
+ if (!existsSync12(pkgPath)) return result;
13382
13523
  try {
13383
- const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
13524
+ const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
13384
13525
  const allDeps = {
13385
13526
  ...pkg.dependencies,
13386
13527
  ...pkg.devDependencies
@@ -13414,7 +13555,7 @@ function detectPython(projectRoot) {
13414
13555
  alembicDir: null
13415
13556
  };
13416
13557
  const markers = ["pyproject.toml", "setup.py", "requirements.txt", "Pipfile"];
13417
- const hasMarker = markers.some((m3) => existsSync11(resolve7(projectRoot, m3)));
13558
+ const hasMarker = markers.some((m3) => existsSync12(resolve8(projectRoot, m3)));
13418
13559
  if (!hasMarker) return result;
13419
13560
  result.detected = true;
13420
13561
  const depFiles = [
@@ -13424,33 +13565,33 @@ function detectPython(projectRoot) {
13424
13565
  { file: "Pipfile" }
13425
13566
  ];
13426
13567
  for (const { file } of depFiles) {
13427
- const filePath = resolve7(projectRoot, file);
13428
- if (existsSync11(filePath)) {
13568
+ const filePath = resolve8(projectRoot, file);
13569
+ if (existsSync12(filePath)) {
13429
13570
  try {
13430
- const content = readFileSync11(filePath, "utf-8").toLowerCase();
13571
+ const content = readFileSync12(filePath, "utf-8").toLowerCase();
13431
13572
  if (content.includes("fastapi")) result.hasFastapi = true;
13432
13573
  if (content.includes("sqlalchemy")) result.hasSqlalchemy = true;
13433
13574
  } catch {
13434
13575
  }
13435
13576
  }
13436
13577
  }
13437
- if (existsSync11(resolve7(projectRoot, "alembic.ini"))) {
13578
+ if (existsSync12(resolve8(projectRoot, "alembic.ini"))) {
13438
13579
  result.hasAlembic = true;
13439
- if (existsSync11(resolve7(projectRoot, "alembic"))) {
13580
+ if (existsSync12(resolve8(projectRoot, "alembic"))) {
13440
13581
  result.alembicDir = "alembic";
13441
13582
  }
13442
- } else if (existsSync11(resolve7(projectRoot, "alembic"))) {
13583
+ } else if (existsSync12(resolve8(projectRoot, "alembic"))) {
13443
13584
  result.hasAlembic = true;
13444
13585
  result.alembicDir = "alembic";
13445
13586
  }
13446
13587
  const candidateRoots = ["app", "src", "backend", "api"];
13447
13588
  for (const candidate of candidateRoots) {
13448
- const candidatePath = resolve7(projectRoot, candidate);
13449
- if (existsSync11(candidatePath) && existsSync11(resolve7(candidatePath, "__init__.py"))) {
13589
+ const candidatePath = resolve8(projectRoot, candidate);
13590
+ if (existsSync12(candidatePath) && existsSync12(resolve8(candidatePath, "__init__.py"))) {
13450
13591
  result.root = candidate;
13451
13592
  break;
13452
13593
  }
13453
- if (existsSync11(candidatePath)) {
13594
+ if (existsSync12(candidatePath)) {
13454
13595
  try {
13455
13596
  const files = readdirSync10(candidatePath);
13456
13597
  if (files.some((f2) => f2.endsWith(".py"))) {
@@ -13470,8 +13611,8 @@ function generateConfig(projectRoot, framework) {
13470
13611
  console.warn(
13471
13612
  "[@massu/core] generateConfig() is deprecated since 1.2.1 \u2014 use buildConfigFromDetection instead. It cannot produce valid configs for monorepos."
13472
13613
  );
13473
- const configPath = resolve7(projectRoot, "massu.config.yaml");
13474
- if (existsSync11(configPath)) {
13614
+ const configPath = resolve8(projectRoot, "massu.config.yaml");
13615
+ if (existsSync12(configPath)) {
13475
13616
  return false;
13476
13617
  }
13477
13618
  const projectName = basename4(projectRoot);
@@ -13517,7 +13658,7 @@ function generateConfig(projectRoot, framework) {
13517
13658
  # Documentation: https://massu.ai/docs/getting-started/configuration
13518
13659
 
13519
13660
  ${yamlStringify(config)}`;
13520
- writeFileSync2(configPath, yamlContent, "utf-8");
13661
+ writeFileSync3(configPath, yamlContent, "utf-8");
13521
13662
  return true;
13522
13663
  }
13523
13664
  function monorepoCommonRoot(packages) {
@@ -13582,10 +13723,10 @@ function buildConfigFromDetection(opts) {
13582
13723
  pathsSource = monorepoCommonRoot(detection.monorepo.packages);
13583
13724
  }
13584
13725
  }
13585
- if (pathsSource === "src" && !existsSync11(resolve7(projectRoot, "src"))) {
13726
+ if (pathsSource === "src" && !existsSync12(resolve8(projectRoot, "src"))) {
13586
13727
  const fallbacks = ["app", "pages", "."];
13587
13728
  for (const fallback of fallbacks) {
13588
- if (fallback === "." || existsSync11(resolve7(projectRoot, fallback))) {
13729
+ if (fallback === "." || existsSync12(resolve8(projectRoot, fallback))) {
13589
13730
  pathsSource = fallback;
13590
13731
  break;
13591
13732
  }
@@ -13669,7 +13810,7 @@ function buildConfigFromDetection(opts) {
13669
13810
  };
13670
13811
  if (pyFw?.framework) pythonBlock.framework = pyFw.framework;
13671
13812
  if (pyFw?.orm) pythonBlock.orm = pyFw.orm;
13672
- if (existsSync11(resolve7(projectRoot, "alembic.ini")) || existsSync11(resolve7(projectRoot, "alembic"))) {
13813
+ if (existsSync12(resolve8(projectRoot, "alembic.ini")) || existsSync12(resolve8(projectRoot, "alembic"))) {
13673
13814
  pythonBlock.alembic_dir = "alembic";
13674
13815
  }
13675
13816
  config.python = pythonBlock;
@@ -13693,11 +13834,11 @@ function applyVariantTemplate(config, templatesDir) {
13693
13834
  }
13694
13835
  }
13695
13836
  if (templateId === null) return config;
13696
- const templatePath = resolve7(templatesDir, templateId, "massu.config.yaml");
13697
- if (!existsSync11(templatePath)) return config;
13837
+ const templatePath = resolve8(templatesDir, templateId, "massu.config.yaml");
13838
+ if (!existsSync12(templatePath)) return config;
13698
13839
  let template;
13699
13840
  try {
13700
- template = yamlParse(readFileSync11(templatePath, "utf-8"));
13841
+ template = yamlParse(readFileSync12(templatePath, "utf-8"));
13701
13842
  } catch {
13702
13843
  return config;
13703
13844
  }
@@ -13756,7 +13897,7 @@ ${yamlStringify(config)}`;
13756
13897
  function writeConfigAtomic(configPath, content) {
13757
13898
  const tmpPath = `${configPath}.tmp`;
13758
13899
  let existingMode;
13759
- if (existsSync11(configPath)) {
13900
+ if (existsSync12(configPath)) {
13760
13901
  try {
13761
13902
  existingMode = statSync7(configPath).mode;
13762
13903
  } catch {
@@ -13764,7 +13905,7 @@ function writeConfigAtomic(configPath, content) {
13764
13905
  }
13765
13906
  }
13766
13907
  try {
13767
- mkdirSync5(dirname6(configPath), { recursive: true });
13908
+ mkdirSync6(dirname6(configPath), { recursive: true });
13768
13909
  const fd = openSync2(tmpPath, "w", 420);
13769
13910
  try {
13770
13911
  const buf = Buffer.from(content, "utf-8");
@@ -13780,15 +13921,15 @@ function writeConfigAtomic(configPath, content) {
13780
13921
  renameSync3(tmpPath, configPath);
13781
13922
  if (existingMode !== void 0) {
13782
13923
  try {
13783
- chmodSync2(configPath, existingMode);
13924
+ chmodSync3(configPath, existingMode);
13784
13925
  } catch {
13785
13926
  }
13786
13927
  }
13787
13928
  return { validated: true };
13788
13929
  } catch (err) {
13789
- if (existsSync11(tmpPath)) {
13930
+ if (existsSync12(tmpPath)) {
13790
13931
  try {
13791
- rmSync2(tmpPath, { force: true });
13932
+ rmSync3(tmpPath, { force: true });
13792
13933
  } catch {
13793
13934
  }
13794
13935
  }
@@ -13797,8 +13938,8 @@ function writeConfigAtomic(configPath, content) {
13797
13938
  }
13798
13939
  function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
13799
13940
  try {
13800
- if (!existsSync11(configPath)) return "Config file does not exist after write";
13801
- 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");
13802
13943
  const parsed = yamlParse(content);
13803
13944
  if (parsed === null || typeof parsed !== "object") {
13804
13945
  return "Config is not a valid YAML object";
@@ -13818,8 +13959,8 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
13818
13959
  if (checkPaths) {
13819
13960
  const src = cfg.paths.source;
13820
13961
  if (src && src !== ".") {
13821
- const srcAbs = resolve7(projectRoot, src);
13822
- if (!existsSync11(srcAbs)) {
13962
+ const srcAbs = resolve8(projectRoot, src);
13963
+ if (!existsSync12(srcAbs)) {
13823
13964
  return `paths.source '${src}' does not exist on disk`;
13824
13965
  }
13825
13966
  }
@@ -13829,8 +13970,8 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
13829
13970
  if (!Array.isArray(rawDirs)) continue;
13830
13971
  for (const d2 of rawDirs) {
13831
13972
  if (typeof d2 !== "string" || d2 === ".") continue;
13832
- const abs = resolve7(projectRoot, d2);
13833
- if (!existsSync11(abs)) {
13973
+ const abs = resolve8(projectRoot, d2);
13974
+ if (!existsSync12(abs)) {
13834
13975
  return `framework.languages.${lang}.source_dirs '${d2}' does not exist on disk`;
13835
13976
  }
13836
13977
  }
@@ -13839,7 +13980,7 @@ function validateWrittenConfig(configPath, projectRoot, checkPaths = true) {
13839
13980
  if (Array.isArray(mRoots)) {
13840
13981
  for (const r2 of mRoots) {
13841
13982
  if (typeof r2 !== "string" || r2 === ".") continue;
13842
- if (!existsSync11(resolve7(projectRoot, r2))) {
13983
+ if (!existsSync12(resolve8(projectRoot, r2))) {
13843
13984
  return `paths.monorepo_roots '${r2}' does not exist on disk`;
13844
13985
  }
13845
13986
  }
@@ -13871,21 +14012,21 @@ function resolveTemplatesDir() {
13871
14012
  const cwd = process.cwd();
13872
14013
  const candidates = [
13873
14014
  // Project-local install: `<project>/node_modules/@massu/core/templates`.
13874
- resolve7(cwd, "node_modules/@massu/core/templates"),
14015
+ resolve8(cwd, "node_modules/@massu/core/templates"),
13875
14016
  // Bundled cli.js layout: cli.js sits at `<package>/dist/cli.js`, so
13876
14017
  // templates live one level up at `<package>/templates`. (Plan 1.5.1
13877
14018
  // bug discovery: pre-existing layout assumed `dist/commands/init.js`
13878
14019
  // depth which never matched the bundled cli, so resolveTemplatesDir
13879
14020
  // returned null in production for both `--template` mode AND the
13880
14021
  // applyVariantTemplate path.)
13881
- resolve7(__dirname2, "../templates"),
14022
+ resolve8(__dirname2, "../templates"),
13882
14023
  // Legacy nested layouts retained as fallbacks (in case a future
13883
14024
  // build moves cli.js back into a subdirectory).
13884
- resolve7(__dirname2, "../../templates"),
13885
- resolve7(__dirname2, "../../../templates")
14025
+ resolve8(__dirname2, "../../templates"),
14026
+ resolve8(__dirname2, "../../../templates")
13886
14027
  ];
13887
14028
  for (const c2 of candidates) {
13888
- if (existsSync11(c2)) return c2;
14029
+ if (existsSync12(c2)) return c2;
13889
14030
  }
13890
14031
  return null;
13891
14032
  }
@@ -13894,14 +14035,14 @@ function copyTemplateConfig(templateName, targetPath, projectName) {
13894
14035
  if (!templatesDir) {
13895
14036
  return { success: false, error: `Templates directory not found (looked in node_modules and dist/src)` };
13896
14037
  }
13897
- const srcPath = resolve7(templatesDir, templateName, "massu.config.yaml");
13898
- if (!existsSync11(srcPath)) {
14038
+ const srcPath = resolve8(templatesDir, templateName, "massu.config.yaml");
14039
+ if (!existsSync12(srcPath)) {
13899
14040
  return { success: false, error: `Template '${templateName}' not found at ${srcPath}` };
13900
14041
  }
13901
14042
  try {
13902
- let content = readFileSync11(srcPath, "utf-8");
14043
+ let content = readFileSync12(srcPath, "utf-8");
13903
14044
  content = content.replace(/\{\{PROJECT_NAME\}\}/g, projectName);
13904
- writeFileSync2(targetPath, content, "utf-8");
14045
+ writeFileSync3(targetPath, content, "utf-8");
13905
14046
  return { success: true };
13906
14047
  } catch (err) {
13907
14048
  return { success: false, error: err instanceof Error ? err.message : String(err) };
@@ -13909,13 +14050,13 @@ function copyTemplateConfig(templateName, targetPath, projectName) {
13909
14050
  }
13910
14051
  function getInstallerVersion() {
13911
14052
  const candidates = [
13912
- resolve7(__dirname2, "../package.json"),
13913
- resolve7(__dirname2, "../../package.json")
14053
+ resolve8(__dirname2, "../package.json"),
14054
+ resolve8(__dirname2, "../../package.json")
13914
14055
  ];
13915
14056
  for (const candidate of candidates) {
13916
- if (existsSync11(candidate)) {
14057
+ if (existsSync12(candidate)) {
13917
14058
  try {
13918
- const pkg = JSON.parse(readFileSync11(candidate, "utf-8"));
14059
+ const pkg = JSON.parse(readFileSync12(candidate, "utf-8"));
13919
14060
  if (typeof pkg.version === "string" && pkg.version.length > 0 && pkg.name === "@massu/core") {
13920
14061
  return pkg.version;
13921
14062
  }
@@ -13928,11 +14069,11 @@ function getInstallerVersion() {
13928
14069
  );
13929
14070
  }
13930
14071
  function registerMcpServer(projectRoot) {
13931
- const mcpPath = resolve7(projectRoot, ".mcp.json");
14072
+ const mcpPath = resolve8(projectRoot, ".mcp.json");
13932
14073
  let existing = {};
13933
- if (existsSync11(mcpPath)) {
14074
+ if (existsSync12(mcpPath)) {
13934
14075
  try {
13935
- existing = JSON.parse(readFileSync11(mcpPath, "utf-8"));
14076
+ existing = JSON.parse(readFileSync12(mcpPath, "utf-8"));
13936
14077
  } catch {
13937
14078
  existing = {};
13938
14079
  }
@@ -13948,17 +14089,17 @@ function registerMcpServer(projectRoot) {
13948
14089
  args: ["-y", `@massu/core@${version}`]
13949
14090
  };
13950
14091
  existing.mcpServers = servers;
13951
- writeFileSync2(mcpPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
14092
+ writeFileSync3(mcpPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
13952
14093
  return true;
13953
14094
  }
13954
14095
  function resolveHooksDir() {
13955
14096
  const cwd = process.cwd();
13956
- const nodeModulesPath = resolve7(cwd, "node_modules/@massu/core/dist/hooks");
13957
- if (existsSync11(nodeModulesPath)) {
14097
+ const nodeModulesPath = resolve8(cwd, "node_modules/@massu/core/dist/hooks");
14098
+ if (existsSync12(nodeModulesPath)) {
13958
14099
  return "node_modules/@massu/core/dist/hooks";
13959
14100
  }
13960
- const localPath = resolve7(__dirname2, "../dist/hooks");
13961
- if (existsSync11(localPath)) {
14101
+ const localPath = resolve8(__dirname2, "../dist/hooks");
14102
+ if (existsSync12(localPath)) {
13962
14103
  return localPath;
13963
14104
  }
13964
14105
  return "node_modules/@massu/core/dist/hooks";
@@ -14104,9 +14245,9 @@ function installHooks(projectRoot) {
14104
14245
  } catch {
14105
14246
  claudeDirName = ".claude";
14106
14247
  }
14107
- const claudeDir = resolve7(projectRoot, claudeDirName);
14108
- if (!existsSync11(claudeDir)) {
14109
- mkdirSync5(claudeDir, { recursive: true });
14248
+ const claudeDir = resolve8(projectRoot, claudeDirName);
14249
+ if (!existsSync12(claudeDir)) {
14250
+ mkdirSync6(claudeDir, { recursive: true });
14110
14251
  }
14111
14252
  const settings = readSettingsLocal(claudeDir);
14112
14253
  const hooksConfig = buildHooksConfig(resolveHooksDir());
@@ -14124,17 +14265,17 @@ function installHooks(projectRoot) {
14124
14265
  }
14125
14266
  function initMemoryDir(projectRoot) {
14126
14267
  const encodedRoot = encodeMemoryDirName(projectRoot);
14127
- const memoryDir = resolve7(homedir4(), `.claude/projects/${encodedRoot}/memory`);
14268
+ const memoryDir = resolve8(homedir5(), `.claude/projects/${encodedRoot}/memory`);
14128
14269
  let migratedFromLegacy = false;
14129
- const legacyDir = resolve7(homedir4(), `.claude/projects/-${encodedRoot}/memory`);
14130
- if (existsSync11(legacyDir) && !existsSync11(memoryDir)) {
14270
+ const legacyDir = resolve8(homedir5(), `.claude/projects/-${encodedRoot}/memory`);
14271
+ if (existsSync12(legacyDir) && !existsSync12(memoryDir)) {
14131
14272
  try {
14132
- mkdirSync5(resolve7(memoryDir, ".."), { recursive: true });
14273
+ mkdirSync6(resolve8(memoryDir, ".."), { recursive: true });
14133
14274
  renameSync3(legacyDir, memoryDir);
14134
14275
  try {
14135
- const legacyParent = resolve7(legacyDir, "..");
14136
- if (existsSync11(legacyParent) && readdirSync10(legacyParent).length === 0) {
14137
- rmSync2(legacyParent, { recursive: false });
14276
+ const legacyParent = resolve8(legacyDir, "..");
14277
+ if (existsSync12(legacyParent) && readdirSync10(legacyParent).length === 0) {
14278
+ rmSync3(legacyParent, { recursive: false });
14138
14279
  }
14139
14280
  } catch {
14140
14281
  }
@@ -14143,13 +14284,13 @@ function initMemoryDir(projectRoot) {
14143
14284
  }
14144
14285
  }
14145
14286
  let created = false;
14146
- if (!existsSync11(memoryDir)) {
14147
- mkdirSync5(memoryDir, { recursive: true });
14287
+ if (!existsSync12(memoryDir)) {
14288
+ mkdirSync6(memoryDir, { recursive: true });
14148
14289
  created = true;
14149
14290
  }
14150
- const memoryMdPath = resolve7(memoryDir, "MEMORY.md");
14291
+ const memoryMdPath = resolve8(memoryDir, "MEMORY.md");
14151
14292
  let memoryMdCreated = false;
14152
- if (!existsSync11(memoryMdPath)) {
14293
+ if (!existsSync12(memoryMdPath)) {
14153
14294
  const projectName = basename4(projectRoot);
14154
14295
  const memoryContent = `# ${projectName} - Massu Memory
14155
14296
 
@@ -14165,7 +14306,7 @@ function initMemoryDir(projectRoot) {
14165
14306
  ## File Index
14166
14307
  <!-- Significant files and directories -->
14167
14308
  `;
14168
- writeFileSync2(memoryMdPath, memoryContent, "utf-8");
14309
+ writeFileSync3(memoryMdPath, memoryContent, "utf-8");
14169
14310
  memoryMdCreated = true;
14170
14311
  }
14171
14312
  return { created, memoryMdCreated, migratedFromLegacy };
@@ -14263,8 +14404,8 @@ async function runInit(argv, overrides) {
14263
14404
  log("Massu AI - Project Setup");
14264
14405
  log("========================");
14265
14406
  log("");
14266
- const configPath = resolve7(projectRoot, "massu.config.yaml");
14267
- if (existsSync11(configPath)) {
14407
+ const configPath = resolve8(projectRoot, "massu.config.yaml");
14408
+ if (existsSync12(configPath)) {
14268
14409
  if (opts.ci && !opts.force) {
14269
14410
  errLog(`error: massu.config.yaml already exists at ${configPath}`);
14270
14411
  errLog(" rerun with --force to overwrite, or remove the file first");
@@ -14292,7 +14433,7 @@ async function runInit(argv, overrides) {
14292
14433
  const validation2 = validateWrittenConfig(configPath, projectRoot, false);
14293
14434
  if (validation2 !== null) {
14294
14435
  try {
14295
- rmSync2(configPath, { force: true });
14436
+ rmSync3(configPath, { force: true });
14296
14437
  } catch {
14297
14438
  }
14298
14439
  errLog(`error: template config failed validation: ${validation2}`);
@@ -14370,7 +14511,7 @@ async function runInit(argv, overrides) {
14370
14511
  const validation = validateWrittenConfig(configPath, projectRoot, !emptyStack);
14371
14512
  if (validation !== null) {
14372
14513
  try {
14373
- rmSync2(configPath, { force: true });
14514
+ rmSync3(configPath, { force: true });
14374
14515
  } catch {
14375
14516
  }
14376
14517
  errLog(`error: generated config failed validation: ${validation}`);
@@ -14403,8 +14544,8 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
14403
14544
  const commandStats = cmdResult.assets.commands;
14404
14545
  const stackResolved = !emptyStack && commandStats && (commandStats.installed > 0 || commandStats.updated > 0 || commandStats.kept > 0);
14405
14546
  if (!stackResolved) {
14406
- const placeholderPath = resolve7(cmdResult.claudeDir, "commands", "_massu-needs-stack.md");
14407
- if (!existsSync11(placeholderPath)) {
14547
+ const placeholderPath = resolve8(cmdResult.claudeDir, "commands", "_massu-needs-stack.md");
14548
+ if (!existsSync12(placeholderPath)) {
14408
14549
  const placeholderBody = [
14409
14550
  "# Massu \u2014 stack not yet detected",
14410
14551
  "",
@@ -14425,8 +14566,8 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
14425
14566
  "\u2014 Massu"
14426
14567
  ].join("\n");
14427
14568
  try {
14428
- mkdirSync5(resolve7(cmdResult.claudeDir, "commands"), { recursive: true });
14429
- writeFileSync2(placeholderPath, placeholderBody, "utf-8");
14569
+ mkdirSync6(resolve8(cmdResult.claudeDir, "commands"), { recursive: true });
14570
+ writeFileSync3(placeholderPath, placeholderBody, "utf-8");
14430
14571
  log(" Wrote _massu-needs-stack.md placeholder (no stack detected yet)");
14431
14572
  } catch {
14432
14573
  }
@@ -14448,8 +14589,8 @@ function installSideEffects(projectRoot, log, skipCommands = false, emptyStack =
14448
14589
  (async () => {
14449
14590
  try {
14450
14591
  const encodedRoot = encodeMemoryDirName(projectRoot);
14451
- const memoryDir = resolve7(homedir4(), ".claude", "projects", encodedRoot, "memory");
14452
- 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") : [];
14453
14594
  if (memFiles.length > 0) {
14454
14595
  const { getMemoryDb: getMemoryDb2 } = await Promise.resolve().then(() => (init_memory_db(), memory_db_exports));
14455
14596
  const db = getMemoryDb2();
@@ -14639,6 +14780,28 @@ var init_license_response_verifier = __esm({
14639
14780
  });
14640
14781
 
14641
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
+ });
14642
14805
  import { createHash as createHash5 } from "crypto";
14643
14806
  function warnLicenseSigOnce(reason) {
14644
14807
  if (_warnedLicenseSig) return;
@@ -14704,11 +14867,12 @@ async function validateLicense(apiKey) {
14704
14867
  const lastValidated = new Date(cached.last_validated);
14705
14868
  const hourAgo = new Date(Date.now() - 60 * 60 * 1e3);
14706
14869
  if (lastValidated > hourAgo) {
14707
- return trusted;
14870
+ return { ...trusted, outcome: "cache_fresh" };
14708
14871
  }
14709
14872
  }
14710
14873
  const config = getConfig();
14711
14874
  const endpoint = config.cloud?.endpoint;
14875
+ let fallbackOutcome = "no_endpoint";
14712
14876
  if (endpoint && /^https?:\/\/.+/.test(endpoint)) {
14713
14877
  try {
14714
14878
  const response = await fetch(`${endpoint}/validate-key`, {
@@ -14740,21 +14904,23 @@ async function validateLicense(apiKey) {
14740
14904
  features,
14741
14905
  sigResult.kind === "valid" ? data : null
14742
14906
  );
14743
- return { tier, validUntil, features };
14907
+ return { tier, validUntil, features, outcome: "validated" };
14744
14908
  }
14745
- return { tier: "free", validUntil: "", features: [] };
14909
+ return { tier: "free", validUntil: "", features: [], outcome: "rejected" };
14746
14910
  }
14911
+ fallbackOutcome = "server_error";
14747
14912
  } catch {
14913
+ fallbackOutcome = "network_error";
14748
14914
  }
14749
14915
  }
14750
14916
  if (cached && trusted) {
14751
14917
  const lastValidated = new Date(cached.last_validated);
14752
14918
  const sevenDaysAgo = new Date(Date.now() - GRACE_PERIOD_MS);
14753
14919
  if (lastValidated > sevenDaysAgo) {
14754
- return trusted;
14920
+ return { ...trusted, outcome: "grace" };
14755
14921
  }
14756
14922
  }
14757
- return { tier: "free", validUntil: "", features: [] };
14923
+ return { tier: "free", validUntil: "", features: [], outcome: fallbackOutcome };
14758
14924
  } finally {
14759
14925
  memDb.close();
14760
14926
  }
@@ -14810,7 +14976,7 @@ async function getCurrentTier() {
14810
14976
  const config = getConfig();
14811
14977
  const apiKey = config.cloud?.apiKey;
14812
14978
  if (!apiKey) {
14813
- cachedTier = { tier: "free", validUntil: "", features: [] };
14979
+ cachedTier = { tier: "free", validUntil: "", features: [], outcome: "no_key" };
14814
14980
  cachedTierTimestamp = Date.now();
14815
14981
  return "free";
14816
14982
  }
@@ -14943,6 +15109,14 @@ async function handleLicenseToolCall(name, _args, _memDb) {
14943
15109
  }
14944
15110
  return { content: [{ type: "text", text: `Unknown license tool: ${name}` }] };
14945
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
+ }
14946
15120
  var _warnedLicenseSig, TIER_LEVELS, TOOL_TIER_MAP, PLAN_TO_TIER_MAP, TIER_LABELS, cachedTier, cachedTierTimestamp, IN_MEMORY_CACHE_TTL_MS, GRACE_PERIOD_MS;
14947
15121
  var init_license = __esm({
14948
15122
  "src/license.ts"() {
@@ -15098,20 +15272,21 @@ var init_hook_registry = __esm({
15098
15272
  // src/commands/doctor.ts
15099
15273
  var doctor_exports = {};
15100
15274
  __export(doctor_exports, {
15275
+ formatLicenseCheck: () => formatLicenseCheck,
15101
15276
  runDoctor: () => runDoctor,
15102
15277
  runValidateConfig: () => runValidateConfig
15103
15278
  });
15104
- import { existsSync as existsSync12, readFileSync as readFileSync12, readdirSync as readdirSync11 } from "fs";
15105
- 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";
15106
15281
  import { fileURLToPath as fileURLToPath3 } from "url";
15107
15282
  import { parse as parseYaml4 } from "yaml";
15108
15283
  function checkConfig(projectRoot) {
15109
- const configPath = resolve8(projectRoot, "massu.config.yaml");
15110
- if (!existsSync12(configPath)) {
15284
+ const configPath = resolve9(projectRoot, "massu.config.yaml");
15285
+ if (!existsSync13(configPath)) {
15111
15286
  return { name: "Configuration", status: "fail", detail: "massu.config.yaml not found. Run: npx massu init" };
15112
15287
  }
15113
15288
  try {
15114
- const content = readFileSync12(configPath, "utf-8");
15289
+ const content = readFileSync13(configPath, "utf-8");
15115
15290
  const parsed = parseYaml4(content);
15116
15291
  if (!parsed || typeof parsed !== "object") {
15117
15292
  return { name: "Configuration", status: "fail", detail: "massu.config.yaml is empty or invalid YAML" };
@@ -15123,11 +15298,11 @@ function checkConfig(projectRoot) {
15123
15298
  }
15124
15299
  function checkMcpServer(projectRoot) {
15125
15300
  const mcpPath = getResolvedPaths().mcpJsonPath;
15126
- if (!existsSync12(mcpPath)) {
15301
+ if (!existsSync13(mcpPath)) {
15127
15302
  return { name: "MCP Server", status: "fail", detail: ".mcp.json not found. Run: npx massu init" };
15128
15303
  }
15129
15304
  try {
15130
- const content = JSON.parse(readFileSync12(mcpPath, "utf-8"));
15305
+ const content = JSON.parse(readFileSync13(mcpPath, "utf-8"));
15131
15306
  const servers = content.mcpServers ?? {};
15132
15307
  if (!servers.massu) {
15133
15308
  return { name: "MCP Server", status: "fail", detail: "massu not registered in .mcp.json. Run: npx massu init" };
@@ -15139,7 +15314,7 @@ function checkMcpServer(projectRoot) {
15139
15314
  }
15140
15315
  function checkHooksConfig(projectRoot) {
15141
15316
  const settingsPath = getResolvedPaths().settingsLocalPath;
15142
- if (!existsSync12(settingsPath)) {
15317
+ if (!existsSync13(settingsPath)) {
15143
15318
  return { name: "Hooks Config", status: "fail", detail: ".claude/settings.local.json not found. Run: npx massu init" };
15144
15319
  }
15145
15320
  try {
@@ -15167,11 +15342,11 @@ function checkHooksConfig(projectRoot) {
15167
15342
  }
15168
15343
  }
15169
15344
  function checkHookFiles(projectRoot) {
15170
- const nodeModulesHooksDir = resolve8(projectRoot, "node_modules/@massu/core/dist/hooks");
15345
+ const nodeModulesHooksDir = resolve9(projectRoot, "node_modules/@massu/core/dist/hooks");
15171
15346
  let hooksDir = nodeModulesHooksDir;
15172
- if (!existsSync12(nodeModulesHooksDir)) {
15173
- const devHooksDir = resolve8(__dirname3, "../../dist/hooks");
15174
- if (existsSync12(devHooksDir)) {
15347
+ if (!existsSync13(nodeModulesHooksDir)) {
15348
+ const devHooksDir = resolve9(__dirname3, "../../dist/hooks");
15349
+ if (existsSync13(devHooksDir)) {
15175
15350
  hooksDir = devHooksDir;
15176
15351
  } else {
15177
15352
  return { name: "Hook Files", status: "fail", detail: "Compiled hooks not found. Run: npm install @massu/core" };
@@ -15179,7 +15354,7 @@ function checkHookFiles(projectRoot) {
15179
15354
  }
15180
15355
  const missing = [];
15181
15356
  for (const hookFile of EXPECTED_HOOKS) {
15182
- if (!existsSync12(resolve8(hooksDir, hookFile))) {
15357
+ if (!existsSync13(resolve9(hooksDir, hookFile))) {
15183
15358
  missing.push(hookFile);
15184
15359
  }
15185
15360
  }
@@ -15205,8 +15380,8 @@ function checkNodeVersion() {
15205
15380
  return { name: "Node.js", status: "fail", detail: `v${version} \u2014 Node.js 18+ is required` };
15206
15381
  }
15207
15382
  async function checkGitRepo(projectRoot) {
15208
- const gitDir = resolve8(projectRoot, ".git");
15209
- if (!existsSync12(gitDir)) {
15383
+ const gitDir = resolve9(projectRoot, ".git");
15384
+ if (!existsSync13(gitDir)) {
15210
15385
  return { name: "Git Repository", status: "warn", detail: "Not a git repository (optional but recommended)" };
15211
15386
  }
15212
15387
  try {
@@ -15224,7 +15399,7 @@ async function checkGitRepo(projectRoot) {
15224
15399
  }
15225
15400
  function checkKnowledgeDb(projectRoot) {
15226
15401
  const knowledgeDbPath = getResolvedPaths().memoryDbPath;
15227
- if (!existsSync12(knowledgeDbPath)) {
15402
+ if (!existsSync13(knowledgeDbPath)) {
15228
15403
  return {
15229
15404
  name: "Knowledge DB",
15230
15405
  status: "warn",
@@ -15235,7 +15410,7 @@ function checkKnowledgeDb(projectRoot) {
15235
15410
  }
15236
15411
  function checkMemoryDir(_projectRoot2) {
15237
15412
  const memoryDir = getResolvedPaths().memoryDir;
15238
- if (!existsSync12(memoryDir)) {
15413
+ if (!existsSync13(memoryDir)) {
15239
15414
  return {
15240
15415
  name: "Memory Directory",
15241
15416
  status: "warn",
@@ -15246,7 +15421,7 @@ function checkMemoryDir(_projectRoot2) {
15246
15421
  }
15247
15422
  function checkShellHooksWired(_projectRoot2) {
15248
15423
  const settingsPath = getResolvedPaths().settingsLocalPath;
15249
- if (!existsSync12(settingsPath)) {
15424
+ if (!existsSync13(settingsPath)) {
15250
15425
  return {
15251
15426
  name: "Shell Hooks",
15252
15427
  status: "fail",
@@ -15274,26 +15449,43 @@ function checkShellHooksWired(_projectRoot2) {
15274
15449
  };
15275
15450
  }
15276
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
+ }
15277
15483
  async function checkLicenseStatus() {
15278
15484
  try {
15485
+ const source = getResolvedApiKeySource();
15279
15486
  const tier = await getCurrentTier();
15280
15487
  const info = await getLicenseInfo();
15281
- if (tier === "free" && !info.validUntil) {
15282
- return { name: "License", status: "pass", detail: "Free (no API key configured)" };
15283
- }
15284
- const days = await daysUntilExpiry();
15285
- if (days >= 0 && info.validUntil) {
15286
- return {
15287
- name: "License",
15288
- status: "pass",
15289
- detail: `${tier.charAt(0).toUpperCase() + tier.slice(1)} (valid until ${info.validUntil})`
15290
- };
15291
- }
15292
- return {
15293
- name: "License",
15294
- status: "pass",
15295
- detail: `${tier.charAt(0).toUpperCase() + tier.slice(1)} (valid)`
15296
- };
15488
+ return formatLicenseCheck({ source, tier, validUntil: info.validUntil, outcome: info.outcome });
15297
15489
  } catch (err) {
15298
15490
  return {
15299
15491
  name: "License",
@@ -15302,11 +15494,31 @@ async function checkLicenseStatus() {
15302
15494
  };
15303
15495
  }
15304
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
+ }
15305
15517
  function checkPythonHealth(projectRoot) {
15306
15518
  const config = getConfig();
15307
15519
  if (!config.python?.root) return null;
15308
- const pythonRoot = resolve8(projectRoot, config.python.root);
15309
- if (!existsSync12(pythonRoot)) {
15520
+ const pythonRoot = resolve9(projectRoot, config.python.root);
15521
+ if (!existsSync13(pythonRoot)) {
15310
15522
  return {
15311
15523
  name: "Python",
15312
15524
  status: "fail",
@@ -15325,8 +15537,8 @@ function checkPythonHealth(projectRoot) {
15325
15537
  if (entry.isDirectory()) {
15326
15538
  const excludeDirs = config.python?.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
15327
15539
  if (!excludeDirs.includes(entry.name)) {
15328
- const subdir = resolve8(dir, entry.name);
15329
- if (depth <= 2 && !existsSync12(resolve8(subdir, "__init__.py"))) {
15540
+ const subdir = resolve9(dir, entry.name);
15541
+ if (depth <= 2 && !existsSync13(resolve9(subdir, "__init__.py"))) {
15330
15542
  try {
15331
15543
  const subEntries = readdirSync11(subdir);
15332
15544
  if (subEntries.some((f2) => f2.endsWith(".py") && f2 !== "__init__.py")) {
@@ -15396,6 +15608,8 @@ async function runDoctor() {
15396
15608
  ];
15397
15609
  const pythonCheck = checkPythonHealth(projectRoot);
15398
15610
  if (pythonCheck) checks.push(pythonCheck);
15611
+ const hygieneCheck = await checkApiKeyHygiene(projectRoot);
15612
+ if (hygieneCheck) checks.push(hygieneCheck);
15399
15613
  let passed = 0;
15400
15614
  let failed = 0;
15401
15615
  let warned = 0;
@@ -15421,15 +15635,15 @@ async function runDoctor() {
15421
15635
  }
15422
15636
  async function runValidateConfig() {
15423
15637
  const projectRoot = process.cwd();
15424
- const configPath = resolve8(projectRoot, "massu.config.yaml");
15425
- if (!existsSync12(configPath)) {
15638
+ const configPath = resolve9(projectRoot, "massu.config.yaml");
15639
+ if (!existsSync13(configPath)) {
15426
15640
  console.error("Error: massu.config.yaml not found in current directory");
15427
15641
  console.error("Run: npx massu init");
15428
15642
  process.exit(1);
15429
15643
  return;
15430
15644
  }
15431
15645
  try {
15432
- const content = readFileSync12(configPath, "utf-8");
15646
+ const content = readFileSync13(configPath, "utf-8");
15433
15647
  const parsed = parseYaml4(content);
15434
15648
  if (!parsed || typeof parsed !== "object") {
15435
15649
  console.error("Error: massu.config.yaml is empty or not a valid YAML object");
@@ -15467,6 +15681,7 @@ var init_doctor = __esm({
15467
15681
  "use strict";
15468
15682
  init_config();
15469
15683
  init_license();
15684
+ init_credentials();
15470
15685
  init_settings_local();
15471
15686
  init_hook_registry();
15472
15687
  __filename3 = fileURLToPath3(import.meta.url);
@@ -15475,6 +15690,114 @@ var init_doctor = __esm({
15475
15690
  }
15476
15691
  });
15477
15692
 
15693
+ // src/commands/login.ts
15694
+ var login_exports = {};
15695
+ __export(login_exports, {
15696
+ runLogin: () => runLogin,
15697
+ runLogout: () => runLogout
15698
+ });
15699
+ import { createInterface as createInterface2 } from "readline";
15700
+ function maskKey(k3) {
15701
+ return k3.length <= 12 ? "****" : `${k3.slice(0, API_KEY_PREFIX.length)}\u2026${k3.slice(-4)}`;
15702
+ }
15703
+ function promptHidden(question) {
15704
+ return new Promise((resolvePromise) => {
15705
+ const rl = createInterface2({ input: process.stdin, output: process.stdout });
15706
+ let promptWritten = false;
15707
+ rl._writeToOutput = (chunk) => {
15708
+ if (!promptWritten) {
15709
+ rl.output.write(question);
15710
+ promptWritten = true;
15711
+ }
15712
+ void chunk;
15713
+ };
15714
+ rl.question(question, (answer) => {
15715
+ rl.close();
15716
+ process.stdout.write("\n");
15717
+ resolvePromise(answer);
15718
+ });
15719
+ });
15720
+ }
15721
+ async function readStdinKey() {
15722
+ const chunks = [];
15723
+ for await (const chunk of process.stdin) {
15724
+ chunks.push(chunk);
15725
+ }
15726
+ return Buffer.concat(chunks).toString("utf-8");
15727
+ }
15728
+ async function runLogin(argv) {
15729
+ const keyFlagIdx = argv.indexOf("--key");
15730
+ const noVerify = argv.includes("--no-verify");
15731
+ let key;
15732
+ if (keyFlagIdx >= 0 && argv[keyFlagIdx + 1]) {
15733
+ key = argv[keyFlagIdx + 1];
15734
+ } else if (process.stdin.isTTY) {
15735
+ key = await promptHidden("Massu API key: ");
15736
+ } else {
15737
+ key = await readStdinKey();
15738
+ }
15739
+ key = (key ?? "").trim();
15740
+ if (!key.startsWith(API_KEY_PREFIX)) {
15741
+ console.error(`Error: API key must start with "${API_KEY_PREFIX}".`);
15742
+ process.exit(1);
15743
+ return;
15744
+ }
15745
+ const path = writeUserCredentials(key);
15746
+ console.log(`
15747
+ Saved API key ${maskKey(key)} to ${path} (permissions 0600).`);
15748
+ console.log("This file lives outside every repository \u2014 your key is never committed to git.");
15749
+ if (!noVerify) {
15750
+ try {
15751
+ const { resetConfig: resetConfig2 } = await Promise.resolve().then(() => (init_config(), config_exports));
15752
+ resetConfig2();
15753
+ const { validateLicense: validateLicense2 } = await Promise.resolve().then(() => (init_license(), license_exports));
15754
+ const info = await validateLicense2(key);
15755
+ if (info.tier !== "free") {
15756
+ const cap = info.tier.charAt(0).toUpperCase() + info.tier.slice(1);
15757
+ console.log(
15758
+ `Verified: ${cap} tier${info.validUntil ? `, valid until ${info.validUntil}` : ""}.`
15759
+ );
15760
+ } else {
15761
+ switch (info.outcome) {
15762
+ case "server_error":
15763
+ console.log(
15764
+ "Saved, but could NOT verify your key: the license server returned an error."
15765
+ );
15766
+ console.log(
15767
+ "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."
15768
+ );
15769
+ break;
15770
+ case "network_error":
15771
+ case "no_endpoint":
15772
+ console.log(
15773
+ "Saved; could not reach the license server right now (offline). Your key will validate automatically on next use."
15774
+ );
15775
+ break;
15776
+ default:
15777
+ console.log(
15778
+ "Verified: this key is on the Free tier (check the key or your subscription at https://massu.ai/dashboard)."
15779
+ );
15780
+ }
15781
+ }
15782
+ } catch {
15783
+ console.log("Saved; could not verify online right now (it will validate on next use).");
15784
+ }
15785
+ }
15786
+ console.log("");
15787
+ }
15788
+ async function runLogout() {
15789
+ const removed = removeUserCredentials();
15790
+ console.log(
15791
+ removed ? `Removed stored credentials at ${credentialsPath()}.` : "No stored credentials to remove."
15792
+ );
15793
+ }
15794
+ var init_login = __esm({
15795
+ "src/commands/login.ts"() {
15796
+ "use strict";
15797
+ init_credentials();
15798
+ }
15799
+ });
15800
+
15478
15801
  // src/commands/install-hooks.ts
15479
15802
  var install_hooks_exports = {};
15480
15803
  __export(install_hooks_exports, {
@@ -15506,8 +15829,8 @@ __export(hook_runner_exports, {
15506
15829
  resolveHookFile: () => resolveHookFile,
15507
15830
  runHookRunner: () => runHookRunner
15508
15831
  });
15509
- import { existsSync as existsSync13 } from "fs";
15510
- import { resolve as resolve9, dirname as dirname8 } from "path";
15832
+ import { existsSync as existsSync14 } from "fs";
15833
+ import { resolve as resolve10, dirname as dirname8 } from "path";
15511
15834
  import { fileURLToPath as fileURLToPath4 } from "url";
15512
15835
  import { spawn } from "child_process";
15513
15836
  function resolveHookFile(hookName) {
@@ -15519,14 +15842,14 @@ function resolveHookFile(hookName) {
15519
15842
  }
15520
15843
  const candidates = [
15521
15844
  // Bundled compiled layout: dist/cli.js → ./hooks/<file>.js
15522
- resolve9(__dirname4, "hooks", file),
15845
+ resolve10(__dirname4, "hooks", file),
15523
15846
  // TS-source dev / sibling layout: src/commands/ → ../hooks/<file>
15524
- resolve9(__dirname4, "../hooks", file),
15847
+ resolve10(__dirname4, "../hooks", file),
15525
15848
  // TS-source dev fallback: src/commands/ → ../../dist/hooks/<file>
15526
- resolve9(__dirname4, "../../dist/hooks", file)
15849
+ resolve10(__dirname4, "../../dist/hooks", file)
15527
15850
  ];
15528
15851
  for (const candidate of candidates) {
15529
- if (existsSync13(candidate)) {
15852
+ if (existsSync14(candidate)) {
15530
15853
  return candidate;
15531
15854
  }
15532
15855
  }
@@ -15591,7 +15914,7 @@ __export(permissions_exports, {
15591
15914
  handlePermissionsSubcommand: () => handlePermissionsSubcommand,
15592
15915
  printPermissionsHelp: () => printPermissionsHelp
15593
15916
  });
15594
- import { resolve as resolve10 } from "path";
15917
+ import { resolve as resolve11 } from "path";
15595
15918
  function resolveClaudeDir2() {
15596
15919
  let claudeDirName = ".claude";
15597
15920
  try {
@@ -15599,7 +15922,7 @@ function resolveClaudeDir2() {
15599
15922
  } catch {
15600
15923
  claudeDirName = ".claude";
15601
15924
  }
15602
- return resolve10(process.cwd(), claudeDirName);
15925
+ return resolve11(process.cwd(), claudeDirName);
15603
15926
  }
15604
15927
  async function handlePermissionsSubcommand(args2) {
15605
15928
  const sub = args2[0];
@@ -15729,8 +16052,8 @@ var init_auto_learning_entitlement = __esm({
15729
16052
  });
15730
16053
 
15731
16054
  // src/commands/license.ts
15732
- var license_exports = {};
15733
- __export(license_exports, {
16055
+ var license_exports2 = {};
16056
+ __export(license_exports2, {
15734
16057
  handleLicenseSubcommand: () => handleLicenseSubcommand,
15735
16058
  printLicenseHelp: () => printLicenseHelp
15736
16059
  });
@@ -15818,8 +16141,8 @@ var init_license2 = __esm({
15818
16141
  });
15819
16142
 
15820
16143
  // src/changelog-generator.ts
15821
- import { existsSync as existsSync14, readFileSync as readFileSync13, readdirSync as readdirSync12 } from "fs";
15822
- import { resolve as resolve11 } from "path";
16144
+ import { existsSync as existsSync15, readFileSync as readFileSync14, readdirSync as readdirSync12 } from "fs";
16145
+ import { resolve as resolve12 } from "path";
15823
16146
  function parseCommitsForPlanTokens(subjects) {
15824
16147
  const tokens = /* @__PURE__ */ new Set();
15825
16148
  const maintenance = [];
@@ -15836,7 +16159,7 @@ function parseCommitsForPlanTokens(subjects) {
15836
16159
  function loadPlanSummaries(tokens, planDir) {
15837
16160
  const result = /* @__PURE__ */ new Map();
15838
16161
  if (tokens.size === 0) return result;
15839
- if (!existsSync14(planDir)) {
16162
+ if (!existsSync15(planDir)) {
15840
16163
  throw new Error(`Plan directory does not exist: ${planDir}`);
15841
16164
  }
15842
16165
  const files = readdirSync12(planDir).filter((f2) => f2.endsWith(".md"));
@@ -15844,8 +16167,8 @@ function loadPlanSummaries(tokens, planDir) {
15844
16167
  let matchedFile = null;
15845
16168
  let content = "";
15846
16169
  for (const file of files) {
15847
- const path = resolve11(planDir, file);
15848
- const text18 = readFileSync13(path, "utf-8");
16170
+ const path = resolve12(planDir, file);
16171
+ const text18 = readFileSync14(path, "utf-8");
15849
16172
  const tokenRe = new RegExp(
15850
16173
  `^\\*\\*Plan Token\\*\\*:\\s*\`?${token.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}\`?(\\s|$)`,
15851
16174
  "m"
@@ -15926,8 +16249,8 @@ __export(changelog_exports, {
15926
16249
  printChangelogHelp: () => printChangelogHelp
15927
16250
  });
15928
16251
  import { execSync } from "child_process";
15929
- import { existsSync as existsSync15, readFileSync as readFileSync14 } from "fs";
15930
- import { resolve as resolve12 } from "path";
16252
+ import { existsSync as existsSync16, readFileSync as readFileSync15 } from "fs";
16253
+ import { resolve as resolve13 } from "path";
15931
16254
  function resolveRepoRoot() {
15932
16255
  try {
15933
16256
  return execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
@@ -15951,25 +16274,25 @@ function getCommitSubjects(range) {
15951
16274
  }
15952
16275
  }
15953
16276
  function getCurrentVersion(repoRoot) {
15954
- const pkgPath = resolve12(repoRoot, "packages/core/package.json");
15955
- if (!existsSync15(pkgPath)) return "0.0.0";
15956
- const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
16277
+ const pkgPath = resolve13(repoRoot, "packages/core/package.json");
16278
+ if (!existsSync16(pkgPath)) return "0.0.0";
16279
+ const pkg = JSON.parse(readFileSync15(pkgPath, "utf-8"));
15957
16280
  return pkg.version || "0.0.0";
15958
16281
  }
15959
16282
  function todayDate() {
15960
16283
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
15961
16284
  }
15962
16285
  function getLatestChangelogEntryBody(repoRoot) {
15963
- const path = resolve12(repoRoot, "CHANGELOG.md");
15964
- if (!existsSync15(path)) return "";
15965
- const content = readFileSync14(path, "utf-8");
16286
+ const path = resolve13(repoRoot, "CHANGELOG.md");
16287
+ if (!existsSync16(path)) return "";
16288
+ const content = readFileSync15(path, "utf-8");
15966
16289
  const m3 = content.match(/^## \[[\d.]+\][^\n]*\n([\s\S]*?)(?=\n## \[|$)/m);
15967
16290
  return m3 ? m3[1] : "";
15968
16291
  }
15969
16292
  async function handleChangelogSubcommand(args2) {
15970
16293
  const sub = args2[0];
15971
16294
  const repoRoot = resolveRepoRoot();
15972
- const planDir = resolve12(repoRoot, "docs/plans");
16295
+ const planDir = resolve13(repoRoot, "docs/plans");
15973
16296
  switch (sub) {
15974
16297
  case "generate": {
15975
16298
  const lastTag = getLastTag();
@@ -16180,7 +16503,7 @@ var init_rule_pack_schema = __esm({
16180
16503
  });
16181
16504
 
16182
16505
  // src/rule-pack-sync.ts
16183
- import { existsSync as existsSync16, writeFileSync as writeFileSync3, mkdirSync as mkdirSync6 } from "fs";
16506
+ import { existsSync as existsSync17, writeFileSync as writeFileSync4, mkdirSync as mkdirSync7 } from "fs";
16184
16507
  import { join as join11, dirname as dirname9 } from "path";
16185
16508
  async function pullInstalledPackRules(db, opts = {}) {
16186
16509
  const config = getConfig();
@@ -16243,7 +16566,7 @@ async function pullInstalledPackRules(db, opts = {}) {
16243
16566
  result.pulled += 1;
16244
16567
  const promptHash = packRulePromptHash(pack.slug, rule);
16245
16568
  const candidatePath = sidecarPath(projectRoot, promptHash);
16246
- if (existsSync16(candidatePath) || alreadyApplied(db, promptHash)) {
16569
+ if (existsSync17(candidatePath) || alreadyApplied(db, promptHash)) {
16247
16570
  result.skipped += 1;
16248
16571
  continue;
16249
16572
  }
@@ -16331,8 +16654,8 @@ function materializeCandidate(projectRoot, candidatePath, promptHash, rule, pack
16331
16654
  draft_text: draftText
16332
16655
  };
16333
16656
  const dir = dirname9(candidatePath);
16334
- if (!existsSync16(dir)) mkdirSync6(dir, { recursive: true });
16335
- writeFileSync3(candidatePath, JSON.stringify(sidecar, null, 2), "utf-8");
16657
+ if (!existsSync17(dir)) mkdirSync7(dir, { recursive: true });
16658
+ writeFileSync4(candidatePath, JSON.stringify(sidecar, null, 2), "utf-8");
16336
16659
  }
16337
16660
  function emitDropTelemetry(db, eventType, data) {
16338
16661
  recordTelemetry(db, eventType, data);
@@ -16446,8 +16769,8 @@ var show_template_exports = {};
16446
16769
  __export(show_template_exports, {
16447
16770
  runShowTemplate: () => runShowTemplate
16448
16771
  });
16449
- import { existsSync as existsSync17, readFileSync as readFileSync15 } from "fs";
16450
- import { resolve as resolve13 } from "path";
16772
+ import { existsSync as existsSync18, readFileSync as readFileSync16 } from "fs";
16773
+ import { resolve as resolve14 } from "path";
16451
16774
  function normalizeBaseName(input) {
16452
16775
  return input.endsWith(".md") ? input.slice(0, -".md".length) : input;
16453
16776
  }
@@ -16475,14 +16798,14 @@ async function runShowTemplate(args2) {
16475
16798
  return;
16476
16799
  }
16477
16800
  const suffix = choice.kind === "hit" ? choice.suffix : "";
16478
- const file = suffix === "" ? resolve13(sourceDir, `${baseName}.md`) : resolve13(sourceDir, `${baseName}${suffix}.md`);
16479
- if (!existsSync17(file)) {
16801
+ const file = suffix === "" ? resolve14(sourceDir, `${baseName}.md`) : resolve14(sourceDir, `${baseName}${suffix}.md`);
16802
+ if (!existsSync18(file)) {
16480
16803
  process.stderr.write(`massu: resolved template "${file}" no longer exists
16481
16804
  `);
16482
16805
  process.exit(1);
16483
16806
  return;
16484
16807
  }
16485
- process.stdout.write(readFileSync15(file, "utf-8"));
16808
+ process.stdout.write(readFileSync16(file, "utf-8"));
16486
16809
  }
16487
16810
  var init_show_template = __esm({
16488
16811
  "src/commands/show-template.ts"() {
@@ -16535,12 +16858,12 @@ var init_passthrough = __esm({
16535
16858
  });
16536
16859
 
16537
16860
  // src/lib/fileLock.ts
16538
- import { mkdirSync as mkdirSync7, readFileSync as readFileSync16, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "fs";
16861
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync17, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "fs";
16539
16862
  import { dirname as dirname10 } from "path";
16540
16863
  import * as lockfile from "proper-lockfile";
16541
16864
  function readLockHolderPid(lockPath) {
16542
16865
  try {
16543
- const raw = readFileSync16(`${lockPath}.pid`, "utf-8").trim();
16866
+ const raw = readFileSync17(`${lockPath}.pid`, "utf-8").trim();
16544
16867
  const pid = Number.parseInt(raw, 10);
16545
16868
  if (!Number.isFinite(pid) || pid <= 0) return null;
16546
16869
  return pid;
@@ -16559,7 +16882,7 @@ function busyWaitSync(ms) {
16559
16882
  Atomics.wait(view, 0, 0, ms);
16560
16883
  }
16561
16884
  function withFileLockSync(lockPath, fn, opts = {}) {
16562
- mkdirSync7(dirname10(lockPath), { recursive: true });
16885
+ mkdirSync8(dirname10(lockPath), { recursive: true });
16563
16886
  const staleMs = opts.staleMs ?? 3e4;
16564
16887
  const blockMs = opts.retries === 0 ? 0 : opts.blockMs ?? 3e4;
16565
16888
  const pollIntervalMs = opts.pollIntervalMs ?? 100;
@@ -16576,7 +16899,7 @@ function withFileLockSync(lockPath, fn, opts = {}) {
16576
16899
  realpath: false
16577
16900
  });
16578
16901
  try {
16579
- writeFileSync4(`${lockPath}.pid`, String(process.pid), "utf-8");
16902
+ writeFileSync5(`${lockPath}.pid`, String(process.pid), "utf-8");
16580
16903
  } catch {
16581
16904
  }
16582
16905
  break;
@@ -16603,7 +16926,7 @@ function withFileLockSync(lockPath, fn, opts = {}) {
16603
16926
  } catch {
16604
16927
  }
16605
16928
  try {
16606
- rmSync3(`${lockPath}.pid`, { force: true });
16929
+ rmSync4(`${lockPath}.pid`, { force: true });
16607
16930
  } catch {
16608
16931
  }
16609
16932
  }
@@ -16622,14 +16945,18 @@ var init_fileLock = __esm({
16622
16945
  this.causeCode = causeCode;
16623
16946
  this.name = "FileLockBusyError";
16624
16947
  }
16948
+ lockPath;
16949
+ holderPid;
16950
+ retryAfterSeconds;
16951
+ causeCode;
16625
16952
  };
16626
16953
  }
16627
16954
  });
16628
16955
 
16629
16956
  // src/lib/installLock.ts
16630
- import { resolve as resolve14 } from "path";
16957
+ import { resolve as resolve15 } from "path";
16631
16958
  function withInstallLock(projectRoot, fn, opts = {}) {
16632
- const lockPath = resolve14(projectRoot, ".massu", "installAll.lock");
16959
+ const lockPath = resolve15(projectRoot, ".massu", "installAll.lock");
16633
16960
  return withFileLockSync(
16634
16961
  lockPath,
16635
16962
  fn,
@@ -16654,6 +16981,10 @@ var init_installLock = __esm({
16654
16981
  this.causeCode = causeCode;
16655
16982
  this.name = "InstallLockBusyError";
16656
16983
  }
16984
+ lockPath;
16985
+ holderPid;
16986
+ retryAfterSeconds;
16987
+ causeCode;
16657
16988
  };
16658
16989
  }
16659
16990
  });
@@ -16665,8 +16996,8 @@ __export(config_refresh_exports, {
16665
16996
  mergeRefresh: () => mergeRefresh,
16666
16997
  runConfigRefresh: () => runConfigRefresh
16667
16998
  });
16668
- import { existsSync as existsSync18, readFileSync as readFileSync17, rmSync as rmSync4 } from "fs";
16669
- import { resolve as resolve15 } from "path";
16999
+ import { existsSync as existsSync19, readFileSync as readFileSync18, rmSync as rmSync5 } from "fs";
17000
+ import { resolve as resolve16 } from "path";
16670
17001
  import { parse as parseYaml5 } from "yaml";
16671
17002
  function flatten(obj, prefix3 = "") {
16672
17003
  const out = {};
@@ -16794,17 +17125,17 @@ function renderDiff(diff) {
16794
17125
  }
16795
17126
  async function runConfigRefresh(opts = {}) {
16796
17127
  const cwd = opts.cwd ?? process.cwd();
16797
- const configPath = resolve15(cwd, "massu.config.yaml");
17128
+ const configPath = resolve16(cwd, "massu.config.yaml");
16798
17129
  const log = opts.silent ? () => {
16799
17130
  } : (s) => process.stdout.write(s);
16800
- if (!existsSync18(configPath)) {
17131
+ if (!existsSync19(configPath)) {
16801
17132
  const message = "massu.config.yaml not found. Run: npx massu init";
16802
17133
  if (!opts.silent) process.stderr.write(message + "\n");
16803
17134
  return { exitCode: 1, applied: false, dryRun: !!opts.dryRun, diff: [], message };
16804
17135
  }
16805
17136
  let existing;
16806
17137
  try {
16807
- const content = readFileSync17(configPath, "utf-8");
17138
+ const content = readFileSync18(configPath, "utf-8");
16808
17139
  const parsed = parseYaml5(content);
16809
17140
  if (!parsed || typeof parsed !== "object") {
16810
17141
  throw new Error("config is not a YAML object");
@@ -16877,10 +17208,10 @@ async function runConfigRefresh(opts = {}) {
16877
17208
  `);
16878
17209
  const stackResolved = installResult.totalInstalled > 0 || installResult.totalUpdated > 0;
16879
17210
  if (stackResolved) {
16880
- const placeholderPath = resolve15(installResult.claudeDir, "commands", "_massu-needs-stack.md");
16881
- if (existsSync18(placeholderPath)) {
17211
+ const placeholderPath = resolve16(installResult.claudeDir, "commands", "_massu-needs-stack.md");
17212
+ if (existsSync19(placeholderPath)) {
16882
17213
  try {
16883
- rmSync4(placeholderPath, { force: true });
17214
+ rmSync5(placeholderPath, { force: true });
16884
17215
  log("Removed _massu-needs-stack.md (stack now declared).\n");
16885
17216
  } catch {
16886
17217
  }
@@ -16987,12 +17318,12 @@ var init_gitToplevel = __esm({
16987
17318
  });
16988
17319
 
16989
17320
  // src/watch/lockfile-detector.ts
16990
- import { existsSync as existsSync19, statSync as statSync8 } from "fs";
16991
- import { resolve as resolve16 } from "path";
17321
+ import { existsSync as existsSync20, statSync as statSync8 } from "fs";
17322
+ import { resolve as resolve17 } from "path";
16992
17323
  function lockfileMidWrite(projectRoot, now = Date.now(), windowMs = LOCKFILE_WINDOW_MS) {
16993
17324
  for (const lf of KNOWN_LOCKFILES) {
16994
- const p19 = resolve16(projectRoot, lf);
16995
- if (!existsSync19(p19)) continue;
17325
+ const p19 = resolve17(projectRoot, lf);
17326
+ if (!existsSync20(p19)) continue;
16996
17327
  try {
16997
17328
  const stat = statSync8(p19);
16998
17329
  const delta = now - stat.mtimeMs;
@@ -17005,7 +17336,7 @@ function lockfileMidWrite(projectRoot, now = Date.now(), windowMs = LOCKFILE_WIN
17005
17336
  function gitMidOperation(projectRoot) {
17006
17337
  const sentinels = ["MERGE_HEAD", "REBASE_HEAD", "CHERRY_PICK_HEAD", "rebase-apply", "rebase-merge"];
17007
17338
  for (const s of sentinels) {
17008
- if (existsSync19(resolve16(projectRoot, ".git", s))) return true;
17339
+ if (existsSync20(resolve17(projectRoot, ".git", s))) return true;
17009
17340
  }
17010
17341
  return false;
17011
17342
  }
@@ -17197,23 +17528,25 @@ var init_paths = __esm({
17197
17528
  this.cap = cap;
17198
17529
  this.name = "WatchSurfaceTooLargeError";
17199
17530
  }
17531
+ fileCount;
17532
+ cap;
17200
17533
  };
17201
17534
  }
17202
17535
  });
17203
17536
 
17204
17537
  // src/watch/state.ts
17205
- 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";
17206
- import { dirname as dirname11, resolve as resolve17 } from "path";
17538
+ 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";
17539
+ import { dirname as dirname11, resolve as resolve18 } from "path";
17207
17540
  function watchStatePath(projectRoot) {
17208
- return resolve17(projectRoot, ".massu", "watch-state.json");
17541
+ return resolve18(projectRoot, ".massu", "watch-state.json");
17209
17542
  }
17210
17543
  function backupStatePath(projectRoot) {
17211
- return resolve17(projectRoot, ".massu", "watch-state.v0.bak.json");
17544
+ return resolve18(projectRoot, ".massu", "watch-state.v0.bak.json");
17212
17545
  }
17213
17546
  function readState(projectRoot) {
17214
17547
  const path = watchStatePath(projectRoot);
17215
- if (!existsSync20(path)) return { ...DEFAULT_STATE };
17216
- const content = readFileSync18(path, "utf-8");
17548
+ if (!existsSync21(path)) return { ...DEFAULT_STATE };
17549
+ const content = readFileSync19(path, "utf-8");
17217
17550
  let raw;
17218
17551
  try {
17219
17552
  raw = JSON.parse(content);
@@ -17255,14 +17588,14 @@ function readState(projectRoot) {
17255
17588
  }
17256
17589
  function archiveCorrupt(projectRoot, content) {
17257
17590
  const bak = backupStatePath(projectRoot);
17258
- mkdirSync8(dirname11(bak), { recursive: true });
17259
- writeFileSync5(bak, content, "utf-8");
17591
+ mkdirSync9(dirname11(bak), { recursive: true });
17592
+ writeFileSync6(bak, content, "utf-8");
17260
17593
  }
17261
17594
  function writeStateAtomic(projectRoot, state) {
17262
17595
  const path = watchStatePath(projectRoot);
17263
17596
  writeStateAtomicCounter = writeStateAtomicCounter + 1 >>> 0;
17264
17597
  const tmp = `${path}.${process.pid}.${writeStateAtomicCounter}.tmp`;
17265
- mkdirSync8(dirname11(path), { recursive: true });
17598
+ mkdirSync9(dirname11(path), { recursive: true });
17266
17599
  let renamed = false;
17267
17600
  try {
17268
17601
  const fd = openSync3(tmp, "w");
@@ -17276,9 +17609,9 @@ function writeStateAtomic(projectRoot, state) {
17276
17609
  renameSync4(tmp, path);
17277
17610
  renamed = true;
17278
17611
  } finally {
17279
- if (!renamed && existsSync20(tmp)) {
17612
+ if (!renamed && existsSync21(tmp)) {
17280
17613
  try {
17281
- rmSync5(tmp, { force: true });
17614
+ rmSync6(tmp, { force: true });
17282
17615
  } catch {
17283
17616
  }
17284
17617
  }
@@ -17312,6 +17645,8 @@ var init_state = __esm({
17312
17645
  this.daemonMax = daemonMax;
17313
17646
  this.name = "WatchStateNewerError";
17314
17647
  }
17648
+ stateVersion;
17649
+ daemonMax;
17315
17650
  };
17316
17651
  writeStateAtomicCounter = 0;
17317
17652
  }
@@ -17556,8 +17891,8 @@ __export(watch_exports, {
17556
17891
  runWatch: () => runWatch
17557
17892
  });
17558
17893
  import { spawnSync as spawnSync3 } from "child_process";
17559
- import { basename as basename5, dirname as dirname12, resolve as resolve18 } from "path";
17560
- import { appendFileSync, existsSync as existsSync21, mkdirSync as mkdirSync9, readFileSync as readFileSync19 } from "fs";
17894
+ import { basename as basename5, dirname as dirname12, resolve as resolve19 } from "path";
17895
+ import { appendFileSync, existsSync as existsSync22, mkdirSync as mkdirSync10, readFileSync as readFileSync20 } from "fs";
17561
17896
  function parseFlags(args2) {
17562
17897
  const out = {
17563
17898
  foreground: false,
@@ -17582,8 +17917,8 @@ function parseFlags(args2) {
17582
17917
  }
17583
17918
  function findClaudeBg() {
17584
17919
  const home = process.env.HOME ?? "";
17585
- const fixed = home ? resolve18(home, ".claude", "bin", "claude-bg") : null;
17586
- if (fixed && existsSync21(fixed)) return fixed;
17920
+ const fixed = home ? resolve19(home, ".claude", "bin", "claude-bg") : null;
17921
+ if (fixed && existsSync22(fixed)) return fixed;
17587
17922
  const which = spawnSync3("which", ["claude-bg"], { encoding: "utf-8" });
17588
17923
  if (which.status === 0 && which.stdout) {
17589
17924
  const p19 = which.stdout.trim();
@@ -17644,7 +17979,7 @@ async function runWatch(args2) {
17644
17979
  }
17645
17980
  function runStatus(root) {
17646
17981
  const path = watchStatePath(root);
17647
- if (!existsSync21(path)) {
17982
+ if (!existsSync22(path)) {
17648
17983
  process.stdout.write("massu watch: not running (no state file)\n");
17649
17984
  return { exitCode: 0 };
17650
17985
  }
@@ -17726,7 +18061,7 @@ async function runForeground(root) {
17726
18061
  }
17727
18062
  throw err;
17728
18063
  }
17729
- return new Promise((resolve41) => {
18064
+ return new Promise((resolve42) => {
17730
18065
  const shutdown = async () => {
17731
18066
  if (stopped) return;
17732
18067
  stopped = true;
@@ -17736,7 +18071,7 @@ async function runForeground(root) {
17736
18071
  process.chdir(priorCwd);
17737
18072
  } catch {
17738
18073
  }
17739
- resolve41({ exitCode: 0 });
18074
+ resolve42({ exitCode: 0 });
17740
18075
  };
17741
18076
  process.on("SIGINT", () => {
17742
18077
  void shutdown();
@@ -17797,23 +18132,23 @@ async function runOnQuiescent(projectRoot) {
17797
18132
  );
17798
18133
  }
17799
18134
  function refreshLogPath(projectRoot) {
17800
- return resolve18(projectRoot, ".massu", "refresh-log.jsonl");
18135
+ return resolve19(projectRoot, ".massu", "refresh-log.jsonl");
17801
18136
  }
17802
18137
  function appendRefreshLog(projectRoot, event) {
17803
18138
  const path = refreshLogPath(projectRoot);
17804
18139
  try {
17805
- mkdirSync9(dirname12(path), { recursive: true });
18140
+ mkdirSync10(dirname12(path), { recursive: true });
17806
18141
  appendFileSync(path, JSON.stringify(event) + "\n", "utf-8");
17807
18142
  } catch {
17808
18143
  }
17809
18144
  }
17810
18145
  function readRefreshLog(projectRoot, limit = 10, opts = {}) {
17811
18146
  const path = refreshLogPath(projectRoot);
17812
- if (!existsSync21(path)) return [];
18147
+ if (!existsSync22(path)) return [];
17813
18148
  const warn = opts.warn ?? ((s) => {
17814
18149
  process.stderr.write(s);
17815
18150
  });
17816
- const lines = readFileSync19(path, "utf-8").split("\n").filter(Boolean);
18151
+ const lines = readFileSync20(path, "utf-8").split("\n").filter(Boolean);
17817
18152
  const tail = lines.slice(-limit);
17818
18153
  const out = [];
17819
18154
  let corrupt = 0;
@@ -17884,14 +18219,14 @@ var init_refresh_log = __esm({
17884
18219
 
17885
18220
  // src/security/atomic-write.ts
17886
18221
  import {
17887
- chmodSync as chmodSync3,
18222
+ chmodSync as chmodSync4,
17888
18223
  closeSync as closeSync4,
17889
- existsSync as existsSync22,
18224
+ existsSync as existsSync23,
17890
18225
  fsyncSync as fsyncSync4,
17891
- mkdirSync as mkdirSync10,
18226
+ mkdirSync as mkdirSync11,
17892
18227
  openSync as openSync4,
17893
18228
  renameSync as renameSync5,
17894
- rmSync as rmSync6,
18229
+ rmSync as rmSync7,
17895
18230
  statSync as statSync9,
17896
18231
  writeSync as writeSync4
17897
18232
  } from "node:fs";
@@ -17900,12 +18235,12 @@ function atomicWrite(path, content, opts = {}) {
17900
18235
  const tmpPath = `${path}.tmp`;
17901
18236
  const parentDir = dirname13(path);
17902
18237
  try {
17903
- if (!existsSync22(parentDir)) {
18238
+ if (!existsSync23(parentDir)) {
17904
18239
  const mkdirOpts = { recursive: true };
17905
18240
  if (opts.ensureParentDirMode !== void 0) {
17906
18241
  mkdirOpts.mode = opts.ensureParentDirMode;
17907
18242
  }
17908
- mkdirSync10(parentDir, mkdirOpts);
18243
+ mkdirSync11(parentDir, mkdirOpts);
17909
18244
  }
17910
18245
  const buf = typeof content === "string" ? Buffer.from(content, "utf-8") : content;
17911
18246
  const openMode = opts.mode ?? 420;
@@ -17917,14 +18252,14 @@ function atomicWrite(path, content, opts = {}) {
17917
18252
  closeSync4(fd);
17918
18253
  }
17919
18254
  if (opts.mode !== void 0) {
17920
- chmodSync3(tmpPath, opts.mode);
18255
+ chmodSync4(tmpPath, opts.mode);
17921
18256
  }
17922
18257
  renameSync5(tmpPath, path);
17923
18258
  return { written: true };
17924
18259
  } catch (err) {
17925
- if (existsSync22(tmpPath)) {
18260
+ if (existsSync23(tmpPath)) {
17926
18261
  try {
17927
- rmSync6(tmpPath, { force: true });
18262
+ rmSync7(tmpPath, { force: true });
17928
18263
  } catch {
17929
18264
  }
17930
18265
  }
@@ -18242,6 +18577,8 @@ var init_fetcher = __esm({
18242
18577
  this.host = host;
18243
18578
  this.name = "FetchAllowlistError";
18244
18579
  }
18580
+ url;
18581
+ host;
18245
18582
  };
18246
18583
  FetchTimeoutError = class extends Error {
18247
18584
  constructor(url, timeoutMs) {
@@ -18250,6 +18587,8 @@ var init_fetcher = __esm({
18250
18587
  this.timeoutMs = timeoutMs;
18251
18588
  this.name = "FetchTimeoutError";
18252
18589
  }
18590
+ url;
18591
+ timeoutMs;
18253
18592
  };
18254
18593
  FetchBodySizeError = class extends Error {
18255
18594
  constructor(url, maxBodyBytes) {
@@ -18260,29 +18599,31 @@ var init_fetcher = __esm({
18260
18599
  this.maxBodyBytes = maxBodyBytes;
18261
18600
  this.name = "FetchBodySizeError";
18262
18601
  }
18602
+ url;
18603
+ maxBodyBytes;
18263
18604
  };
18264
18605
  }
18265
18606
  });
18266
18607
 
18267
18608
  // src/security/manifest-cache.ts
18268
- import { existsSync as existsSync23, readFileSync as readFileSync20, statSync as statSync10 } from "node:fs";
18269
- import { homedir as homedir5 } from "node:os";
18270
- import { resolve as resolve19 } from "node:path";
18609
+ import { existsSync as existsSync24, readFileSync as readFileSync21, statSync as statSync10 } from "node:fs";
18610
+ import { homedir as homedir6 } from "node:os";
18611
+ import { resolve as resolve20 } from "node:path";
18271
18612
  import { z as z4 } from "zod";
18272
18613
  function defaultCachePaths() {
18273
- const dir = resolve19(homedir5(), ".massu");
18614
+ const dir = resolve20(homedir6(), ".massu");
18274
18615
  return {
18275
- cachePath: resolve19(dir, "adapter-manifest.json"),
18276
- lockPath: resolve19(dir, ".adapter-manifest.lock")
18616
+ cachePath: resolve20(dir, "adapter-manifest.json"),
18617
+ lockPath: resolve20(dir, ".adapter-manifest.lock")
18277
18618
  };
18278
18619
  }
18279
18620
  function loadCachedManifest(paths = defaultCachePaths()) {
18280
- if (!existsSync23(paths.cachePath)) {
18621
+ if (!existsSync24(paths.cachePath)) {
18281
18622
  return { kind: "absent" };
18282
18623
  }
18283
18624
  let raw;
18284
18625
  try {
18285
- const content = readFileSync20(paths.cachePath, "utf-8");
18626
+ const content = readFileSync21(paths.cachePath, "utf-8");
18286
18627
  raw = JSON.parse(content);
18287
18628
  } catch (err) {
18288
18629
  return {
@@ -18471,15 +18812,15 @@ var init_adapter_origin = __esm({
18471
18812
  });
18472
18813
 
18473
18814
  // src/security/local-fingerprint.ts
18474
- import { existsSync as existsSync24, readFileSync as readFileSync21, lstatSync as lstatSync5 } from "node:fs";
18475
- import { homedir as homedir6 } from "node:os";
18476
- import { resolve as resolve20, isAbsolute } from "node:path";
18815
+ import { existsSync as existsSync25, readFileSync as readFileSync22, lstatSync as lstatSync5 } from "node:fs";
18816
+ import { homedir as homedir7 } from "node:os";
18817
+ import { resolve as resolve21, isAbsolute } from "node:path";
18477
18818
  import { createHash as createHash7 } from "node:crypto";
18478
18819
  import { z as z5 } from "zod";
18479
18820
  function computeLocalFingerprint(localPaths, projectRoot) {
18480
18821
  const tuples = [];
18481
18822
  for (const p19 of localPaths) {
18482
- const abs = isAbsolute(p19) ? p19 : resolve20(projectRoot, p19);
18823
+ const abs = isAbsolute(p19) ? p19 : resolve21(projectRoot, p19);
18483
18824
  let contentTag;
18484
18825
  try {
18485
18826
  const lst = lstatSync5(abs);
@@ -18488,7 +18829,7 @@ function computeLocalFingerprint(localPaths, projectRoot) {
18488
18829
  } else if (!lst.isFile()) {
18489
18830
  contentTag = "<not-a-file>";
18490
18831
  } else {
18491
- contentTag = createHash7("sha256").update(readFileSync21(abs)).digest("hex");
18832
+ contentTag = createHash7("sha256").update(readFileSync22(abs)).digest("hex");
18492
18833
  }
18493
18834
  } catch {
18494
18835
  contentTag = "<missing>";
@@ -18500,10 +18841,10 @@ function computeLocalFingerprint(localPaths, projectRoot) {
18500
18841
  return createHash7("sha256").update(canonical).digest("hex");
18501
18842
  }
18502
18843
  function readFingerprintSentinel(path = FINGERPRINT_PATH) {
18503
- if (!existsSync24(path)) return null;
18844
+ if (!existsSync25(path)) return null;
18504
18845
  let raw;
18505
18846
  try {
18506
- raw = JSON.parse(readFileSync21(path, "utf-8"));
18847
+ raw = JSON.parse(readFileSync22(path, "utf-8"));
18507
18848
  } catch {
18508
18849
  return null;
18509
18850
  }
@@ -18551,7 +18892,7 @@ var init_local_fingerprint = __esm({
18551
18892
  "use strict";
18552
18893
  init_atomic_write();
18553
18894
  init_manifest_schema();
18554
- FINGERPRINT_PATH = resolve20(homedir6(), ".massu", "adapters-local-fingerprint.json");
18895
+ FINGERPRINT_PATH = resolve21(homedir7(), ".massu", "adapters-local-fingerprint.json");
18555
18896
  FingerprintSentinelSchema = z5.object({
18556
18897
  fingerprint: z5.string().regex(/^[0-9a-f]{64}$/),
18557
18898
  source: z5.enum(["cli", "cli-resync"]),
@@ -18567,15 +18908,15 @@ var init_local_fingerprint = __esm({
18567
18908
  });
18568
18909
 
18569
18910
  // src/security/install-tracking.ts
18570
- import { readFileSync as readFileSync22, readdirSync as readdirSync13, lstatSync as lstatSync6, existsSync as existsSync25 } from "node:fs";
18911
+ import { readFileSync as readFileSync23, readdirSync as readdirSync13, lstatSync as lstatSync6, existsSync as existsSync26 } from "node:fs";
18571
18912
  import { join as join12, relative as relative5, sep } from "node:path";
18572
- import { homedir as homedir7 } from "node:os";
18573
- import { resolve as resolve21 } from "node:path";
18913
+ import { homedir as homedir8 } from "node:os";
18914
+ import { resolve as resolve22 } from "node:path";
18574
18915
  import { createHash as createHash8 } from "node:crypto";
18575
18916
  import { z as z6 } from "zod";
18576
18917
  function containsHiddenDirs(packageDir) {
18577
18918
  for (const hidden of EXCLUDED_DIR_NAMES) {
18578
- if (existsSync25(`${packageDir}/${hidden}`)) {
18919
+ if (existsSync26(`${packageDir}/${hidden}`)) {
18579
18920
  return hidden;
18580
18921
  }
18581
18922
  }
@@ -18621,7 +18962,7 @@ function sha256OfDir(dir, opts = {}) {
18621
18962
  files.sort((a2, b2) => a2.relativePath < b2.relativePath ? -1 : a2.relativePath > b2.relativePath ? 1 : 0);
18622
18963
  const top = createHash8("sha256");
18623
18964
  for (const f2 of files) {
18624
- const fileHash = createHash8("sha256").update(readFileSync22(f2.absPath)).digest("hex");
18965
+ const fileHash = createHash8("sha256").update(readFileSync23(f2.absPath)).digest("hex");
18625
18966
  top.update(f2.relativePath, "utf-8");
18626
18967
  top.update("\0", "utf-8");
18627
18968
  top.update(fileHash, "utf-8");
@@ -18630,10 +18971,10 @@ function sha256OfDir(dir, opts = {}) {
18630
18971
  return top.digest("hex");
18631
18972
  }
18632
18973
  function readInstalledManifest(path = INSTALLED_MANIFEST_PATH) {
18633
- if (!existsSync25(path)) return {};
18974
+ if (!existsSync26(path)) return {};
18634
18975
  let raw;
18635
18976
  try {
18636
- raw = JSON.parse(readFileSync22(path, "utf-8"));
18977
+ raw = JSON.parse(readFileSync23(path, "utf-8"));
18637
18978
  } catch {
18638
18979
  return {};
18639
18980
  }
@@ -18697,7 +19038,7 @@ var init_install_tracking = __esm({
18697
19038
  "src/security/install-tracking.ts"() {
18698
19039
  "use strict";
18699
19040
  init_atomic_write();
18700
- INSTALLED_MANIFEST_PATH = resolve21(homedir7(), ".massu", "adapter-manifest-installed.json");
19041
+ INSTALLED_MANIFEST_PATH = resolve22(homedir8(), ".massu", "adapter-manifest-installed.json");
18701
19042
  DEFAULT_MAX_FILE_BYTES = 64 * 1024 * 1024;
18702
19043
  EXCLUDED_DIR_NAMES = /* @__PURE__ */ new Set([".git", "node_modules", ".cache", ".tmp"]);
18703
19044
  InstallEntrySchema = z6.object({
@@ -18720,12 +19061,12 @@ var init_install_tracking = __esm({
18720
19061
  });
18721
19062
 
18722
19063
  // src/detect/adapters/discover.ts
18723
- import { existsSync as existsSync26, readdirSync as readdirSync14, readFileSync as readFileSync23, lstatSync as lstatSync7 } from "node:fs";
18724
- import { resolve as resolve22, isAbsolute as isAbsolute2 } from "node:path";
19064
+ import { existsSync as existsSync27, readdirSync as readdirSync14, readFileSync as readFileSync24, lstatSync as lstatSync7 } from "node:fs";
19065
+ import { resolve as resolve23, isAbsolute as isAbsolute2 } from "node:path";
18725
19066
  import { z as z7 } from "zod";
18726
19067
  function walkNodeModules(projectRoot, warnings) {
18727
- const nodeModulesDir = resolve22(projectRoot, "node_modules");
18728
- if (!existsSync26(nodeModulesDir)) {
19068
+ const nodeModulesDir = resolve23(projectRoot, "node_modules");
19069
+ if (!existsSync27(nodeModulesDir)) {
18729
19070
  return [];
18730
19071
  }
18731
19072
  const candidates = [];
@@ -18738,7 +19079,7 @@ function walkNodeModules(projectRoot, warnings) {
18738
19079
  }
18739
19080
  for (const entry of topLevelEntries) {
18740
19081
  if (entry.startsWith(".")) continue;
18741
- const entryPath = resolve22(nodeModulesDir, entry);
19082
+ const entryPath = resolve23(nodeModulesDir, entry);
18742
19083
  let entryStat;
18743
19084
  try {
18744
19085
  entryStat = lstatSync7(entryPath);
@@ -18760,7 +19101,7 @@ function walkNodeModules(projectRoot, warnings) {
18760
19101
  continue;
18761
19102
  }
18762
19103
  for (const sub of scopedEntries) {
18763
- const subPath = resolve22(entryPath, sub);
19104
+ const subPath = resolve23(entryPath, sub);
18764
19105
  let subStat;
18765
19106
  try {
18766
19107
  subStat = lstatSync7(subPath);
@@ -18779,11 +19120,11 @@ function walkNodeModules(projectRoot, warnings) {
18779
19120
  return candidates;
18780
19121
  }
18781
19122
  function tryReadAdapterPackage(packageDir, warnings) {
18782
- const pkgJsonPath = resolve22(packageDir, "package.json");
18783
- if (!existsSync26(pkgJsonPath)) return null;
19123
+ const pkgJsonPath = resolve23(packageDir, "package.json");
19124
+ if (!existsSync27(pkgJsonPath)) return null;
18784
19125
  let raw;
18785
19126
  try {
18786
- raw = JSON.parse(readFileSync23(pkgJsonPath, "utf-8"));
19127
+ raw = JSON.parse(readFileSync24(pkgJsonPath, "utf-8"));
18787
19128
  } catch (err) {
18788
19129
  warnings.push(
18789
19130
  `skipping ${packageDir}: package.json parse failed (${err instanceof Error ? err.message : String(err)})`
@@ -18917,8 +19258,8 @@ function discoverAdapters(opts) {
18917
19258
  const localSet = new Set(opts.configLocalPaths);
18918
19259
  for (const localPath of opts.configLocalPaths) {
18919
19260
  if (seenIds.has(localPath)) continue;
18920
- const absPath = isAbsolute2(localPath) ? localPath : resolve22(opts.projectRoot, localPath);
18921
- if (!existsSync26(absPath)) {
19261
+ const absPath = isAbsolute2(localPath) ? localPath : resolve23(opts.projectRoot, localPath);
19262
+ if (!existsSync27(absPath)) {
18922
19263
  warnings.push(
18923
19264
  `local adapter file not found: ${localPath} (resolved to ${absPath}). Remove via: massu adapters remove-local ${localPath}`
18924
19265
  );
@@ -19000,8 +19341,8 @@ __export(adapters_exports, {
19000
19341
  runAdaptersResyncLocalFingerprint: () => runAdaptersResyncLocalFingerprint,
19001
19342
  runAdaptersSearch: () => runAdaptersSearch
19002
19343
  });
19003
- import { existsSync as existsSync27, readFileSync as readFileSync24 } from "node:fs";
19004
- import { resolve as resolve23 } from "node:path";
19344
+ import { existsSync as existsSync28, readFileSync as readFileSync25 } from "node:fs";
19345
+ import { resolve as resolve24 } from "node:path";
19005
19346
  import { parseDocument } from "yaml";
19006
19347
  async function handleAdaptersSubcommand(args2) {
19007
19348
  const sub = args2[0];
@@ -19268,8 +19609,8 @@ function mutateLocalArray(mutator, command) {
19268
19609
  );
19269
19610
  return { exitCode: 2 };
19270
19611
  }
19271
- const yamlPath = resolve23(projectRoot, "massu.config.yaml");
19272
- if (!existsSync27(yamlPath)) {
19612
+ const yamlPath = resolve24(projectRoot, "massu.config.yaml");
19613
+ if (!existsSync28(yamlPath)) {
19273
19614
  process.stderr.write(
19274
19615
  `${command}: massu.config.yaml not found at ${yamlPath}. Run \`massu init\` first.
19275
19616
  `
@@ -19278,7 +19619,7 @@ function mutateLocalArray(mutator, command) {
19278
19619
  }
19279
19620
  let yamlText;
19280
19621
  try {
19281
- yamlText = readFileSync24(yamlPath, "utf-8");
19622
+ yamlText = readFileSync25(yamlPath, "utf-8");
19282
19623
  } catch (err) {
19283
19624
  process.stderr.write(`${command}: failed to read ${yamlPath}: ${err instanceof Error ? err.message : String(err)}
19284
19625
  `);
@@ -19299,7 +19640,7 @@ function mutateLocalArray(mutator, command) {
19299
19640
  if (next === null) {
19300
19641
  return { exitCode: 0 };
19301
19642
  }
19302
- const lockPath = resolve23(projectRoot, ".massu", "adapters-local-mutate.lock");
19643
+ const lockPath = resolve24(projectRoot, ".massu", "adapters-local-mutate.lock");
19303
19644
  return withFileLockSync(lockPath, () => {
19304
19645
  doc.setIn(["adapters", "local"], next);
19305
19646
  const newYaml = doc.toString();
@@ -19368,23 +19709,23 @@ async function runAdaptersInstall(args2) {
19368
19709
  `);
19369
19710
  return { exitCode: 1 };
19370
19711
  }
19371
- const packageDir = resolve23(projectRoot, "node_modules", ...packageName.split("/"));
19372
- if (!existsSync27(packageDir)) {
19712
+ const packageDir = resolve24(projectRoot, "node_modules", ...packageName.split("/"));
19713
+ if (!existsSync28(packageDir)) {
19373
19714
  process.stderr.write(
19374
19715
  `install: ${packageName} is not installed in node_modules. Run \`npm install ${packageName}\` first.
19375
19716
  `
19376
19717
  );
19377
19718
  return { exitCode: 1 };
19378
19719
  }
19379
- const pkgJsonPath = resolve23(packageDir, "package.json");
19380
- if (!existsSync27(pkgJsonPath)) {
19720
+ const pkgJsonPath = resolve24(packageDir, "package.json");
19721
+ if (!existsSync28(pkgJsonPath)) {
19381
19722
  process.stderr.write(`install: ${packageName} has no package.json at ${pkgJsonPath}
19382
19723
  `);
19383
19724
  return { exitCode: 1 };
19384
19725
  }
19385
19726
  let pkgJson;
19386
19727
  try {
19387
- pkgJson = JSON.parse(readFileSync24(pkgJsonPath, "utf-8"));
19728
+ pkgJson = JSON.parse(readFileSync25(pkgJsonPath, "utf-8"));
19388
19729
  } catch (err) {
19389
19730
  process.stderr.write(`install: ${packageName} has malformed package.json: ${err instanceof Error ? err.message : String(err)}
19390
19731
  `);
@@ -19503,8 +19844,8 @@ async function runAdaptersResign(_args) {
19503
19844
  removeInstalledManifestEntry(name);
19504
19845
  continue;
19505
19846
  }
19506
- const packageDir = resolve23(projectRoot, "node_modules", ...name.split("/"));
19507
- if (!existsSync27(packageDir)) {
19847
+ const packageDir = resolve24(projectRoot, "node_modules", ...name.split("/"));
19848
+ if (!existsSync28(packageDir)) {
19508
19849
  removed++;
19509
19850
  warnings.push(`${name}@${entry.version}: not present in node_modules \u2014 REMOVED from sidecar`);
19510
19851
  removeInstalledManifestEntry(name);
@@ -19603,10 +19944,10 @@ var init_sql_table_names = __esm({
19603
19944
  // src/db.ts
19604
19945
  import Database2 from "better-sqlite3";
19605
19946
  import { dirname as dirname14, join as join13 } from "path";
19606
- import { existsSync as existsSync28, mkdirSync as mkdirSync11, readdirSync as readdirSync15, statSync as statSync11 } from "fs";
19947
+ import { existsSync as existsSync29, mkdirSync as mkdirSync12, readdirSync as readdirSync15, statSync as statSync11 } from "fs";
19607
19948
  function getCodeGraphDb() {
19608
19949
  const dbPath = getResolvedPaths().codegraphDbPath;
19609
- if (!existsSync28(dbPath)) {
19950
+ if (!existsSync29(dbPath)) {
19610
19951
  throw new CodegraphDbNotInitializedError(dbPath);
19611
19952
  }
19612
19953
  const db = new Database2(dbPath, { readonly: true });
@@ -19616,8 +19957,8 @@ function getCodeGraphDb() {
19616
19957
  function getDataDb() {
19617
19958
  const dbPath = getResolvedPaths().dataDbPath;
19618
19959
  const dir = dirname14(dbPath);
19619
- if (!existsSync28(dir)) {
19620
- mkdirSync11(dir, { recursive: true });
19960
+ if (!existsSync29(dir)) {
19961
+ mkdirSync12(dir, { recursive: true });
19621
19962
  }
19622
19963
  const db = new Database2(dbPath);
19623
19964
  db.pragma("journal_mode = WAL");
@@ -19926,12 +20267,12 @@ var init_db = __esm({
19926
20267
  // src/knowledge-db.ts
19927
20268
  import Database3 from "better-sqlite3";
19928
20269
  import { dirname as dirname15 } from "path";
19929
- import { existsSync as existsSync29, mkdirSync as mkdirSync12 } from "fs";
20270
+ import { existsSync as existsSync30, mkdirSync as mkdirSync13 } from "fs";
19930
20271
  function getKnowledgeDb() {
19931
20272
  const dbPath = getResolvedPaths().knowledgeDbPath;
19932
20273
  const dir = dirname15(dbPath);
19933
- if (!existsSync29(dir)) {
19934
- mkdirSync12(dir, { recursive: true });
20274
+ if (!existsSync30(dir)) {
20275
+ mkdirSync13(dir, { recursive: true });
19935
20276
  }
19936
20277
  const db = new Database3(dbPath);
19937
20278
  db.pragma("journal_mode = WAL");
@@ -20102,10 +20443,10 @@ var init_knowledge_db = __esm({
20102
20443
  });
20103
20444
 
20104
20445
  // src/security-utils.ts
20105
- import { resolve as resolve24, normalize } from "path";
20446
+ import { resolve as resolve25, normalize } from "path";
20106
20447
  function ensureWithinRoot(filePath, projectRoot) {
20107
- const resolvedRoot = resolve24(projectRoot);
20108
- const resolvedPath = resolve24(resolvedRoot, filePath);
20448
+ const resolvedRoot = resolve25(projectRoot);
20449
+ const resolvedPath = resolve25(resolvedRoot, filePath);
20109
20450
  const normalizedPath = normalize(resolvedPath);
20110
20451
  const normalizedRoot = normalize(resolvedRoot);
20111
20452
  if (!normalizedPath.startsWith(normalizedRoot + "/") && normalizedPath !== normalizedRoot) {
@@ -20178,8 +20519,8 @@ var init_rules = __esm({
20178
20519
  });
20179
20520
 
20180
20521
  // src/import-resolver.ts
20181
- import { readFileSync as readFileSync25, existsSync as existsSync30, statSync as statSync12 } from "fs";
20182
- import { resolve as resolve25, dirname as dirname16, join as join14 } from "path";
20522
+ import { readFileSync as readFileSync26, existsSync as existsSync31, statSync as statSync12 } from "fs";
20523
+ import { resolve as resolve26, dirname as dirname16, join as join14 } from "path";
20183
20524
  function parseImports(source) {
20184
20525
  const imports = [];
20185
20526
  const lines = source.split("\n");
@@ -20235,23 +20576,23 @@ function resolveImportPath(specifier, fromFile) {
20235
20576
  let basePath;
20236
20577
  if (specifier.startsWith("@/")) {
20237
20578
  const paths = getResolvedPaths();
20238
- basePath = resolve25(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
20579
+ basePath = resolve26(paths.pathAlias["@"] ?? paths.srcDir, specifier.slice(2));
20239
20580
  } else {
20240
- basePath = resolve25(dirname16(fromFile), specifier);
20581
+ basePath = resolve26(dirname16(fromFile), specifier);
20241
20582
  }
20242
- if (existsSync30(basePath) && !isDirectory(basePath)) {
20583
+ if (existsSync31(basePath) && !isDirectory(basePath)) {
20243
20584
  return toRelative(basePath);
20244
20585
  }
20245
20586
  const resolvedPaths = getResolvedPaths();
20246
20587
  for (const ext of resolvedPaths.extensions) {
20247
20588
  const withExt = basePath + ext;
20248
- if (existsSync30(withExt)) {
20589
+ if (existsSync31(withExt)) {
20249
20590
  return toRelative(withExt);
20250
20591
  }
20251
20592
  }
20252
20593
  for (const indexFile of resolvedPaths.indexFiles) {
20253
20594
  const indexPath = join14(basePath, indexFile);
20254
- if (existsSync30(indexPath)) {
20595
+ if (existsSync31(indexPath)) {
20255
20596
  return toRelative(indexPath);
20256
20597
  }
20257
20598
  }
@@ -20287,11 +20628,11 @@ function buildImportIndex(dataDb, codegraphDb) {
20287
20628
  const batchSize = 500;
20288
20629
  let batch = [];
20289
20630
  for (const file of files) {
20290
- const absPath = ensureWithinRoot(resolve25(projectRoot, file.path), projectRoot);
20291
- if (!existsSync30(absPath)) continue;
20631
+ const absPath = ensureWithinRoot(resolve26(projectRoot, file.path), projectRoot);
20632
+ if (!existsSync31(absPath)) continue;
20292
20633
  let source;
20293
20634
  try {
20294
- source = readFileSync25(absPath, "utf-8");
20635
+ source = readFileSync26(absPath, "utf-8");
20295
20636
  } catch {
20296
20637
  continue;
20297
20638
  }
@@ -20328,15 +20669,15 @@ var init_import_resolver = __esm({
20328
20669
  });
20329
20670
 
20330
20671
  // src/trpc-index.ts
20331
- import { readFileSync as readFileSync26, existsSync as existsSync31, readdirSync as readdirSync16 } from "fs";
20332
- import { resolve as resolve26, join as join15 } from "path";
20672
+ import { readFileSync as readFileSync27, existsSync as existsSync32, readdirSync as readdirSync16 } from "fs";
20673
+ import { resolve as resolve27, join as join15 } from "path";
20333
20674
  function parseRootRouter() {
20334
20675
  const paths = getResolvedPaths();
20335
20676
  const rootPath = paths.rootRouterPath;
20336
- if (!existsSync31(rootPath)) {
20677
+ if (!existsSync32(rootPath)) {
20337
20678
  throw new Error(`Root router not found at ${rootPath}`);
20338
20679
  }
20339
- const source = readFileSync26(rootPath, "utf-8");
20680
+ const source = readFileSync27(rootPath, "utf-8");
20340
20681
  const mappings = [];
20341
20682
  const importMap = /* @__PURE__ */ new Map();
20342
20683
  const importRegex = /import\s+\{[^}]*?(\w+Router)[^}]*\}\s+from\s+['"]\.\/routers\/([^'"]+)['"]/g;
@@ -20344,16 +20685,16 @@ function parseRootRouter() {
20344
20685
  while ((match = importRegex.exec(source)) !== null) {
20345
20686
  const variable = match[1];
20346
20687
  let filePath = match[2];
20347
- const fullPath = resolve26(paths.routersDir, filePath);
20688
+ const fullPath = resolve27(paths.routersDir, filePath);
20348
20689
  for (const ext of [".ts", ".tsx", ""]) {
20349
20690
  const candidate = fullPath + ext;
20350
20691
  const routersRelPath = getConfig().paths.routers ?? "src/server/api/routers";
20351
- if (existsSync31(candidate)) {
20692
+ if (existsSync32(candidate)) {
20352
20693
  filePath = routersRelPath + "/" + filePath + ext;
20353
20694
  break;
20354
20695
  }
20355
20696
  const indexCandidate = join15(fullPath, "index.ts");
20356
- if (existsSync31(indexCandidate)) {
20697
+ if (existsSync32(indexCandidate)) {
20357
20698
  filePath = routersRelPath + "/" + filePath + "/index.ts";
20358
20699
  break;
20359
20700
  }
@@ -20372,9 +20713,9 @@ function parseRootRouter() {
20372
20713
  return mappings;
20373
20714
  }
20374
20715
  function extractProcedures(routerFilePath) {
20375
- const absPath = resolve26(getProjectRoot(), routerFilePath);
20376
- if (!existsSync31(absPath)) return [];
20377
- const source = readFileSync26(absPath, "utf-8");
20716
+ const absPath = resolve27(getProjectRoot(), routerFilePath);
20717
+ if (!existsSync32(absPath)) return [];
20718
+ const source = readFileSync27(absPath, "utf-8");
20378
20719
  const procedures = [];
20379
20720
  const seen = /* @__PURE__ */ new Set();
20380
20721
  const procRegex = /(\w+)\s*:\s*(protected|public)Procedure/g;
@@ -20397,13 +20738,13 @@ function findUICallSites(routerKey, procedureName) {
20397
20738
  const root = getProjectRoot();
20398
20739
  const src = config.paths.source;
20399
20740
  const searchDirs = [
20400
- resolve26(root, config.paths.pages ?? src + "/app"),
20401
- resolve26(root, config.paths.components ?? src + "/components"),
20402
- resolve26(root, config.paths.hooks ?? src + "/hooks")
20741
+ resolve27(root, config.paths.pages ?? src + "/app"),
20742
+ resolve27(root, config.paths.components ?? src + "/components"),
20743
+ resolve27(root, config.paths.hooks ?? src + "/hooks")
20403
20744
  ];
20404
20745
  const searchPattern = `api.${routerKey}.${procedureName}`;
20405
20746
  for (const dir of searchDirs) {
20406
- if (!existsSync31(dir)) continue;
20747
+ if (!existsSync32(dir)) continue;
20407
20748
  searchDirectory(dir, searchPattern, callSites);
20408
20749
  }
20409
20750
  return callSites;
@@ -20417,7 +20758,7 @@ function searchDirectory(dir, pattern, results) {
20417
20758
  searchDirectory(fullPath, pattern, results);
20418
20759
  } else if (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx")) {
20419
20760
  try {
20420
- const source = readFileSync26(fullPath, "utf-8");
20761
+ const source = readFileSync27(fullPath, "utf-8");
20421
20762
  const lines = source.split("\n");
20422
20763
  for (let i = 0; i < lines.length; i++) {
20423
20764
  if (lines[i].includes(pattern)) {
@@ -20481,8 +20822,8 @@ var init_trpc_index = __esm({
20481
20822
  });
20482
20823
 
20483
20824
  // src/page-deps.ts
20484
- import { readFileSync as readFileSync27, existsSync as existsSync32 } from "fs";
20485
- import { resolve as resolve27 } from "path";
20825
+ import { readFileSync as readFileSync28, existsSync as existsSync33 } from "fs";
20826
+ import { resolve as resolve28 } from "path";
20486
20827
  function deriveRoute(pageFile) {
20487
20828
  let route = pageFile.replace(/^src\/app/, "").replace(/\/page\.tsx?$/, "").replace(/\/page\.jsx?$/, "");
20488
20829
  return route || "/";
@@ -20520,10 +20861,10 @@ function findRouterCalls(files) {
20520
20861
  const routers = /* @__PURE__ */ new Set();
20521
20862
  const projectRoot = getProjectRoot();
20522
20863
  for (const file of files) {
20523
- const absPath = ensureWithinRoot(resolve27(projectRoot, file), projectRoot);
20524
- if (!existsSync32(absPath)) continue;
20864
+ const absPath = ensureWithinRoot(resolve28(projectRoot, file), projectRoot);
20865
+ if (!existsSync33(absPath)) continue;
20525
20866
  try {
20526
- const source = readFileSync27(absPath, "utf-8");
20867
+ const source = readFileSync28(absPath, "utf-8");
20527
20868
  const apiCallRegex = /api\.(\w+)\.\w+/g;
20528
20869
  let match;
20529
20870
  while ((match = apiCallRegex.exec(source)) !== null) {
@@ -20541,10 +20882,10 @@ function findTablesFromRouters(routerNames, dataDb) {
20541
20882
  `SELECT DISTINCT router_file FROM ${t("trpc_procedures")} WHERE router_name = ? LIMIT 10000`
20542
20883
  ).all(routerName);
20543
20884
  for (const proc of procs) {
20544
- const absPath = ensureWithinRoot(resolve27(getProjectRoot(), proc.router_file), getProjectRoot());
20545
- if (!existsSync32(absPath)) continue;
20885
+ const absPath = ensureWithinRoot(resolve28(getProjectRoot(), proc.router_file), getProjectRoot());
20886
+ if (!existsSync33(absPath)) continue;
20546
20887
  try {
20547
- const source = readFileSync27(absPath, "utf-8");
20888
+ const source = readFileSync28(absPath, "utf-8");
20548
20889
  const dbPattern = getConfig().dbAccessPattern ?? "ctx.db.{table}";
20549
20890
  const regexStr = dbPattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace("\\{table\\}", "(\\w+)");
20550
20891
  const tableRegex = new RegExp(regexStr + "\\.", "g");
@@ -20816,14 +21157,14 @@ var init_domains = __esm({
20816
21157
  });
20817
21158
 
20818
21159
  // src/schema-mapper.ts
20819
- import { readFileSync as readFileSync28, existsSync as existsSync33, readdirSync as readdirSync17 } from "fs";
21160
+ import { readFileSync as readFileSync29, existsSync as existsSync34, readdirSync as readdirSync17 } from "fs";
20820
21161
  import { join as join16 } from "path";
20821
21162
  function parsePrismaSchema() {
20822
21163
  const schemaPath = getResolvedPaths().prismaSchemaPath;
20823
- if (!existsSync33(schemaPath)) {
21164
+ if (!existsSync34(schemaPath)) {
20824
21165
  throw new Error(`Prisma schema not found at ${schemaPath}`);
20825
21166
  }
20826
- const source = readFileSync28(schemaPath, "utf-8");
21167
+ const source = readFileSync29(schemaPath, "utf-8");
20827
21168
  const models = [];
20828
21169
  const sourceLines = source.split("\n");
20829
21170
  let i = 0;
@@ -20881,7 +21222,7 @@ function toSnakeCase(str) {
20881
21222
  function findColumnUsageInRouters(tableName) {
20882
21223
  const usage = /* @__PURE__ */ new Map();
20883
21224
  const routersDir = getResolvedPaths().routersDir;
20884
- if (!existsSync33(routersDir)) return usage;
21225
+ if (!existsSync34(routersDir)) return usage;
20885
21226
  scanDirectory(routersDir, tableName, usage);
20886
21227
  return usage;
20887
21228
  }
@@ -20898,7 +21239,7 @@ function scanDirectory(dir, tableName, usage) {
20898
21239
  }
20899
21240
  function scanFile(absPath, tableName, usage) {
20900
21241
  try {
20901
- const source = readFileSync28(absPath, "utf-8");
21242
+ const source = readFileSync29(absPath, "utf-8");
20902
21243
  if (!source.includes(tableName)) return;
20903
21244
  const relPath = absPath.slice(getProjectRoot().length + 1);
20904
21245
  const lines = source.split("\n");
@@ -20943,7 +21284,7 @@ function detectMismatches(models) {
20943
21284
  }
20944
21285
  function findFilesUsingColumn(dir, column, tableName) {
20945
21286
  const result = [];
20946
- if (!existsSync33(dir)) return result;
21287
+ if (!existsSync34(dir)) return result;
20947
21288
  const entries = readdirSync17(dir, { withFileTypes: true });
20948
21289
  for (const entry of entries) {
20949
21290
  const fullPath = join16(dir, entry.name);
@@ -20951,7 +21292,7 @@ function findFilesUsingColumn(dir, column, tableName) {
20951
21292
  result.push(...findFilesUsingColumn(fullPath, column, tableName));
20952
21293
  } else if (entry.name.endsWith(".ts")) {
20953
21294
  try {
20954
- const source = readFileSync28(fullPath, "utf-8");
21295
+ const source = readFileSync29(fullPath, "utf-8");
20955
21296
  if (source.includes(tableName) && source.includes(column)) {
20956
21297
  result.push(fullPath.slice(getProjectRoot().length + 1));
20957
21298
  }
@@ -21096,12 +21437,12 @@ var init_import_parser = __esm({
21096
21437
  });
21097
21438
 
21098
21439
  // src/python/import-resolver.ts
21099
- import { readFileSync as readFileSync29, existsSync as existsSync34, readdirSync as readdirSync18 } from "fs";
21100
- import { resolve as resolve29, join as join17, relative as relative6, dirname as dirname17 } from "path";
21440
+ import { readFileSync as readFileSync30, existsSync as existsSync35, readdirSync as readdirSync18 } from "fs";
21441
+ import { resolve as resolve30, join as join17, relative as relative6, dirname as dirname17 } from "path";
21101
21442
  function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
21102
21443
  const projectRoot = getProjectRoot();
21103
21444
  if (level > 0) {
21104
- let baseDir = dirname17(resolve29(projectRoot, fromFile));
21445
+ let baseDir = dirname17(resolve30(projectRoot, fromFile));
21105
21446
  for (let i = 1; i < level; i++) {
21106
21447
  baseDir = dirname17(baseDir);
21107
21448
  }
@@ -21113,17 +21454,17 @@ function resolvePythonModulePath(module, fromFile, pythonRoot, level) {
21113
21454
  return tryResolvePythonPath(baseDir, projectRoot);
21114
21455
  }
21115
21456
  const parts = module.split(".");
21116
- const candidate = join17(resolve29(projectRoot, pythonRoot), ...parts);
21457
+ const candidate = join17(resolve30(projectRoot, pythonRoot), ...parts);
21117
21458
  return tryResolvePythonPath(candidate, projectRoot);
21118
21459
  }
21119
21460
  function tryResolvePythonPath(basePath, projectRoot) {
21120
- if (existsSync34(basePath + ".py")) {
21461
+ if (existsSync35(basePath + ".py")) {
21121
21462
  return relative6(projectRoot, basePath + ".py");
21122
21463
  }
21123
- if (existsSync34(join17(basePath, "__init__.py"))) {
21464
+ if (existsSync35(join17(basePath, "__init__.py"))) {
21124
21465
  return relative6(projectRoot, join17(basePath, "__init__.py"));
21125
21466
  }
21126
- if (basePath.endsWith(".py") && existsSync34(basePath)) {
21467
+ if (basePath.endsWith(".py") && existsSync35(basePath)) {
21127
21468
  return relative6(projectRoot, basePath);
21128
21469
  }
21129
21470
  return null;
@@ -21146,7 +21487,7 @@ function walkPythonFiles(dir, excludeDirs) {
21146
21487
  }
21147
21488
  function buildPythonImportIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"]) {
21148
21489
  const projectRoot = getProjectRoot();
21149
- const absRoot = resolve29(projectRoot, pythonRoot);
21490
+ const absRoot = resolve30(projectRoot, pythonRoot);
21150
21491
  dataDb.exec(`DELETE FROM ${t("py_imports")}`);
21151
21492
  const insertStmt = dataDb.prepare(
21152
21493
  `INSERT INTO ${t("py_imports")} (source_file, target_file, import_type, imported_names, line) VALUES (?, ?, ?, ?, ?)`
@@ -21163,7 +21504,7 @@ function buildPythonImportIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__"
21163
21504
  const relFile = relative6(projectRoot, absFile);
21164
21505
  let source;
21165
21506
  try {
21166
- source = readFileSync29(absFile, "utf-8");
21507
+ source = readFileSync30(absFile, "utf-8");
21167
21508
  } catch {
21168
21509
  continue;
21169
21510
  }
@@ -21430,7 +21771,7 @@ var init_route_parser = __esm({
21430
21771
  });
21431
21772
 
21432
21773
  // src/python/route-indexer.ts
21433
- import { readFileSync as readFileSync30, readdirSync as readdirSync19 } from "fs";
21774
+ import { readFileSync as readFileSync31, readdirSync as readdirSync19 } from "fs";
21434
21775
  import { join as join18, relative as relative7 } from "path";
21435
21776
  function walkPyFiles(dir, excludeDirs) {
21436
21777
  const files = [];
@@ -21463,7 +21804,7 @@ function buildPythonRouteIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__",
21463
21804
  const relFile = relative7(projectRoot, absFile);
21464
21805
  let source;
21465
21806
  try {
21466
- source = readFileSync30(absFile, "utf-8");
21807
+ source = readFileSync31(absFile, "utf-8");
21467
21808
  } catch {
21468
21809
  continue;
21469
21810
  }
@@ -21675,7 +22016,7 @@ var init_model_parser = __esm({
21675
22016
  });
21676
22017
 
21677
22018
  // src/python/model-indexer.ts
21678
- import { readFileSync as readFileSync31, readdirSync as readdirSync20 } from "fs";
22019
+ import { readFileSync as readFileSync32, readdirSync as readdirSync20 } from "fs";
21679
22020
  import { join as join19, relative as relative8 } from "path";
21680
22021
  function walkPyFiles2(dir, excludeDirs) {
21681
22022
  const files = [];
@@ -21711,7 +22052,7 @@ function buildPythonModelIndex(dataDb, pythonRoot, excludeDirs = ["__pycache__",
21711
22052
  const relFile = relative8(projectRoot, absFile);
21712
22053
  let source;
21713
22054
  try {
21714
- source = readFileSync31(absFile, "utf-8");
22055
+ source = readFileSync32(absFile, "utf-8");
21715
22056
  } catch {
21716
22057
  continue;
21717
22058
  }
@@ -21972,7 +22313,7 @@ var init_migration_parser = __esm({
21972
22313
  });
21973
22314
 
21974
22315
  // src/python/migration-indexer.ts
21975
- import { readFileSync as readFileSync32, readdirSync as readdirSync21 } from "fs";
22316
+ import { readFileSync as readFileSync33, readdirSync as readdirSync21 } from "fs";
21976
22317
  import { join as join20, relative as relative9 } from "path";
21977
22318
  function buildPythonMigrationIndex(dataDb, alembicDir) {
21978
22319
  const projectRoot = getProjectRoot();
@@ -21998,7 +22339,7 @@ function buildPythonMigrationIndex(dataDb, alembicDir) {
21998
22339
  for (const absFile of files) {
21999
22340
  let source;
22000
22341
  try {
22001
- source = readFileSync32(absFile, "utf-8");
22342
+ source = readFileSync33(absFile, "utf-8");
22002
22343
  } catch {
22003
22344
  continue;
22004
22345
  }
@@ -22033,7 +22374,7 @@ var init_migration_indexer = __esm({
22033
22374
  });
22034
22375
 
22035
22376
  // src/python/coupling-detector.ts
22036
- import { readFileSync as readFileSync33, readdirSync as readdirSync22 } from "fs";
22377
+ import { readFileSync as readFileSync34, readdirSync as readdirSync22 } from "fs";
22037
22378
  import { join as join21, relative as relative10 } from "path";
22038
22379
  function buildPythonCouplingIndex(dataDb) {
22039
22380
  const projectRoot = getProjectRoot();
@@ -22070,7 +22411,7 @@ function buildPythonCouplingIndex(dataDb) {
22070
22411
  const relFile = relative10(projectRoot, absFile);
22071
22412
  let source;
22072
22413
  try {
22073
- source = readFileSync33(absFile, "utf-8");
22414
+ source = readFileSync34(absFile, "utf-8");
22074
22415
  } catch {
22075
22416
  continue;
22076
22417
  }
@@ -22476,8 +22817,8 @@ var init_memory_tools = __esm({
22476
22817
  });
22477
22818
 
22478
22819
  // src/docs-tools.ts
22479
- import { readFileSync as readFileSync34, existsSync as existsSync35 } from "fs";
22480
- import { resolve as resolve30, basename as basename6 } from "path";
22820
+ import { readFileSync as readFileSync35, existsSync as existsSync36 } from "fs";
22821
+ import { resolve as resolve31, basename as basename6 } from "path";
22481
22822
  function p3(baseName) {
22482
22823
  return `${getConfig().toolPrefix}_${baseName}`;
22483
22824
  }
@@ -22532,10 +22873,10 @@ function handleDocsToolCall(name, args2) {
22532
22873
  }
22533
22874
  function loadDocsMap() {
22534
22875
  const mapPath = getResolvedPaths().docsMapPath;
22535
- if (!existsSync35(mapPath)) {
22876
+ if (!existsSync36(mapPath)) {
22536
22877
  throw new Error(`docs-map.json not found at ${mapPath}`);
22537
22878
  }
22538
- return JSON.parse(readFileSync34(mapPath, "utf-8"));
22879
+ return JSON.parse(readFileSync35(mapPath, "utf-8"));
22539
22880
  }
22540
22881
  function matchesPattern(filePath, pattern) {
22541
22882
  const regexStr = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\{\{GLOBSTAR\}\}/g, ".*");
@@ -22605,13 +22946,13 @@ function extractFrontmatter(content) {
22605
22946
  }
22606
22947
  function extractProcedureNames(routerPath) {
22607
22948
  const root = getProjectRoot();
22608
- const absPath = ensureWithinRoot(resolve30(getResolvedPaths().srcDir, "..", routerPath), root);
22609
- if (!existsSync35(absPath)) {
22610
- const altPath = ensureWithinRoot(resolve30(getResolvedPaths().srcDir, "../server/api/routers", basename6(routerPath)), root);
22611
- if (!existsSync35(altPath)) return [];
22612
- return extractProcedureNamesFromContent(readFileSync34(altPath, "utf-8"));
22949
+ const absPath = ensureWithinRoot(resolve31(getResolvedPaths().srcDir, "..", routerPath), root);
22950
+ if (!existsSync36(absPath)) {
22951
+ const altPath = ensureWithinRoot(resolve31(getResolvedPaths().srcDir, "../server/api/routers", basename6(routerPath)), root);
22952
+ if (!existsSync36(altPath)) return [];
22953
+ return extractProcedureNamesFromContent(readFileSync35(altPath, "utf-8"));
22613
22954
  }
22614
- return extractProcedureNamesFromContent(readFileSync34(absPath, "utf-8"));
22955
+ return extractProcedureNamesFromContent(readFileSync35(absPath, "utf-8"));
22615
22956
  }
22616
22957
  function extractProcedureNamesFromContent(content) {
22617
22958
  const procRegex = /\.(?:query|mutation)\s*\(/g;
@@ -22651,8 +22992,8 @@ function handleDocsAudit(args2) {
22651
22992
  for (const [mappingId, triggeringFiles] of affectedMappings) {
22652
22993
  const mapping = docsMap.mappings.find((m3) => m3.id === mappingId);
22653
22994
  if (!mapping) continue;
22654
- const helpPagePath = ensureWithinRoot(resolve30(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
22655
- if (!existsSync35(helpPagePath)) {
22995
+ const helpPagePath = ensureWithinRoot(resolve31(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
22996
+ if (!existsSync36(helpPagePath)) {
22656
22997
  results.push({
22657
22998
  helpPage: mapping.helpPage,
22658
22999
  mappingId,
@@ -22664,7 +23005,7 @@ function handleDocsAudit(args2) {
22664
23005
  });
22665
23006
  continue;
22666
23007
  }
22667
- const content = readFileSync34(helpPagePath, "utf-8");
23008
+ const content = readFileSync35(helpPagePath, "utf-8");
22668
23009
  const sections = extractSections(content);
22669
23010
  const frontmatter = extractFrontmatter(content);
22670
23011
  const staleReasons = [];
@@ -22704,9 +23045,9 @@ function handleDocsAudit(args2) {
22704
23045
  });
22705
23046
  for (const [guideName, parentId] of Object.entries(docsMap.userGuideInheritance.examples)) {
22706
23047
  if (parentId === mappingId) {
22707
- const guidePath = ensureWithinRoot(resolve30(getResolvedPaths().helpSitePath, `pages/user-guides/${guideName}/index.mdx`), getProjectRoot());
22708
- if (existsSync35(guidePath)) {
22709
- const guideContent = readFileSync34(guidePath, "utf-8");
23048
+ const guidePath = ensureWithinRoot(resolve31(getResolvedPaths().helpSitePath, `pages/user-guides/${guideName}/index.mdx`), getProjectRoot());
23049
+ if (existsSync36(guidePath)) {
23050
+ const guideContent = readFileSync35(guidePath, "utf-8");
22710
23051
  const guideFrontmatter = extractFrontmatter(guideContent);
22711
23052
  if (!guideFrontmatter?.lastVerified || status === "STALE") {
22712
23053
  results.push({
@@ -22739,14 +23080,14 @@ function handleDocsCoverage(args2) {
22739
23080
  const gaps = [];
22740
23081
  const mappings = filterDomain ? docsMap.mappings.filter((m3) => m3.id === filterDomain) : docsMap.mappings;
22741
23082
  for (const mapping of mappings) {
22742
- const helpPagePath = ensureWithinRoot(resolve30(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
22743
- const exists = existsSync35(helpPagePath);
23083
+ const helpPagePath = ensureWithinRoot(resolve31(getResolvedPaths().helpSitePath, mapping.helpPage), getProjectRoot());
23084
+ const exists = existsSync36(helpPagePath);
22744
23085
  let hasContent = false;
22745
23086
  let lineCount = 0;
22746
23087
  let lastVerified = null;
22747
23088
  let status = null;
22748
23089
  if (exists) {
22749
- const content = readFileSync34(helpPagePath, "utf-8");
23090
+ const content = readFileSync35(helpPagePath, "utf-8");
22750
23091
  lineCount = content.split("\n").length;
22751
23092
  hasContent = lineCount > 10;
22752
23093
  const frontmatter = extractFrontmatter(content);
@@ -23087,8 +23428,8 @@ var init_observability_tools = __esm({
23087
23428
  });
23088
23429
 
23089
23430
  // src/sentinel-db.ts
23090
- import { existsSync as existsSync36 } from "fs";
23091
- import { resolve as resolve31 } from "path";
23431
+ import { existsSync as existsSync37 } from "fs";
23432
+ import { resolve as resolve32 } from "path";
23092
23433
  function parsePortalScope(raw) {
23093
23434
  if (!raw) return [];
23094
23435
  try {
@@ -23324,23 +23665,23 @@ function validateFeatures(db, domainFilter) {
23324
23665
  const missingProcedures = [];
23325
23666
  const missingPages = [];
23326
23667
  for (const comp of components) {
23327
- const absPath = resolve31(PROJECT_ROOT, comp.component_file);
23328
- if (!existsSync36(absPath)) {
23668
+ const absPath = resolve32(PROJECT_ROOT, comp.component_file);
23669
+ if (!existsSync37(absPath)) {
23329
23670
  missingComponents.push(comp.component_file);
23330
23671
  }
23331
23672
  }
23332
23673
  for (const proc of procedures) {
23333
- const routerPath = resolve31(PROJECT_ROOT, `src/server/api/routers/${proc.router_name}.ts`);
23334
- if (!existsSync36(routerPath)) {
23674
+ const routerPath = resolve32(PROJECT_ROOT, `src/server/api/routers/${proc.router_name}.ts`);
23675
+ if (!existsSync37(routerPath)) {
23335
23676
  missingProcedures.push({ router: proc.router_name, procedure: proc.procedure_name });
23336
23677
  }
23337
23678
  }
23338
23679
  for (const page of pages) {
23339
23680
  const routeToPath = page.page_route.replace(/^\/(portal-[^/]+\/)?/, "src/app/").replace(/\/$/, "") + "/page.tsx";
23340
- const absPath = resolve31(PROJECT_ROOT, routeToPath);
23341
- if (page.page_route.startsWith("/") && !existsSync36(absPath)) {
23342
- const altPath = resolve31(PROJECT_ROOT, `src/app${page.page_route}/page.tsx`);
23343
- if (!existsSync36(altPath)) {
23681
+ const absPath = resolve32(PROJECT_ROOT, routeToPath);
23682
+ if (page.page_route.startsWith("/") && !existsSync37(absPath)) {
23683
+ const altPath = resolve32(PROJECT_ROOT, `src/app${page.page_route}/page.tsx`);
23684
+ if (!existsSync37(altPath)) {
23344
23685
  missingPages.push(page.page_route);
23345
23686
  }
23346
23687
  }
@@ -23866,8 +24207,8 @@ var init_sentinel_tools = __esm({
23866
24207
  });
23867
24208
 
23868
24209
  // src/sentinel-scanner.ts
23869
- import { readFileSync as readFileSync35, existsSync as existsSync37, readdirSync as readdirSync23, statSync as statSync13 } from "fs";
23870
- import { resolve as resolve32, join as join22, basename as basename7, dirname as dirname18, relative as relative11 } from "path";
24210
+ import { readFileSync as readFileSync36, existsSync as existsSync38, readdirSync as readdirSync23, statSync as statSync13 } from "fs";
24211
+ import { resolve as resolve33, join as join22, basename as basename7, dirname as dirname18, relative as relative11 } from "path";
23871
24212
  function inferDomain(filePath) {
23872
24213
  const domains = getConfig().domains;
23873
24214
  const path = filePath.toLowerCase();
@@ -23998,8 +24339,8 @@ function scanComponentExports(dataDb) {
23998
24339
  const projectRoot = getProjectRoot();
23999
24340
  const componentsBase = config.paths.components ?? config.paths.source + "/components";
24000
24341
  const componentDirs = [];
24001
- const basePath = resolve32(projectRoot, componentsBase);
24002
- if (existsSync37(basePath)) {
24342
+ const basePath = resolve33(projectRoot, componentsBase);
24343
+ if (existsSync38(basePath)) {
24003
24344
  try {
24004
24345
  const entries = readdirSync23(basePath, { withFileTypes: true });
24005
24346
  for (const entry of entries) {
@@ -24011,12 +24352,12 @@ function scanComponentExports(dataDb) {
24011
24352
  }
24012
24353
  }
24013
24354
  for (const dir of componentDirs) {
24014
- const absDir = resolve32(projectRoot, dir);
24015
- if (!existsSync37(absDir)) continue;
24355
+ const absDir = resolve33(projectRoot, dir);
24356
+ if (!existsSync38(absDir)) continue;
24016
24357
  const files = walkDir2(absDir).filter((f2) => f2.endsWith(".tsx") || f2.endsWith(".ts"));
24017
24358
  for (const file of files) {
24018
24359
  const relPath = relative11(projectRoot, file);
24019
- const source = readFileSync35(file, "utf-8");
24360
+ const source = readFileSync36(file, "utf-8");
24020
24361
  const annotations = parseFeatureAnnotations(source);
24021
24362
  if (annotations.length > 0) {
24022
24363
  for (const ann of annotations) {
@@ -25059,7 +25400,7 @@ var init_audit_trail = __esm({
25059
25400
  });
25060
25401
 
25061
25402
  // src/validation-engine.ts
25062
- import { existsSync as existsSync38, readFileSync as readFileSync36 } from "fs";
25403
+ import { existsSync as existsSync39, readFileSync as readFileSync37 } from "fs";
25063
25404
  function p10(baseName) {
25064
25405
  return `${getConfig().toolPrefix}_${baseName}`;
25065
25406
  }
@@ -25090,7 +25431,7 @@ function validateFile(filePath, projectRoot) {
25090
25431
  });
25091
25432
  return checks;
25092
25433
  }
25093
- if (!existsSync38(absPath)) {
25434
+ if (!existsSync39(absPath)) {
25094
25435
  checks.push({
25095
25436
  name: "file_exists",
25096
25437
  severity: "error",
@@ -25099,7 +25440,7 @@ function validateFile(filePath, projectRoot) {
25099
25440
  });
25100
25441
  return checks;
25101
25442
  }
25102
- const source = readFileSync36(absPath, "utf-8");
25443
+ const source = readFileSync37(absPath, "utf-8");
25103
25444
  const lines = source.split("\n");
25104
25445
  if (activeChecks.rule_compliance !== false) {
25105
25446
  for (const ruleSet of config.rules) {
@@ -25545,7 +25886,7 @@ var init_adr_generator = __esm({
25545
25886
  });
25546
25887
 
25547
25888
  // src/security-scorer.ts
25548
- import { existsSync as existsSync39, readFileSync as readFileSync37 } from "fs";
25889
+ import { existsSync as existsSync40, readFileSync as readFileSync38 } from "fs";
25549
25890
  function p12(baseName) {
25550
25891
  return `${getConfig().toolPrefix}_${baseName}`;
25551
25892
  }
@@ -25569,12 +25910,12 @@ function scoreFileSecurity(filePath, projectRoot) {
25569
25910
  }]
25570
25911
  };
25571
25912
  }
25572
- if (!existsSync39(absPath)) {
25913
+ if (!existsSync40(absPath)) {
25573
25914
  return { riskScore: 0, findings: [] };
25574
25915
  }
25575
25916
  let source;
25576
25917
  try {
25577
- source = readFileSync37(absPath, "utf-8");
25918
+ source = readFileSync38(absPath, "utf-8");
25578
25919
  } catch {
25579
25920
  return { riskScore: 0, findings: [] };
25580
25921
  }
@@ -25864,8 +26205,8 @@ var init_security_scorer = __esm({
25864
26205
  });
25865
26206
 
25866
26207
  // src/dependency-scorer.ts
25867
- import { existsSync as existsSync40, readFileSync as readFileSync38 } from "fs";
25868
- import { resolve as resolve33 } from "path";
26208
+ import { existsSync as existsSync41, readFileSync as readFileSync39 } from "fs";
26209
+ import { resolve as resolve34 } from "path";
25869
26210
  function p13(baseName) {
25870
26211
  return `${getConfig().toolPrefix}_${baseName}`;
25871
26212
  }
@@ -25897,10 +26238,10 @@ function calculateDepRisk(factors) {
25897
26238
  return Math.min(100, risk);
25898
26239
  }
25899
26240
  function getInstalledPackages(projectRoot) {
25900
- const pkgPath = resolve33(projectRoot, "package.json");
25901
- if (!existsSync40(pkgPath)) return /* @__PURE__ */ new Map();
26241
+ const pkgPath = resolve34(projectRoot, "package.json");
26242
+ if (!existsSync41(pkgPath)) return /* @__PURE__ */ new Map();
25902
26243
  try {
25903
- const pkg = JSON.parse(readFileSync38(pkgPath, "utf-8"));
26244
+ const pkg = JSON.parse(readFileSync39(pkgPath, "utf-8"));
25904
26245
  const packages = /* @__PURE__ */ new Map();
25905
26246
  for (const [name, version] of Object.entries(pkg.dependencies ?? {})) {
25906
26247
  packages.set(name, version);
@@ -26506,8 +26847,8 @@ var init_regression_detector = __esm({
26506
26847
 
26507
26848
  // src/knowledge-indexer.ts
26508
26849
  import { createHash as createHash9 } from "crypto";
26509
- import { readFileSync as readFileSync39, readdirSync as readdirSync24, statSync as statSync14, existsSync as existsSync41 } from "fs";
26510
- import { resolve as resolve34, relative as relative12, basename as basename8, extname as extname2 } from "path";
26850
+ import { readFileSync as readFileSync40, readdirSync as readdirSync24, statSync as statSync14, existsSync as existsSync42 } from "fs";
26851
+ import { resolve as resolve35, relative as relative12, basename as basename8, extname as extname2 } from "path";
26511
26852
  function getKnowledgePaths() {
26512
26853
  const resolved = getResolvedPaths();
26513
26854
  const config = getConfig();
@@ -26535,7 +26876,7 @@ function discoverMarkdownFiles(baseDir) {
26535
26876
  try {
26536
26877
  const entries = readdirSync24(dir, { withFileTypes: true });
26537
26878
  for (const entry of entries) {
26538
- const fullPath = resolve34(dir, entry.name);
26879
+ const fullPath = resolve35(dir, entry.name);
26539
26880
  if (entry.isDirectory()) {
26540
26881
  if (entry.name === "archive" && dir.includes("session-state")) continue;
26541
26882
  if (entry.name === "archive" && dir.includes("status")) continue;
@@ -26814,11 +27155,11 @@ function indexAllKnowledge(db) {
26814
27155
  files.push(...memFiles);
26815
27156
  } catch {
26816
27157
  }
26817
- if (existsSync41(paths.plansDir)) {
27158
+ if (existsSync42(paths.plansDir)) {
26818
27159
  const planFiles = discoverMarkdownFiles(paths.plansDir);
26819
27160
  files.push(...planFiles);
26820
27161
  }
26821
- if (existsSync41(paths.docsDir)) {
27162
+ if (existsSync42(paths.docsDir)) {
26822
27163
  const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
26823
27164
  const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f2) => !f2.includes("/plans/") && !excludePatterns.some((p19) => f2.includes(p19)));
26824
27165
  files.push(...docsFiles);
@@ -26861,8 +27202,8 @@ function indexAllKnowledge(db) {
26861
27202
  } catch {
26862
27203
  }
26863
27204
  for (const filePath of files) {
26864
- if (!existsSync41(filePath)) continue;
26865
- const content = readFileSync39(filePath, "utf-8");
27205
+ if (!existsSync42(filePath)) continue;
27206
+ const content = readFileSync40(filePath, "utf-8");
26866
27207
  const hash = hashContent2(content);
26867
27208
  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);
26868
27209
  const category = categorizeFile(filePath);
@@ -26997,10 +27338,10 @@ function isKnowledgeStale(db) {
26997
27338
  files.push(...discoverMarkdownFiles(paths.memoryDir));
26998
27339
  } catch {
26999
27340
  }
27000
- if (existsSync41(paths.plansDir)) {
27341
+ if (existsSync42(paths.plansDir)) {
27001
27342
  files.push(...discoverMarkdownFiles(paths.plansDir));
27002
27343
  }
27003
- if (existsSync41(paths.docsDir)) {
27344
+ if (existsSync42(paths.docsDir)) {
27004
27345
  const excludePatterns = getConfig().conventions?.excludePatterns ?? ["/ARCHIVE/", "/SESSION-HISTORY/"];
27005
27346
  const docsFiles = discoverMarkdownFiles(paths.docsDir).filter((f2) => !f2.includes("/plans/") && !excludePatterns.some((p19) => f2.includes(p19)));
27006
27347
  files.push(...docsFiles);
@@ -27029,8 +27370,8 @@ var init_knowledge_indexer = __esm({
27029
27370
  });
27030
27371
 
27031
27372
  // src/knowledge-tools.ts
27032
- import { readFileSync as readFileSync40, writeFileSync as writeFileSync6, appendFileSync as appendFileSync2, readdirSync as readdirSync25 } from "fs";
27033
- import { resolve as resolve35, basename as basename9 } from "path";
27373
+ import { readFileSync as readFileSync41, writeFileSync as writeFileSync7, appendFileSync as appendFileSync2, readdirSync as readdirSync25 } from "fs";
27374
+ import { resolve as resolve36, basename as basename9 } from "path";
27034
27375
  function p16(baseName) {
27035
27376
  return `${getConfig().toolPrefix}_${baseName}`;
27036
27377
  }
@@ -27769,7 +28110,7 @@ function handleCorrect(db, args2) {
27769
28110
  if (!wrong || !correction || !rule) {
27770
28111
  return text15("Error: wrong, correction, and rule are all required.");
27771
28112
  }
27772
- const correctionsPath = resolve35(getResolvedPaths().memoryDir, "corrections.md");
28113
+ const correctionsPath = resolve36(getResolvedPaths().memoryDir, "corrections.md");
27773
28114
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
27774
28115
  const title = rule.slice(0, 60);
27775
28116
  const entry = `
@@ -27782,14 +28123,14 @@ ${crRule ? `- **CR**: ${crRule}
27782
28123
  `;
27783
28124
  let existing = "";
27784
28125
  try {
27785
- existing = readFileSync40(correctionsPath, "utf-8");
28126
+ existing = readFileSync41(correctionsPath, "utf-8");
27786
28127
  } catch {
27787
28128
  }
27788
28129
  const archiveIdx = existing.indexOf("## Archived");
27789
28130
  if (archiveIdx > 0) {
27790
28131
  const before = existing.slice(0, archiveIdx);
27791
28132
  const after = existing.slice(archiveIdx);
27792
- writeFileSync6(correctionsPath, before + entry + after);
28133
+ writeFileSync7(correctionsPath, before + entry + after);
27793
28134
  } else {
27794
28135
  appendFileSync2(correctionsPath, entry);
27795
28136
  }
@@ -28737,8 +29078,8 @@ var init_framework_supports = __esm({
28737
29078
  });
28738
29079
 
28739
29080
  // src/tools.ts
28740
- import { readFileSync as readFileSync41, existsSync as existsSync43 } from "fs";
28741
- import { resolve as resolve36, basename as basename10 } from "path";
29081
+ import { readFileSync as readFileSync42, existsSync as existsSync44 } from "fs";
29082
+ import { resolve as resolve37, basename as basename10 } from "path";
28742
29083
  function prefix2() {
28743
29084
  return getConfig().toolPrefix;
28744
29085
  }
@@ -28773,7 +29114,7 @@ function ensureIndexes(dataDb, codegraphDb, force = false) {
28773
29114
  if (config.python?.root) {
28774
29115
  const pythonRoot = config.python.root;
28775
29116
  const excludeDirs = config.python.exclude_dirs || ["__pycache__", ".venv", "venv", ".mypy_cache", ".pytest_cache"];
28776
- if (force || isPythonDataStale(dataDb, resolve36(getProjectRoot(), pythonRoot))) {
29117
+ if (force || isPythonDataStale(dataDb, resolve37(getProjectRoot(), pythonRoot))) {
28777
29118
  const pyImports = buildPythonImportIndex(dataDb, pythonRoot, excludeDirs);
28778
29119
  results.push(`Python imports: ${pyImports}`);
28779
29120
  const pyRoutes = buildPythonRouteIndex(dataDb, pythonRoot, excludeDirs);
@@ -29156,9 +29497,9 @@ function handleContext(file, dataDb, codegraphDb) {
29156
29497
  try {
29157
29498
  const resolvedPaths = getResolvedPaths();
29158
29499
  const root = getProjectRoot();
29159
- const absFilePath = ensureWithinRoot(resolve36(resolvedPaths.srcDir, "..", file), root);
29160
- if (existsSync43(absFilePath)) {
29161
- const fileContent = readFileSync41(absFilePath, "utf-8").slice(0, 3e3);
29500
+ const absFilePath = ensureWithinRoot(resolve37(resolvedPaths.srcDir, "..", file), root);
29501
+ if (existsSync44(absFilePath)) {
29502
+ const fileContent = readFileSync42(absFilePath, "utf-8").slice(0, 3e3);
29162
29503
  const keywords = [];
29163
29504
  if (fileContent.includes("ctx.db")) keywords.push("database", "schema");
29164
29505
  if (fileContent.includes("BigInt") || fileContent.includes("Decimal")) keywords.push("BigInt", "serialization");
@@ -29595,11 +29936,11 @@ function handleSchema(args2) {
29595
29936
  lines.push("Checking all column references against Prisma schema...");
29596
29937
  lines.push("");
29597
29938
  const projectRoot = getProjectRoot();
29598
- const absPath = ensureWithinRoot(resolve36(projectRoot, file), projectRoot);
29599
- if (!existsSync43(absPath)) {
29939
+ const absPath = ensureWithinRoot(resolve37(projectRoot, file), projectRoot);
29940
+ if (!existsSync44(absPath)) {
29600
29941
  return text17(`File not found: ${file}`);
29601
29942
  }
29602
- const source = readFileSync41(absPath, "utf-8");
29943
+ const source = readFileSync42(absPath, "utf-8");
29603
29944
  const config = getConfig();
29604
29945
  const dbPattern = config.dbAccessPattern ?? "ctx.db.{table}";
29605
29946
  const regexStr = dbPattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace("\\{table\\}", "(\\w+)");
@@ -30004,8 +30345,8 @@ var init_server_dispatch = __esm({
30004
30345
 
30005
30346
  // src/server.ts
30006
30347
  var server_exports = {};
30007
- import { readFileSync as readFileSync42 } from "fs";
30008
- import { resolve as resolve37, dirname as dirname19 } from "path";
30348
+ import { readFileSync as readFileSync43 } from "fs";
30349
+ import { resolve as resolve38, dirname as dirname19 } from "path";
30009
30350
  import { fileURLToPath as fileURLToPath5 } from "url";
30010
30351
  function pruneMemoryOnStartup() {
30011
30352
  try {
@@ -30040,7 +30381,7 @@ var init_server = __esm({
30040
30381
  __dirname5 = dirname19(fileURLToPath5(import.meta.url));
30041
30382
  PKG_VERSION = (() => {
30042
30383
  try {
30043
- const pkg = JSON.parse(readFileSync42(resolve37(__dirname5, "..", "package.json"), "utf-8"));
30384
+ const pkg = JSON.parse(readFileSync43(resolve38(__dirname5, "..", "package.json"), "utf-8"));
30044
30385
  return pkg.version ?? "0.0.0";
30045
30386
  } catch {
30046
30387
  return "0.0.0";
@@ -30326,19 +30667,19 @@ var config_upgrade_exports = {};
30326
30667
  __export(config_upgrade_exports, {
30327
30668
  runConfigUpgrade: () => runConfigUpgrade
30328
30669
  });
30329
- import { existsSync as existsSync44, readFileSync as readFileSync43, writeFileSync as writeFileSync7, copyFileSync, unlinkSync as unlinkSync2 } from "fs";
30330
- import { resolve as resolve38 } from "path";
30670
+ import { existsSync as existsSync45, readFileSync as readFileSync44, writeFileSync as writeFileSync8, copyFileSync, unlinkSync as unlinkSync2 } from "fs";
30671
+ import { resolve as resolve39 } from "path";
30331
30672
  import { parse as parseYaml6 } from "yaml";
30332
30673
  async function runConfigUpgrade(opts = {}) {
30333
30674
  const cwd = opts.cwd ?? process.cwd();
30334
- const configPath = resolve38(cwd, "massu.config.yaml");
30675
+ const configPath = resolve39(cwd, "massu.config.yaml");
30335
30676
  const bakPath = `${configPath}.bak`;
30336
30677
  const log = opts.silent ? () => {
30337
30678
  } : (s) => process.stdout.write(s);
30338
30679
  const err = opts.silent ? () => {
30339
30680
  } : (s) => process.stderr.write(s);
30340
30681
  if (opts.rollback) {
30341
- if (!existsSync44(bakPath)) {
30682
+ if (!existsSync45(bakPath)) {
30342
30683
  const message = `No backup found at ${bakPath}`;
30343
30684
  err(message + "\n");
30344
30685
  return { exitCode: 1, action: "none", message };
@@ -30354,14 +30695,14 @@ async function runConfigUpgrade(opts = {}) {
30354
30695
  return { exitCode: 2, action: "none", message };
30355
30696
  }
30356
30697
  }
30357
- if (!existsSync44(configPath)) {
30698
+ if (!existsSync45(configPath)) {
30358
30699
  const message = "massu.config.yaml not found. Run: npx massu init";
30359
30700
  err(message + "\n");
30360
30701
  return { exitCode: 1, action: "none", message };
30361
30702
  }
30362
30703
  let existing;
30363
30704
  try {
30364
- const content = readFileSync43(configPath, "utf-8");
30705
+ const content = readFileSync44(configPath, "utf-8");
30365
30706
  const parsed = parseYaml6(content);
30366
30707
  if (!parsed || typeof parsed !== "object") {
30367
30708
  throw new Error("config is not a YAML object");
@@ -30384,8 +30725,8 @@ async function runConfigUpgrade(opts = {}) {
30384
30725
  fingerprint: computeFingerprint(detection)
30385
30726
  };
30386
30727
  try {
30387
- const original = readFileSync43(configPath, "utf-8");
30388
- writeFileSync7(bakPath, original, "utf-8");
30728
+ const original = readFileSync44(configPath, "utf-8");
30729
+ writeFileSync8(bakPath, original, "utf-8");
30389
30730
  } catch (e2) {
30390
30731
  const message = `Failed to write backup: ${e2 instanceof Error ? e2.message : String(e2)}`;
30391
30732
  err(message + "\n");
@@ -30418,8 +30759,8 @@ var config_check_drift_exports = {};
30418
30759
  __export(config_check_drift_exports, {
30419
30760
  runConfigCheckDrift: () => runConfigCheckDrift
30420
30761
  });
30421
- import { existsSync as existsSync45, readFileSync as readFileSync44 } from "fs";
30422
- import { resolve as resolve39 } from "path";
30762
+ import { existsSync as existsSync46, readFileSync as readFileSync45 } from "fs";
30763
+ import { resolve as resolve40 } from "path";
30423
30764
  import { parse as parseYaml7 } from "yaml";
30424
30765
  function renderChanges(changes) {
30425
30766
  if (changes.length === 0) return "(none)\n";
@@ -30427,12 +30768,12 @@ function renderChanges(changes) {
30427
30768
  }
30428
30769
  async function runConfigCheckDrift(opts = {}) {
30429
30770
  const cwd = opts.cwd ?? process.cwd();
30430
- const configPath = resolve39(cwd, "massu.config.yaml");
30771
+ const configPath = resolve40(cwd, "massu.config.yaml");
30431
30772
  const log = opts.silent ? () => {
30432
30773
  } : (s) => process.stdout.write(s);
30433
30774
  const err = opts.silent ? () => {
30434
30775
  } : (s) => process.stderr.write(s);
30435
- if (!existsSync45(configPath)) {
30776
+ if (!existsSync46(configPath)) {
30436
30777
  const message = "massu.config.yaml not found. Run: npx massu init";
30437
30778
  err(message + "\n");
30438
30779
  return {
@@ -30446,7 +30787,7 @@ async function runConfigCheckDrift(opts = {}) {
30446
30787
  }
30447
30788
  let config;
30448
30789
  try {
30449
- const content = readFileSync44(configPath, "utf-8");
30790
+ const content = readFileSync45(configPath, "utf-8");
30450
30791
  const parsed = parseYaml7(content);
30451
30792
  if (!parsed || typeof parsed !== "object") {
30452
30793
  throw new Error("config is not a YAML object");
@@ -30513,8 +30854,8 @@ var init_config_check_drift = __esm({
30513
30854
  });
30514
30855
 
30515
30856
  // src/cli.ts
30516
- import { readFileSync as readFileSync45 } from "fs";
30517
- import { resolve as resolve40, dirname as dirname20 } from "path";
30857
+ import { readFileSync as readFileSync46 } from "fs";
30858
+ import { resolve as resolve41, dirname as dirname20 } from "path";
30518
30859
  import { fileURLToPath as fileURLToPath6 } from "url";
30519
30860
  var __filename5 = fileURLToPath6(import.meta.url);
30520
30861
  var __dirname6 = dirname20(__filename5);
@@ -30532,6 +30873,16 @@ async function main() {
30532
30873
  await runDoctor2();
30533
30874
  break;
30534
30875
  }
30876
+ case "login": {
30877
+ const { runLogin: runLogin2 } = await Promise.resolve().then(() => (init_login(), login_exports));
30878
+ await runLogin2(args.slice(1));
30879
+ break;
30880
+ }
30881
+ case "logout": {
30882
+ const { runLogout: runLogout2 } = await Promise.resolve().then(() => (init_login(), login_exports));
30883
+ await runLogout2();
30884
+ break;
30885
+ }
30535
30886
  case "install-hooks": {
30536
30887
  const { runInstallHooks: runInstallHooks2 } = await Promise.resolve().then(() => (init_install_hooks(), install_hooks_exports));
30537
30888
  await runInstallHooks2();
@@ -30555,7 +30906,7 @@ async function main() {
30555
30906
  return;
30556
30907
  }
30557
30908
  case "license": {
30558
- const { handleLicenseSubcommand: handleLicenseSubcommand2 } = await Promise.resolve().then(() => (init_license2(), license_exports));
30909
+ const { handleLicenseSubcommand: handleLicenseSubcommand2 } = await Promise.resolve().then(() => (init_license2(), license_exports2));
30559
30910
  const result = await handleLicenseSubcommand2(args.slice(1));
30560
30911
  process.exit(result.exitCode);
30561
30912
  return;
@@ -30692,6 +31043,10 @@ Usage:
30692
31043
  Commands:
30693
31044
  init Set up Massu AI in your project (one command, full setup)
30694
31045
  doctor Check installation health
31046
+ login Store your API key user-level in ~/.massu/credentials (git-safe).
31047
+ Prefer the interactive prompt or MASSU_API_KEY env; --key <k>
31048
+ exposes the key to shell history. Use --no-verify to skip online check.
31049
+ logout Remove the stored user-level API key
30695
31050
  install-hooks Install/update Claude Code hooks
30696
31051
  install-commands Install/update slash commands (use --skip-permissions to opt out of MCP allowlist seeding)
30697
31052
  show-template Print the resolved variant of a bundled template (e.g. for diffs)
@@ -30742,7 +31097,7 @@ Examples:
30742
31097
  }
30743
31098
  function printVersion() {
30744
31099
  try {
30745
- const pkg = JSON.parse(readFileSync45(resolve40(__dirname6, "../package.json"), "utf-8"));
31100
+ const pkg = JSON.parse(readFileSync46(resolve41(__dirname6, "../package.json"), "utf-8"));
30746
31101
  console.log(`massu v${pkg.version}`);
30747
31102
  } catch {
30748
31103
  console.log("massu v0.1.0");