@bonginkan/maria 4.3.45 → 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.45"}`
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.45"}`,
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.45",
16420
- description: "\u{1F680} MARIA v4.3.45 - 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",
@@ -26426,7 +26477,7 @@ var init_about_command = __esm({
26426
26477
  async execute(args2, context2) {
26427
26478
  const output3 = [];
26428
26479
  output3.push("");
26429
- output3.push(chalk40__default.default.cyan.bold("\u{1F916} About MARIA v4.3.45"));
26480
+ output3.push(chalk40__default.default.cyan.bold("\u{1F916} About MARIA v4.3.46"));
26430
26481
  output3.push(chalk40__default.default.gray("\u2550".repeat(40)));
26431
26482
  output3.push("");
26432
26483
  output3.push(chalk40__default.default.white.bold("MARIA - Minimal API, Maximum Power"));
@@ -38916,9 +38967,10 @@ function extractFirstJson5(text) {
38916
38967
  async function inferCodeArgs(rawText) {
38917
38968
  const system = [
38918
38969
  "You extract structured options for a code command.",
38919
- '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 }.',
38920
38971
  "Decide from the user text whether planOnly or dryRun should be true. Do not explain.",
38921
- "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."
38922
38974
  ].join("\n");
38923
38975
  const resp = await callAPI("/v1/ai-proxy", {
38924
38976
  method: "POST",
@@ -38939,11 +38991,19 @@ ${rawText}`,
38939
38991
  } catch {
38940
38992
  return {};
38941
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
+ }
38942
39001
  const out = {};
38943
39002
  if (typeof parsed.planOnly === "boolean") out.planOnly = parsed.planOnly;
38944
39003
  if (typeof parsed.dryRun === "boolean") out.dryRun = parsed.dryRun;
38945
39004
  if (typeof parsed.output === "string" && (parsed.output === "names" || parsed.output === "summary" || parsed.output === "detail")) out.output = parsed.output;
38946
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;
38947
39007
  if (out.planOnly) out.dryRun = false;
38948
39008
  return out;
38949
39009
  }
@@ -39091,6 +39151,12 @@ var init_FilePlanBuilder = __esm({
39091
39151
  }
39092
39152
  });
39093
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
+ }
39094
39160
  const warnings = [];
39095
39161
  const skipped = [];
39096
39162
  const seenLC = /* @__PURE__ */ new Set();
@@ -39107,6 +39173,10 @@ async function validatePlan(plans, opts) {
39107
39173
  }
39108
39174
  const safe = [];
39109
39175
  for (const fp of outFiltered) {
39176
+ if (fp.action === "skip") {
39177
+ skipped.push(fp.path);
39178
+ continue;
39179
+ }
39110
39180
  const rel = fp.path.replace(/^\/+/, "");
39111
39181
  if (rel.includes("..")) {
39112
39182
  warnings.push(`Path traversal denied: ${fp.path}`);
@@ -39188,7 +39258,18 @@ async function validatePlan(plans, opts) {
39188
39258
  }
39189
39259
  result.push({ ...fp, action: willModify ? "modify" : "create" });
39190
39260
  }
39191
- 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;
39192
39273
  }
39193
39274
  async function exists(p) {
39194
39275
  try {
@@ -39982,7 +40063,7 @@ ${h2.head}`);
39982
40063
  const resp = await executeChat([
39983
40064
  { role: "system", content: system },
39984
40065
  { role: "user", content: user }
39985
- ]);
40066
+ ], { provider: "google", model: "gemini-2.5-flash-lite" });
39986
40067
  const raw = (resp?.output || "").trim();
39987
40068
  const jsonText = extractJsonSafe(raw, "array") || raw;
39988
40069
  const arr = JSON.parse(jsonText);
@@ -39999,7 +40080,7 @@ ${h2.head}`);
39999
40080
  return [];
40000
40081
  }
40001
40082
  }
40002
- async function llmMapBlocksBatch(root, request, blocks, repoFiles) {
40083
+ async function llmMapBlocksBatch(root, request, blocks, repoFiles, opts) {
40003
40084
  try {
40004
40085
  const candidates = repoFiles.filter((p) => /\.(html|css|js|ts|tsx)$/i.test(p)).slice(0, 120);
40005
40086
  const blockSnippets = blocks.slice(0, 20).map((b, i2) => {
@@ -40017,26 +40098,43 @@ ${head2}
40017
40098
  ${h2.head}`);
40018
40099
  }
40019
40100
  const system = [
40020
- "For each provided code block, decide whether to MODIFY an existing repo file or CREATE a new file.",
40021
- 'Return JSON array of objects: [{ "index": number, "action": "modify"|"create", "path": string }].',
40022
- "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 }].'
40023
40109
  ].join("\n");
40024
40110
  const user = [
40025
40111
  `Request: ${request}`,
40026
40112
  "Blocks:",
40027
40113
  blockSnippets,
40028
40114
  "Candidates:",
40029
- samples.join("\n\n")
40115
+ samples.join("\n\n"),
40116
+ `TargetDirHints: ${(opts?.dirHints || []).join(", ")}`
40030
40117
  ].join("\n\n");
40031
- const spin = new ProcessAnimation();
40032
- spin.start();
40033
- const resp = await executeChat([
40034
- { role: "system", content: system },
40035
- { role: "user", content: user }
40036
- ]);
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;
40037
40126
  try {
40038
- spin.stop();
40039
- } 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
+ }
40040
40138
  }
