@bonginkan/maria 4.3.44 → 4.3.46

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
@@ -224,7 +224,8 @@ var init_version = __esm({
224
224
  var ThinkingAnimation, ProcessAnimation;
225
225
  var init_animations = __esm({
226
226
  "src/utils/animations.ts"() {
227
- ThinkingAnimation = class {
227
+ ThinkingAnimation = class _ThinkingAnimation {
228
+ static current = null;
228
229
  frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
229
230
  currentFrame = 0;
230
231
  interval = null;
@@ -233,6 +234,13 @@ var init_animations = __esm({
233
234
  this.message = message;
234
235
  }
235
236
  start() {
237
+ if (_ThinkingAnimation.current && _ThinkingAnimation.current !== this) {
238
+ try {
239
+ _ThinkingAnimation.current.stop();
240
+ } catch {
241
+ }
242
+ }
243
+ _ThinkingAnimation.current = this;
236
244
  this.interval = setInterval(() => {
237
245
  process.stdout.write(
238
246
  `\r${chalk40__default.default.cyan(this.frames[this.currentFrame])} ${chalk40__default.default.gray(this.message)}...`
@@ -246,12 +254,16 @@ var init_animations = __esm({
246
254
  this.interval = null;
247
255
  process.stdout.write("\r\x1B[K");
248
256
  }
257
+ if (_ThinkingAnimation.current === this) {
258
+ _ThinkingAnimation.current = null;
259
+ }
249
260
  }
250
261
  updateMessage(message) {
251
262
  this.message = message;
252
263
  }
253
264
  };
254
- ProcessAnimation = class {
265
+ ProcessAnimation = class _ProcessAnimation {
266
+ static current = null;
255
267
  stages = [
256
268
  { icon: "\u{1F9E0}", message: "Understanding your request" },
257
269
  { icon: "\u{1F50D}", message: "Analyzing context" },
@@ -265,7 +277,18 @@ var init_animations = __esm({
265
277
  interval = null;
266
278
  stageInterval = null;
267
279
  startTime = 0;
280
+ // Expose whether any ProcessAnimation is currently active
281
+ static hasActive() {
282
+ return !!_ProcessAnimation.current;
283
+ }
268
284
  start() {
285
+ if (_ProcessAnimation.current && _ProcessAnimation.current !== this) {
286
+ try {
287
+ _ProcessAnimation.current.stop();
288
+ } catch {
289
+ }
290
+ }
291
+ _ProcessAnimation.current = this;
269
292
  this.startTime = Date.now();
270
293
  this.currentStage = 0;
271
294
  this.currentFrame = 0;
@@ -293,6 +316,9 @@ var init_animations = __esm({
293
316
  this.stageInterval = null;
294
317
  }
295
318
  process.stdout.write("\r\x1B[K");
319
+ if (_ProcessAnimation.current === this) {
320
+ _ProcessAnimation.current = null;
321
+ }
296
322
  }
297
323
  setStage(stageIndex) {
298
324
  if (stageIndex >= 0 && stageIndex < this.stages.length) {
@@ -1709,7 +1735,7 @@ var init_AuthenticationManager = __esm({
1709
1735
  const response = await fetch(`${this.apiBase}/api/user/profile`, {
1710
1736
  headers: {
1711
1737
  "Authorization": `Bearer ${tokens2.accessToken}`,
1712
- "User-Agent": `maria-cli/${process.env.CLI_VERSION || "4.3.44"}`
1738
+ "User-Agent": `maria-cli/${process.env.CLI_VERSION || "4.3.46"}`
1713
1739
  }
1714
1740
  });
1715
1741
  if (response.status === 401) {
@@ -2434,7 +2460,7 @@ async function callApi(path65, init3 = {}) {
2434
2460
  "Authorization": `Bearer ${token}`,
2435
2461
  "X-Device-Id": getDeviceId(),
2436
2462
  "X-Session-Id": getSessionId() || "",
2437
- "User-Agent": `maria-cli/${process.env.CLI_VERSION || "4.3.44"}`,
2463
+ "User-Agent": `maria-cli/${process.env.CLI_VERSION || "4.3.46"}`,
2438
2464
  "Content-Type": init3.headers?.["Content-Type"] || "application/json"
2439
2465
  });
2440
2466
  const doFetch = async (token) => {
@@ -3135,15 +3161,27 @@ async function callAPI(endpoint, options = {}) {
3135
3161
  throw err;
3136
3162
  }
3137
3163
  }
3138
- async function executeChat(messages) {
3164
+ async function executeChat(messages, options) {
3139
3165
  const maxAttempts = 4;
3140
3166
  let lastErr;
3141
3167
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
3142
3168
  try {
3169
+ if (process.env.MARIA_DEBUG === "1") {
3170
+ try {
3171
+ console.log("[DEBUG/ai] chat.request", { attempt, provider: options?.provider, model: options?.model, messages: messages.slice(0, 3) });
3172
+ } catch {
3173
+ }
3174
+ }
3143
3175
  const response = await callAPI("/v1/ai-proxy", {
3144
3176
  method: "POST",
3145
- body: { messages, taskType: "chat" }
3177
+ body: { messages, taskType: "chat", ...options?.provider ? { provider: options.provider } : {}, ...options?.model ? { model: options.model } : {} }
3146
3178
  });
3179
+ if (process.env.MARIA_DEBUG === "1") {
3180
+ try {
3181
+ console.log("[DEBUG/ai] chat.response.head", String(response?.data?.content || response?.output || "").slice(0, 1200));
3182
+ } catch {
3183
+ }
3184
+ }
3147
3185
  return response;
3148
3186
  } catch (e2) {
3149
3187
  lastErr = e2;
@@ -3175,6 +3213,13 @@ async function executeCode(input3) {
3175
3213
  let lastErr;
3176
3214
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
3177
3215
  try {
3216
+ if (process.env.MARIA_DEBUG === "1") {
3217
+ try {
3218
+ const dbg = typeof input3 === "string" ? { prompt: String(input3).slice(0, 800) } : { prompt: input3.prompt.slice(0, 800), provider: input3.provider, model: input3.model, attachments: Array.isArray(input3.attachments) ? input3.attachments.length : 0 };
3219
+ console.log("[DEBUG/ai] code.request", { attempt, ...dbg });
3220
+ } catch {
3221
+ }
3222
+ }
3178
3223
  const response = await callAPI("/v1/ai-proxy", {
3179
3224
  method: "POST",
3180
3225
  body
@@ -3185,6 +3230,12 @@ async function executeCode(input3) {
3185
3230
  if (response.data?.content) {
3186
3231
  response.output = response.data.content;
3187
3232
  }
3233
+ if (process.env.MARIA_DEBUG === "1") {
3234
+ try {
3235
+ console.log("[DEBUG/ai] code.response.head", String(response?.output || "").slice(0, 1200));
3236
+ } catch {
3237
+ }
3238
+ }
3188
3239
  return response;
3189
3240
  } catch (e2) {
3190
3241
  lastErr = e2;
@@ -16416,8 +16467,8 @@ var require_package = __commonJS({
16416
16467
  "package.json"(exports, module) {
16417
16468
  module.exports = {
16418
16469
  name: "@bonginkan/maria",
16419
- version: "4.3.44",
16420
- description: "\u{1F680} MARIA v4.3.44 - 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.",
16470
+ version: "4.3.46",
16471
+ description: "\u{1F680} MARIA v4.3.46 - 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.",
16421
16472
  keywords: [
16422
16473
  "ai",
16423
16474
  "cli",
@@ -24411,7 +24462,10 @@ var init_video_command = __esm({
24411
24462
  const base = (process.env.MARIA_API_BASE || "https://api.maria-code.ai").replace(/\/$/, "");
24412
24463
  const savedPaths = [];
24413
24464
  const baseDir = "video";
24414
- const promptPrefix = (cli.prompt || "").slice(0, 20).replace(/\s+/g, "_").replace(/[^A-Za-z0-9_\-]/g, "_") || "untitled";
24465
+ const rawPrefix = (cli.prompt || "").slice(0, 20).replace(/\s+/g, "_");
24466
+ let promptPrefix = rawPrefix.replace(/[\x00-\x1F<>:"/\\|?*]/g, "_").replace(/[. ]+$/g, "");
24467
+ if (!promptPrefix) promptPrefix = "untitled";
24468
+ if (/^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i.test(promptPrefix)) promptPrefix = `${promptPrefix}_`;
24415
24469
  const utc = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
24416
24470
  let idx = 0;
24417
24471
  for (const relRaw of filesFromApi) {
@@ -26423,7 +26477,7 @@ var init_about_command = __esm({
26423
26477
  async execute(args2, context2) {
26424
26478
  const output3 = [];
26425
26479
  output3.push("");
26426
- output3.push(chalk40__default.default.cyan.bold("\u{1F916} About MARIA v4.3.44"));
26480
+ output3.push(chalk40__default.default.cyan.bold("\u{1F916} About MARIA v4.3.46"));
26427
26481
  output3.push(chalk40__default.default.gray("\u2550".repeat(40)));
26428
26482
  output3.push("");
26429
26483
  output3.push(chalk40__default.default.white.bold("MARIA - Minimal API, Maximum Power"));
@@ -38913,9 +38967,10 @@ function extractFirstJson5(text) {
38913
38967
  async function inferCodeArgs(rawText) {
38914
38968
  const system = [
38915
38969
  "You extract structured options for a code command.",
38916
- 'Return JSON only with keys: { "planOnly"?: boolean, "dryRun"?: boolean, "output"?: "names"|"summary"|"detail", "previewLines"?: number }.',
38970
+ 'Return JSON only with keys: { "planOnly"?: boolean, "dryRun"?: boolean, "output"?: "names"|"summary"|"detail", "previewLines"?: number, "onlyAttached"?: boolean }.',
38917
38971
  "Decide from the user text whether planOnly or dryRun should be true. Do not explain.",
38918
- "Only include output if the user requests preview detail or summary mode. Only include previewLines if a specific number of lines is requested."
38972
+ "Only include output if the user requests preview detail or summary mode. Only include previewLines if a specific number of lines is requested.",
38973
+ "Set onlyAttached=true when the user indicates editing only attached/uploaded/provided files, or restrict changes to referenced files."
38919
38974
  ].join("\n");
38920
38975
  const resp = await callAPI("/v1/ai-proxy", {
38921
38976
  method: "POST",
@@ -38936,11 +38991,19 @@ ${rawText}`,
38936
38991
  } catch {
38937
38992
  return {};
38938
38993
  }
38994
+ if (process.env.MARIA_DEBUG === "1") {
38995
+ try {
38996
+ console.log("[DEBUG/code] inferCodeArgs.response.raw", raw.slice(0, 1200));
38997
+ console.log("[DEBUG/code] inferCodeArgs.parsed", parsed);
38998
+ } catch {
38999
+ }
39000
+ }
38939
39001
  const out = {};
38940
39002
  if (typeof parsed.planOnly === "boolean") out.planOnly = parsed.planOnly;
38941
39003
  if (typeof parsed.dryRun === "boolean") out.dryRun = parsed.dryRun;
38942
39004
  if (typeof parsed.output === "string" && (parsed.output === "names" || parsed.output === "summary" || parsed.output === "detail")) out.output = parsed.output;
38943
39005
  if (typeof parsed.previewLines === "number" && Number.isFinite(parsed.previewLines) && parsed.previewLines > 0) out.previewLines = Math.min(2e3, Math.floor(parsed.previewLines));
39006
+ if (typeof parsed.onlyAttached === "boolean") out.onlyAttached = parsed.onlyAttached;
38944
39007
  if (out.planOnly) out.dryRun = false;
38945
39008
  return out;
38946
39009
  }
@@ -39088,6 +39151,12 @@ var init_FilePlanBuilder = __esm({
39088
39151
  }
39089
39152
  });
39090
39153
  async function validatePlan(plans, opts) {
39154
+ if (process.env.MARIA_DEBUG === "1") {
39155
+ try {
39156
+ console.log("[DEBUG/orchestrator] validatePlan.input.count", plans.length);
39157
+ } catch {
39158
+ }
39159
+ }
39091
39160
  const warnings = [];
39092
39161
  const skipped = [];
39093
39162
  const seenLC = /* @__PURE__ */ new Set();
@@ -39104,6 +39173,10 @@ async function validatePlan(plans, opts) {
39104
39173
  }
39105
39174
  const safe = [];
39106
39175
  for (const fp of outFiltered) {
39176
+ if (fp.action === "skip") {
39177
+ skipped.push(fp.path);
39178
+ continue;
39179
+ }
39107
39180
  const rel = fp.path.replace(/^\/+/, "");
39108
39181
  if (rel.includes("..")) {
39109
39182
  warnings.push(`Path traversal denied: ${fp.path}`);
@@ -39185,7 +39258,18 @@ async function validatePlan(plans, opts) {
39185
39258
  }
39186
39259
  result.push({ ...fp, action: willModify ? "modify" : "create" });
39187
39260
  }
39188
- return { files: result, skipped, warnings };
39261
+ const out = { files: result, skipped, warnings };
39262
+ if (process.env.MARIA_DEBUG === "1") {
39263
+ try {
39264
+ console.log("[DEBUG/orchestrator] validatePlan.output", {
39265
+ files: out.files.map((f3) => ({ path: f3.path, action: f3.action })),
39266
+ skipped: out.skipped,
39267
+ warnings: out.warnings
39268
+ });
39269
+ } catch {
39270
+ }
39271
+ }
39272
+ return out;
39189
39273
  }
39190
39274
  async function exists(p) {
39191
39275
  try {
@@ -39979,7 +40063,7 @@ ${h2.head}`);
39979
40063
  const resp = await executeChat([
39980
40064
  { role: "system", content: system },
39981
40065
  { role: "user", content: user }
39982
- ]);
40066
+ ], { provider: "google", model: "gemini-2.5-flash-lite" });
39983
40067
  const raw = (resp?.output || "").trim();
39984
40068
  const jsonText = extractJsonSafe(raw, "array") || raw;
39985
40069
  const arr = JSON.parse(jsonText);
@@ -39996,7 +40080,7 @@ ${h2.head}`);
39996
40080
  return [];
39997
40081
  }
39998
40082
  }
39999
- async function llmMapBlocksBatch(root, request, blocks, repoFiles) {
40083
+ async function llmMapBlocksBatch(root, request, blocks, repoFiles, opts) {
40000
40084
  try {
40001
40085
  const candidates = repoFiles.filter((p) => /\.(html|css|js|ts|tsx)$/i.test(p)).slice(0, 120);
40002
40086
  const blockSnippets = blocks.slice(0, 20).map((b, i2) => {
@@ -40014,26 +40098,43 @@ ${head2}
40014
40098
  ${h2.head}`);
40015
40099
  }
40016
40100
  const system = [
40017
- "For each provided code block, decide whether to MODIFY an existing repo file or CREATE a new file.",
40018
- 'Return JSON array of objects: [{ "index": number, "action": "modify"|"create", "path": string }].',
40019
- "When action is modify, path MUST be one of the candidate repo-relative paths listed."
40101
+ "You act as a precise file mapper for code edits.",
40102
+ "For each provided code block, decide to MODIFY an existing repo file or CREATE a new file.",
40103
+ "Rules:",
40104
+ "- Prefer MODIFY when an existing file in candidates plausibly matches the block (same technology, same area).",
40105
+ "- If any TargetDirHints are provided, prefer files under those directories when choosing MODIFY targets.",
40106
+ "- Only choose CREATE if NO suitable candidate exists. Do NOT invent frameworks or restructure.",
40107
+ "- When MODIFY, the path MUST be one of the candidate repo-relative paths listed.",
40108
+ 'Return JSON array: [{ "index": number, "action": "modify"|"create", "path": string }].'
40020
40109
  ].join("\n");
40021
40110
  const user = [
40022
40111
  `Request: ${request}`,
40023
40112
  "Blocks:",
40024
40113
  blockSnippets,
40025
40114
  "Candidates:",
40026
- samples.join("\n\n")
40115
+ samples.join("\n\n"),
40116
+ `TargetDirHints: ${(opts?.dirHints || []).join(", ")}`
40027
40117
  ].join("\n\n");
40028
- const spin = new ProcessAnimation();
40029
- spin.start();
40030
- const resp = await executeChat([
40031
- { role: "system", content: system },
40032
- { role: "user", content: user }
40033
- ]);
40118
+ let startedLocalSpinner = false;
40119
+ let spin = null;
40120
+ if (!ProcessAnimation.hasActive()) {
40121
+ spin = new ProcessAnimation();
40122
+ spin.start();
40123
+ startedLocalSpinner = true;
40124
+ }
40125
+ let resp;
40034
40126
  try {
40035
- spin.stop();
40036
- } catch {
40127
+ resp = await executeChat([
40128
+ { role: "system", content: system },
40129
+ { role: "user", content: user }
40130
+ ], { provider: "google", model: "gemini-2.5-flash-lite" });
40131
+ } finally {
40132
+ if (startedLocalSpinner && spin) {
40133
+ try {
40134
+ spin.stop();
40135
+ } catch {
40136
+ }
40137
+ }
40037
40138
  }
40038
40139
  const raw = (resp?.output || "").trim();
40039
40140
  const jsonText = extractJsonSafe(raw, "array") || raw;
@@ -40087,12 +40188,25 @@ function trackCodeFallback(event) {
40087
40188
  }
40088
40189
  }
40089
40190
  async function orchestrate(request, opts) {
40191
+ const dbg = (...a) => {
40192
+ if (process.env.MARIA_DEBUG === "1") {
40193
+ try {
40194
+ console.log("[DEBUG/orchestrator]", ...a);
40195
+ } catch {
40196
+ }
40197
+ }
40198
+ };
40090
40199
  const profile = await scanRepo(opts.root);
40200
+ dbg("start", { root: opts.root, flags: {
40201
+ ...opts.flags
40202
+ /* redact */
40203
+ }, attachedFiles: Array.isArray(opts.attachedFiles) ? opts.attachedFiles.length : 0 });
40091
40204
  const initial = [];
40092
40205
  const fallbackNotices = [];
40093
40206
  const withNotices = (base) => fallbackNotices.length > 0 ? [...fallbackNotices, ...base] : base;
40094
40207
  const explicitFilesRaw = parseExplicitFilenames(request);
40095
40208
  const explicitFiles = explicitFilesRaw.length > 0 ? await resolveExplicitPaths(opts.root, explicitFilesRaw, request) : [];
40209
+ dbg("explicitFiles", { raw: explicitFilesRaw, resolved: explicitFiles });
40096
40210
  const explicitAbsMap = /* @__PURE__ */ Object.create(null);
40097
40211
  if (explicitFiles.length > 0) {
40098
40212
  const pathMod = await import('path');
@@ -40104,12 +40218,14 @@ async function orchestrate(request, opts) {
40104
40218
  explicitFiles,
40105
40219
  attachmentsCount: Array.isArray(opts.attachedFiles) ? opts.attachedFiles.length : 0
40106
40220
  });
40221
+ dbg("intent", { isEditIntent });
40107
40222
  let editTargets = explicitFiles;
40108
40223
  if (isEditIntent && editTargets.length === 0) {
40109
40224
  try {
40110
40225
  const repoFiles = await getRepoFiles(opts.root);
40111
40226
  const llmTargets = await llmSelectEditTargets(opts.root, request, repoFiles);
40112
40227
  editTargets = llmTargets;
40228
+ dbg("llmSelectEditTargets", { editTargets });
40113
40229
  } catch {
40114
40230
  }
40115
40231
  }
@@ -40121,6 +40237,7 @@ async function orchestrate(request, opts) {
40121
40237
  maxAttachments: opts.flags.maxAttachments || 50,
40122
40238
  allowDotfiles: !!opts.flags.allowDotfiles
40123
40239
  });
40240
+ dbg("attachment.map", { mappedCount: mapRes.mapped.length, warnings: mapRes.warnings });
40124
40241
  mapRes.warnings.slice();
40125
40242
  for (const m2 of mapRes.mapped) {
40126
40243
  initial.push({
@@ -40132,7 +40249,7 @@ async function orchestrate(request, opts) {
40132
40249
  });
40133
40250
  }
40134
40251
  }
40135
- const onlyAttached = !!opts.flags.onlyAttached;
40252
+ const onlyAttached = !!(opts.flags.onlyAttached && Array.isArray(opts.attachedFiles) && opts.attachedFiles.length > 0);
40136
40253
  if (!onlyAttached) {
40137
40254
  let codeOutput = "";
40138
40255
  if (process.env.MARIA_E2E_FAKE_CODE === "1") {
@@ -40152,10 +40269,37 @@ async function orchestrate(request, opts) {
40152
40269
  "[BEGIN file: path]\n<content>\n[END]",
40153
40270
  "Do not include any prose before/after; no menus/questions/suggestions; start immediately with ``` or [BEGIN file: ...]."
40154
40271
  ].join("\n");
40272
+ let targetFilesAbs = [];
40273
+ try {
40274
+ const pathMod = await import('path');
40275
+ const fromExplicit = Array.isArray(explicitFiles) && explicitFiles.length > 0 ? explicitFiles.map((rel) => explicitAbsMap[rel] || pathMod.join(opts.root, rel)) : [];
40276
+ const fromEditTargets = Array.isArray(editTargets) && editTargets.length > 0 ? editTargets.map((rel) => pathMod.isAbsolute(rel) ? rel : pathMod.join(opts.root, rel)) : [];
40277
+ const seen = /* @__PURE__ */ new Set();
40278
+ for (const p of [...fromExplicit, ...fromEditTargets]) {
40279
+ const norm = (p || "").replace(/\\/g, "/");
40280
+ if (!norm) continue;
40281
+ if (seen.has(norm)) continue;
40282
+ seen.add(norm);
40283
+ targetFilesAbs.push(norm);
40284
+ }
40285
+ } catch {
40286
+ }
40287
+ const targetDirsAbs = Array.from(new Set(targetFilesAbs.map((p) => p.split("/").slice(0, -1).join("/")).filter(Boolean)));
40288
+ const targetFilesSection = targetFilesAbs.length > 0 ? ["// TARGET FILES (absolute):", ...targetFilesAbs.map((p) => `// - ${p}`)].join("\n") : "";
40289
+ const targetDirsSection = targetDirsAbs.length > 0 ? ["// TARGET DIRECTORIES (absolute):", ...targetDirsAbs.map((p) => `// - ${p}`)].join("\n") : "";
40155
40290
  const requestPreamble = isEditIntent ? [
40156
- "// EDIT MODE: modify existing files, preserve unrelated content.",
40157
- "// Follow existing structure and only touch requested files.",
40158
- editContext ? "// Current file snapshots provided below." : ""
40291
+ "// EDIT MODE RULES:",
40292
+ "// 1) Read the entire target file(s) BEFORE making changes. Assume omitted lines must remain exactly as-is.",
40293
+ "// 2) Preserve unrelated content and formatting (indentation, EOLs, imports order, license headers).",
40294
+ "// 3) Do NOT rename, move, or delete files unless explicitly requested.",
40295
+ "// 4) Apply the MINIMAL necessary change to achieve the request. Avoid broad refactors.",
40296
+ "// 5) When returning a whole file, keep everything identical except for the exact lines you changed.",
40297
+ "// 6) Do NOT introduce unrelated edits or code style churn.",
40298
+ "// 7) Edit ONLY the files listed under TARGET FILES or inside TARGET DIRECTORIES unless explicitly instructed otherwise.",
40299
+ "// 8) Do NOT create or scaffold new projects or files outside these targets (no package.json, no CRA/Vite scaffolds, etc.) unless such files already exist and are being modified.",
40300
+ editContext ? "// Current file snapshots provided below." : "",
40301
+ targetFilesSection,
40302
+ targetDirsSection
40159
40303
  ].filter(Boolean).join("\n") : "";
40160
40304
  const enriched = `${FILE_FORMAT_INSTRUCTIONS}
40161
40305
 
@@ -40163,6 +40307,7 @@ ${requestPreamble}
40163
40307
  ${request}
40164
40308
 
40165
40309
  ${editContext}`;
40310
+ dbg("executeCode.prompt.head", enriched.slice(0, 1400));
40166
40311
  const ctxAttachments = Array.isArray(opts.attachedFiles) && opts.attachedFiles.length > 0 ? opts.attachedFiles.map((f3) => ({
40167
40312
  name: f3.originalName,
40168
40313
  path: f3.pathHint,
@@ -40196,6 +40341,68 @@ ${editContext}`;
40196
40341
  } catch {
40197
40342
  }
40198
40343
  }
40344
+ try {
40345
+ const fs52 = await import('fs/promises');
40346
+ const pathMod = await import('path');
40347
+ const dirCandidates = Array.from(new Set(String(request).match(/[A-Za-z]:\\[^\s"']+|\.?\/?[^\s"']+[\/\\][^\s"']*/g) || []));
40348
+ const maxFilesPerDir = 80;
40349
+ for (const raw2 of dirCandidates) {
40350
+ try {
40351
+ const normalized2 = raw2.replace(/^"|"$/g, "").replace(/^'|'$/g, "");
40352
+ const abs = pathMod.isAbsolute(normalized2) ? normalized2 : pathMod.join(opts.root, normalized2);
40353
+ const st = await fs52.stat(abs).catch(() => null);
40354
+ if (!st || !st.isDirectory()) continue;
40355
+ const collected = [];
40356
+ const walk2 = async (d) => {
40357
+ if (collected.length >= maxFilesPerDir) return;
40358
+ let entries = [];
40359
+ try {
40360
+ entries = await fs52.readdir(d, { withFileTypes: true });
40361
+ } catch {
40362
+ return;
40363
+ }
40364
+ for (const e2 of entries) {
40365
+ const name2 = e2.name;
40366
+ if (name2 === ".git" || name2 === "node_modules" || name2 === "dist" || name2 === "build" || name2 === ".maria") continue;
40367
+ const full = pathMod.join(d, name2);
40368
+ if (e2.isDirectory()) {
40369
+ await walk2(full);
40370
+ if (collected.length >= maxFilesPerDir) break;
40371
+ continue;
40372
+ }
40373
+ collected.push(full);
40374
+ if (collected.length >= maxFilesPerDir) break;
40375
+ }
40376
+ };
40377
+ await walk2(abs);
40378
+ for (const f3 of collected) {
40379
+ try {
40380
+ const key = f3.toLowerCase();
40381
+ if (attachedPathSet.has(key)) continue;
40382
+ const buf = await fs52.readFile(f3);
40383
+ const head2 = buf.subarray(0, Math.min(buf.length, 4096));
40384
+ let binaryLike = false;
40385
+ for (let i2 = 0; i2 < head2.length; i2++) {
40386
+ if (head2[i2] === 0) {
40387
+ binaryLike = true;
40388
+ break;
40389
+ }
40390
+ }
40391
+ if (binaryLike) continue;
40392
+ const sample = buf.subarray(0, Math.min(buf.length, 8192)).toString("utf8");
40393
+ const printable = sample.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, "");
40394
+ const ratio = sample.length === 0 ? 0 : printable.length / sample.length;
40395
+ if (ratio < 0.6 && sample.length > 0) continue;
40396
+ pathAttachments.push({ name: pathMod.basename(f3), path: f3, mime: "text/plain", data_base64: buf.toString("base64") });
40397
+ attachedPathSet.add(key);
40398
+ } catch {
40399
+ }
40400
+ }
40401
+ } catch {
40402
+ }
40403
+ }
40404
+ } catch {
40405
+ }
40199
40406
  if (isEditIntent && Array.isArray(editTargets) && editTargets.length > 0) {
40200
40407
  try {
40201
40408
  const fs52 = await import('fs/promises');
@@ -40250,9 +40457,34 @@ ${editContext}`;
40250
40457
  hydratedCtx.push(...ctxAttachments);
40251
40458
  }
40252
40459
  }
40253
- const allAttachments = (hydratedCtx.length ? hydratedCtx : ctxAttachments).concat(pathAttachments);
40460
+ const pathMod2 = await import('path');
40461
+ const normalizeMime = (p, m2) => {
40462
+ if (!p) return m2 || "text/plain";
40463
+ const ext2 = pathMod2.extname(p).toLowerCase();
40464
+ if (m2 && m2 !== "application/octet-stream") return m2;
40465
+ if (ext2 === ".pdf") return "application/pdf";
40466
+ if (ext2 === ".png") return "image/png";
40467
+ if (ext2 === ".jpg" || ext2 === ".jpeg") return "image/jpeg";
40468
+ if (ext2 === ".webp") return "image/webp";
40469
+ if (ext2 === ".gif") return "image/gif";
40470
+ if (ext2 === ".bmp") return "image/bmp";
40471
+ if (ext2 === ".svg") return "image/svg+xml";
40472
+ if (ext2 === ".tif" || ext2 === ".tiff") return "image/tiff";
40473
+ if (ext2 === ".heic") return "image/heic";
40474
+ if (ext2 === ".heif") return "image/heif";
40475
+ return "text/plain";
40476
+ };
40477
+ const allAttachments = (hydratedCtx.length ? hydratedCtx : ctxAttachments).concat(pathAttachments).map((a) => ({
40478
+ ...a,
40479
+ mime: normalizeMime(a.path, a.mime)
40480
+ }));
40254
40481
  const response = await executeCode(allAttachments.length > 0 ? { prompt: enriched, provider: "google", model: "gemini-2.5-flash", attachments: allAttachments } : enriched);
40482
+ try {
40483
+ dbg("executeCode.attachments.meta", { ctx: (hydratedCtx.length ? hydratedCtx : ctxAttachments).length, path: pathAttachments.length });
40484
+ } catch {
40485
+ }
40255
40486
  const raw = (response.output || response?.data?.content || "").trim();
40487
+ dbg("executeCode.output.head", raw.slice(0, 1200));
40256
40488
  if (!raw) {
40257
40489
  return {
40258
40490
  ok: false,
@@ -40290,6 +40522,7 @@ ${editContext}`;
40290
40522
  codeOutput = outcome.data?.output || "";
40291
40523
  }
40292
40524
  const blocks = extractBlocks(codeOutput);
40525
+ dbg("extractBlocks.count", blocks.length);
40293
40526
  if (explicitFiles.length > 0 || isEditIntent && editTargets.length > 0) {
40294
40527
  const mapped = /* @__PURE__ */ new Set();
40295
40528
  const targets = explicitFiles.length > 0 ? explicitFiles : editTargets;
@@ -40327,23 +40560,49 @@ ${editContext}`;
40327
40560
  } else {
40328
40561
  try {
40329
40562
  const repoFiles = await getRepoFiles(opts.root);
40330
- const decisions = await llmMapBlocksBatch(opts.root, request, blocks, repoFiles);
40563
+ let dirHints = [];
40564
+ try {
40565
+ const fs52 = await import('fs/promises');
40566
+ const pathMod = await import('path');
40567
+ const rawDirs = Array.from(new Set(String(request).match(/[A-Za-z]:\\[^\s"']+|\.?\/?[^\s"']+[\/\\][^\s"']*/g) || []));
40568
+ for (const raw of rawDirs) {
40569
+ try {
40570
+ const normalized2 = raw.replace(/^"|"$/g, "").replace(/^'|'$/g, "");
40571
+ const abs = pathMod.isAbsolute(normalized2) ? normalized2 : pathMod.join(opts.root, normalized2);
40572
+ const st = await fs52.stat(abs).catch(() => null);
40573
+ if (!st || !st.isDirectory()) continue;
40574
+ const rel = pathMod.relative(opts.root, abs).replace(/\\/g, "/").replace(/^\/+/, "");
40575
+ if (!rel || rel.startsWith("..")) continue;
40576
+ dirHints.push(rel);
40577
+ } catch {
40578
+ }
40579
+ }
40580
+ if (dirHints.length === 0 && Array.isArray(editTargets) && editTargets.length > 0) {
40581
+ dirHints = Array.from(new Set(editTargets.map((p) => (p || "").replace(/^\/+/, "").split("/").slice(0, -1).join("/")).filter(Boolean)));
40582
+ }
40583
+ } catch {
40584
+ }
40585
+ const decisions = await llmMapBlocksBatch(opts.root, request, blocks, repoFiles, { dirHints });
40586
+ dbg("llmMapBlocksBatch.decisions", decisions);
40331
40587
  for (let i2 = 0; i2 < blocks.length; i2++) {
40332
40588
  const b = blocks[i2];
40333
40589
  const d = decisions[i2] || { action: "create", path: suggestName2(request, b.language, i2) };
40334
- if (d.action === "modify" && repoFiles.includes(d.path)) {
40590
+ const hinted = b.filename && String(b.filename).trim() || extractFilenameHintFromComment(b.code);
40591
+ const finalPath = hinted && hinted.trim() || d.path;
40592
+ if (d.action === "modify" && repoFiles.includes(finalPath)) {
40335
40593
  const lang = languageFromExt(d.path.replace(/^.*(\.[a-z0-9]+)$/i, "$1"));
40336
- initial.push({ path: d.path, kind: "source", action: "modify", description: "Modify existing file", language: lang, preview: b.code });
40594
+ initial.push({ path: finalPath, kind: "source", action: "modify", description: "Modify existing file", language: lang, preview: b.code, noNormalize: true });
40337
40595
  } else {
40338
- const pth = d.path || suggestName2(request, b.language, i2);
40339
- initial.push({ path: pth, kind: "source", action: "create", description: describe2(b.language, ""), language: b.language, preview: b.code });
40596
+ const pth = finalPath && finalPath.trim() || d.path || suggestName2(request, b.language, i2);
40597
+ initial.push({ path: pth, kind: "source", action: "create", description: describe2(b.language, ""), language: b.language, preview: b.code, noNormalize: true });
40340
40598
  }
40341
40599
  }
40342
40600
  } catch {
40343
40601
  for (let i2 = 0; i2 < blocks.length; i2++) {
40344
40602
  const b = blocks[i2];
40345
- const path65 = suggestName2(request, b.language, i2);
40346
- initial.push({ path: path65, kind: "source", action: "create", description: describe2(b.language, ""), language: b.language, preview: b.code });
40603
+ const hinted = b.filename && String(b.filename).trim() || extractFilenameHintFromComment(b.code);
40604
+ const path65 = hinted && hinted.trim() || suggestName2(request, b.language, i2);
40605
+ initial.push({ path: path65, kind: "source", action: "create", description: describe2(b.language, ""), language: b.language, preview: b.code, noNormalize: true });
40347
40606
  }
40348
40607
  }
40349
40608
  }
@@ -40359,13 +40618,13 @@ ${editContext}`;
40359
40618
  }
40360
40619
  }
40361
40620
  }
40362
- if (explicitFiles.length > 0 && initial.filter((f3) => !!f3.preview).length === 0) {
40621
+ if (!isEditIntent && explicitFiles.length > 0 && initial.filter((f3) => !!f3.preview).length === 0) {
40363
40622
  for (const f3 of explicitFiles) {
40364
40623
  initial.push(scaffoldForFilename(f3, explicitFiles));
40365
40624
  }
40366
40625
  }
40367
40626
  const normalized = await normalizePlans(
40368
- initial.map((p) => p.noNormalize ? p : p),
40627
+ initial.map((p) => p.noNormalize ? { ...p } : p),
40369
40628
  { root: opts.root }
40370
40629
  );
40371
40630
  try {
@@ -40390,11 +40649,26 @@ ${editContext}`;
40390
40649
  }
40391
40650
  } catch {
40392
40651
  }
40393
- const validated = await validatePlan(normalized, { root: opts.root, profile, flags: { maxFiles: opts.flags.maxFiles, yes: opts.flags.yes, interactive: !!opts.flags.interactive, ...opts.flags } });
40652
+ let filtered = normalized;
40653
+ if (isEditIntent) {
40654
+ const keepCreates = !!opts.flags.onlyAttached;
40655
+ if (!keepCreates) {
40656
+ filtered = normalized.map((f3) => f3.action === "create" ? { ...f3, action: "skip" } : f3);
40657
+ }
40658
+ }
40659
+ const baseFlags = { ...opts.flags };
40660
+ const effectiveFlags = {
40661
+ ...baseFlags,
40662
+ maxFiles: opts.flags.maxFiles,
40663
+ interactive: !!opts.flags.interactive,
40664
+ // Force yes when editing; if baseFlags.yes is already true, keep it true
40665
+ yes: baseFlags?.yes === true || isEditIntent
40666
+ };
40667
+ const validated = await validatePlan(filtered, { root: opts.root, profile, flags: effectiveFlags });
40394
40668
  const hasAnyModify = validated.files.some((f3) => f3.action === "modify") || isEditIntent;
40395
40669
  const outputMode = !opts.flags.hideCode && hasAnyModify ? "diff" : resolveOutputMode(opts.flags.output, validated.files.length, !!opts.flags.hideCode);
40396
40670
  const skippedSetForLookup = new Set(validated.skipped || []);
40397
- const skippedPlans = normalized.filter((f3) => skippedSetForLookup.has(f3.path));
40671
+ const skippedPlans = filtered.filter((f3) => skippedSetForLookup.has(f3.path));
40398
40672
  const remainingSkipped = (validated.skipped || []).filter((p) => !skippedPlans.some((sp) => sp.path === p)).map((p) => ({ path: p, kind: "source", action: "skip", description: "" }));
40399
40673
  const displayFiles = validated.files.concat(skippedPlans).concat(remainingSkipped);
40400
40674
  const summary = summarizePlan(displayFiles);
@@ -40587,6 +40861,21 @@ function extractBlocks(content) {
40587
40861
  if (blocks.length === 0 && looksLikeCode(content)) blocks.push({ language: detectLanguage(content), code: content.trim() });
40588
40862
  return blocks;
40589
40863
  }
40864
+ function extractFilenameHintFromComment(code) {
40865
+ try {
40866
+ const first = (code.split(/\r?\n/, 1)[0] || "").trim();
40867
+ let m2 = first.match(/^\/\/\s*filename:\s*(.+)$/i);
40868
+ if (m2?.[1]) return m2[1].trim();
40869
+ m2 = first.match(/^#\s*filename:\s*(.+)$/i);
40870
+ if (m2?.[1]) return m2[1].trim();
40871
+ m2 = first.match(/^<!--\s*filename:\s*(.+?)\s*-->$/i);
40872
+ if (m2?.[1]) return m2[1].trim();
40873
+ m2 = first.match(/^\/\*\s*filename:\s*(.+?)\s*\*\/$/i);
40874
+ if (m2?.[1]) return m2[1].trim();
40875
+ } catch {
40876
+ }
40877
+ return void 0;
40878
+ }
40590
40879
  function looksLikeCode(s2) {
40591
40880
  return ["function ", "const ", "let ", "var ", "class ", "def ", "import ", "export "].some((k) => s2.includes(k));
40592
40881
  }
@@ -40744,48 +41033,100 @@ function parseExplicitFilenames(request) {
40744
41033
  return out;
40745
41034
  }
40746
41035
  async function detectEditIntentLLM(root, request, ctx2) {
41036
+ let existingDirs = [];
41037
+ let existingFiles = [];
41038
+ let anyExistingPathMentioned = false;
40747
41039
  try {
41040
+ const fs52 = await import('fs/promises');
41041
+ const pathMod = await import('path');
41042
+ const rawPaths = Array.from(new Set(String(request).match(/[A-Za-z]:\\[^\s"']+|\.?\/?[^\s"']+[\/\\][^\s"']*/g) || []));
41043
+ existingDirs = [];
41044
+ existingFiles = [];
41045
+ for (const raw2 of rawPaths) {
41046
+ try {
41047
+ const normalized = raw2.replace(/^"|"$/g, "").replace(/^'|'$/g, "");
41048
+ const abs = pathMod.isAbsolute(normalized) ? normalized : pathMod.join(root, normalized);
41049
+ const st = await fs52.stat(abs).catch(() => null);
41050
+ if (!st) continue;
41051
+ anyExistingPathMentioned = true;
41052
+ const rel = pathMod.relative(root, abs).replace(/\\/g, "/");
41053
+ if (!rel || rel.startsWith("..")) continue;
41054
+ if (st.isDirectory()) existingDirs.push(rel);
41055
+ else if (st.isFile()) existingFiles.push(rel);
41056
+ } catch {
41057
+ }
41058
+ }
40748
41059
  const repoFiles = await getRepoFiles(root);
40749
- const candidates = repoFiles;
41060
+ let scope = [];
41061
+ if (existingDirs.length > 0) {
41062
+ const dirSet = existingDirs.map((d) => d.replace(/\\/g, "/").replace(/^\/+/, "").toLowerCase());
41063
+ scope = repoFiles.filter((p) => dirSet.some((d) => p.toLowerCase().startsWith(d + "/")));
41064
+ } else {
41065
+ scope = repoFiles;
41066
+ }
40750
41067
  const headSnippets = [];
40751
- for (const p of candidates) {
41068
+ for (const p of scope) {
40752
41069
  const h2 = await readHeadTail(root, p, 5);
40753
41070
  headSnippets.push(`- ${p}
40754
41071
  ${h2.head}`);
40755
41072
  }
40756
41073
  const system = [
40757
- "Classify the user intent for the MARIA code orchestrator.",
40758
- "Decide strictly between EDIT_EXISTING (modify existing files) or CREATE_NEW (generate new project/files).",
40759
- "User is likely to have EDIT_EXITING intent when:",
40760
- "- the coding language is not specified",
40761
- "- it has a clear and/or relevant path of the file/directory exists",
40762
- "- it has attachments",
40763
- "- the wording implies modifying existing files (ex. fix, improve, update, change, patch, make it, clean up, etc.)",
40764
- 'Return JSON: { "intent": "EDIT_EXISTING" | "CREATE_NEW" } only. No commentary.'
41074
+ "You classify the intent for a code operation in an existing repository.",
41075
+ "Choose strictly one: EDIT_EXISTING (modify existing files) or CREATE_NEW (generate a new project/files).",
41076
+ "Decision rules (apply in order):",
41077
+ "1) If the request references existing repo directory/file paths and does NOT explicitly say to create a new project/template/scaffold, prefer EDIT_EXISTING.",
41078
+ "2) Wording like fix/improve/update/change/patch/make it work/enable X implies EDIT_EXISTING.",
41079
+ "3) Wording like create/new project/scaffold/from scratch/template implies CREATE_NEW.",
41080
+ "4) If attachments or explicit file paths are present, that increases likelihood of EDIT_EXISTING.",
41081
+ 'Return ONLY compact JSON: { "intent": "EDIT_EXISTING" | "CREATE_NEW" }.'
40765
41082
  ].join("\n");
41083
+ const evidence = {
41084
+ explicitFilesCount: (ctx2.explicitFiles || []).length,
41085
+ attachmentsCount: ctx2.attachmentsCount || 0,
41086
+ existingDirCount: existingDirs.length,
41087
+ existingFileMentions: existingFiles.length,
41088
+ existingDirs,
41089
+ existingFiles
41090
+ };
40766
41091
  const user = [
40767
41092
  `Request: ${request}`,
40768
- `ExplicitFiles: ${JSON.stringify(ctx2.explicitFiles || [])}`,
40769
- `AttachmentsCount: ${ctx2.attachmentsCount}`,
40770
- "Repo snapshot (paths with file heads):",
41093
+ `Evidence: ${JSON.stringify(evidence)}`,
41094
+ "Repo snapshot (trimmed):",
40771
41095
  headSnippets.join("\n\n")
40772
41096
  ].join("\n\n");
40773
- const spin1 = new ProcessAnimation();
40774
- spin1.start();
40775
- const resp = await executeChat([
40776
- { role: "system", content: system },
40777
- { role: "user", content: user }
40778
- ]);
41097
+ if (existingDirs.length > 0 || existingFiles.length > 0 || ctx2.explicitFiles && ctx2.explicitFiles.length > 0 || ctx2.attachmentsCount > 0) {
41098
+ return true;
41099
+ }
41100
+ let startedLocalSpinner = false;
41101
+ let spin1 = null;
41102
+ if (!ProcessAnimation.hasActive()) {
41103
+ spin1 = new ProcessAnimation();
41104
+ spin1.start();
41105
+ startedLocalSpinner = true;
41106
+ }
41107
+ let resp;
40779
41108
  try {
40780
- spin1.stop();
40781
- } catch {
41109
+ resp = await executeChat([
41110
+ { role: "system", content: system },
41111
+ { role: "user", content: user }
41112
+ ], { provider: "google", model: "gemini-2.5-flash-lite" });
41113
+ } finally {
41114
+ if (startedLocalSpinner && spin1) {
41115
+ try {
41116
+ spin1.stop();
41117
+ } catch {
41118
+ }
41119
+ }
40782
41120
  }
40783
41121
  const raw = (resp?.output || "").trim();
40784
41122
  const jsonText = extractJsonSafe(raw, "object") || raw;
40785
41123
  const parsed = JSON.parse(jsonText);
41124
+ if (existingDirs.length > 0 || existingFiles.length > 0 || anyExistingPathMentioned || ctx2.explicitFiles && ctx2.explicitFiles.length > 0 || ctx2.attachmentsCount > 0) {
41125
+ return true;
41126
+ }
40786
41127
  return parsed?.intent === "EDIT_EXISTING";
40787
41128
  } catch {
40788
- return ctx2.explicitFiles && ctx2.explicitFiles.length > 0 || ctx2.attachmentsCount > 0;
41129
+ return ctx2.explicitFiles && ctx2.explicitFiles.length > 0 || ctx2.attachmentsCount > 0 || existingDirs.length > 0 || existingFiles.length > 0 || anyExistingPathMentioned;
40789
41130
  }
40790
41131
  }
40791
41132
  function sanitizeFolderName(name2) {
@@ -40953,7 +41294,7 @@ async function resolveExplicitPaths(root, files, hintText) {
40953
41294
  const chat = await executeChat([
40954
41295
  { role: "system", content: system },
40955
41296
  { role: "user", content: user }
40956
- ]);
41297
+ ], { provider: "google", model: "gemini-2.5-flash-lite" });
40957
41298
  const raw = (chat.output || "").trim();
40958
41299
  const pick = ranked.find((r2) => r2 === raw) || ranked.find((r2) => raw.includes(r2)) || ranked[0];
40959
41300
  return pick.replace(/^\/+/, "");
@@ -41128,7 +41469,16 @@ var init_code_command = __esm({
41128
41469
  }
41129
41470
  ];
41130
41471
  async execute(commandArgs, context2) {
41472
+ const debug = (...args2) => {
41473
+ if (process.env.MARIA_DEBUG === "1") {
41474
+ try {
41475
+ console.log("[DEBUG/code]", ...args2);
41476
+ } catch {
41477
+ }
41478
+ }
41479
+ };
41131
41480
  const request = await this.ensureLanguageDefaults(commandArgs.raw.join(" ").trim());
41481
+ debug("request", request);
41132
41482
  if (!request) {
41133
41483
  return this.error("Please provide a code request \xB7 Example: /code create button component\nTip: Use --plan-only to safely review the plan, or --output detail to preview snippet heads.");
41134
41484
  }
@@ -41140,6 +41490,7 @@ var init_code_command = __esm({
41140
41490
  const explicitDry = commandArgs.raw.includes("--dry-run");
41141
41491
  const explicitOutput = commandArgs.raw.some((x2) => x2.startsWith("--output") || x2 === "--verbose" || x2 === "-v");
41142
41492
  const explicitPreview = commandArgs.raw.some((x2) => x2.startsWith("--preview-lines"));
41493
+ const explicitOnlyAttached = commandArgs.raw.includes("--only-attached");
41143
41494
  const preSpin = new ProcessAnimation();
41144
41495
  preSpin.start();
41145
41496
  let inferred = {};
@@ -41151,6 +41502,8 @@ var init_code_command = __esm({
41151
41502
  } catch {
41152
41503
  }
41153
41504
  }
41505
+ debug("inferCodeArgs.raw", rawText);
41506
+ debug("inferCodeArgs.result", inferred);
41154
41507
  if (!explicitPlan && !explicitDry) {
41155
41508
  if (typeof inferred.planOnly === "boolean") opts.planOnly = inferred.planOnly;
41156
41509
  if (typeof inferred.dryRun === "boolean") opts.dryRun = inferred.dryRun;
@@ -41161,6 +41514,9 @@ var init_code_command = __esm({
41161
41514
  if (!explicitPreview && typeof inferred.previewLines === "number") {
41162
41515
  opts.previewLines = inferred.previewLines;
41163
41516
  }
41517
+ if (!explicitOnlyAttached && typeof inferred.onlyAttached === "boolean") {
41518
+ opts.onlyAttached = inferred.onlyAttached;
41519
+ }
41164
41520
  } catch {
41165
41521
  }
41166
41522
  if (opts.planOnly) {
@@ -41175,13 +41531,27 @@ var init_code_command = __esm({
41175
41531
  const root = opts.root || process.cwd();
41176
41532
  const { orchestrate: orchestrate2 } = await Promise.resolve().then(() => (init_Orchestrator(), Orchestrator_exports));
41177
41533
  const attachments = await this.collectAttachedFiles(context2).catch(() => []);
41534
+ try {
41535
+ if (process.env.MARIA_DEBUG === "1") {
41536
+ const attView = attachments.map((a) => ({ name: a.originalName, size: a.size, mime: a.mime, pathHint: a.pathHint })).slice(0, 50);
41537
+ console.log("[DEBUG/code] collected.attachments", { count: attachments.length, attachments: attView });
41538
+ }
41539
+ } catch {
41540
+ }
41178
41541
  const abort = new AbortController();
41179
41542
  const onSigint = () => abort.abort();
41180
41543
  process.once("SIGINT", onSigint);
41181
- const spinner = new ProcessAnimation();
41182
- spinner.start();
41544
+ let startedLocalSpinner = false;
41545
+ let spinner = null;
41546
+ if (!ProcessAnimation.hasActive()) {
41547
+ spinner = new ProcessAnimation();
41548
+ spinner.start();
41549
+ startedLocalSpinner = true;
41550
+ }
41183
41551
  try {
41184
- 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 });
41552
+ const effectiveOnlyAttached = opts.onlyAttached && attachments.length > 0;
41553
+ 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: effectiveOnlyAttached, 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 });
41554
+ debug("orchestrate.summaryLines.head", Array.isArray(res?.summaryLines) ? res.summaryLines.slice(0, 10) : []);
41185
41555
  if (opts.planOnly) {
41186
41556
  const fs52 = await import('fs/promises');
41187
41557
  const path65 = await import('path');
@@ -41234,9 +41604,11 @@ var init_code_command = __esm({
41234
41604
  const out = Array.isArray(res?.summaryLines) ? res.summaryLines.join("\n") : "";
41235
41605
  return this.success(out);
41236
41606
  } finally {
41237
- try {
41238
- spinner.stop();
41239
- } catch {
41607
+ if (startedLocalSpinner && spinner) {
41608
+ try {
41609
+ spinner.stop();
41610
+ } catch {
41611
+ }
41240
41612
  }
41241
41613
  process.removeListener("SIGINT", onSigint);
41242
41614
  }
@@ -41359,12 +41731,6 @@ ${pretty}`);
41359
41731
  if (llmLang) {
41360
41732
  const hint2 = (() => {
41361
41733
  const l = llmLang.toLowerCase();
41362
- if (l === "tsx") return "TypeScript (React/TSX)";
41363
- if (l === "jsx") return "JavaScript (React/JSX)";
41364
- if (l === "typescript") return "TypeScript";
41365
- if (l === "javascript") return "JavaScript";
41366
- if (l === "html") return "HTML";
41367
- if (l === "css") return "CSS";
41368
41734
  return llmLang;
41369
41735
  })();
41370
41736
  return raw + ` (Use ${hint2})`;
@@ -41377,7 +41743,7 @@ ${pretty}`);
41377
41743
  preSpin.start();
41378
41744
  const system = [
41379
41745
  "You analyze a user's code-generation request.",
41380
- "Decide if the user explicitly specified a programming language or framework/tooling (e.g., TypeScript, Python, Rust, Java, React, Vue, Node, etc.).",
41746
+ "Decide if the user explicitly specified a programming language or framework/tooling.",
41381
41747
  'Return ONLY compact JSON with shape {"explicitLanguage": boolean, "language"?: string}.',
41382
41748
  "Do not add any commentary."
41383
41749
  ].join("\n");
@@ -41386,7 +41752,7 @@ ${pretty}`);
41386
41752
  method: "POST",
41387
41753
  body: {
41388
41754
  provider: "google",
41389
- model: "gemini-2.5-flash",
41755
+ model: "gemini-2.5-flash-lite",
41390
41756
  taskType: "chat",
41391
41757
  prompt: `${system}
41392
41758
 
@@ -41431,6 +41797,14 @@ ${user}`
41431
41797
  if (parsed && parsed.explicitLanguage) return raw;
41432
41798
  } catch {
41433
41799
  }
41800
+ try {
41801
+ const pathMod = await import('path');
41802
+ const hasPathToken = /[A-Za-z]:\\[^\s"']+|\.?\/?[^\s"']+[\/\\][^\s"']*/.test(raw);
41803
+ if (hasPathToken) {
41804
+ return raw;
41805
+ }
41806
+ } catch {
41807
+ }
41434
41808
  const hint = " (Use TypeScript and React; prefer functional components and node)";
41435
41809
  return raw + hint;
41436
41810
  }
@@ -41440,9 +41814,7 @@ ${user}`
41440
41814
  const system = [
41441
41815
  "You are a programming language classifier.",
41442
41816
  "Given multiple short code excerpts, determine the dominant language across them.",
41443
- "Respond with ONLY a single token language name from this set:",
41444
- "[typescript, tsx, javascript, jsx, python, java, go, rust, php, cpp, c, swift, kotlin, ruby, csharp, html, css, scss, json, yaml, markdown].",
41445
- "If unsure between tsx and jsx, choose tsx if TypeScript types appear, else jsx."
41817
+ "Respond with ONLY language name(s)"
41446
41818
  ].join("\n");
41447
41819
  const joined = samples.slice(0, 20).map((s2, i2) => `// sample ${i2 + 1}
41448
41820
  ${s2}`).join("\n\n");
@@ -41451,7 +41823,7 @@ ${s2}`).join("\n\n");
41451
41823
  method: "POST",
41452
41824
  body: {
41453
41825
  provider: "google",
41454
- model: "gemini-2.5-flash",
41826
+ model: "gemini-2.5-flash-lite",
41455
41827
  taskType: "chat",
41456
41828
  prompt: `${system}
41457
41829
 
@@ -41757,64 +42129,6 @@ ${joined}`
41757
42129
  return false;
41758
42130
  }
41759
42131
  }
41760
- /**
41761
- * Extract code blocks from AI response
41762
- */
41763
- extractCodeBlocks(content) {
41764
- const blocks = [];
41765
- const codeBlockRegex = /```(\w*)\n([\s\S]*?)```/g;
41766
- let match2;
41767
- while ((match2 = codeBlockRegex.exec(content)) !== null) {
41768
- blocks.push({
41769
- language: match2[1] || "javascript",
41770
- code: match2[2].trim()
41771
- });
41772
- }
41773
- if (blocks.length === 0 && this.looksLikeCode(content)) {
41774
- blocks.push({
41775
- code: content.trim(),
41776
- language: this.detectLanguage(content)
41777
- });
41778
- }
41779
- return blocks;
41780
- }
41781
- /**
41782
- * Check if content looks like code
41783
- */
41784
- looksLikeCode(content) {
41785
- const codeIndicators = [
41786
- "function ",
41787
- "const ",
41788
- "let ",
41789
- "var ",
41790
- "class ",
41791
- "def ",
41792
- "import ",
41793
- "export ",
41794
- "{",
41795
- "}",
41796
- ";",
41797
- "//",
41798
- "/*"
41799
- ];
41800
- return codeIndicators.some((indicator) => content.includes(indicator));
41801
- }
41802
- /**
41803
- * Detect programming language from content
41804
- */
41805
- detectLanguage(code) {
41806
- const hasReact = /(^|\s)from\s+['"]react['"]|^\s*import\s+React/m.test(code);
41807
- const hasJSX = /<([A-Za-z][\w:-]*)(\s|>|\/)>?/m.test(code);
41808
- const hasTS = /(\binterface\s+\w+\b|\btype\s+\w+\s*=|:\s*[A-Za-z_][\w<>\[\]| &?:]*)/m.test(code) || /React\.FC\s*</m.test(code);
41809
- if (hasReact || hasJSX) return hasTS ? "tsx" : "jsx";
41810
- if (code.includes("interface ") || code.includes(": string")) return "typescript";
41811
- if (code.includes("def ") || code.includes("print(")) return "python";
41812
- if (code.includes("func ") || code.includes("package main")) return "go";
41813
- if (code.includes("fn ") || code.includes("let mut")) return "rust";
41814
- if (code.includes("<?php")) return "php";
41815
- if (code.includes("#include")) return "cpp";
41816
- return "javascript";
41817
- }
41818
42132
  /**
41819
42133
  * Save code block to file
41820
42134
  */
@@ -50506,11 +50820,10 @@ var init_responsive_width = __esm({
50506
50820
  }
50507
50821
  });
50508
50822
  function createSpinner(text) {
50509
- let frame = 0;
50510
50823
  let active = false;
50511
- let timer;
50512
- const render = () => {
50513
- const f3 = SPINNER_FRAMES[frame = (frame + 1) % SPINNER_FRAMES.length];
50824
+ let frame = 0;
50825
+ const renderOnce = () => {
50826
+ const f3 = SPINNER_FRAMES[frame % SPINNER_FRAMES.length];
50514
50827
  process6__namespace.default.stdout.write(`\r ${chalk40__default.default.cyan(f3)} ${text}`);
50515
50828
  };
50516
50829
  return {
@@ -50520,13 +50833,11 @@ function createSpinner(text) {
50520
50833
  start() {
50521
50834
  if (active) return;
50522
50835
  active = true;
50523
- render();
50524
- timer = setInterval(render, 80);
50836
+ renderOnce();
50525
50837
  },
50526
50838
  stop(symbol = "\u2714") {
50527
50839
  if (!active) return;
50528
50840
  active = false;
50529
- if (timer) clearInterval(timer);
50530
50841
  const finalSymbol = symbol === "\u2714" ? chalk40__default.default.green(symbol) : symbol === "\u26A0" ? chalk40__default.default.yellow(symbol) : symbol === "\u2716" ? chalk40__default.default.red(symbol) : symbol;
50531
50842
  process6__namespace.default.stdout.write(`\r ${finalSymbol} ${text}
50532
50843
  `);