@bonginkan/maria 4.3.38 → 4.3.40

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.cjs CHANGED
@@ -189,13 +189,13 @@ function getPackageJson() {
189
189
  )
190
190
  ];
191
191
  let packageJsonPath = null;
192
- for (const path64 of possiblePaths) {
193
- if (fs19.existsSync(path64)) {
192
+ for (const path65 of possiblePaths) {
193
+ if (fs19.existsSync(path65)) {
194
194
  try {
195
- const content = fs19.readFileSync(path64, "utf-8");
195
+ const content = fs19.readFileSync(path65, "utf-8");
196
196
  const parsed = JSON.parse(content);
197
197
  if (parsed.name === "@bonginkan/maria") {
198
- packageJsonPath = path64;
198
+ packageJsonPath = path65;
199
199
  break;
200
200
  }
201
201
  } catch {
@@ -1709,7 +1709,7 @@ var init_AuthenticationManager = __esm({
1709
1709
  const response = await fetch(`${this.apiBase}/api/user/profile`, {
1710
1710
  headers: {
1711
1711
  "Authorization": `Bearer ${tokens2.accessToken}`,
1712
- "User-Agent": `maria-cli/${process.env.CLI_VERSION || "4.3.38"}`
1712
+ "User-Agent": `maria-cli/${process.env.CLI_VERSION || "4.3.40"}`
1713
1713
  }
1714
1714
  });
1715
1715
  if (response.status === 401) {
@@ -2421,9 +2421,9 @@ function clientThrottle(endpoint) {
2421
2421
  }
2422
2422
  rateLimitMap.set(endpoint, now2);
2423
2423
  }
2424
- async function callApi(path64, init3 = {}) {
2424
+ async function callApi(path65, init3 = {}) {
2425
2425
  const apiBase = process.env.MARIA_API_BASE || "https://api.maria-code.ai";
2426
- const fullUrl = `${apiBase}${path64}`;
2426
+ const fullUrl = `${apiBase}${path65}`;
2427
2427
  let tokens2 = await authManager.getValidTokens();
2428
2428
  if (!tokens2) {
2429
2429
  console.log(chalk40__default.default.red(ERR.AUTH_REQUIRED.msg));
@@ -2434,7 +2434,7 @@ async function callApi(path64, init3 = {}) {
2434
2434
  "Authorization": `Bearer ${token}`,
2435
2435
  "X-Device-Id": getDeviceId(),
2436
2436
  "X-Session-Id": getSessionId() || "",
2437
- "User-Agent": `maria-cli/${process.env.CLI_VERSION || "4.3.38"}`,
2437
+ "User-Agent": `maria-cli/${process.env.CLI_VERSION || "4.3.40"}`,
2438
2438
  "Content-Type": init3.headers?.["Content-Type"] || "application/json"
2439
2439
  });
2440
2440
  const doFetch = async (token) => {
@@ -2492,8 +2492,8 @@ async function callApi(path64, init3 = {}) {
2492
2492
  }
2493
2493
  return response;
2494
2494
  }
2495
- async function callApiJson(path64, init3 = {}) {
2496
- const response = await callApi(path64, init3);
2495
+ async function callApiJson(path65, init3 = {}) {
2496
+ const response = await callApi(path65, init3);
2497
2497
  if (!response.ok) {
2498
2498
  const error2 = await response.json().catch(() => ({
2499
2499
  message: `API error: ${response.status} ${response.statusText}`
@@ -2502,8 +2502,8 @@ async function callApiJson(path64, init3 = {}) {
2502
2502
  }
2503
2503
  return response.json();
2504
2504
  }
2505
- async function* streamApi(path64, init3 = {}) {
2506
- const response = await callApi(path64, {
2505
+ async function* streamApi(path65, init3 = {}) {
2506
+ const response = await callApi(path65, {
2507
2507
  ...init3,
2508
2508
  headers: {
2509
2509
  ...init3.headers,
@@ -2529,13 +2529,13 @@ async function* streamApi(path64, init3 = {}) {
2529
2529
  reader.releaseLock();
2530
2530
  }
2531
2531
  }
2532
- async function uploadFile(path64, file, metadata5 = {}) {
2532
+ async function uploadFile(path65, file, metadata5 = {}) {
2533
2533
  const formData = new FormData();
2534
2534
  formData.append("file", new Blob([file]));
2535
2535
  Object.entries(metadata5).forEach(([key, value]) => {
2536
2536
  formData.append(key, String(value));
2537
2537
  });
2538
- return callApiJson(path64, {
2538
+ return callApiJson(path65, {
2539
2539
  method: "POST",
2540
2540
  body: formData,
2541
2541
  headers: {
@@ -3208,7 +3208,7 @@ async function mapInputToTopLevelCommand(input3) {
3208
3208
  const system = [
3209
3209
  "You are a router for the MARIA CLI.",
3210
3210
  "Decide the best command for a single user input.",
3211
- "Allowed commands: /help, /image, /code, /video, /whoami, /login, /logout, /evaluate, /research, chat.",
3211
+ "Allowed commands: /help, /image, /code, /video, /whoami, /login, /logout, /evaluate, /research, /novel, chat.",
3212
3212
  "Note that /image and /video are for generating them. If a path of a file is provided, you should double think why it's referenced (it may be for coding or chatting or other commands, regarding on the context).",
3213
3213
  'Return JSON only with keys: { "command": string, "args"?: string[], "confidence": number }.',
3214
3214
  "Select chat when the input is a general question or conversation rather than a specific slash command.",
@@ -3236,7 +3236,7 @@ ${input3}`,
3236
3236
  }
3237
3237
  if (!parsed || typeof parsed.command !== "string") return null;
3238
3238
  const cmd = parsed.command;
3239
- if (!["/help", "/image", "/code", "/video", "/whoami", "/login", "/logout", "/evaluate", "/research", "chat"].includes(cmd)) return null;
3239
+ if (!["/help", "/image", "/code", "/video", "/whoami", "/login", "/logout", "/evaluate", "/research", "/novel", "chat"].includes(cmd)) return null;
3240
3240
  const out = { command: cmd };
3241
3241
  if (Array.isArray(parsed.args)) {
3242
3242
  out.args = parsed.args.filter((a) => typeof a === "string" && a.trim()).map((s2) => s2.trim());
@@ -8114,12 +8114,12 @@ var init_esm4 = __esm({
8114
8114
  /**
8115
8115
  * Get the Path object referenced by the string path, resolved from this Path
8116
8116
  */
8117
- resolve(path64) {
8118
- if (!path64) {
8117
+ resolve(path65) {
8118
+ if (!path65) {
8119
8119
  return this;
8120
8120
  }
8121
- const rootPath = this.getRootString(path64);
8122
- const dir = path64.substring(rootPath.length);
8121
+ const rootPath = this.getRootString(path65);
8122
+ const dir = path65.substring(rootPath.length);
8123
8123
  const dirParts = dir.split(this.splitSep);
8124
8124
  const result = rootPath ? this.getRoot(rootPath).#resolveParts(dirParts) : this.#resolveParts(dirParts);
8125
8125
  return result;
@@ -8871,8 +8871,8 @@ var init_esm4 = __esm({
8871
8871
  /**
8872
8872
  * @internal
8873
8873
  */
8874
- getRootString(path64) {
8875
- return path10.win32.parse(path64).root;
8874
+ getRootString(path65) {
8875
+ return path10.win32.parse(path65).root;
8876
8876
  }
8877
8877
  /**
8878
8878
  * @internal
@@ -8918,8 +8918,8 @@ var init_esm4 = __esm({
8918
8918
  /**
8919
8919
  * @internal
8920
8920
  */
8921
- getRootString(path64) {
8922
- return path64.startsWith("/") ? "/" : "";
8921
+ getRootString(path65) {
8922
+ return path65.startsWith("/") ? "/" : "";
8923
8923
  }
8924
8924
  /**
8925
8925
  * @internal
@@ -8968,8 +8968,8 @@ var init_esm4 = __esm({
8968
8968
  *
8969
8969
  * @internal
8970
8970
  */
8971
- constructor(cwd2 = process.cwd(), pathImpl, sep4, { nocase, childrenCacheSize = 16 * 1024, fs: fs51 = defaultFS } = {}) {
8972
- this.#fs = fsFromOption(fs51);
8971
+ constructor(cwd2 = process.cwd(), pathImpl, sep4, { nocase, childrenCacheSize = 16 * 1024, fs: fs52 = defaultFS } = {}) {
8972
+ this.#fs = fsFromOption(fs52);
8973
8973
  if (cwd2 instanceof URL || cwd2.startsWith("file://")) {
8974
8974
  cwd2 = url.fileURLToPath(cwd2);
8975
8975
  }
@@ -9008,11 +9008,11 @@ var init_esm4 = __esm({
9008
9008
  /**
9009
9009
  * Get the depth of a provided path, string, or the cwd
9010
9010
  */
9011
- depth(path64 = this.cwd) {
9012
- if (typeof path64 === "string") {
9013
- path64 = this.cwd.resolve(path64);
9011
+ depth(path65 = this.cwd) {
9012
+ if (typeof path65 === "string") {
9013
+ path65 = this.cwd.resolve(path65);
9014
9014
  }
9015
- return path64.depth();
9015
+ return path65.depth();
9016
9016
  }
9017
9017
  /**
9018
9018
  * Return the cache of child entries. Exposed so subclasses can create
@@ -9499,9 +9499,9 @@ var init_esm4 = __esm({
9499
9499
  process11();
9500
9500
  return results;
9501
9501
  }
9502
- chdir(path64 = this.cwd) {
9502
+ chdir(path65 = this.cwd) {
9503
9503
  const oldCwd = this.cwd;
9504
- this.cwd = typeof path64 === "string" ? this.cwd.resolve(path64) : path64;
9504
+ this.cwd = typeof path65 === "string" ? this.cwd.resolve(path65) : path65;
9505
9505
  this.cwd[setAsCwd](oldCwd);
9506
9506
  }
9507
9507
  };
@@ -9527,8 +9527,8 @@ var init_esm4 = __esm({
9527
9527
  /**
9528
9528
  * @internal
9529
9529
  */
9530
- newRoot(fs51) {
9531
- return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs51 });
9530
+ newRoot(fs52) {
9531
+ return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs52 });
9532
9532
  }
9533
9533
  /**
9534
9534
  * Return true if the provided path string is an absolute path
@@ -9556,8 +9556,8 @@ var init_esm4 = __esm({
9556
9556
  /**
9557
9557
  * @internal
9558
9558
  */
9559
- newRoot(fs51) {
9560
- return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs51 });
9559
+ newRoot(fs52) {
9560
+ return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs52 });
9561
9561
  }
9562
9562
  /**
9563
9563
  * Return true if the provided path string is an absolute path
@@ -9876,8 +9876,8 @@ var init_processor = __esm({
9876
9876
  }
9877
9877
  // match, absolute, ifdir
9878
9878
  entries() {
9879
- return [...this.store.entries()].map(([path64, n]) => [
9880
- path64,
9879
+ return [...this.store.entries()].map(([path65, n]) => [
9880
+ path65,
9881
9881
  !!(n & 2),
9882
9882
  !!(n & 1)
9883
9883
  ]);
@@ -10090,9 +10090,9 @@ var init_walker = __esm({
10090
10090
  signal;
10091
10091
  maxDepth;
10092
10092
  includeChildMatches;
10093
- constructor(patterns, path64, opts) {
10093
+ constructor(patterns, path65, opts) {
10094
10094
  this.patterns = patterns;
10095
- this.path = path64;
10095
+ this.path = path65;
10096
10096
  this.opts = opts;
10097
10097
  this.#sep = !opts.posix && opts.platform === "win32" ? "\\" : "/";
10098
10098
  this.includeChildMatches = opts.includeChildMatches !== false;
@@ -10111,11 +10111,11 @@ var init_walker = __esm({
10111
10111
  });
10112
10112
  }
10113
10113
  }
10114
- #ignored(path64) {
10115
- return this.seen.has(path64) || !!this.#ignore?.ignored?.(path64);
10114
+ #ignored(path65) {
10115
+ return this.seen.has(path65) || !!this.#ignore?.ignored?.(path65);
10116
10116
  }
10117
- #childrenIgnored(path64) {
10118
- return !!this.#ignore?.childrenIgnored?.(path64);
10117
+ #childrenIgnored(path65) {
10118
+ return !!this.#ignore?.childrenIgnored?.(path65);
10119
10119
  }
10120
10120
  // backpressure mechanism
10121
10121
  pause() {
@@ -10330,8 +10330,8 @@ var init_walker = __esm({
10330
10330
  };
10331
10331
  GlobWalker = class extends GlobUtil {
10332
10332
  matches = /* @__PURE__ */ new Set();
10333
- constructor(patterns, path64, opts) {
10334
- super(patterns, path64, opts);
10333
+ constructor(patterns, path65, opts) {
10334
+ super(patterns, path65, opts);
10335
10335
  }
10336
10336
  matchEmit(e2) {
10337
10337
  this.matches.add(e2);
@@ -10368,8 +10368,8 @@ var init_walker = __esm({
10368
10368
  };
10369
10369
  GlobStream = class extends GlobUtil {
10370
10370
  results;
10371
- constructor(patterns, path64, opts) {
10372
- super(patterns, path64, opts);
10371
+ constructor(patterns, path65, opts) {
10372
+ super(patterns, path65, opts);
10373
10373
  this.results = new Minipass({
10374
10374
  signal: this.signal,
10375
10375
  objectMode: true
@@ -16225,9 +16225,9 @@ var init_SafetyGuard = __esm({
16225
16225
  }
16226
16226
  }
16227
16227
  if (action.args.paths && Array.isArray(action.args.paths)) {
16228
- for (const path64 of action.args.paths) {
16229
- if (!this.validatePath(path64)) {
16230
- violations.push(`Invalid path: ${path64}`);
16228
+ for (const path65 of action.args.paths) {
16229
+ if (!this.validatePath(path65)) {
16230
+ violations.push(`Invalid path: ${path65}`);
16231
16231
  }
16232
16232
  }
16233
16233
  }
@@ -16283,15 +16283,15 @@ var init_SafetyGuard = __esm({
16283
16283
  * Validate file path against allowed/blocked lists
16284
16284
  */
16285
16285
  validatePath(filePath) {
16286
- const path64 = __require("path");
16287
- const resolvedPath = path64.resolve(filePath);
16286
+ const path65 = __require("path");
16287
+ const resolvedPath = path65.resolve(filePath);
16288
16288
  for (const blockedPath of this.constraints.blockedPaths) {
16289
- if (resolvedPath.startsWith(path64.resolve(blockedPath))) {
16289
+ if (resolvedPath.startsWith(path65.resolve(blockedPath))) {
16290
16290
  return false;
16291
16291
  }
16292
16292
  }
16293
16293
  for (const allowedPath of this.constraints.allowedPaths) {
16294
- if (resolvedPath.startsWith(path64.resolve(allowedPath))) {
16294
+ if (resolvedPath.startsWith(path65.resolve(allowedPath))) {
16295
16295
  return true;
16296
16296
  }
16297
16297
  }
@@ -16320,9 +16320,9 @@ var init_SafetyGuard = __esm({
16320
16320
  violations.push(`Invalid path in ${action.type}: ${action.args.path}`);
16321
16321
  }
16322
16322
  if (action.args.paths && Array.isArray(action.args.paths)) {
16323
- for (const path64 of action.args.paths) {
16324
- if (!this.validatePath(path64)) {
16325
- violations.push(`Invalid path in ${action.type}: ${path64}`);
16323
+ for (const path65 of action.args.paths) {
16324
+ if (!this.validatePath(path65)) {
16325
+ violations.push(`Invalid path in ${action.type}: ${path65}`);
16326
16326
  }
16327
16327
  }
16328
16328
  }
@@ -16382,8 +16382,8 @@ var require_package = __commonJS({
16382
16382
  "package.json"(exports, module) {
16383
16383
  module.exports = {
16384
16384
  name: "@bonginkan/maria",
16385
- version: "4.3.38",
16386
- description: "\u{1F680} MARIA v4.3.38 - Enterprise AI Development Platform with identity system and character voice implementation. Features 74 production-ready commands with comprehensive fallback implementation, local LLM support, and zero external dependencies. Includes natural language coding, AI safety evaluation, intelligent evolution system, episodic memory with PII masking, and real-time monitoring dashboard. Built with TypeScript AST-powered code generation, OAuth2.0 + PKCE authentication, quantum-resistant cryptography, and enterprise-grade performance.",
16385
+ version: "4.3.40",
16386
+ description: "\u{1F680} MARIA v4.3.40 - Enterprise AI Development Platform with identity system and character voice implementation. Features 74 production-ready commands with comprehensive fallback implementation, local LLM support, and zero external dependencies. Includes natural language coding, AI safety evaluation, intelligent evolution system, episodic memory with PII masking, and real-time monitoring dashboard. Built with TypeScript AST-powered code generation, OAuth2.0 + PKCE authentication, quantum-resistant cryptography, and enterprise-grade performance.",
16387
16387
  keywords: [
16388
16388
  "ai",
16389
16389
  "cli",
@@ -17817,8 +17817,8 @@ var init_ConfigActionExecutor = __esm({
17817
17817
  /**
17818
17818
  * Set nested configuration value using dot notation
17819
17819
  */
17820
- setNestedValue(obj, path64, value) {
17821
- const keys = path64.split(".");
17820
+ setNestedValue(obj, path65, value) {
17821
+ const keys = path65.split(".");
17822
17822
  let current = obj;
17823
17823
  for (let i2 = 0; i2 < keys.length - 1; i2++) {
17824
17824
  const key = keys[i2];
@@ -26389,7 +26389,7 @@ var init_about_command = __esm({
26389
26389
  async execute(args2, context2) {
26390
26390
  const output3 = [];
26391
26391
  output3.push("");
26392
- output3.push(chalk40__default.default.cyan.bold("\u{1F916} About MARIA v4.3.38"));
26392
+ output3.push(chalk40__default.default.cyan.bold("\u{1F916} About MARIA v4.3.40"));
26393
26393
  output3.push(chalk40__default.default.gray("\u2550".repeat(40)));
26394
26394
  output3.push("");
26395
26395
  output3.push(chalk40__default.default.white.bold("MARIA - Minimal API, Maximum Power"));
@@ -33959,22 +33959,22 @@ var init_from = __esm({
33959
33959
  init_file();
33960
33960
  init_fetch_blob();
33961
33961
  ({ stat: stat9 } = fs19.promises);
33962
- blobFromSync = (path64, type) => fromBlob(fs19.statSync(path64), path64, type);
33963
- blobFrom = (path64, type) => stat9(path64).then((stat13) => fromBlob(stat13, path64, type));
33964
- fileFrom = (path64, type) => stat9(path64).then((stat13) => fromFile(stat13, path64, type));
33965
- fileFromSync = (path64, type) => fromFile(fs19.statSync(path64), path64, type);
33966
- fromBlob = (stat13, path64, type = "") => new fetch_blob_default([new BlobDataItem({
33967
- path: path64,
33962
+ blobFromSync = (path65, type) => fromBlob(fs19.statSync(path65), path65, type);
33963
+ blobFrom = (path65, type) => stat9(path65).then((stat13) => fromBlob(stat13, path65, type));
33964
+ fileFrom = (path65, type) => stat9(path65).then((stat13) => fromFile(stat13, path65, type));
33965
+ fileFromSync = (path65, type) => fromFile(fs19.statSync(path65), path65, type);
33966
+ fromBlob = (stat13, path65, type = "") => new fetch_blob_default([new BlobDataItem({
33967
+ path: path65,
33968
33968
  size: stat13.size,
33969
33969
  lastModified: stat13.mtimeMs,
33970
33970
  start: 0
33971
33971
  })], { type });
33972
- fromFile = (stat13, path64, type = "") => new file_default([new BlobDataItem({
33973
- path: path64,
33972
+ fromFile = (stat13, path65, type = "") => new file_default([new BlobDataItem({
33973
+ path: path65,
33974
33974
  size: stat13.size,
33975
33975
  lastModified: stat13.mtimeMs,
33976
33976
  start: 0
33977
- })], path10.basename(path64), { type, lastModified: stat13.mtimeMs });
33977
+ })], path10.basename(path65), { type, lastModified: stat13.mtimeMs });
33978
33978
  BlobDataItem = class _BlobDataItem {
33979
33979
  #path;
33980
33980
  #start;
@@ -37743,12 +37743,12 @@ ${this.toYamlLike(value, indent + 1)}`;
37743
37743
  }
37744
37744
  static async loadFromFile(configPath) {
37745
37745
  const { importNodeBuiltin: importNodeBuiltin2 } = await Promise.resolve().then(() => (init_import_helper(), import_helper_exports));
37746
- const fs51 = await importNodeBuiltin2("fs");
37746
+ const fs52 = await importNodeBuiltin2("fs");
37747
37747
  const _path = await importNodeBuiltin2("_path");
37748
37748
  const os23 = await importNodeBuiltin2("os");
37749
37749
  const targetPath = configPath || _path.join(os23.homedir(), ".maria", "config.json");
37750
37750
  try {
37751
- const data = await fs51.promises.readFile(targetPath, "utf-8");
37751
+ const data = await fs52.promises.readFile(targetPath, "utf-8");
37752
37752
  return JSON.parse(data);
37753
37753
  } catch (innerError) {
37754
37754
  if (innerError?.code === "ENOENT") {
@@ -37762,25 +37762,25 @@ ${this.toYamlLike(value, indent + 1)}`;
37762
37762
  }
37763
37763
  async save(configPath, options) {
37764
37764
  const { importNodeBuiltin: importNodeBuiltin2 } = await Promise.resolve().then(() => (init_import_helper(), import_helper_exports));
37765
- const fs51 = await importNodeBuiltin2("fs");
37765
+ const fs52 = await importNodeBuiltin2("fs");
37766
37766
  const _path = await importNodeBuiltin2("_path");
37767
37767
  const os23 = await importNodeBuiltin2("os");
37768
37768
  const targetPath = configPath || _path.join(os23.homedir(), ".maria", "config.json");
37769
37769
  try {
37770
37770
  if (options?.backup) {
37771
37771
  try {
37772
- await fs51.promises.access(targetPath);
37772
+ await fs52.promises.access(targetPath);
37773
37773
  const backupPath = `${targetPath}.backup.${Date.now()}`;
37774
- await fs51.promises.copyFile(targetPath, backupPath);
37774
+ await fs52.promises.copyFile(targetPath, backupPath);
37775
37775
  } catch {
37776
37776
  }
37777
37777
  }
37778
- await fs51.promises.mkdir(_path.dirname(targetPath), { recursive: true });
37778
+ await fs52.promises.mkdir(_path.dirname(targetPath), { recursive: true });
37779
37779
  const dataToSave = this.getAll({
37780
37780
  maskSensitive: options?.maskSensitive ?? true,
37781
37781
  includeSourceMap: options?.includeSourceMap ?? false
37782
37782
  });
37783
- await fs51.promises.writeFile(
37783
+ await fs52.promises.writeFile(
37784
37784
  targetPath,
37785
37785
  JSON.stringify(dataToSave, null, 2),
37786
37786
  { mode: 384 }
@@ -37824,12 +37824,12 @@ ${this.toYamlLike(value, indent + 1)}`;
37824
37824
  }
37825
37825
  if (outputPath) {
37826
37826
  const { importNodeBuiltin: importNodeBuiltin2 } = await Promise.resolve().then(() => (init_import_helper(), import_helper_exports));
37827
- const fs51 = await importNodeBuiltin2("fs");
37827
+ const fs52 = await importNodeBuiltin2("fs");
37828
37828
  const _path = await importNodeBuiltin2(
37829
37829
  "_path"
37830
37830
  );
37831
- await fs51.promises.mkdir(_path.dirname(outputPath), { recursive: true });
37832
- await fs51.promises.writeFile(outputPath, content, "utf-8");
37831
+ await fs52.promises.mkdir(_path.dirname(outputPath), { recursive: true });
37832
+ await fs52.promises.writeFile(outputPath, content, "utf-8");
37833
37833
  console.log(`\u2705 Configuration exported to ${outputPath}`);
37834
37834
  }
37835
37835
  return content;
@@ -38216,88 +38216,6 @@ var init_SystemCommandFactory = __esm({
38216
38216
  };
38217
38217
  }
38218
38218
  });
38219
- function handleRateLimitError(error2) {
38220
- console.log();
38221
- console.log(chalk40__default.default.yellow("\u23F1\uFE0F Rate Limit Exceeded"));
38222
- console.log(chalk40__default.default.gray("\u2501".repeat(50)));
38223
- const route = error2.route || error2.details?.endpoint || "API";
38224
- const plan = error2.plan || error2.details?.plan || "Unknown";
38225
- const limit = error2.limit || (error2.details?.limit ? parseInt(error2.details.limit) : null);
38226
- const retryAfter = error2.retryAfter || error2.details?.retryAfterSeconds;
38227
- const resetAt = error2.resetAt || (error2.details?.resetAt ? Date.parse(error2.details.resetAt) : null);
38228
- const waitTime = Number.isFinite(retryAfter) ? `${retryAfter} second${retryAfter > 1 ? "s" : ""}` : "a few seconds";
38229
- let resetTime = "";
38230
- if (resetAt && Number.isFinite(resetAt)) {
38231
- const resetDate = new Date(resetAt);
38232
- const now2 = /* @__PURE__ */ new Date();
38233
- const diffMs = resetDate.getTime() - now2.getTime();
38234
- if (diffMs > 0 && diffMs < 6e4) {
38235
- resetTime = `in ${Math.ceil(diffMs / 1e3)} seconds`;
38236
- } else if (diffMs > 0) {
38237
- resetTime = `at ${resetDate.toLocaleTimeString()}`;
38238
- }
38239
- }
38240
- console.log(chalk40__default.default.white(`Plan: ${chalk40__default.default.bold(plan)}`));
38241
- console.log(chalk40__default.default.white(`Endpoint: ${chalk40__default.default.bold(route)}`));
38242
- if (limit !== null) {
38243
- console.log(chalk40__default.default.white(`Limit: ${chalk40__default.default.bold(limit)} requests`));
38244
- }
38245
- if (error2.remaining !== void 0) {
38246
- console.log(chalk40__default.default.white(`Remaining: ${chalk40__default.default.bold(error2.remaining)} requests`));
38247
- }
38248
- console.log();
38249
- console.log(chalk40__default.default.cyan(`Please wait ${chalk40__default.default.bold(waitTime)} before trying again`));
38250
- if (resetTime) {
38251
- console.log(chalk40__default.default.gray(`Rate limit resets ${resetTime}`));
38252
- }
38253
- if (error2.hint) {
38254
- console.log();
38255
- console.log(chalk40__default.default.magenta("\u{1F4A1} " + error2.hint));
38256
- }
38257
- console.log(chalk40__default.default.gray("\u2501".repeat(50)));
38258
- console.log();
38259
- }
38260
- async function parseRateLimitResponse(response) {
38261
- if (response.status !== 429) {
38262
- return null;
38263
- }
38264
- const rateLimitInfo = {
38265
- limit: parseInt(response.headers.get("RateLimit-Limit") || "0") || void 0,
38266
- remaining: parseInt(response.headers.get("RateLimit-Remaining") || "0") || void 0,
38267
- resetAt: parseInt(response.headers.get("RateLimit-Reset") || "0") || void 0,
38268
- retryAfter: parseInt(response.headers.get("Retry-After") || "0") || void 0
38269
- };
38270
- try {
38271
- const data = await response.json();
38272
- if (data.error === "rate_limited" || data.error === "rate_limit_exceeded") {
38273
- return {
38274
- ...data,
38275
- // Merge header info if not in body
38276
- limit: data.limit || rateLimitInfo.limit,
38277
- remaining: data.remaining || rateLimitInfo.remaining,
38278
- resetAt: data.resetAt || rateLimitInfo.resetAt,
38279
- retryAfter: data.retryAfter || rateLimitInfo.retryAfter
38280
- };
38281
- }
38282
- return {
38283
- error: "rate_limited",
38284
- message: data.message || "Rate limit exceeded. Please wait and try again.",
38285
- hint: data.hint || "Consider upgrading your plan for higher limits.",
38286
- ...rateLimitInfo
38287
- };
38288
- } catch {
38289
- return {
38290
- error: "rate_limited",
38291
- message: "Rate limit exceeded. Please wait and try again.",
38292
- hint: "Consider upgrading your plan for higher limits.",
38293
- ...rateLimitInfo
38294
- };
38295
- }
38296
- }
38297
- var init_rate_limit_handler = __esm({
38298
- "src/services/api-client/rate-limit-handler.ts"() {
38299
- }
38300
- });
38301
38219
  function extractCodeInfo(codeBlock) {
38302
38220
  const match2 = codeBlock.match(/```([^\n]*)\n?([\s\S]*?)```/);
38303
38221
  if (!match2) {
@@ -38542,8 +38460,408 @@ var init_code_utils = __esm({
38542
38460
  }
38543
38461
  });
38544
38462
 
38545
- // src/services/code-orchestrator/ArgumentInference.ts
38463
+ // src/services/creative/NovelArgumentInference.ts
38546
38464
  function extractFirstJson4(text) {
38465
+ if (!text) return null;
38466
+ const start = text.indexOf("{");
38467
+ const end = text.lastIndexOf("}");
38468
+ if (start !== -1 && end !== -1 && end > start) {
38469
+ const cand = text.slice(start, end + 1);
38470
+ try {
38471
+ JSON.parse(cand);
38472
+ return cand;
38473
+ } catch {
38474
+ }
38475
+ }
38476
+ return null;
38477
+ }
38478
+ async function inferNovelArgs(rawText) {
38479
+ const system = [
38480
+ "You extract structured options for a novel generation command.",
38481
+ 'Return JSON ONLY with keys among: { "title"?: string, "lang"?: string, "format"?: "md"|"markdown"|"txt"|"plaintext", "genre"?: string, "planOnly"?: boolean, "chapters"?: number }.',
38482
+ 'Infer reasonable values from natural language. Keep chapters between 3 and 50 if specified. If format suggests plain text, choose "plaintext".',
38483
+ "If the user requests outline only, set planOnly=true.",
38484
+ "Do not add commentary."
38485
+ ].join("\n");
38486
+ const resp = await callAPI("/v1/ai-proxy", {
38487
+ method: "POST",
38488
+ body: {
38489
+ prompt: `${system}
38490
+
38491
+ ---
38492
+
38493
+ ${rawText}`,
38494
+ taskType: "creative"
38495
+ }
38496
+ });
38497
+ const raw = (resp?.data?.content || resp?.output || "").trim();
38498
+ const jsonText = extractFirstJson4(raw) || raw;
38499
+ let parsed = {};
38500
+ try {
38501
+ parsed = JSON.parse(jsonText);
38502
+ } catch {
38503
+ return {};
38504
+ }
38505
+ const out = {};
38506
+ if (typeof parsed.title === "string" && parsed.title.trim()) out.title = parsed.title.trim();
38507
+ if (typeof parsed.lang === "string" && parsed.lang.trim()) out.lang = parsed.lang.trim().toLowerCase();
38508
+ if (typeof parsed.format === "string") {
38509
+ const f3 = parsed.format.toLowerCase();
38510
+ if (f3 === "md" || f3 === "markdown") out.format = "md";
38511
+ else if (f3 === "txt" || f3 === "plaintext" || f3 === "text") out.format = "txt";
38512
+ }
38513
+ if (typeof parsed.genre === "string" && parsed.genre.trim()) out.genre = parsed.genre.trim();
38514
+ if (typeof parsed.planOnly === "boolean") out.planOnly = parsed.planOnly;
38515
+ if (typeof parsed.chapters === "number" && Number.isFinite(parsed.chapters)) {
38516
+ const n = Math.max(1, Math.min(100, Math.floor(parsed.chapters)));
38517
+ out.chapters = n;
38518
+ }
38519
+ return out;
38520
+ }
38521
+ var init_NovelArgumentInference = __esm({
38522
+ "src/services/creative/NovelArgumentInference.ts"() {
38523
+ init_api_caller();
38524
+ }
38525
+ });
38526
+
38527
+ // src/slash-commands/categories/creative/novel.command.ts
38528
+ var novel_command_exports = {};
38529
+ __export(novel_command_exports, {
38530
+ NovelCommand: () => NovelCommand,
38531
+ default: () => novel_command_default
38532
+ });
38533
+ function normalizeFormat(fmt) {
38534
+ const f3 = (fmt).toLowerCase();
38535
+ if (f3 === "txt" || f3 === "plaintext" || f3 === "text") return "txt";
38536
+ return "md";
38537
+ }
38538
+ function normalizeLang(lang) {
38539
+ const l = (lang).toLowerCase();
38540
+ if (!l) return "en";
38541
+ if (["en", "ja", "jp", "zh", "ko", "fr", "de", "es", "it", "pt"].includes(l)) {
38542
+ return l === "jp" ? "ja" : l;
38543
+ }
38544
+ return "en";
38545
+ }
38546
+ async function writeText(filePath, content) {
38547
+ const dir = path10__namespace.dirname(filePath);
38548
+ await fsp__namespace.mkdir(dir, { recursive: true });
38549
+ await fsp__namespace.writeFile(filePath, content, "utf8");
38550
+ }
38551
+ function extractFirstJsonArray(text) {
38552
+ if (!text) return null;
38553
+ const t2 = text.trim();
38554
+ const fenced = t2.match(/```json\s*([\s\S]*?)```/i) || t2.match(/```\s*([\s\S]*?)```/);
38555
+ if (fenced && fenced[1]) {
38556
+ const body = fenced[1].trim();
38557
+ try {
38558
+ const arr = JSON.parse(body);
38559
+ return Array.isArray(arr) ? arr : null;
38560
+ } catch {
38561
+ }
38562
+ }
38563
+ const defenced = t2.replace(/```json|```/gi, "").trim();
38564
+ try {
38565
+ const arr = JSON.parse(defenced);
38566
+ return Array.isArray(arr) ? arr : null;
38567
+ } catch {
38568
+ }
38569
+ const s2 = t2.indexOf("[");
38570
+ const e2 = t2.lastIndexOf("]");
38571
+ if (s2 !== -1 && e2 !== -1 && e2 > s2) {
38572
+ const slice = t2.slice(s2, e2 + 1);
38573
+ try {
38574
+ const arr = JSON.parse(slice);
38575
+ return Array.isArray(arr) ? arr : null;
38576
+ } catch {
38577
+ }
38578
+ }
38579
+ return null;
38580
+ }
38581
+ async function generatePlan(prompt, lang, title, genre) {
38582
+ const system = [
38583
+ "You generate a detailed novel outline (plot).",
38584
+ "Return Markdown with clear sections: Title, Logline, Themes, Characters, Chapter Outline.",
38585
+ "The language should match the requested language."
38586
+ ].join("\n");
38587
+ const user = [
38588
+ `Language: ${lang}`,
38589
+ `Title: ${title}`,
38590
+ genre ? `Genre: ${genre}` : "",
38591
+ "Prompt:",
38592
+ prompt
38593
+ ].filter(Boolean).join("\n");
38594
+ const resp = await callAPI("/v1/ai-proxy", { method: "POST", body: { prompt: `${system}
38595
+
38596
+ ---
38597
+
38598
+ ${user}`, taskType: "creative" } });
38599
+ const out = (resp?.data?.content || resp?.output || "").trim();
38600
+ return out;
38601
+ }
38602
+ async function generateChapters(planMarkdown, lang, chapters) {
38603
+ const system = [
38604
+ "You expand a novel outline into chapter texts.",
38605
+ "Return only JSON array: [{ index: number, title: string, content: string }].",
38606
+ "Write in the requested language and maintain consistency with the outline."
38607
+ ].join("\n");
38608
+ const prompt = `${system}
38609
+
38610
+ LANG=${lang}
38611
+ CHAPTERS=${chapters}
38612
+
38613
+ OUTLINE:
38614
+ ${planMarkdown}`;
38615
+ const resp = await callAPI("/v1/ai-proxy", { method: "POST", body: { prompt, taskType: "creative" } });
38616
+ const raw = (resp?.data?.content || resp?.output || "").trim();
38617
+ const arr = extractFirstJsonArray(raw);
38618
+ if (Array.isArray(arr)) {
38619
+ return arr.map((it, i2) => ({
38620
+ index: typeof it.index === "number" ? it.index : i2,
38621
+ title: typeof it.title === "string" ? it.title : `Chapter ${i2 + 1}`,
38622
+ content: typeof it.content === "string" ? it.content : ""
38623
+ }));
38624
+ }
38625
+ return [{ index: 0, title: "Chapter 1", content: raw }];
38626
+ }
38627
+ async function generateTitle(idea, lang, genre) {
38628
+ const system = [
38629
+ "You craft a concise, compelling novel title. Return only the title string.",
38630
+ "Consider the idea and genre. Use the requested language."
38631
+ ].join("\n");
38632
+ const user = [
38633
+ `Language: ${lang}`,
38634
+ genre ? `Genre: ${genre}` : "",
38635
+ "Idea:",
38636
+ idea
38637
+ ].filter(Boolean).join("\n");
38638
+ const resp = await callAPI("/v1/ai-proxy", { method: "POST", body: { prompt: `${system}
38639
+
38640
+ ---
38641
+
38642
+ ${user}`, taskType: "creative" } });
38643
+ const raw = (resp?.data?.content || resp?.output || "").trim();
38644
+ try {
38645
+ const obj = JSON.parse(raw);
38646
+ if (typeof obj?.title === "string") return obj.title.trim();
38647
+ } catch {
38648
+ }
38649
+ const matched = raw.match(/```[a-z]*\s*([\s\S]*?)```/i);
38650
+ const text = matched ? matched[1].trim() : raw;
38651
+ return text.split("\n")[0].trim();
38652
+ }
38653
+ var NovelCommand, novel_command_default;
38654
+ var init_novel_command = __esm({
38655
+ "src/slash-commands/categories/creative/novel.command.ts"() {
38656
+ init_base_command();
38657
+ init_code_utils();
38658
+ init_api_caller();
38659
+ init_NovelArgumentInference();
38660
+ init_animations();
38661
+ NovelCommand = class extends BaseCommand {
38662
+ name = "novel";
38663
+ category = "creative";
38664
+ description = "Generate a novel: outline plan and full chapters with language/format options";
38665
+ aliases = ["story"];
38666
+ usage = "<idea or title> [--lang <code>] [--format md|txt] [--genre <name>] [--plan-only] [--chapters <n>]";
38667
+ examples = [
38668
+ { input: "/novel cyberpunk heist in Tokyo --lang ja", description: "\u65E5\u672C\u8A9E\u3067\u30B5\u30A4\u30D0\u30FC\u30D1\u30F3\u30AF\u5C0F\u8AAC\u3092\u751F\u6210" },
38669
+ { input: '/novel "A voyage to Europa" --format txt --genre sci-fi', description: "\u82F1\u8A9E\u30FB\u30D7\u30EC\u30FC\u30F3\u30C6\u30AD\u30B9\u30C8\u3067\u751F\u6210" },
38670
+ { input: "/novel detective mystery --plan-only", description: "\u30D7\u30ED\u30C3\u30C8\u306E\u307F\u3092\u4F5C\u6210\u3057\u3066\u4FDD\u5B58" }
38671
+ ];
38672
+ async execute(args2, context2) {
38673
+ const raw = Array.isArray(args2.raw) ? args2.raw : [];
38674
+ const ideaText = raw.join(" ").trim();
38675
+ if (!ideaText) {
38676
+ return this.error("Usage: /novel <idea or title> [--lang <code>] [--format md|txt] [--genre <name>] [--plan-only] [--chapters <n>]");
38677
+ }
38678
+ const explicit = {
38679
+ lang: typeof args2.options["lang"] === "string" ? String(args2.options["lang"]) : void 0,
38680
+ format: typeof args2.options["format"] === "string" ? String(args2.options["format"]) : void 0,
38681
+ genre: typeof args2.options["genre"] === "string" ? String(args2.options["genre"]) : void 0,
38682
+ chapters: typeof args2.options["chapters"] === "string" ? Number(args2.options["chapters"]) : void 0,
38683
+ planOnly: args2.flags["plan-only"] === true || args2.flags["sow"] === true
38684
+ };
38685
+ let inferred = {};
38686
+ try {
38687
+ const spin = new ProcessAnimation();
38688
+ spin.start();
38689
+ try {
38690
+ inferred = await inferNovelArgs(ideaText);
38691
+ } finally {
38692
+ try {
38693
+ spin.stop();
38694
+ } catch {
38695
+ }
38696
+ }
38697
+ } catch {
38698
+ }
38699
+ const lang = normalizeLang(explicit.lang || inferred.lang || "en");
38700
+ const format = normalizeFormat(explicit.format || inferred.format || "md");
38701
+ const planOnly = explicit.planOnly || inferred.planOnly === true;
38702
+ const chapters = Number.isFinite(explicit.chapters) && explicit.chapters > 0 ? Math.floor(explicit.chapters) : typeof inferred.chapters === "number" && inferred.chapters > 0 ? Math.floor(inferred.chapters) : 8;
38703
+ const genre = explicit.genre || inferred.genre;
38704
+ let generatedTitle = inferred.title;
38705
+ if (!generatedTitle) {
38706
+ const spin = new ProcessAnimation();
38707
+ spin.start();
38708
+ try {
38709
+ generatedTitle = await generateTitle(ideaText, lang, genre);
38710
+ } finally {
38711
+ try {
38712
+ spin.stop();
38713
+ } catch {
38714
+ }
38715
+ }
38716
+ }
38717
+ const titleRaw = (generatedTitle || "novel").slice(0, 120);
38718
+ const title = sanitizeFilenameStrict(titleRaw);
38719
+ const root = process.cwd();
38720
+ const novelDir = await ensureUniquePath(safeResolve(root, path10__namespace.join("novel", title)));
38721
+ await fsp__namespace.mkdir(novelDir, { recursive: true });
38722
+ let plan = "";
38723
+ {
38724
+ const spin = new ProcessAnimation();
38725
+ spin.start();
38726
+ try {
38727
+ plan = await generatePlan(ideaText, lang, title, genre);
38728
+ } finally {
38729
+ try {
38730
+ spin.stop();
38731
+ } catch {
38732
+ }
38733
+ }
38734
+ }
38735
+ const planExt = format === "txt" ? ".txt" : ".md";
38736
+ const planPath = await ensureUniquePath(path10__namespace.join(novelDir, `00_plan${planExt}`));
38737
+ await writeText(planPath, plan);
38738
+ if (planOnly) {
38739
+ const msg2 = [`Saved plan to ${planPath}`, "", "Files:", `- ${planPath}`].join("\n");
38740
+ return this.success(msg2, { dir: novelDir, plan: planPath });
38741
+ }
38742
+ const chapterObjs = await (async () => {
38743
+ const spin = new ProcessAnimation();
38744
+ spin.start();
38745
+ try {
38746
+ return await generateChapters(plan, lang, chapters);
38747
+ } finally {
38748
+ try {
38749
+ spin.stop();
38750
+ } catch {
38751
+ }
38752
+ }
38753
+ })();
38754
+ let index = 1;
38755
+ const saved = [planPath];
38756
+ for (const ch of chapterObjs) {
38757
+ const num = String(index).padStart(2, "0");
38758
+ const chTitle = sanitizeFilenameStrict(ch.title || `chapter-${index}`);
38759
+ const file = path10__namespace.join(novelDir, `${num}_${chTitle}${planExt}`);
38760
+ const finalPath = await ensureUniquePath(file);
38761
+ const content = format === "md" ? `# ${ch.title}
38762
+
38763
+ ${ch.content}` : ch.content;
38764
+ await writeText(finalPath, content);
38765
+ saved.push(finalPath);
38766
+ index++;
38767
+ }
38768
+ const msg = [
38769
+ `Saved novel to ${novelDir}`,
38770
+ "",
38771
+ "Files:",
38772
+ ...saved.map((p) => `- ${p}`)
38773
+ ].join("\n");
38774
+ return this.success(msg, { dir: novelDir, files: saved });
38775
+ }
38776
+ };
38777
+ novel_command_default = NovelCommand;
38778
+ }
38779
+ });
38780
+ function handleRateLimitError(error2) {
38781
+ console.log();
38782
+ console.log(chalk40__default.default.yellow("\u23F1\uFE0F Rate Limit Exceeded"));
38783
+ console.log(chalk40__default.default.gray("\u2501".repeat(50)));
38784
+ const route = error2.route || error2.details?.endpoint || "API";
38785
+ const plan = error2.plan || error2.details?.plan || "Unknown";
38786
+ const limit = error2.limit || (error2.details?.limit ? parseInt(error2.details.limit) : null);
38787
+ const retryAfter = error2.retryAfter || error2.details?.retryAfterSeconds;
38788
+ const resetAt = error2.resetAt || (error2.details?.resetAt ? Date.parse(error2.details.resetAt) : null);
38789
+ const waitTime = Number.isFinite(retryAfter) ? `${retryAfter} second${retryAfter > 1 ? "s" : ""}` : "a few seconds";
38790
+ let resetTime = "";
38791
+ if (resetAt && Number.isFinite(resetAt)) {
38792
+ const resetDate = new Date(resetAt);
38793
+ const now2 = /* @__PURE__ */ new Date();
38794
+ const diffMs = resetDate.getTime() - now2.getTime();
38795
+ if (diffMs > 0 && diffMs < 6e4) {
38796
+ resetTime = `in ${Math.ceil(diffMs / 1e3)} seconds`;
38797
+ } else if (diffMs > 0) {
38798
+ resetTime = `at ${resetDate.toLocaleTimeString()}`;
38799
+ }
38800
+ }
38801
+ console.log(chalk40__default.default.white(`Plan: ${chalk40__default.default.bold(plan)}`));
38802
+ console.log(chalk40__default.default.white(`Endpoint: ${chalk40__default.default.bold(route)}`));
38803
+ if (limit !== null) {
38804
+ console.log(chalk40__default.default.white(`Limit: ${chalk40__default.default.bold(limit)} requests`));
38805
+ }
38806
+ if (error2.remaining !== void 0) {
38807
+ console.log(chalk40__default.default.white(`Remaining: ${chalk40__default.default.bold(error2.remaining)} requests`));
38808
+ }
38809
+ console.log();
38810
+ console.log(chalk40__default.default.cyan(`Please wait ${chalk40__default.default.bold(waitTime)} before trying again`));
38811
+ if (resetTime) {
38812
+ console.log(chalk40__default.default.gray(`Rate limit resets ${resetTime}`));
38813
+ }
38814
+ if (error2.hint) {
38815
+ console.log();
38816
+ console.log(chalk40__default.default.magenta("\u{1F4A1} " + error2.hint));
38817
+ }
38818
+ console.log(chalk40__default.default.gray("\u2501".repeat(50)));
38819
+ console.log();
38820
+ }
38821
+ async function parseRateLimitResponse(response) {
38822
+ if (response.status !== 429) {
38823
+ return null;
38824
+ }
38825
+ const rateLimitInfo = {
38826
+ limit: parseInt(response.headers.get("RateLimit-Limit") || "0") || void 0,
38827
+ remaining: parseInt(response.headers.get("RateLimit-Remaining") || "0") || void 0,
38828
+ resetAt: parseInt(response.headers.get("RateLimit-Reset") || "0") || void 0,
38829
+ retryAfter: parseInt(response.headers.get("Retry-After") || "0") || void 0
38830
+ };
38831
+ try {
38832
+ const data = await response.json();
38833
+ if (data.error === "rate_limited" || data.error === "rate_limit_exceeded") {
38834
+ return {
38835
+ ...data,
38836
+ // Merge header info if not in body
38837
+ limit: data.limit || rateLimitInfo.limit,
38838
+ remaining: data.remaining || rateLimitInfo.remaining,
38839
+ resetAt: data.resetAt || rateLimitInfo.resetAt,
38840
+ retryAfter: data.retryAfter || rateLimitInfo.retryAfter
38841
+ };
38842
+ }
38843
+ return {
38844
+ error: "rate_limited",
38845
+ message: data.message || "Rate limit exceeded. Please wait and try again.",
38846
+ hint: data.hint || "Consider upgrading your plan for higher limits.",
38847
+ ...rateLimitInfo
38848
+ };
38849
+ } catch {
38850
+ return {
38851
+ error: "rate_limited",
38852
+ message: "Rate limit exceeded. Please wait and try again.",
38853
+ hint: "Consider upgrading your plan for higher limits.",
38854
+ ...rateLimitInfo
38855
+ };
38856
+ }
38857
+ }
38858
+ var init_rate_limit_handler = __esm({
38859
+ "src/services/api-client/rate-limit-handler.ts"() {
38860
+ }
38861
+ });
38862
+
38863
+ // src/services/code-orchestrator/ArgumentInference.ts
38864
+ function extractFirstJson5(text) {
38547
38865
  const fence = /```json\r?\n([\s\S]*?)```/i.exec(text);
38548
38866
  if (fence) return fence[1];
38549
38867
  const start = text.indexOf("{");
@@ -38577,7 +38895,7 @@ ${rawText}`,
38577
38895
  }
38578
38896
  });
38579
38897
  const raw = (resp?.data?.content || resp?.output || "").trim();
38580
- const jsonText = extractFirstJson4(raw) || raw;
38898
+ const jsonText = extractFirstJson5(raw) || raw;
38581
38899
  let parsed = {};
38582
38900
  try {
38583
38901
  parsed = JSON.parse(jsonText);
@@ -38689,10 +39007,10 @@ function suggestName(fp, p) {
38689
39007
  const base = (fp.description || "file").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
38690
39008
  return base || "file";
38691
39009
  }
38692
- function guessKindByPath(path64) {
38693
- if (/__tests__|\.spec\.(t|j)sx?$|^tests\//.test(path64)) return "test";
38694
- if (/^docs\//.test(path64) || /\.md$/.test(path64)) return "doc";
38695
- if (/\.(json|cjs|js|ts)$/.test(path64) && !/src\//.test(path64)) return "config";
39010
+ function guessKindByPath(path65) {
39011
+ if (/__tests__|\.spec\.(t|j)sx?$|^tests\//.test(path65)) return "test";
39012
+ if (/^docs\//.test(path65) || /\.md$/.test(path65)) return "doc";
39013
+ if (/\.(json|cjs|js|ts)$/.test(path65) && !/src\//.test(path65)) return "config";
38696
39014
  return "source";
38697
39015
  }
38698
39016
  var init_PathInferencer = __esm({
@@ -38956,9 +39274,9 @@ function formatPlanAsDiff(files, opts) {
38956
39274
  function readCurrentFileSafe(root, rel, abs) {
38957
39275
  if (!root && !abs) return "";
38958
39276
  try {
38959
- const fs51 = __require("fs");
39277
+ const fs52 = __require("fs");
38960
39278
  const p = abs ? abs : __require("path").join(root, rel);
38961
- return fs51.existsSync(p) ? fs51.readFileSync(p, "utf8") : "";
39279
+ return fs52.existsSync(p) ? fs52.readFileSync(p, "utf8") : "";
38962
39280
  } catch {
38963
39281
  return "";
38964
39282
  }
@@ -39293,7 +39611,8 @@ async function listRepoFiles(root) {
39293
39611
  }
39294
39612
  for (const e2 of entries) {
39295
39613
  const name2 = e2.name;
39296
- if (name2 === ".git" || name2 === "node_modules" || name2 === "dist" || name2 === ".maria") continue;
39614
+ if (name2 === ".git" || name2 === "node_modules" || name2 === "dist" || name2 === ".maria" || // macOS system/hidden dirs that shouldn't be scanned
39615
+ name2 === ".DS_Store" || name2 === ".Spotlight-V100" || name2 === ".Trashes" || name2 === ".fseventsd" || name2 === ".TemporaryItems") continue;
39297
39616
  const full = path10__namespace.default.join(dir, name2);
39298
39617
  const rel = path10__namespace.default.relative(root, full).replace(/\\/g, "/");
39299
39618
  if (e2.isDirectory()) {
@@ -39546,6 +39865,168 @@ var Orchestrator_exports = {};
39546
39865
  __export(Orchestrator_exports, {
39547
39866
  orchestrate: () => orchestrate
39548
39867
  });
39868
+ async function getRepoFiles(root) {
39869
+ if (_repoFileIndexCache && _repoFileIndexCache.root === root) return _repoFileIndexCache.files;
39870
+ const fs52 = await import('fs/promises');
39871
+ const pathMod = await import('path');
39872
+ const ignoreDir = /* @__PURE__ */ new Set([
39873
+ ".git",
39874
+ "node_modules",
39875
+ "dist",
39876
+ "build",
39877
+ ".maria",
39878
+ ".next",
39879
+ "coverage",
39880
+ ".DS_Store",
39881
+ ".Spotlight-V100",
39882
+ ".Trashes",
39883
+ ".fseventsd",
39884
+ ".TemporaryItems"
39885
+ ]);
39886
+ const out = [];
39887
+ async function walk2(dir) {
39888
+ let entries = [];
39889
+ try {
39890
+ entries = await fs52.readdir(dir, { withFileTypes: true });
39891
+ } catch {
39892
+ return;
39893
+ }
39894
+ for (const e2 of entries) {
39895
+ const name2 = e2.name;
39896
+ if (ignoreDir.has(name2)) continue;
39897
+ const full = pathMod.join(dir, name2);
39898
+ if (e2.isDirectory()) {
39899
+ await walk2(full);
39900
+ continue;
39901
+ }
39902
+ out.push(pathMod.relative(root, full).replace(/\\/g, "/"));
39903
+ }
39904
+ }
39905
+ await walk2(root);
39906
+ _repoFileIndexCache = { root, files: out };
39907
+ return out;
39908
+ }
39909
+ function languageFromExt(ext2) {
39910
+ const e2 = ext2.toLowerCase().replace(/^\./, "");
39911
+ if (e2 === "ts") return "typescript";
39912
+ if (e2 === "tsx") return "tsx";
39913
+ if (e2 === "js" || e2 === "mjs" || e2 === "cjs") return "javascript";
39914
+ if (e2 === "jsx") return "jsx";
39915
+ if (e2 === "json") return "json";
39916
+ if (e2 === "html") return "html";
39917
+ if (e2 === "css" || e2 === "scss") return "css";
39918
+ if (e2 === "md") return "markdown";
39919
+ if (e2 === "yaml" || e2 === "yml") return "yaml";
39920
+ return void 0;
39921
+ }
39922
+ async function readHeadTail(root, rel, n) {
39923
+ try {
39924
+ const fs52 = await import('fs/promises');
39925
+ const pathMod = await import('path');
39926
+ const full = pathMod.join(root, rel);
39927
+ const buf = await fs52.readFile(full, "utf8");
39928
+ const lines = buf.split(/\r?\n/);
39929
+ const head2 = lines.slice(0, n).join("\n");
39930
+ const tail = lines.slice(Math.max(0, lines.length - n)).join("\n");
39931
+ return { head: head2, tail };
39932
+ } catch {
39933
+ return { head: "", tail: "" };
39934
+ }
39935
+ }
39936
+ async function llmSelectEditTargets(root, request, repoFiles) {
39937
+ try {
39938
+ const candidates = repoFiles.filter((p) => /\.(html|css|js|ts|tsx)$/i.test(p)).slice(0, 150);
39939
+ const samples = [];
39940
+ for (const p of candidates.slice(0, 60)) {
39941
+ const h2 = await readHeadTail(root, p, 5);
39942
+ samples.push(`- ${p}
39943
+ ${h2.head}`);
39944
+ }
39945
+ const system = [
39946
+ "You select existing repository files that should be EDITED to satisfy a user request.",
39947
+ "Consider path names and the first lines. Return JSON array of repo-relative paths to edit.",
39948
+ "Keep the list short (<= 10). If none are relevant, return []. Do not invent paths."
39949
+ ].join("\n");
39950
+ const user = [`Request: ${request}`, "Candidates:", samples.join("\n\n")].join("\n\n");
39951
+ const resp = await executeChat([
39952
+ { role: "system", content: system },
39953
+ { role: "user", content: user }
39954
+ ]);
39955
+ const raw = (resp?.output || "").trim();
39956
+ const jsonText = (() => {
39957
+ try {
39958
+ const m2 = raw.match(/\[[\s\S]*\]/);
39959
+ return m2 ? m2[0] : raw;
39960
+ } catch {
39961
+ return raw;
39962
+ }
39963
+ })();
39964
+ const arr = JSON.parse(jsonText);
39965
+ const set = new Set(candidates.map((c) => c.toLowerCase()));
39966
+ const out = [];
39967
+ if (Array.isArray(arr)) {
39968
+ for (const v of arr) {
39969
+ if (typeof v === "string" && set.has(v.toLowerCase())) out.push(v);
39970
+ if (out.length >= 10) break;
39971
+ }
39972
+ }
39973
+ return out;
39974
+ } catch {
39975
+ return [];
39976
+ }
39977
+ }
39978
+ async function llmMapBlockToFile(root, request, block, repoFiles) {
39979
+ try {
39980
+ const lang = (block.language || "").toLowerCase();
39981
+ const extFilter = lang.includes("html") ? /\.html$/i : /(ts|tsx)/.test(lang) ? /\.(ts|tsx)$/i : /(js|jsx)/.test(lang) ? /\.(js|jsx)$/i : /\.css$/i;
39982
+ const candidates = repoFiles.filter((p) => extFilter.test(p)).slice(0, 80);
39983
+ const head2 = block.code.split(/\r?\n/).slice(0, 20).join("\n");
39984
+ const samples = [];
39985
+ for (const p of candidates.slice(0, 40)) {
39986
+ const h2 = await readHeadTail(root, p, 8);
39987
+ samples.push(`- ${p}
39988
+ ${h2.head}`);
39989
+ }
39990
+ const system = [
39991
+ "Decide where to apply an EDIT vs CREATE for a code block within an existing repository.",
39992
+ 'Return JSON: { "action": "modify"|"create", "path": string }.',
39993
+ "If modify, path must be one of the candidate repo-relative paths. If create, propose a sensible repo-relative path."
39994
+ ].join("\n");
39995
+ const user = [
39996
+ `Request: ${request}`,
39997
+ "Block:",
39998
+ "```",
39999
+ head2,
40000
+ "```",
40001
+ "Candidates:",
40002
+ samples.join("\n\n")
40003
+ ].join("\n");
40004
+ const resp = await executeChat([
40005
+ { role: "system", content: system },
40006
+ { role: "user", content: user }
40007
+ ]);
40008
+ const raw = (resp?.output || "").trim();
40009
+ const jsonText = (() => {
40010
+ try {
40011
+ const m2 = raw.match(/\{[\s\S]*\}/);
40012
+ return m2 ? m2[0] : raw;
40013
+ } catch {
40014
+ return raw;
40015
+ }
40016
+ })();
40017
+ const parsed = JSON.parse(jsonText);
40018
+ if (parsed && (parsed.action === "modify" || parsed.action === "create") && typeof parsed.path === "string") {
40019
+ return { action: parsed.action, path: parsed.path.replace(/^\/+/, "") };
40020
+ }
40021
+ } catch {
40022
+ }
40023
+ const desired = typeof block.filename === "string" && block.filename.trim() ? block.filename.trim() : null;
40024
+ const fallback2 = desired || suggestName2(request, block.language, 0);
40025
+ return { action: "create", path: fallback2 };
40026
+ }
40027
+ function tokenizeRequest(text) {
40028
+ return Array.from(new Set(text.toLowerCase().replace(/[^a-z0-9_-]+/g, " ").split(/\s+/).filter((t2) => t2.length >= 3)));
40029
+ }
39549
40030
  async function ensureCodeFallbackManager() {
39550
40031
  const policy = await loadFallbackPolicy().catch(() => getDefaultFallbackPolicy());
39551
40032
  const signature = JSON.stringify(policy);
@@ -39584,11 +40065,19 @@ async function orchestrate(request, opts) {
39584
40065
  explicitAbsMap[rel] = pathMod.join(opts.root, rel);
39585
40066
  }
39586
40067
  }
39587
- const isEditIntent = detectEditIntent(request, {
40068
+ const isEditIntent = await detectEditIntentEnhanced(opts.root, request, {
39588
40069
  hasAttachments: !!(opts.attachedFiles && opts.attachedFiles.length > 0),
39589
- explicitFilesCount: explicitFiles.length
39590
- });
39591
- const editContext = isEditIntent && explicitFiles.length > 0 ? await buildEditContext(opts.root, explicitFiles, 200, 512 * 1024) : "";
40070
+ explicitFiles});
40071
+ let editTargets = explicitFiles;
40072
+ if (isEditIntent && editTargets.length === 0) {
40073
+ try {
40074
+ const repoFiles = await getRepoFiles(opts.root);
40075
+ const llmTargets = await llmSelectEditTargets(opts.root, request, repoFiles);
40076
+ editTargets = llmTargets;
40077
+ } catch {
40078
+ }
40079
+ }
40080
+ const editContext = isEditIntent && editTargets.length > 0 ? await buildEditContext(opts.root, editTargets, 200, 512 * 1024) : "";
39592
40081
  if (opts.attachedFiles && opts.attachedFiles.length > 0) {
39593
40082
  const mapRes = await mapAttachmentsToTargets(opts.attachedFiles, {
39594
40083
  root: opts.root,
@@ -39645,16 +40134,17 @@ ${editContext}`;
39645
40134
  data_base64: f3.content ? Buffer.from(f3.content, "utf8").toString("base64") : void 0
39646
40135
  })).map((a) => a.data_base64 ? a : { ...a, data_base64: void 0 }) : [];
39647
40136
  const pathAttachments = [];
40137
+ const attachedPathSet = /* @__PURE__ */ new Set();
39648
40138
  if (explicitFiles.length > 0) {
39649
40139
  try {
39650
- const fs51 = await import('fs/promises');
40140
+ const fs52 = await import('fs/promises');
39651
40141
  const pathMod = await import('path');
39652
40142
  for (const rel of explicitFiles) {
39653
40143
  try {
39654
40144
  const full = explicitAbsMap[rel] || pathMod.join(opts.root, rel);
39655
- const stat13 = await fs51.stat(full).catch(() => null);
40145
+ const stat13 = await fs52.stat(full).catch(() => null);
39656
40146
  if (!stat13 || !stat13.isFile()) continue;
39657
- const buf = await fs51.readFile(full);
40147
+ const buf = await fs52.readFile(full);
39658
40148
  const ext2 = (pathMod.extname(full) || "").toLowerCase();
39659
40149
  const mime = ext2 === ".pdf" ? "application/pdf" : ext2 === ".png" ? "image/png" : ext2 === ".jpg" || ext2 === ".jpeg" ? "image/jpeg" : ext2 === ".webp" ? "image/webp" : ext2 === ".gif" ? "image/gif" : ext2 === ".bmp" ? "image/bmp" : ext2 === ".svg" ? "image/svg+xml" : ext2 === ".tif" || ext2 === ".tiff" ? "image/tiff" : ext2 === ".heic" ? "image/heic" : ext2 === ".heif" ? "image/heif" : "text/plain";
39660
40150
  pathAttachments.push({
@@ -39663,6 +40153,33 @@ ${editContext}`;
39663
40153
  mime,
39664
40154
  data_base64: buf.toString("base64")
39665
40155
  });
40156
+ attachedPathSet.add(full.toLowerCase());
40157
+ } catch {
40158
+ }
40159
+ }
40160
+ } catch {
40161
+ }
40162
+ }
40163
+ if (isEditIntent && Array.isArray(editTargets) && editTargets.length > 0) {
40164
+ try {
40165
+ const fs52 = await import('fs/promises');
40166
+ const pathMod = await import('path');
40167
+ for (const rel of editTargets) {
40168
+ try {
40169
+ const full = pathMod.isAbsolute(rel) ? rel : pathMod.join(opts.root, rel);
40170
+ const key = full.toLowerCase();
40171
+ if (attachedPathSet.has(key)) continue;
40172
+ const stat13 = await fs52.stat(full).catch(() => null);
40173
+ if (!stat13 || !stat13.isFile()) continue;
40174
+ const ext2 = (pathMod.extname(full) || "").toLowerCase();
40175
+ const buf = await fs52.readFile(full);
40176
+ pathAttachments.push({
40177
+ name: pathMod.basename(full),
40178
+ path: full,
40179
+ mime: "text/plain",
40180
+ data_base64: buf.toString("base64")
40181
+ });
40182
+ attachedPathSet.add(key);
39666
40183
  } catch {
39667
40184
  }
39668
40185
  }
@@ -39672,7 +40189,7 @@ ${editContext}`;
39672
40189
  const hydratedCtx = [];
39673
40190
  if (ctxAttachments.length > 0) {
39674
40191
  try {
39675
- const fs51 = await import('fs/promises');
40192
+ const fs52 = await import('fs/promises');
39676
40193
  for (const a of ctxAttachments) {
39677
40194
  if (a.data_base64) {
39678
40195
  hydratedCtx.push(a);
@@ -39683,12 +40200,12 @@ ${editContext}`;
39683
40200
  continue;
39684
40201
  }
39685
40202
  try {
39686
- const stat13 = await fs51.stat(p).catch(() => null);
40203
+ const stat13 = await fs52.stat(p).catch(() => null);
39687
40204
  if (!stat13 || !stat13.isFile()) {
39688
40205
  hydratedCtx.push(a);
39689
40206
  continue;
39690
40207
  }
39691
- const buf = await fs51.readFile(p);
40208
+ const buf = await fs52.readFile(p);
39692
40209
  hydratedCtx.push({ ...a, data_base64: buf.toString("base64") });
39693
40210
  } catch {
39694
40211
  hydratedCtx.push(a);
@@ -39738,46 +40255,60 @@ ${editContext}`;
39738
40255
  codeOutput = outcome.data?.output || "";
39739
40256
  }
39740
40257
  const blocks = extractBlocks(codeOutput);
39741
- if (explicitFiles.length > 0) {
40258
+ if (explicitFiles.length > 0 || isEditIntent && editTargets.length > 0) {
39742
40259
  const mapped = /* @__PURE__ */ new Set();
40260
+ const targets = explicitFiles.length > 0 ? explicitFiles : editTargets;
40261
+ const absMap = explicitFiles.length > 0 ? explicitAbsMap : /* @__PURE__ */ Object.create(null);
39743
40262
  const htmlIdx = blocks.findIndex((b) => /html/i.test(b.language));
39744
- const htmlFile = explicitFiles.find((f3) => f3.toLowerCase().endsWith(".html"));
40263
+ const htmlFile = targets.find((f3) => f3.toLowerCase().endsWith(".html"));
39745
40264
  if (htmlIdx >= 0 && htmlFile) {
39746
- initial.push({ path: htmlFile, absPath: explicitAbsMap[htmlFile], noNormalize: true, kind: "source", action: "create", description: "HTML page", language: "html", preview: blocks[htmlIdx].code });
40265
+ initial.push({ path: htmlFile, absPath: absMap[htmlFile], noNormalize: true, kind: "source", action: "modify", description: "HTML page", language: "html", preview: blocks[htmlIdx].code });
39747
40266
  mapped.add(htmlFile);
39748
40267
  }
39749
40268
  const jsIdx = blocks.findIndex((b) => /(ts|tsx|jsx|javascript|js)/i.test(b.language));
39750
- const jsFile = explicitFiles.find((f3) => f3.toLowerCase().endsWith(".js"));
40269
+ const jsFile = targets.find((f3) => f3.toLowerCase().endsWith(".js"));
39751
40270
  if (jsIdx >= 0 && jsFile && !mapped.has(jsFile)) {
39752
- initial.push({ path: jsFile, absPath: explicitAbsMap[jsFile], noNormalize: true, kind: "source", action: "create", description: "Script", language: "javascript", preview: blocks[jsIdx].code });
40271
+ initial.push({ path: jsFile, absPath: absMap[jsFile], noNormalize: true, kind: "source", action: "modify", description: "Script", language: "javascript", preview: blocks[jsIdx].code });
39753
40272
  mapped.add(jsFile);
39754
40273
  }
39755
40274
  const cssIdx = blocks.findIndex((b) => /css/i.test(b.language));
39756
- const cssFile = explicitFiles.find((f3) => f3.toLowerCase().endsWith(".css"));
40275
+ const cssFile = targets.find((f3) => f3.toLowerCase().endsWith(".css"));
39757
40276
  if (cssIdx >= 0 && cssFile && !mapped.has(cssFile)) {
39758
- initial.push({ path: cssFile, absPath: explicitAbsMap[cssFile], noNormalize: true, kind: "source", action: "create", description: "Stylesheet", language: "css", preview: blocks[cssIdx].code });
40277
+ initial.push({ path: cssFile, absPath: absMap[cssFile], noNormalize: true, kind: "source", action: "modify", description: "Stylesheet", language: "css", preview: blocks[cssIdx].code });
39759
40278
  mapped.add(cssFile);
39760
40279
  }
39761
- for (const f3 of explicitFiles) {
40280
+ for (const f3 of targets) {
39762
40281
  if (mapped.has(f3)) continue;
39763
- const plan = scaffoldForFilename(f3, explicitFiles);
39764
- plan.absPath = explicitAbsMap[f3];
39765
- plan.noNormalize = true;
39766
- initial.push(plan);
40282
+ const ext2 = (() => {
40283
+ const m2 = f3.lastIndexOf(".");
40284
+ return m2 >= 0 ? f3.slice(m2) : "";
40285
+ })();
40286
+ const lang = languageFromExt(ext2);
40287
+ const strongMatch = /pong|retro|game|index\.(html)$|script\.(js|ts)$|style\.css/i.test(f3) || tokenizeRequest(request).some((t2) => f3.toLowerCase().includes(t2));
40288
+ if (strongMatch) {
40289
+ initial.push({ path: f3, absPath: absMap[f3], noNormalize: true, kind: "source", action: "modify", description: "Edit target", language: lang, preview: "" });
40290
+ }
39767
40291
  }
39768
40292
  } else {
39769
- for (let i2 = 0; i2 < blocks.length; i2++) {
39770
- const b = blocks[i2];
39771
- const desired = typeof b.filename === "string" && b.filename.trim() ? b.filename.trim() : null;
39772
- const path64 = desired || suggestName2(request, b.language, i2);
39773
- initial.push({
39774
- path: path64,
39775
- kind: "source",
39776
- action: "create",
39777
- description: describe2(b.language, ""),
39778
- language: b.language,
39779
- preview: b.code
39780
- });
40293
+ try {
40294
+ const repoFiles = await getRepoFiles(opts.root);
40295
+ for (let i2 = 0; i2 < blocks.length; i2++) {
40296
+ const b = blocks[i2];
40297
+ const decision = await llmMapBlockToFile(opts.root, request, b, repoFiles);
40298
+ if (decision.action === "modify" && repoFiles.includes(decision.path)) {
40299
+ const lang = languageFromExt(decision.path.replace(/^.*(\.[a-z0-9]+)$/i, "$1"));
40300
+ initial.push({ path: decision.path, kind: "source", action: "modify", description: "Modify existing file", language: lang, preview: b.code });
40301
+ } else {
40302
+ const path65 = decision.path || suggestName2(request, b.language, i2);
40303
+ initial.push({ path: path65, kind: "source", action: "create", description: describe2(b.language, ""), language: b.language, preview: b.code });
40304
+ }
40305
+ }
40306
+ } catch {
40307
+ for (let i2 = 0; i2 < blocks.length; i2++) {
40308
+ const b = blocks[i2];
40309
+ const path65 = suggestName2(request, b.language, i2);
40310
+ initial.push({ path: path65, kind: "source", action: "create", description: describe2(b.language, ""), language: b.language, preview: b.code });
40311
+ }
39781
40312
  }
39782
40313
  }
39783
40314
  }
@@ -39802,13 +40333,13 @@ ${editContext}`;
39802
40333
  { root: opts.root }
39803
40334
  );
39804
40335
  try {
39805
- const [{ access: access18 }, path64] = await Promise.all([
40336
+ const [{ access: access18 }, path65] = await Promise.all([
39806
40337
  import('fs/promises'),
39807
40338
  import('path')
39808
40339
  ]);
39809
40340
  for (const p of normalized) {
39810
40341
  try {
39811
- const absCandidate = p.absPath ? p.absPath : path64.join(opts.root, p.path);
40342
+ const absCandidate = p.absPath ? p.absPath : path65.join(opts.root, p.path);
39812
40343
  await access18(absCandidate);
39813
40344
  p.action = "modify";
39814
40345
  if (isEditIntent) {
@@ -39817,6 +40348,9 @@ ${editContext}`;
39817
40348
  } catch {
39818
40349
  p.action = "create";
39819
40350
  }
40351
+ if (p.action === "modify" && (!p.preview || p.preview.length === 0)) {
40352
+ p.action = "skip";
40353
+ }
39820
40354
  }
39821
40355
  } catch {
39822
40356
  }
@@ -40046,12 +40580,12 @@ function languageExt(lang) {
40046
40580
  }
40047
40581
  async function journalResume(root, request, files) {
40048
40582
  try {
40049
- const fs51 = await import('fs/promises');
40583
+ const fs52 = await import('fs/promises');
40050
40584
  const dir = path10__namespace.default.join(root, ".maria", "memory");
40051
- await fs51.mkdir(dir, { recursive: true });
40585
+ await fs52.mkdir(dir, { recursive: true });
40052
40586
  const out = path10__namespace.default.join(dir, "resume-plan.json");
40053
40587
  const payload = { request, createdAt: (/* @__PURE__ */ new Date()).toISOString(), files };
40054
- await fs51.writeFile(out, JSON.stringify(payload, null, 2), "utf8");
40588
+ await fs52.writeFile(out, JSON.stringify(payload, null, 2), "utf8");
40055
40589
  } catch {
40056
40590
  }
40057
40591
  }
@@ -40179,6 +40713,68 @@ function detectEditIntent(request, ctx2) {
40179
40713
  const mentionsEdit = editKeywords.some((k) => r2.includes(k));
40180
40714
  return ctx2.hasAttachments || ctx2.explicitFilesCount > 0 || mentionsEdit;
40181
40715
  }
40716
+ async function detectEditIntentEnhanced(root, request, ctx2) {
40717
+ const baseline = detectEditIntent(request, { hasAttachments: ctx2.hasAttachments, explicitFilesCount: ctx2.explicitFiles.length });
40718
+ if (baseline) return true;
40719
+ const r2 = request.toLowerCase();
40720
+ const softHints = ["improve", "enhance", "make it", "change the", "adjust", "tweak", "rework", "revamp", "retro", "modernize", "clean up", "polish"];
40721
+ const hasSoftHint = softHints.some((k) => r2.includes(k));
40722
+ if (!hasSoftHint) return false;
40723
+ try {
40724
+ const fs52 = await import('fs/promises');
40725
+ const pathMod = await import('path');
40726
+ const { loadGlobby: loadGlobby2 } = await Promise.resolve().then(() => (init_esm_compat(), esm_compat_exports));
40727
+ const globby = await loadGlobby2();
40728
+ const ignore = [
40729
+ "**/node_modules/**",
40730
+ "**/.git/**",
40731
+ "**/dist/**",
40732
+ "**/build/**",
40733
+ "**/.maria/**",
40734
+ "**/.next/**",
40735
+ "**/coverage/**",
40736
+ "**/.DS_Store/**",
40737
+ "**/.DS_Store",
40738
+ "**/.Spotlight-V100/**",
40739
+ "**/.Spotlight-V100",
40740
+ "**/.Trashes/**",
40741
+ "**/.Trashes",
40742
+ "**/.fseventsd/**",
40743
+ "**/.fseventsd",
40744
+ "**/.TemporaryItems/**",
40745
+ "**/.TemporaryItems"
40746
+ ];
40747
+ const candidates = await globby(["**/*.{html,css,js,ts,tsx}"], { cwd: root, absolute: true, gitignore: true, ignore, deep: 3 });
40748
+ if (candidates && candidates.length > 0) return true;
40749
+ } catch {
40750
+ }
40751
+ try {
40752
+ const short = request.trim().length <= 160;
40753
+ if (!short) return false;
40754
+ const system = [
40755
+ "You are classifying whether the user intends to EDIT existing files vs CREATE a new project.",
40756
+ 'Return JSON: { "edit": true|false } only.'
40757
+ ].join("\n");
40758
+ const user = `Instruction: ${request}`;
40759
+ const resp = await executeChat([
40760
+ { role: "system", content: system },
40761
+ { role: "user", content: user }
40762
+ ]);
40763
+ const raw = (resp?.output || "").trim();
40764
+ const jsonText = (() => {
40765
+ try {
40766
+ const m2 = raw.match(/\{[\s\S]*\}/);
40767
+ return m2 ? m2[0] : raw;
40768
+ } catch {
40769
+ return raw;
40770
+ }
40771
+ })();
40772
+ const parsed = JSON.parse(jsonText);
40773
+ if (typeof parsed?.edit === "boolean") return !!parsed.edit;
40774
+ } catch {
40775
+ }
40776
+ return false;
40777
+ }
40182
40778
  function sanitizeFolderName(name2) {
40183
40779
  const base = name2.toLowerCase().replace(/[`~!@#$%^&*()+=\[\]{}|;:'",<>/?\\]/g, " ").replace(/\s+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
40184
40780
  return base || "project";
@@ -40211,14 +40807,14 @@ async function ensureTopFolder(root, proposed, plans) {
40211
40807
  return { folderName: unique };
40212
40808
  }
40213
40809
  async function ensureUniqueFolder(root, base) {
40214
- const fs51 = await import('fs/promises');
40810
+ const fs52 = await import('fs/promises');
40215
40811
  const pathMod = await import('path');
40216
40812
  let name2 = sanitizeFolderName(base);
40217
40813
  let suffix = 0;
40218
40814
  for (; suffix < Number.MAX_SAFE_INTEGER; ) {
40219
40815
  const candidate = suffix === 0 ? name2 : `${name2}-${String(suffix).padStart(2, "0")}`;
40220
40816
  try {
40221
- await fs51.access(pathMod.join(root, candidate));
40817
+ await fs52.access(pathMod.join(root, candidate));
40222
40818
  suffix += 1;
40223
40819
  } catch {
40224
40820
  return candidate;
@@ -40228,13 +40824,13 @@ async function ensureUniqueFolder(root, base) {
40228
40824
  }
40229
40825
  async function buildEditContext(root, files, maxLines, maxBytes) {
40230
40826
  try {
40231
- const fs51 = await import('fs/promises');
40827
+ const fs52 = await import('fs/promises');
40232
40828
  const pathMod = await import('path');
40233
40829
  const sections = [];
40234
40830
  for (const rel of files) {
40235
40831
  const full = pathMod.join(root, rel);
40236
40832
  try {
40237
- const buf = await fs51.readFile(full);
40833
+ const buf = await fs52.readFile(full);
40238
40834
  const clipped = buf.length > maxBytes ? buf.subarray(0, maxBytes) : buf;
40239
40835
  const text = clipped.toString("utf8").replace(/\r\n/g, "\n");
40240
40836
  const lines = text.split("\n").slice(0, maxLines).join("\n");
@@ -40251,84 +40847,58 @@ ${lines}
40251
40847
  }
40252
40848
  }
40253
40849
  async function resolveExplicitPaths(root, files, hintText) {
40254
- const fs51 = await import('fs/promises');
40850
+ const fs52 = await import('fs/promises');
40255
40851
  const pathMod = await import('path');
40256
- const { loadGlobby: loadGlobby2 } = await Promise.resolve().then(() => (init_esm_compat(), esm_compat_exports));
40257
- const globby = await loadGlobby2();
40258
- const ignore = [
40259
- "**/node_modules/**",
40260
- "**/.git/**",
40261
- "**/dist/**",
40262
- "**/build/**",
40263
- "**/.maria/**",
40264
- "**/.next/**",
40265
- "**/coverage/**"
40266
- ];
40267
- const hintTokens = Array.from(new Set(hintText.toLowerCase().replace(/[^a-z0-9_/.-]+/g, " ").split(/\s+/).filter(Boolean)));
40268
- async function findBest(rel) {
40269
- const normalized = rel.replace(/^\/+/, "").replace(/\\/g, "/");
40270
- const fullExact = pathMod.join(root, normalized);
40271
- try {
40272
- await fs51.access(fullExact);
40273
- return normalized;
40274
- } catch {
40852
+ const ignoreDir = /* @__PURE__ */ new Set([
40853
+ ".git",
40854
+ "node_modules",
40855
+ "dist",
40856
+ "build",
40857
+ ".maria",
40858
+ ".next",
40859
+ "coverage",
40860
+ ".DS_Store",
40861
+ ".Spotlight-V100",
40862
+ ".Trashes",
40863
+ ".fseventsd",
40864
+ ".TemporaryItems"
40865
+ ]);
40866
+ async function buildRepoIndex() {
40867
+ if (_repoFileIndexCache && _repoFileIndexCache.root === root) return _repoFileIndexCache.files;
40868
+ const out2 = [];
40869
+ async function walk2(dir) {
40870
+ let entries = [];
40871
+ try {
40872
+ entries = await fs52.readdir(dir, { withFileTypes: true });
40873
+ } catch {
40874
+ return;
40875
+ }
40876
+ for (const e2 of entries) {
40877
+ const name2 = e2.name;
40878
+ if (ignoreDir.has(name2)) continue;
40879
+ const full = pathMod.join(dir, name2);
40880
+ if (e2.isDirectory()) {
40881
+ await walk2(full);
40882
+ continue;
40883
+ }
40884
+ out2.push(pathMod.relative(root, full).replace(/\\/g, "/"));
40885
+ }
40275
40886
  }
40887
+ await walk2(root);
40888
+ _repoFileIndexCache = { root, files: out2 };
40889
+ return out2;
40890
+ }
40891
+ const allFiles = await buildRepoIndex();
40892
+ const hintTokens = Array.from(new Set(hintText.toLowerCase().replace(/[^a-z0-9_/.-]+/g, " ").split(/\s+/).filter(Boolean)));
40893
+ async function rankCandidates(candidates, desired) {
40894
+ const normalized = desired.replace(/^\/+/, "").replace(/\\/g, "/");
40276
40895
  const lowerRel = normalized.toLowerCase();
40277
40896
  const ext2 = (pathMod.extname(normalized) || "").toLowerCase();
40278
40897
  const base = pathMod.basename(normalized);
40279
- const nameNoExt = base.replace(/\.[^.]+$/, "").toLowerCase();
40898
+ base.replace(/\.[^.]+$/, "").toLowerCase();
40280
40899
  const parentPath = pathMod.dirname(normalized);
40281
40900
  const parent = parentPath.split("/").pop() || "";
40282
40901
  const dirParts = parentPath === "." ? [] : parentPath.split("/").filter(Boolean).map((s2) => s2.toLowerCase());
40283
- const patterns = [];
40284
- patterns.push(`**/${normalized}`);
40285
- if (parent && parent !== "." && parent !== "/") patterns.push(`**/${parent}/${base}`);
40286
- if (base) patterns.push(`**/${base}`);
40287
- patterns.push(`**/*${base}`);
40288
- if (!ext2) {
40289
- patterns.push(`**/${nameNoExt}.*`);
40290
- }
40291
- const uniqPatterns = Array.from(new Set(patterns));
40292
- const candidatesSet = /* @__PURE__ */ new Set();
40293
- for (const pat of uniqPatterns) {
40294
- const found = await globby(pat, { cwd: root, absolute: false, gitignore: true, ignore });
40295
- for (const f3 of found) {
40296
- candidatesSet.add(f3.replace(/\\/g, "/"));
40297
- }
40298
- if (candidatesSet.size > 200) break;
40299
- }
40300
- let candidates = Array.from(candidatesSet);
40301
- if (dirParts.length > 0) {
40302
- const filtered = candidates.filter((relp) => {
40303
- const segs = relp.toLowerCase().split("/");
40304
- let pos = -1;
40305
- for (const part of dirParts) {
40306
- const next = segs.indexOf(part, pos + 1);
40307
- if (next === -1) return false;
40308
- pos = next;
40309
- }
40310
- return true;
40311
- });
40312
- if (filtered.length > 0) candidates = filtered;
40313
- }
40314
- if (candidates.length === 0) {
40315
- const prefixes = ["", "src/", "app/", "pages/"];
40316
- for (const pre of prefixes) {
40317
- const cand = pathMod.join(root, pre + normalized);
40318
- try {
40319
- await fs51.access(cand);
40320
- return (pre + normalized).replace(/^\/+/, "");
40321
- } catch {
40322
- }
40323
- }
40324
- if (dirParts.length > 0) {
40325
- if (/^(src|app|pages)\//i.test(normalized)) return normalized;
40326
- const pref = await pickExistingFolderPrefix(root, parentPath);
40327
- const combined = (pref + normalized).replace(/^\/+/, "").replace(/^(src\/)src\//i, "$1");
40328
- return combined;
40329
- }
40330
- return normalized.replace(/^(src\/)src\//i, "$1");
40331
- }
40332
40902
  function score(relPath) {
40333
40903
  const lower2 = relPath.toLowerCase();
40334
40904
  let s2 = 0;
@@ -40352,10 +40922,36 @@ async function resolveExplicitPaths(root, files, hintText) {
40352
40922
  s2 -= Math.min(10, Math.floor(relPath.length / 80));
40353
40923
  return s2;
40354
40924
  }
40355
- let best = candidates[0];
40925
+ const limited = candidates.slice(0, Math.min(50, candidates.length));
40926
+ if (limited.length > 1) {
40927
+ try {
40928
+ const ranked = limited.map((p) => ({ p, s: score(p) })).sort((a, b) => b.s - a.s).slice(0, 5).map((x2) => x2.p);
40929
+ const system = [
40930
+ "You are helping choose the most relevant target file path for an edit operation.",
40931
+ "Given a user request and several candidate repo-relative paths, pick ONE best path.",
40932
+ "Return ONLY the raw path text. No code blocks. No explanations."
40933
+ ].join("\n");
40934
+ const user = [
40935
+ `User request: ${hintText}`,
40936
+ "Candidates:",
40937
+ ...ranked.map((r2, i2) => `${i2 + 1}. ${r2}`),
40938
+ "",
40939
+ "Answer with exactly one of the candidate paths."
40940
+ ].join("\n");
40941
+ const chat = await executeChat([
40942
+ { role: "system", content: system },
40943
+ { role: "user", content: user }
40944
+ ]);
40945
+ const raw = (chat.output || "").trim();
40946
+ const pick = ranked.find((r2) => r2 === raw) || ranked.find((r2) => raw.includes(r2)) || ranked[0];
40947
+ return pick.replace(/^\/+/, "");
40948
+ } catch {
40949
+ }
40950
+ }
40951
+ let best = limited[0];
40356
40952
  let bestScore = score(best);
40357
- for (let i2 = 1; i2 < candidates.length; i2++) {
40358
- const c = candidates[i2];
40953
+ for (let i2 = 1; i2 < limited.length; i2++) {
40954
+ const c = limited[i2];
40359
40955
  const sc = score(c);
40360
40956
  if (sc > bestScore) {
40361
40957
  best = c;
@@ -40365,20 +40961,41 @@ async function resolveExplicitPaths(root, files, hintText) {
40365
40961
  return best.replace(/^\/+/, "");
40366
40962
  }
40367
40963
  const out = [];
40368
- for (const f3 of files) {
40369
- out.push(await findBest(f3));
40964
+ for (const desired of files) {
40965
+ const normalized = desired.replace(/^\/+/, "").replace(/\\/g, "/");
40966
+ if (allFiles.includes(normalized)) {
40967
+ out.push(normalized);
40968
+ continue;
40969
+ }
40970
+ const base = pathMod.basename(normalized).toLowerCase();
40971
+ const nameNoExt = base.replace(/\.[^.]+$/, "");
40972
+ const candidates = allFiles.filter((p) => {
40973
+ const b = pathMod.basename(p).toLowerCase();
40974
+ return b === base || b.includes(base) || !base.includes(".") && (b === `${nameNoExt}.ts` || b === `${nameNoExt}.js` || b.startsWith(nameNoExt));
40975
+ });
40976
+ if (candidates.length === 0) {
40977
+ const parentPath = pathMod.dirname(normalized);
40978
+ if (parentPath && parentPath !== ".") {
40979
+ const pref = await pickExistingFolderPrefix(root, parentPath);
40980
+ out.push((pref + normalized).replace(/^\/+/, "").replace(/^(src\/)src\//i, "$1"));
40981
+ } else {
40982
+ out.push(normalized.replace(/^(src\/)src\//i, "$1"));
40983
+ }
40984
+ continue;
40985
+ }
40986
+ out.push(await rankCandidates(candidates, normalized));
40370
40987
  }
40371
40988
  return out;
40372
40989
  }
40373
40990
  async function pickExistingFolderPrefix(root, parentPath) {
40374
- const fs51 = await import('fs/promises');
40991
+ const fs52 = await import('fs/promises');
40375
40992
  const pathMod = await import('path');
40376
40993
  const parts = parentPath.replace(/^\/+/, "").split("/").filter(Boolean);
40377
40994
  const prefixes = ["src", "app", "pages", ""];
40378
40995
  for (const pre of prefixes) {
40379
40996
  const test = pre ? pathMod.join(root, pre, ...parts) : pathMod.join(root, ...parts);
40380
40997
  try {
40381
- await fs51.access(test);
40998
+ await fs52.access(test);
40382
40999
  return pre ? `${pre}/` : "";
40383
41000
  } catch {
40384
41001
  }
@@ -40425,7 +41042,7 @@ requestAnimationFrame(step);`;
40425
41042
  }
40426
41043
  return { path: filename, kind: "source", action: "create", description: "Source file", preview: "" };
40427
41044
  }
40428
- var codeFallbackManager, codeFallbackPolicySignature;
41045
+ var codeFallbackManager, _repoFileIndexCache, codeFallbackPolicySignature;
40429
41046
  var init_Orchestrator = __esm({
40430
41047
  "src/services/code-orchestrator/Orchestrator.ts"() {
40431
41048
  init_RepoScanner();
@@ -40440,6 +41057,7 @@ var init_Orchestrator = __esm({
40440
41057
  init_FallbackManager();
40441
41058
  init_policy();
40442
41059
  codeFallbackManager = null;
41060
+ _repoFileIndexCache = null;
40443
41061
  codeFallbackPolicySignature = null;
40444
41062
  }
40445
41063
  });
@@ -40552,8 +41170,8 @@ var init_code_command = __esm({
40552
41170
  try {
40553
41171
  const res = await orchestrate2(request, { root, flags: { planOnly: opts.planOnly, apply: opts.apply, dryRun: opts.dryRun, interactive: opts.interactive, yes: opts.yes, maxFiles: opts.maxFiles, output: opts.output, hideCode: opts.noCode, previewLines: this.normalizePreviewLines(opts.previewLines), verbose: opts.verbose, onlyAttached: opts.onlyAttached, attachMode: opts.attachMode, maxAttachments: opts.maxAttachments, diffLines: opts.diffLines, diffBytes: opts.diffBytes, diffHunks: opts.diffHunks, diffGlobalMaxFiles: opts.diffGlobalMaxFiles, diffGlobalMaxBytes: opts.diffGlobalMaxBytes, allowDotfiles: opts.allowDotfiles }, abortSignal: abort.signal, attachedFiles: attachments });
40554
41172
  if (opts.planOnly) {
40555
- const fs51 = await import('fs/promises');
40556
- const path64 = await import('path');
41173
+ const fs52 = await import('fs/promises');
41174
+ const path65 = await import('path');
40557
41175
  const spec = res?.specMarkdown;
40558
41176
  const lines = Array.isArray(res?.summaryLines) ? res.summaryLines : [];
40559
41177
  const planItems = [];
@@ -40587,13 +41205,13 @@ var init_code_command = __esm({
40587
41205
  if (planItems.length) md.push(...planItems);
40588
41206
  else md.push("- (no summary available)");
40589
41207
  }
40590
- const plansDir = path64.join(root, ".maria", "plans");
40591
- await fs51.mkdir(plansDir, { recursive: true });
41208
+ const plansDir = path65.join(root, ".maria", "plans");
41209
+ await fs52.mkdir(plansDir, { recursive: true });
40592
41210
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
40593
41211
  const fileName = `code-plan-${ts}.md`;
40594
- const outPath = path64.join(plansDir, fileName);
40595
- await fs51.writeFile(outPath, md.join("\n") + "\n", "utf8");
40596
- const rel = path64.relative(root, outPath);
41212
+ const outPath = path65.join(plansDir, fileName);
41213
+ await fs52.writeFile(outPath, md.join("\n") + "\n", "utf8");
41214
+ const rel = path65.relative(root, outPath);
40597
41215
  return this.success(`Code plan saved: ${rel}`);
40598
41216
  }
40599
41217
  const detail = res?.detailLines;
@@ -40653,6 +41271,97 @@ ${pretty}`);
40653
41271
  // Add default language hints when not specified by the user (LLM-assisted detection)
40654
41272
  async ensureLanguageDefaults(raw) {
40655
41273
  try {
41274
+ const fs52 = await import('fs/promises');
41275
+ const pathMod = await import('path');
41276
+ const cwd2 = process.cwd();
41277
+ const ignoreDir = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".maria", ".next", "coverage", ".DS_Store", ".Spotlight-V100", ".Trashes", ".fseventsd", ".TemporaryItems"]);
41278
+ const pathTokens = /* @__PURE__ */ new Set();
41279
+ const fileLike = raw.match(/([\w\-./\\:]+\.[A-Za-z0-9]{1,10})/g) || [];
41280
+ for (const t2 of fileLike) pathTokens.add(t2);
41281
+ const slashLike = raw.match(/[^\s"']*[\\/][^\s"']+/g) || [];
41282
+ for (const t2 of slashLike) pathTokens.add(t2);
41283
+ const listFilesUnder = async (dir, maxCount) => {
41284
+ const out = [];
41285
+ const walk2 = async (d) => {
41286
+ if (out.length >= maxCount) return;
41287
+ let entries = [];
41288
+ try {
41289
+ entries = await fs52.readdir(d, { withFileTypes: true });
41290
+ } catch {
41291
+ return;
41292
+ }
41293
+ for (const e2 of entries) {
41294
+ const name2 = e2.name;
41295
+ if (ignoreDir.has(name2)) continue;
41296
+ const full = pathMod.join(d, name2);
41297
+ if (e2.isDirectory()) {
41298
+ await walk2(full);
41299
+ if (out.length >= maxCount) break;
41300
+ continue;
41301
+ }
41302
+ if (/\.(ts|tsx|js|jsx|py|java|kt|go|rs|rb|swift|cs|c|cpp|hpp|php|scala|hs|ex|exs|dart|lua|zig|sol|sql|html|css|scss|md|json)$/i.test(name2)) {
41303
+ out.push(full);
41304
+ if (out.length >= maxCount) break;
41305
+ }
41306
+ }
41307
+ };
41308
+ await walk2(dir);
41309
+ return out;
41310
+ };
41311
+ const readHead = async (full, lines) => {
41312
+ try {
41313
+ const buf = await fs52.readFile(full, "utf8");
41314
+ return buf.split(/\r?\n/).slice(0, lines).join("\n");
41315
+ } catch {
41316
+ return "";
41317
+ }
41318
+ };
41319
+ const samples = [];
41320
+ for (const tok of Array.from(pathTokens)) {
41321
+ const abs = pathMod.isAbsolute(tok) ? tok : pathMod.join(cwd2, tok);
41322
+ try {
41323
+ const st = await fs52.stat(abs);
41324
+ if (st.isFile()) {
41325
+ samples.push(await readHead(abs, 10));
41326
+ } else if (st.isDirectory()) {
41327
+ const files = await listFilesUnder(abs, 25);
41328
+ for (const f3 of files) samples.push(await readHead(f3, 10));
41329
+ }
41330
+ } catch {
41331
+ }
41332
+ if (samples.length >= 50) break;
41333
+ }
41334
+ if (samples.length > 0) {
41335
+ const preSpin = new ProcessAnimation();
41336
+ preSpin.start();
41337
+ let llmLang = null;
41338
+ try {
41339
+ llmLang = await this.detectLanguageLLMFromSamples(samples);
41340
+ } finally {
41341
+ try {
41342
+ preSpin.stop();
41343
+ } catch {
41344
+ }
41345
+ }
41346
+ if (llmLang) {
41347
+ const hint2 = (() => {
41348
+ const l = llmLang.toLowerCase();
41349
+ if (l === "tsx") return "TypeScript (React/TSX)";
41350
+ if (l === "jsx") return "JavaScript (React/JSX)";
41351
+ if (l === "typescript") return "TypeScript";
41352
+ if (l === "javascript") return "JavaScript";
41353
+ if (l === "html") return "HTML";
41354
+ if (l === "css") return "CSS";
41355
+ return llmLang;
41356
+ })();
41357
+ return raw + ` (Use ${hint2})`;
41358
+ }
41359
+ }
41360
+ } catch {
41361
+ }
41362
+ try {
41363
+ const preSpin = new ProcessAnimation();
41364
+ preSpin.start();
40656
41365
  const system = [
40657
41366
  "You analyze a user's code-generation request.",
40658
41367
  "Decide if the user explicitly specified a programming language or framework/tooling (e.g., TypeScript, Python, Rust, Java, React, Vue, Node, etc.).",
@@ -40671,8 +41380,12 @@ ${pretty}`);
40671
41380
  ${user}`
40672
41381
  }
40673
41382
  });
41383
+ try {
41384
+ preSpin.stop();
41385
+ } catch {
41386
+ }
40674
41387
  const content = (resp?.data?.content || resp?.content || "").trim();
40675
- const extractFirstJson6 = (text) => {
41388
+ const extractFirstJson7 = (text) => {
40676
41389
  const fence = /```\s*json\s*\r?\n([\s\S]*?)```/i.exec(text);
40677
41390
  if (fence) return fence[1];
40678
41391
  const generic = /```\s*\r?\n([\s\S]*?)```/i.exec(text);
@@ -40695,7 +41408,7 @@ ${user}`
40695
41408
  }
40696
41409
  return null;
40697
41410
  };
40698
- const jsonText = extractFirstJson6(content) || content;
41411
+ const jsonText = extractFirstJson7(content) || content;
40699
41412
  let parsed = {};
40700
41413
  try {
40701
41414
  parsed = JSON.parse(jsonText);
@@ -40708,6 +41421,76 @@ ${user}`
40708
41421
  const hint = " (Use TypeScript and React; prefer functional components and node)";
40709
41422
  return raw + hint;
40710
41423
  }
41424
+ // LLM-based language detection from sample code heads (top-10 lines per file)
41425
+ async detectLanguageLLMFromSamples(samples) {
41426
+ try {
41427
+ const system = [
41428
+ "You are a programming language classifier.",
41429
+ "Given multiple short code excerpts, determine the dominant language across them.",
41430
+ "Respond with ONLY a single token language name from this set:",
41431
+ "[typescript, tsx, javascript, jsx, python, java, go, rust, php, cpp, c, swift, kotlin, ruby, csharp, html, css, scss, json, yaml, markdown].",
41432
+ "If unsure between tsx and jsx, choose tsx if TypeScript types appear, else jsx."
41433
+ ].join("\n");
41434
+ const joined = samples.slice(0, 20).map((s2, i2) => `// sample ${i2 + 1}
41435
+ ${s2}`).join("\n\n");
41436
+ const { callAPI: callAPI2 } = await Promise.resolve().then(() => (init_api_caller(), api_caller_exports));
41437
+ const resp = await callAPI2("/v1/ai-proxy", {
41438
+ method: "POST",
41439
+ body: {
41440
+ provider: "google",
41441
+ model: "gemini-2.5-flash",
41442
+ taskType: "chat",
41443
+ prompt: `${system}
41444
+
41445
+ ${joined}`
41446
+ }
41447
+ });
41448
+ const content = (resp?.data?.content || resp?.content || "").trim();
41449
+ const token = content.replace(/```[\s\S]*?```/g, "").trim().toLowerCase();
41450
+ const norm = this.normalizeLanguageName(token);
41451
+ return norm || null;
41452
+ } catch {
41453
+ return null;
41454
+ }
41455
+ }
41456
+ normalizeLanguageName(lang) {
41457
+ const l = (lang || "").toLowerCase().trim();
41458
+ if (!l) return null;
41459
+ const map = {
41460
+ "ts": "typescript",
41461
+ "typescript": "typescript",
41462
+ "tsx": "tsx",
41463
+ "jsx": "jsx",
41464
+ "js": "javascript",
41465
+ "javascript": "javascript",
41466
+ "py": "python",
41467
+ "python": "python",
41468
+ "java": "java",
41469
+ "go": "go",
41470
+ "rust": "rust",
41471
+ "rs": "rust",
41472
+ "php": "php",
41473
+ "c++": "cpp",
41474
+ "cpp": "cpp",
41475
+ "c": "c",
41476
+ "swift": "swift",
41477
+ "kotlin": "kotlin",
41478
+ "rb": "ruby",
41479
+ "ruby": "ruby",
41480
+ "cs": "csharp",
41481
+ "c#": "csharp",
41482
+ "csharp": "csharp",
41483
+ "html": "html",
41484
+ "css": "css",
41485
+ "scss": "scss",
41486
+ "json": "json",
41487
+ "yaml": "yaml",
41488
+ "yml": "yaml",
41489
+ "markdown": "markdown",
41490
+ "md": "markdown"
41491
+ };
41492
+ return map[l] || null;
41493
+ }
40711
41494
  // v2.0 helpers
40712
41495
  parseV2Options(raw) {
40713
41496
  const opts = { planOnly: false, apply: false, dryRun: false, interactive: false, yes: false, rollback: true, output: void 0, noCode: false, previewLines: 0, root: void 0, maxFiles: void 0, verbose: false, gitGuard: void 0, allowDotfiles: false, confirmOverwrites: [], gitCommit: void 0, gitBranch: void 0, gitTag: void 0, gitTagPrefix: void 0, gitPush: void 0, gitPushRemote: void 0, onlyAttached: false, attachMode: "assist", maxAttachments: 50, diffLines: void 0, diffBytes: void 0, diffHunks: void 0, diffGlobalMaxFiles: void 0, diffGlobalMaxBytes: void 0 };
@@ -40903,16 +41686,16 @@ ${user}`
40903
41686
  }
40904
41687
  async persistLastPlan(root, plans) {
40905
41688
  try {
40906
- const fs51 = await import('fs/promises');
41689
+ const fs52 = await import('fs/promises');
40907
41690
  const p = path10__namespace.join(root, ".maria");
40908
- await fs51.mkdir(p, { recursive: true });
41691
+ await fs52.mkdir(p, { recursive: true });
40909
41692
  const out = path10__namespace.join(p, "last-plan.json");
40910
- await fs51.writeFile(out, JSON.stringify({ createdAt: (/* @__PURE__ */ new Date()).toISOString(), plans }, null, 2), "utf8");
41693
+ await fs52.writeFile(out, JSON.stringify({ createdAt: (/* @__PURE__ */ new Date()).toISOString(), plans }, null, 2), "utf8");
40911
41694
  } catch {
40912
41695
  }
40913
41696
  }
40914
41697
  async applyPlan(plans, options) {
40915
- const fs51 = await import('fs/promises');
41698
+ const fs52 = await import('fs/promises');
40916
41699
  const created = [];
40917
41700
  const modified = [];
40918
41701
  const skipped = [];
@@ -40931,9 +41714,9 @@ ${user}`
40931
41714
  continue;
40932
41715
  }
40933
41716
  const tmp = full + `.tmp-${process.pid}-${Date.now()}`;
40934
- await fs51.mkdir(path10__namespace.dirname(full), { recursive: true });
40935
- await fs51.writeFile(tmp, plan.preview || "", "utf8");
40936
- await fs51.rename(tmp, full);
41717
+ await fs52.mkdir(path10__namespace.dirname(full), { recursive: true });
41718
+ await fs52.writeFile(tmp, plan.preview || "", "utf8");
41719
+ await fs52.rename(tmp, full);
40937
41720
  if (exists2) modified.push(plan.path);
40938
41721
  else created.push(plan.path);
40939
41722
  written++;
@@ -40944,7 +41727,7 @@ ${user}`
40944
41727
  if (options.rollback) {
40945
41728
  for (const p of [...created, ...modified]) {
40946
41729
  try {
40947
- await fs51.unlink(path10__namespace.join(options.root, p));
41730
+ await fs52.unlink(path10__namespace.join(options.root, p));
40948
41731
  } catch {
40949
41732
  }
40950
41733
  }
@@ -40954,8 +41737,8 @@ ${user}`
40954
41737
  }
40955
41738
  async pathExists(p) {
40956
41739
  try {
40957
- const fs51 = await import('fs/promises');
40958
- await fs51.access(p);
41740
+ const fs52 = await import('fs/promises');
41741
+ await fs52.access(p);
40959
41742
  return true;
40960
41743
  } catch {
40961
41744
  return false;
@@ -41066,17 +41849,17 @@ ${user}`
41066
41849
  // Attempt to collect attached files from context; map to AttachedFileContext
41067
41850
  async collectAttachedFiles(context2) {
41068
41851
  const list = [];
41069
- const fs51 = await import('fs/promises');
41070
- const path64 = await import('path');
41852
+ const fs52 = await import('fs/promises');
41853
+ const path65 = await import('path');
41071
41854
  const att = context2 && (context2.attachments || context2.input?.attachments) || [];
41072
41855
  for (const a of att) {
41073
41856
  try {
41074
41857
  const p = a.path || a.filePath || a.name || "";
41075
- const originalName = a.name || path64.basename(p || `attachment_${Date.now().toString(36)}`);
41858
+ const originalName = a.name || path65.basename(p || `attachment_${Date.now().toString(36)}`);
41076
41859
  let content = a.content;
41077
41860
  if (!content && p) {
41078
- const abs = path64.isAbsolute(p) ? p : path64.join(process.cwd(), p);
41079
- content = await fs51.readFile(abs, "utf8");
41861
+ const abs = path65.isAbsolute(p) ? p : path65.join(process.cwd(), p);
41862
+ content = await fs52.readFile(abs, "utf8");
41080
41863
  }
41081
41864
  if (!content) continue;
41082
41865
  list.push({ originalName, pathHint: p || void 0, content, size: Buffer.byteLength(content, "utf8"), mime: a.mime || a.type });
@@ -50659,17 +51442,17 @@ var init_GraphEngine = __esm({
50659
51442
  const visited = /* @__PURE__ */ new Set();
50660
51443
  const queue = [{ id: from, path: [from] }];
50661
51444
  while (queue.length) {
50662
- const { id, path: path64 } = queue.shift();
51445
+ const { id, path: path65 } = queue.shift();
50663
51446
  if (visited.has(id)) continue;
50664
51447
  visited.add(id);
50665
51448
  const neighbors = this.edges.get(id) || /* @__PURE__ */ new Set();
50666
51449
  for (const e2 of neighbors) {
50667
51450
  if (e2.to === to) {
50668
- const res = [...path64, to];
51451
+ const res = [...path65, to];
50669
51452
  this.recordQueryTime(Date.now() - start);
50670
51453
  return res;
50671
51454
  }
50672
- if (!visited.has(e2.to)) queue.push({ id: e2.to, path: [...path64, e2.to] });
51455
+ if (!visited.has(e2.to)) queue.push({ id: e2.to, path: [...path65, e2.to] });
50673
51456
  }
50674
51457
  }
50675
51458
  this.recordQueryTime(Date.now() - start);
@@ -53336,12 +54119,12 @@ This will:
53336
54119
  };
53337
54120
  const result = await this.deltaDetector.detectDelta(root, deltaOptions);
53338
54121
  const files = [
53339
- ...result.changed.map((path64) => ({
53340
- _path: path64,
53341
- type: previousState?.fileHashes?.[path64] ? "modified" : "added"
54122
+ ...result.changed.map((path65) => ({
54123
+ _path: path65,
54124
+ type: previousState?.fileHashes?.[path65] ? "modified" : "added"
53342
54125
  })),
53343
- ...result.deleted.map((path64) => ({
53344
- _path: path64,
54126
+ ...result.deleted.map((path65) => ({
54127
+ _path: path65,
53345
54128
  type: "deleted"
53346
54129
  }))
53347
54130
  ];
@@ -56777,13 +57560,13 @@ async function checkShield(commandName, userPlan = "free") {
56777
57560
  }
56778
57561
  async function loadManifest() {
56779
57562
  try {
56780
- const fs51 = await import('fs');
56781
- const path64 = await import('path');
56782
- const manifestPath = path64.join(
57563
+ const fs52 = await import('fs');
57564
+ const path65 = await import('path');
57565
+ const manifestPath = path65.join(
56783
57566
  __dirname,
56784
57567
  "../command-manifest-v2.1.json"
56785
57568
  );
56786
- const content = fs51.readFileSync(manifestPath, "utf-8");
57569
+ const content = fs52.readFileSync(manifestPath, "utf-8");
56787
57570
  return JSON.parse(content);
56788
57571
  } catch {
56789
57572
  return { commands: [] };
@@ -58278,7 +59061,7 @@ ${user}`,
58278
59061
  };
58279
59062
  }
58280
59063
  });
58281
- function extractFirstJson5(text) {
59064
+ function extractFirstJson6(text) {
58282
59065
  const fence = /```json\r?\n([\s\S]*?)```/i.exec(text);
58283
59066
  if (fence) return fence[1];
58284
59067
  const start = text.indexOf("{");
@@ -58325,7 +59108,7 @@ ${user}`,
58325
59108
  }
58326
59109
  });
58327
59110
  const raw = (response?.data?.content || response?.output || "").trim();
58328
- const jsonText = extractFirstJson5(raw) || raw;
59111
+ const jsonText = extractFirstJson6(raw) || raw;
58329
59112
  let parsed = {};
58330
59113
  try {
58331
59114
  parsed = JSON.parse(jsonText);
@@ -59185,6 +59968,15 @@ async function registerBuiltInCommands() {
59185
59968
  } catch (error2) {
59186
59969
  console.error("Failed to register media commands:", error2);
59187
59970
  }
59971
+ try {
59972
+ const NovelModule = await Promise.resolve().then(() => (init_novel_command(), novel_command_exports));
59973
+ const NovelClass = NovelModule.NovelCommand || NovelModule.default;
59974
+ const novel = new NovelClass();
59975
+ if (novel.initialize) await novel.initialize();
59976
+ commandRegistry.register(novel);
59977
+ } catch (error2) {
59978
+ console.error("Failed to register novel command:", error2);
59979
+ }
59188
59980
  try {
59189
59981
  const { codeCommand: codeCommand2 } = await Promise.resolve().then(() => (init_code_command(), code_command_exports));
59190
59982
  try {
@@ -61780,16 +62572,16 @@ var init_ai_response_service = __esm({
61780
62572
  if (p) candidates.add(p);
61781
62573
  }
61782
62574
  if (candidates.size > 0) {
61783
- const fs51 = await import('fs/promises');
62575
+ const fs52 = await import('fs/promises');
61784
62576
  const pathMod = await import('path');
61785
62577
  const cwd2 = process.cwd();
61786
62578
  for (const cand of candidates) {
61787
62579
  try {
61788
62580
  const normalized = cand.replace(/^"|"$/g, "").replace(/^'|'$/g, "");
61789
62581
  const abs = pathMod.isAbsolute(normalized) ? normalized : pathMod.join(cwd2, normalized);
61790
- const st = await fs51.stat(abs).catch(() => null);
62582
+ const st = await fs52.stat(abs).catch(() => null);
61791
62583
  if (!st || !st.isFile()) continue;
61792
- const buf = await fs51.readFile(abs);
62584
+ const buf = await fs52.readFile(abs);
61793
62585
  const ext2 = (pathMod.extname(abs) || "").toLowerCase();
61794
62586
  const mime = ext2 === ".pdf" ? "application/pdf" : ext2 === ".png" ? "image/png" : ext2 === ".jpg" || ext2 === ".jpeg" ? "image/jpeg" : ext2 === ".webp" ? "image/webp" : ext2 === ".gif" ? "image/gif" : ext2 === ".bmp" ? "image/bmp" : ext2 === ".svg" ? "image/svg+xml" : ext2 === ".tif" || ext2 === ".tiff" ? "image/tiff" : ext2 === ".heic" ? "image/heic" : ext2 === ".heif" ? "image/heif" : "text/plain";
61795
62587
  autoAttachments.push({ name: pathMod.basename(abs), path: abs, mime, data_base64: buf.toString("base64") });
@@ -70134,8 +70926,8 @@ var init_ConfigService = __esm({
70134
70926
  /**
70135
70927
  * ネストされた設定値の取得
70136
70928
  */
70137
- getNestedValue(path64) {
70138
- const keys = path64.split(".");
70929
+ getNestedValue(path65) {
70930
+ const keys = path65.split(".");
70139
70931
  let value = this._config;
70140
70932
  for (const key of keys) {
70141
70933
  if (value && typeof value === "object" && key in value) {
@@ -70165,8 +70957,8 @@ var init_ConfigService = __esm({
70165
70957
  /**
70166
70958
  * ネストされた設定値の更新
70167
70959
  */
70168
- async setNestedValue(path64, value) {
70169
- const keys = path64.split(".");
70960
+ async setNestedValue(path65, value) {
70961
+ const keys = path65.split(".");
70170
70962
  const lastKey = keys.pop();
70171
70963
  let target = this._config;
70172
70964
  for (const key of keys) {
@@ -70179,7 +70971,7 @@ var init_ConfigService = __esm({
70179
70971
  target[lastKey] = value;
70180
70972
  this.validateConfig();
70181
70973
  this.emitChange({
70182
- path: path64,
70974
+ path: path65,
70183
70975
  oldValue,
70184
70976
  newValue: value,
70185
70977
  timestamp: /* @__PURE__ */ new Date()
@@ -70224,8 +71016,8 @@ var init_ConfigService = __esm({
70224
71016
  setupAutoSave() {
70225
71017
  process.on("exit", () => {
70226
71018
  if (this._isDirty) {
70227
- const fs51 = __require("fs");
70228
- fs51.writeFileSync(
71019
+ const fs52 = __require("fs");
71020
+ fs52.writeFileSync(
70229
71021
  this._userConfigPath,
70230
71022
  JSON.stringify(this._config, null, 2),
70231
71023
  "utf-8"
@@ -70236,13 +71028,13 @@ var init_ConfigService = __esm({
70236
71028
  /**
70237
71029
  * 変更リスナーの登録
70238
71030
  */
70239
- onChange(path64, listener) {
70240
- if (!this._listeners.has(path64)) {
70241
- this._listeners.set(path64, []);
71031
+ onChange(path65, listener) {
71032
+ if (!this._listeners.has(path65)) {
71033
+ this._listeners.set(path65, []);
70242
71034
  }
70243
- this._listeners.get(path64).push(listener);
71035
+ this._listeners.get(path65).push(listener);
70244
71036
  return () => {
70245
- const listeners = this._listeners.get(path64);
71037
+ const listeners = this._listeners.get(path65);
70246
71038
  if (listeners) {
70247
71039
  const index = listeners.indexOf(listener);
70248
71040
  if (index !== -1) {
@@ -70731,7 +71523,7 @@ var init_ValidationService = __esm({
70731
71523
  );
70732
71524
  this._schemas.set(
70733
71525
  "filePath",
70734
- zod.z.string().min(1).max(this._config.maxFilePathLength).refine((path64) => !this.containsPathTraversal(path64), {
71526
+ zod.z.string().min(1).max(this._config.maxFilePathLength).refine((path65) => !this.containsPathTraversal(path65), {
70735
71527
  message: "Path traversal detected"
70736
71528
  })
70737
71529
  );
@@ -70870,11 +71662,11 @@ var init_ValidationService = __esm({
70870
71662
  /**
70871
71663
  * ファイルパス検証
70872
71664
  */
70873
- validateFilePath(path64) {
71665
+ validateFilePath(path65) {
70874
71666
  try {
70875
71667
  const schema = this._schemas.get("filePath");
70876
- const result = schema.parse(path64);
70877
- if (this.isSystemPath(path64)) {
71668
+ const result = schema.parse(path65);
71669
+ if (this.isSystemPath(path65)) {
70878
71670
  return {
70879
71671
  valid: false,
70880
71672
  errors: [
@@ -71020,8 +71812,8 @@ var init_ValidationService = __esm({
71020
71812
  /**
71021
71813
  * パストラバーサルの検出
71022
71814
  */
71023
- containsPathTraversal(path64) {
71024
- return /\.\.[/\\]/.test(path64) || path64.includes("..\\") || path64.includes("../");
71815
+ containsPathTraversal(path65) {
71816
+ return /\.\.[/\\]/.test(path65) || path65.includes("..\\") || path65.includes("../");
71025
71817
  }
71026
71818
  /**
71027
71819
  * 危険なコマンドの判定
@@ -71048,7 +71840,7 @@ var init_ValidationService = __esm({
71048
71840
  /**
71049
71841
  * システムパスの判定
71050
71842
  */
71051
- isSystemPath(path64) {
71843
+ isSystemPath(path65) {
71052
71844
  const systemPaths = [
71053
71845
  "/etc",
71054
71846
  "/sys",
@@ -71062,7 +71854,7 @@ var init_ValidationService = __esm({
71062
71854
  "/sbin"
71063
71855
  ];
71064
71856
  return systemPaths.some(
71065
- (sysPath) => path64.toLowerCase().startsWith(sysPath.toLowerCase())
71857
+ (sysPath) => path65.toLowerCase().startsWith(sysPath.toLowerCase())
71066
71858
  );
71067
71859
  }
71068
71860
  /**
@@ -75589,10 +76381,10 @@ Run /doctor --fix to automatically fix ${fixableCount} issue(s)`
75589
76381
  const finalize = async (text, data) => {
75590
76382
  if (outputPath) {
75591
76383
  try {
75592
- const fs51 = await import('fs/promises');
75593
- const path64 = await import('path');
75594
- const abs = path64.resolve(outputPath);
75595
- await fs51.writeFile(abs, text, "utf-8");
76384
+ const fs52 = await import('fs/promises');
76385
+ const path65 = await import('path');
76386
+ const abs = path65.resolve(outputPath);
76387
+ await fs52.writeFile(abs, text, "utf-8");
75596
76388
  const msg = `Saved doctor report to ${abs}`;
75597
76389
  return { ok: true, message: msg, data: { path: abs, bytes: Buffer.byteLength(text, "utf-8") } };
75598
76390
  } catch (e2) {
@@ -75682,10 +76474,10 @@ Run /doctor --fix to automatically fix ${fixableCount} issue(s)`
75682
76474
  const finalize = async (text, data) => {
75683
76475
  if (outputPath) {
75684
76476
  try {
75685
- const fs51 = await import('fs/promises');
75686
- const path64 = await import('path');
75687
- const abs = path64.resolve(outputPath);
75688
- await fs51.writeFile(abs, text, "utf-8");
76477
+ const fs52 = await import('fs/promises');
76478
+ const path65 = await import('path');
76479
+ const abs = path65.resolve(outputPath);
76480
+ await fs52.writeFile(abs, text, "utf-8");
75689
76481
  const msg = `Saved metrics to ${abs}`;
75690
76482
  return { ok: true, message: msg, data: { path: abs, bytes: Buffer.byteLength(text, "utf-8") } };
75691
76483
  } catch (e2) {
@@ -76074,15 +76866,15 @@ var init_SessionOrchestrator = __esm({
76074
76866
  /**
76075
76867
  * 設定の取得
76076
76868
  */
76077
- getConfig(path64) {
76078
- return this._configService?.getNestedValue(path64);
76869
+ getConfig(path65) {
76870
+ return this._configService?.getNestedValue(path65);
76079
76871
  }
76080
76872
  /**
76081
76873
  * 設定の更新
76082
76874
  */
76083
- async setConfig(path64, value) {
76875
+ async setConfig(path65, value) {
76084
76876
  if (this._configService) {
76085
- await this._configService.setNestedValue(path64, value);
76877
+ await this._configService.setNestedValue(path65, value);
76086
76878
  }
76087
76879
  }
76088
76880
  /**
@@ -76200,11 +76992,11 @@ var init_interactive_session2 = __esm({
76200
76992
  getStats() {
76201
76993
  return this.orchestrator.getSessionStats();
76202
76994
  }
76203
- getConfig(path64) {
76204
- return this.orchestrator.getConfig(path64);
76995
+ getConfig(path65) {
76996
+ return this.orchestrator.getConfig(path65);
76205
76997
  }
76206
- async setConfig(path64, value) {
76207
- await this.orchestrator.setConfig(path64, value);
76998
+ async setConfig(path65, value) {
76999
+ await this.orchestrator.setConfig(path65, value);
76208
77000
  }
76209
77001
  };
76210
77002
  }