40041
40139
  const raw = (resp?.output || "").trim();
40042
40140
  const jsonText = extractJsonSafe(raw, "array") || raw;
@@ -40090,12 +40188,25 @@ function trackCodeFallback(event) {
40090
40188
  }
40091
40189
  }
40092
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
+ };
40093
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 });
40094
40204
  const initial = [];
40095
40205
  const fallbackNotices = [];
40096
40206
  const withNotices = (base) => fallbackNotices.length > 0 ? [...fallbackNotices, ...base] : base;
40097
40207
  const explicitFilesRaw = parseExplicitFilenames(request);
40098
40208
  const explicitFiles = explicitFilesRaw.length > 0 ? await resolveExplicitPaths(opts.root, explicitFilesRaw, request) : [];
40209
+ dbg("explicitFiles", { raw: explicitFilesRaw, resolved: explicitFiles });
40099
40210
  const explicitAbsMap = /* @__PURE__ */ Object.create(null);
40100
40211
  if (explicitFiles.length > 0) {
40101
40212
  const pathMod = await import('path');
@@ -40107,12 +40218,14 @@ async function orchestrate(request, opts) {
40107
40218
  explicitFiles,
40108
40219
  attachmentsCount: Array.isArray(opts.attachedFiles) ? opts.attachedFiles.length : 0
40109
40220
  });
40221
+ dbg("intent", { isEditIntent });
40110
40222
  let editTargets = explicitFiles;
40111
40223
  if (isEditIntent && editTargets.length === 0) {
40112
40224
  try {
40113
40225
  const repoFiles = await getRepoFiles(opts.root);
40114
40226
  const llmTargets = await llmSelectEditTargets(opts.root, request, repoFiles);
40115
40227
  editTargets = llmTargets;
40228
+ dbg("llmSelectEditTargets", { editTargets });
40116
40229
  } catch {
40117
40230
  }
40118
40231
  }
@@ -40124,6 +40237,7 @@ async function orchestrate(request, opts) {
40124
40237
  maxAttachments: opts.flags.maxAttachments || 50,
40125
40238
  allowDotfiles: !!opts.flags.allowDotfiles
40126
40239
  });
40240
+ dbg("attachment.map", { mappedCount: mapRes.mapped.length, warnings: mapRes.warnings });
40127
40241
  mapRes.warnings.slice();
40128
40242
  for (const m2 of mapRes.mapped) {
40129
40243
  initial.push({
@@ -40135,7 +40249,7 @@ async function orchestrate(request, opts) {
40135
40249
  });
40136
40250
  }
40137
40251
  }
40138
- const onlyAttached = !!opts.flags.onlyAttached;
40252
+ const onlyAttached = !!(opts.flags.onlyAttached && Array.isArray(opts.attachedFiles) && opts.attachedFiles.length > 0);
40139
40253
  if (!onlyAttached) {
40140
40254
  let codeOutput = "";
40141
40255
  if (process.env.MARIA_E2E_FAKE_CODE === "1") {
@@ -40155,10 +40269,37 @@ async function orchestrate(request, opts) {
40155
40269
  "[BEGIN file: path]\n<content>\n[END]",
40156
40270
  "Do not include any prose before/after; no menus/questions/suggestions; start immediately with ``` or [BEGIN file: ...]."
40157
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") : "";
40158
40290
  const requestPreamble = isEditIntent ? [
40159
- "// EDIT MODE: modify existing files, preserve unrelated content.",
40160
- "// Follow existing structure and only touch requested files.",
40161
- 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
40162
40303
  ].filter(Boolean).join("\n") : "";
40163
40304
  const enriched = `${FILE_FORMAT_INSTRUCTIONS}
40164
40305
 
@@ -40166,6 +40307,7 @@ ${requestPreamble}
40166
40307
  ${request}
40167
40308
 
40168
40309
  ${editContext}`;
40310
+ dbg("executeCode.prompt.head", enriched.slice(0, 1400));
40169
40311
  const ctxAttachments = Array.isArray(opts.attachedFiles) && opts.attachedFiles.length > 0 ? opts.attachedFiles.map((f3) => ({
40170
40312
  name: f3.originalName,
40171
40313
  path: f3.pathHint,
@@ -40199,6 +40341,68 @@ ${editContext}`;
40199
40341
  } catch {
40200
40342
  }
40201
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
+ }
40202
40406
  if (isEditIntent && Array.isArray(editTargets) && editTargets.length > 0) {
40203
40407
  try {
40204
40408
  const fs52 = await import('fs/promises');
@@ -40253,9 +40457,34 @@ ${editContext}`;
40253
40457
  hydratedCtx.push(...ctxAttachments);
40254
40458
  }
40255
40459
  }
40256
- 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
+ }));
40257
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
+ }
40258
40486
  const raw = (response.output || response?.data?.content || "").trim();
40487
+ dbg("executeCode.output.head", raw.slice(0, 1200));
40259
40488
  if (!raw) {
40260
40489
  return {
40261
40490
  ok: false,
@@ -40293,6 +40522,7 @@ ${editContext}`;
40293
40522
  codeOutput = outcome.data?.output || "";
40294
40523
  }
40295
40524
  const blocks = extractBlocks(codeOutput);
40525
+ dbg("extractBlocks.count", blocks.length);
40296
40526
  if (explicitFiles.length > 0 || isEditIntent && editTargets.length > 0) {
40297
40527
  const mapped = /* @__PURE__ */ new Set();
40298
40528
  const targets = explicitFiles.length > 0 ? explicitFiles : editTargets;
@@ -40330,23 +40560,49 @@ ${editContext}`;
40330
40560
  } else {
40331
40561
  try {
40332
40562
  const repoFiles = await getRepoFiles(opts.root);
40333
- 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);
40334
40587
  for (let i2 = 0; i2 < blocks.length; i2++) {
40335
40588
  const b = blocks[i2];
40336
40589
  const d = decisions[i2] || { action: "create", path: suggestName2(request, b.language, i2) };
40337
- 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)) {
40338
40593
  const lang = languageFromExt(d.path.replace(/^.*(\.[a-z0-9]+)$/i, "$1"));
40339
- 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 });
40340
40595
  } else {
40341
- const pth = d.path || suggestName2(request, b.language, i2);
40342
- 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 });
40343
40598
  }
40344
40599
  }
40345
40600
  } catch {
40346
40601
  for (let i2 = 0; i2 < blocks.length; i2++) {
40347
40602
  const b = blocks[i2];
40348
- const path65 = suggestName2(request, b.language, i2);
40349
- 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 });
40350
40606
  }
40351
40607
  }
40352
40608
  }
@@ -40362,13 +40618,13 @@ ${editContext}`;
40362
40618
  }
40363
40619
  }
40364
40620
  }
40365
- if (explicitFiles.length > 0 && initial.filter((f3) => !!f3.preview).length === 0) {
40621
+ if (!isEditIntent && explicitFiles.length > 0 && initial.filter((f3) => !!f3.preview).length === 0) {
40366
40622
  for (const f3 of explicitFiles) {
40367
40623
  initial.push(scaffoldForFilename(f3, explicitFiles));
40368
40624
  }
40369
40625
  }
40370
40626
  const normalized = await normalizePlans(
40371
- initial.map((p) => p.noNormalize ? p : p),
40627
+ initial.map((p) => p.noNormalize ? { ...p } : p),
40372
40628
  { root: opts.root }
40373
40629
  );
40374
40630
  try {
@@ -40393,11 +40649,26 @@ ${editContext}`;
40393
40649
  }
40394
40650
  } catch {
40395
40651
  }
40396
- 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 });
40397
40668
  const hasAnyModify = validated.files.some((f3) => f3.action === "modify") || isEditIntent;
40398
40669
  const outputMode = !opts.flags.hideCode && hasAnyModify ? "diff" : resolveOutputMode(opts.flags.output, validated.files.length, !!opts.flags.hideCode);
40399
40670
  const skippedSetForLookup = new Set(validated.skipped || []);
40400
- const skippedPlans = normalized.filter((f3) => skippedSetForLookup.has(f3.path));
40671
+ const skippedPlans = filtered.filter((f3) => skippedSetForLookup.has(f3.path));
40401
40672
  const remainingSkipped = (validated.skipped || []).filter((p) => !skippedPlans.some((sp) => sp.path === p)).map((p) => ({ path: p, kind: "source", action: "skip", description: "" }));
40402
40673
  const displayFiles = validated.files.concat(skippedPlans).concat(remainingSkipped);
40403
40674
  const summary = summarizePlan(displayFiles);
@@ -40590,6 +40861,21 @@ function extractBlocks(content) {
40590
40861
  if (blocks.length === 0 && looksLikeCode(content)) blocks.push({ language: detectLanguage(content), code: content.trim() });
40591
40862
  return blocks;
40592
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
+ }
40593
40879
  function looksLikeCode(s2) {
40594
40880
  return ["function ", "const ", "let ", "var ", "class ", "def ", "import ", "export "].some((k) => s2.includes(k));
40595
40881
  }
@@ -40747,48 +41033,100 @@ function parseExplicitFilenames(request) {
40747
41033
  return out;
40748
41034
  }
40749
41035
  async function detectEditIntentLLM(root, request, ctx2) {
41036
+ let existingDirs = [];
41037
+ let existingFiles = [];
41038
+ let anyExistingPathMentioned = false;
40750
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
+ }
40751
41059
  const repoFiles = await getRepoFiles(root);
40752
- 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
+ }
40753
41067
  const headSnippets = [];
40754
- for (const p of candidates) {
41068
+ for (const p of scope) {
40755
41069
  const h2 = await readHeadTail(root, p, 5);
40756
41070
  headSnippets.push(`- ${p}
40757
41071
  ${h2.head}`);
40758
41072
  }
40759
41073
  const system = [
40760
- "Classify the user intent for the MARIA code orchestrator.",
40761
- "Decide strictly between EDIT_EXISTING (modify existing files) or CREATE_NEW (generate new project/files).",
40762
- "User is likely to have EDIT_EXITING intent when:",
40763
- "- the coding language is not specified",
40764
- "- it has a clear and/or relevant path of the file/directory exists",
40765
- "- it has attachments",
40766
- "- the wording implies modifying existing files (ex. fix, improve, update, change, patch, make it, clean up, etc.)",
40767
- '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" }.'
40768
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
+ };
40769
41091
  const user = [
40770
41092
  `Request: ${request}`,
40771
- `ExplicitFiles: ${JSON.stringify(ctx2.explicitFiles || [])}`,
40772
- `AttachmentsCount: ${ctx2.attachmentsCount}`,
40773
- "Repo snapshot (paths with file heads):",
41093
+ `Evidence: ${JSON.stringify(evidence)}`,
41094
+ "Repo snapshot (trimmed):",
40774
41095
  headSnippets.join("\n\n")
40775
41096
  ].join("\n\n");
40776
- const spin1 = new ProcessAnimation();
40777
- spin1.start();
40778
- const resp = await executeChat([
40779
- { role: "system", content: system },
40780
- { role: "user", content: user }
40781
- ]);
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;
40782
41108
  try {
40783
- spin1.stop();
40784
- } 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
+ }
40785
41120
  }
40786
41121
  const raw = (resp?.output || "").trim();
40787
41122
  const jsonText = extractJsonSafe(raw, "object") || raw;
40788
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
+ }
40789
41127
  return parsed?.intent === "EDIT_EXISTING";
40790
41128
  } catch {
40791
- 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;
40792
41130
  }
40793
41131
  }
40794
41132
  function sanitizeFolderName(name2) {
@@ -40956,7 +41294,7 @@ async function resolveExplicitPaths(root, files, hintText) {
40956
41294
  const chat = await executeChat([
40957
41295
  { role: "system", content: system },
40958
41296
  { role: "user", content: user }
40959
- ]);
41297
+ ], { provider: "google", model: "gemini-2.5-flash-lite" });
40960
41298
  const raw = (chat.output || "").trim();
40961
41299
  const pick = ranked.find((r2) => r2 === raw) || ranked.find((r2) => raw.includes(r2)) || ranked[0];
40962
41300
  return pick.replace(/^\/+/, "");
@@ -41131,7 +41469,16 @@ var init_code_command = __esm({
41131
41469
  }
41132
41470
  ];
41133
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
+ };
41134
41480
  const request = await this.ensureLanguageDefaults(commandArgs.raw.join(" ").trim());
41481
+ debug("request", request);
41135
41482
  if (!request) {
41136
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.");
41137
41484
  }
@@ -41143,6 +41490,7 @@ var init_code_command = __esm({
41143
41490
  const explicitDry = commandArgs.raw.includes("--dry-run");
41144
41491
  const explicitOutput = commandArgs.raw.some((x2) => x2.startsWith("--output") || x2 === "--verbose" || x2 === "-v");
41145
41492
  const explicitPreview = commandArgs.raw.some((x2) => x2.startsWith("--preview-lines"));
41493
+ const explicitOnlyAttached = commandArgs.raw.includes("--only-attached");
41146
41494
  const preSpin = new ProcessAnimation();
41147
41495
  preSpin.start();
41148
41496
  let inferred = {};
@@ -41154,6 +41502,8 @@ var init_code_command = __esm({
41154
41502
  } catch {
41155
41503
  }
41156
41504
  }
41505
+ debug("inferCodeArgs.raw", rawText);
41506
+ debug("inferCodeArgs.result", inferred);
41157
41507
  if (!explicitPlan && !explicitDry) {
41158
41508
  if (typeof inferred.planOnly === "boolean") opts.planOnly = inferred.planOnly;
41159
41509
  if (typeof inferred.dryRun === "boolean") opts.dryRun = inferred.dryRun;
@@ -41164,6 +41514,9 @@ var init_code_command = __esm({
41164
41514
  if (!explicitPreview && typeof inferred.previewLines === "number") {
41165
41515
  opts.previewLines = inferred.previewLines;
41166
41516
  }
41517
+ if (!explicitOnlyAttached && typeof inferred.onlyAttached === "boolean") {
41518
+ opts.onlyAttached = inferred.onlyAttached;
41519
+ }
41167
41520
  } catch {
41168
41521
  }
41169
41522
  if (opts.planOnly) {
@@ -41178,13 +41531,27 @@ var init_code_command = __esm({
41178
41531
  const root = opts.root || process.cwd();
41179
41532
  const { orchestrate: orchestrate2 } = await Promise.resolve().then(() => (init_Orchestrator(), Orchestrator_exports));
41180
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
+ }
41181
41541
  const abort = new AbortController();
41182
41542
  const onSigint = () => abort.abort();
41183
41543
  process.once("SIGINT", onSigint);
41184
- const spinner = new ProcessAnimation();
41185
- 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
+ }
41186
41551
  try {
41187
- 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) : []);
41188
41555
  if (opts.planOnly) {
41189
41556
  const fs52 = await import('fs/promises');
41190
41557
  const path65 = await import('path');
@@ -41237,9 +41604,11 @@ var init_code_command = __esm({
41237
41604
  const out = Array.isArray(res?.summaryLines) ? res.summaryLines.join("\n") : "";
41238
41605
  return this.success(out);
41239
41606
  } finally {
41240
- try {
41241
- spinner.stop();
41242
- } catch {
41607
+ if (startedLocalSpinner && spinner) {
41608
+ try {
41609
+ spinner.stop();
41610
+ } catch {
41611
+ }
41243
41612
  }
41244
41613
  process.removeListener("SIGINT", onSigint);
41245
41614
  }
@@ -41362,12 +41731,6 @@ ${pretty}`);
41362
41731
  if (llmLang) {
41363
41732
  const hint2 = (() => {
41364
41733
  const l = llmLang.toLowerCase();
41365
- if (l === "tsx") return "TypeScript (React/TSX)";
41366
- if (l === "jsx") return "JavaScript (React/JSX)";
41367
- if (l === "typescript") return "TypeScript";
41368
- if (l === "javascript") return "JavaScript";
41369
- if (l === "html") return "HTML";
41370
- if (l === "css") return "CSS";
41371
41734
  return llmLang;
41372
41735
  })();
41373
41736
  return raw + ` (Use ${hint2})`;
@@ -41380,7 +41743,7 @@ ${pretty}`);
41380
41743
  preSpin.start();
41381
41744
  const system = [
41382
41745
  "You analyze a user's code-generation request.",
41383
- "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.",
41384
41747
  'Return ONLY compact JSON with shape {"explicitLanguage": boolean, "language"?: string}.',
41385
41748
  "Do not add any commentary."
41386
41749
  ].join("\n");
@@ -41389,7 +41752,7 @@ ${pretty}`);
41389
41752
  method: "POST",
41390
41753
  body: {
41391
41754
  provider: "google",
41392
- model: "gemini-2.5-flash",
41755
+ model: "gemini-2.5-flash-lite",
41393
41756
  taskType: "chat",
41394
41757
  prompt: `${system}
41395
41758
 
@@ -41434,6 +41797,14 @@ ${user}`
41434
41797
  if (parsed && parsed.explicitLanguage) return raw;
41435
41798
  } catch {
41436
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
+ }
41437
41808
  const hint = " (Use TypeScript and React; prefer functional components and node)";
41438
41809
  return raw + hint;
41439
41810
  }
@@ -41443,9 +41814,7 @@ ${user}`
41443
41814
  const system = [
41444
41815
  "You are a programming language classifier.",
41445
41816
  "Given multiple short code excerpts, determine the dominant language across them.",
41446
- "Respond with ONLY a single token language name from this set:",
41447
- "[typescript, tsx, javascript, jsx, python, java, go, rust, php, cpp, c, swift, kotlin, ruby, csharp, html, css, scss, json, yaml, markdown].",
41448
- "If unsure between tsx and jsx, choose tsx if TypeScript types appear, else jsx."
41817
+ "Respond with ONLY language name(s)"
41449
41818
  ].join("\n");
41450
41819
  const joined = samples.slice(0, 20).map((s2, i2) => `// sample ${i2 + 1}
41451
41820
  ${s2}`).join("\n\n");
@@ -41454,7 +41823,7 @@ ${s2}`).join("\n\n");
41454
41823
  method: "POST",
41455
41824
  body: {
41456
41825
  provider: "google",
41457
- model: "gemini-2.5-flash",
41826
+ model: "gemini-2.5-flash-lite",
41458
41827
  taskType: "chat",
41459
41828
  prompt: `${system}
41460
41829
 
@@ -41760,64 +42129,6 @@ ${joined}`
41760
42129
  return false;
41761
42130
  }
41762
42131
  }
41763
- /**
41764
- * Extract code blocks from AI response
41765
- */
41766
- extractCodeBlocks(content) {
41767
- const blocks = [];
41768
- const codeBlockRegex = /```(\w*)\n([\s\S]*?)```/g;
41769
- let match2;
41770
- while ((match2 = codeBlockRegex.exec(content)) !== null) {
41771
- blocks.push({
41772
- language: match2[1] || "javascript",
41773
- code: match2[2].trim()
41774
- });
41775
- }
41776
- if (blocks.length === 0 && this.looksLikeCode(content)) {
41777
- blocks.push({
41778
- code: content.trim(),
41779
- language: this.detectLanguage(content)
41780
- });
41781
- }
41782
- return blocks;
41783
- }
41784
- /**
41785
- * Check if content looks like code
41786
- */
41787
- looksLikeCode(content) {
41788
- const codeIndicators = [
41789
- "function ",
41790
- "const ",
41791
- "let ",
41792
- "var ",
41793
- "class ",
41794
- "def ",
41795
- "import ",
41796
- "export ",
41797
- "{",
41798
- "}",
41799
- ";",
41800
- "//",
41801
- "/*"
41802
- ];
41803
- return codeIndicators.some((indicator) => content.includes(indicator));
41804
- }
41805
- /**
41806
- * Detect programming language from content
41807
- */
41808
- detectLanguage(code) {
41809
- const hasReact = /(^|\s)from\s+['"]react['"]|^\s*import\s+React/m.test(code);
41810
- const hasJSX = /<([A-Za-z][\w:-]*)(\s|>|\/)>?/m.test(code);
41811
- const hasTS = /(\binterface\s+\w+\b|\btype\s+\w+\s*=|:\s*[A-Za-z_][\w<>\[\]| &?:]*)/m.test(code) || /React\.FC\s*</m.test(code);
41812
- if (hasReact || hasJSX) return hasTS ? "tsx" : "jsx";
41813
- if (code.includes("interface ") || code.includes(": string")) return "typescript";
41814
- if (code.includes("def ") || code.includes("print(")) return "python";
41815
- if (code.includes("func ") || code.includes("package main")) return "go";
41816
- if (code.includes("fn ") || code.includes("let mut")) return "rust";
41817
- if (code.includes("<?php")) return "php";
41818
- if (code.includes("#include")) return "cpp";
41819
- return "javascript";
41820
- }
41821
42132
  /**
41822
42133
  * Save code block to file
41823
42134
  */
@@ -50509,11 +50820,10 @@ var init_responsive_width = __esm({
50509
50820
  }
50510
50821
  });
50511
50822
  function createSpinner(text) {
50512
- let frame = 0;
50513
50823
  let active = false;
50514
- let timer;
50515
- const render = () => {
50516
- 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];
50517
50827
  process6__namespace.default.stdout.write(`\r ${chalk40__default.default.cyan(f3)} ${text}`);
50518
50828
  };
50519
50829
  return {
@@ -50523,13 +50833,11 @@ function createSpinner(text) {
50523
50833
  start() {
50524
50834
  if (active) return;
50525
50835
  active = true;
50526
- render();
50527
- timer = setInterval(render, 80);
50836
+ renderOnce();
50528
50837
  },
50529
50838
  stop(symbol = "\u2714") {
50530
50839
  if (!active) return;
50531
50840
  active = false;
50532
- if (timer) clearInterval(timer);
50533
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;
50534
50842
  process6__namespace.default.stdout.write(`\r ${finalSymbol} ${text}
50535
50843
  `);