@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.
@@ -26066,8 +26066,8 @@ var require_package = __commonJS({
26066
26066
  "package.json"(exports, module) {
26067
26067
  module.exports = {
26068
26068
  name: "@bonginkan/maria",
26069
- version: "4.3.45",
26070
- 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.",
26069
+ version: "4.3.46",
26070
+ 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.",
26071
26071
  keywords: [
26072
26072
  "ai",
26073
26073
  "cli",
@@ -26619,7 +26619,8 @@ var init_env_loader = __esm({
26619
26619
  var ThinkingAnimation, ProcessAnimation;
26620
26620
  var init_animations = __esm({
26621
26621
  "src/utils/animations.ts"() {
26622
- ThinkingAnimation = class {
26622
+ ThinkingAnimation = class _ThinkingAnimation {
26623
+ static current = null;
26623
26624
  frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
26624
26625
  currentFrame = 0;
26625
26626
  interval = null;
@@ -26628,6 +26629,13 @@ var init_animations = __esm({
26628
26629
  this.message = message;
26629
26630
  }
26630
26631
  start() {
26632
+ if (_ThinkingAnimation.current && _ThinkingAnimation.current !== this) {
26633
+ try {
26634
+ _ThinkingAnimation.current.stop();
26635
+ } catch {
26636
+ }
26637
+ }
26638
+ _ThinkingAnimation.current = this;
26631
26639
  this.interval = setInterval(() => {
26632
26640
  process.stdout.write(
26633
26641
  `\r${chalk14__default.default.cyan(this.frames[this.currentFrame])} ${chalk14__default.default.gray(this.message)}...`
@@ -26641,12 +26649,16 @@ var init_animations = __esm({
26641
26649
  this.interval = null;
26642
26650
  process.stdout.write("\r\x1B[K");
26643
26651
  }
26652
+ if (_ThinkingAnimation.current === this) {
26653
+ _ThinkingAnimation.current = null;
26654
+ }
26644
26655
  }
26645
26656
  updateMessage(message) {
26646
26657
  this.message = message;
26647
26658
  }
26648
26659
  };
26649
- ProcessAnimation = class {
26660
+ ProcessAnimation = class _ProcessAnimation {
26661
+ static current = null;
26650
26662
  stages = [
26651
26663
  { icon: "\u{1F9E0}", message: "Understanding your request" },
26652
26664
  { icon: "\u{1F50D}", message: "Analyzing context" },
@@ -26660,7 +26672,18 @@ var init_animations = __esm({
26660
26672
  interval = null;
26661
26673
  stageInterval = null;
26662
26674
  startTime = 0;
26675
+ // Expose whether any ProcessAnimation is currently active
26676
+ static hasActive() {
26677
+ return !!_ProcessAnimation.current;
26678
+ }
26663
26679
  start() {
26680
+ if (_ProcessAnimation.current && _ProcessAnimation.current !== this) {
26681
+ try {
26682
+ _ProcessAnimation.current.stop();
26683
+ } catch {
26684
+ }
26685
+ }
26686
+ _ProcessAnimation.current = this;
26664
26687
  this.startTime = Date.now();
26665
26688
  this.currentStage = 0;
26666
26689
  this.currentFrame = 0;
@@ -26688,6 +26711,9 @@ var init_animations = __esm({
26688
26711
  this.stageInterval = null;
26689
26712
  }
26690
26713
  process.stdout.write("\r\x1B[K");
26714
+ if (_ProcessAnimation.current === this) {
26715
+ _ProcessAnimation.current = null;
26716
+ }
26691
26717
  }
26692
26718
  setStage(stageIndex) {
26693
26719
  if (stageIndex >= 0 && stageIndex < this.stages.length) {
@@ -28104,7 +28130,7 @@ var init_AuthenticationManager = __esm({
28104
28130
  const response = await fetch(`${this.apiBase}/api/user/profile`, {
28105
28131
  headers: {
28106
28132
  "Authorization": `Bearer ${tokens2.accessToken}`,
28107
- "User-Agent": `maria-cli/${process.env.CLI_VERSION || "4.3.45"}`
28133
+ "User-Agent": `maria-cli/${process.env.CLI_VERSION || "4.3.46"}`
28108
28134
  }
28109
28135
  });
28110
28136
  if (response.status === 401) {
@@ -28758,7 +28784,7 @@ async function callApi(path65, init3 = {}) {
28758
28784
  "Authorization": `Bearer ${token}`,
28759
28785
  "X-Device-Id": getDeviceId(),
28760
28786
  "X-Session-Id": getSessionId() || "",
28761
- "User-Agent": `maria-cli/${process.env.CLI_VERSION || "4.3.45"}`,
28787
+ "User-Agent": `maria-cli/${process.env.CLI_VERSION || "4.3.46"}`,
28762
28788
  "Content-Type": init3.headers?.["Content-Type"] || "application/json"
28763
28789
  });
28764
28790
  const doFetch = async (token) => {
@@ -29102,15 +29128,27 @@ async function callAPI(endpoint, options = {}) {
29102
29128
  throw err;
29103
29129
  }
29104
29130
  }
29105
- async function executeChat(messages) {
29131
+ async function executeChat(messages, options) {
29106
29132
  const maxAttempts = 4;
29107
29133
  let lastErr;
29108
29134
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
29109
29135
  try {
29136
+ if (process.env.MARIA_DEBUG === "1") {
29137
+ try {
29138
+ console.log("[DEBUG/ai] chat.request", { attempt, provider: options?.provider, model: options?.model, messages: messages.slice(0, 3) });
29139
+ } catch {
29140
+ }
29141
+ }
29110
29142
  const response = await callAPI("/v1/ai-proxy", {
29111
29143
  method: "POST",
29112
- body: { messages, taskType: "chat" }
29144
+ body: { messages, taskType: "chat", ...options?.provider ? { provider: options.provider } : {}, ...options?.model ? { model: options.model } : {} }
29113
29145
  });
29146
+ if (process.env.MARIA_DEBUG === "1") {
29147
+ try {
29148
+ console.log("[DEBUG/ai] chat.response.head", String(response?.data?.content || response?.output || "").slice(0, 1200));
29149
+ } catch {
29150
+ }
29151
+ }
29114
29152
  return response;
29115
29153
  } catch (e2) {
29116
29154
  lastErr = e2;
@@ -29142,6 +29180,13 @@ async function executeCode(input3) {
29142
29180
  let lastErr;
29143
29181
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
29144
29182
  try {
29183
+ if (process.env.MARIA_DEBUG === "1") {
29184
+ try {
29185
+ 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 };
29186
+ console.log("[DEBUG/ai] code.request", { attempt, ...dbg });
29187
+ } catch {
29188
+ }
29189
+ }
29145
29190
  const response = await callAPI("/v1/ai-proxy", {
29146
29191
  method: "POST",
29147
29192
  body
@@ -29152,6 +29197,12 @@ async function executeCode(input3) {
29152
29197
  if (response.data?.content) {
29153
29198
  response.output = response.data.content;
29154
29199
  }
29200
+ if (process.env.MARIA_DEBUG === "1") {
29201
+ try {
29202
+ console.log("[DEBUG/ai] code.response.head", String(response?.output || "").slice(0, 1200));
29203
+ } catch {
29204
+ }
29205
+ }
29155
29206
  return response;
29156
29207
  } catch (e2) {
29157
29208
  lastErr = e2;
@@ -51298,7 +51349,7 @@ var init_about_command = __esm({
51298
51349
  async execute(args2, context2) {
51299
51350
  const output3 = [];
51300
51351
  output3.push("");
51301
- output3.push(chalk14__default.default.cyan.bold("\u{1F916} About MARIA v4.3.45"));
51352
+ output3.push(chalk14__default.default.cyan.bold("\u{1F916} About MARIA v4.3.46"));
51302
51353
  output3.push(chalk14__default.default.gray("\u2550".repeat(40)));
51303
51354
  output3.push("");
51304
51355
  output3.push(chalk14__default.default.white.bold("MARIA - Minimal API, Maximum Power"));
@@ -55025,9 +55076,10 @@ function extractFirstJson5(text) {
55025
55076
  async function inferCodeArgs(rawText) {
55026
55077
  const system = [
55027
55078
  "You extract structured options for a code command.",
55028
- 'Return JSON only with keys: { "planOnly"?: boolean, "dryRun"?: boolean, "output"?: "names"|"summary"|"detail", "previewLines"?: number }.',
55079
+ 'Return JSON only with keys: { "planOnly"?: boolean, "dryRun"?: boolean, "output"?: "names"|"summary"|"detail", "previewLines"?: number, "onlyAttached"?: boolean }.',
55029
55080
  "Decide from the user text whether planOnly or dryRun should be true. Do not explain.",
55030
- "Only include output if the user requests preview detail or summary mode. Only include previewLines if a specific number of lines is requested."
55081
+ "Only include output if the user requests preview detail or summary mode. Only include previewLines if a specific number of lines is requested.",
55082
+ "Set onlyAttached=true when the user indicates editing only attached/uploaded/provided files, or restrict changes to referenced files."
55031
55083
  ].join("\n");
55032
55084
  const resp = await callAPI("/v1/ai-proxy", {
55033
55085
  method: "POST",
@@ -55048,11 +55100,19 @@ ${rawText}`,
55048
55100
  } catch {
55049
55101
  return {};
55050
55102
  }
55103
+ if (process.env.MARIA_DEBUG === "1") {
55104
+ try {
55105
+ console.log("[DEBUG/code] inferCodeArgs.response.raw", raw.slice(0, 1200));
55106
+ console.log("[DEBUG/code] inferCodeArgs.parsed", parsed);
55107
+ } catch {
55108
+ }
55109
+ }
55051
55110
  const out = {};
55052
55111
  if (typeof parsed.planOnly === "boolean") out.planOnly = parsed.planOnly;
55053
55112
  if (typeof parsed.dryRun === "boolean") out.dryRun = parsed.dryRun;
55054
55113
  if (typeof parsed.output === "string" && (parsed.output === "names" || parsed.output === "summary" || parsed.output === "detail")) out.output = parsed.output;
55055
55114
  if (typeof parsed.previewLines === "number" && Number.isFinite(parsed.previewLines) && parsed.previewLines > 0) out.previewLines = Math.min(2e3, Math.floor(parsed.previewLines));
55115
+ if (typeof parsed.onlyAttached === "boolean") out.onlyAttached = parsed.onlyAttached;
55056
55116
  if (out.planOnly) out.dryRun = false;
55057
55117
  return out;
55058
55118
  }
@@ -55200,6 +55260,12 @@ var init_FilePlanBuilder = __esm({
55200
55260
  }
55201
55261
  });
55202
55262
  async function validatePlan(plans, opts) {
55263
+ if (process.env.MARIA_DEBUG === "1") {
55264
+ try {
55265
+ console.log("[DEBUG/orchestrator] validatePlan.input.count", plans.length);
55266
+ } catch {
55267
+ }
55268
+ }
55203
55269
  const warnings = [];
55204
55270
  const skipped = [];
55205
55271
  const seenLC = /* @__PURE__ */ new Set();
@@ -55216,6 +55282,10 @@ async function validatePlan(plans, opts) {
55216
55282
  }
55217
55283
  const safe = [];
55218
55284
  for (const fp of outFiltered) {
55285
+ if (fp.action === "skip") {
55286
+ skipped.push(fp.path);
55287
+ continue;
55288
+ }
55219
55289
  const rel = fp.path.replace(/^\/+/, "");
55220
55290
  if (rel.includes("..")) {
55221
55291
  warnings.push(`Path traversal denied: ${fp.path}`);
@@ -55297,7 +55367,18 @@ async function validatePlan(plans, opts) {
55297
55367
  }
55298
55368
  result.push({ ...fp, action: willModify ? "modify" : "create" });
55299
55369
  }
55300
- return { files: result, skipped, warnings };
55370
+ const out = { files: result, skipped, warnings };
55371
+ if (process.env.MARIA_DEBUG === "1") {
55372
+ try {
55373
+ console.log("[DEBUG/orchestrator] validatePlan.output", {
55374
+ files: out.files.map((f3) => ({ path: f3.path, action: f3.action })),
55375
+ skipped: out.skipped,
55376
+ warnings: out.warnings
55377
+ });
55378
+ } catch {
55379
+ }
55380
+ }
55381
+ return out;
55301
55382
  }
55302
55383
  async function exists(p) {
55303
55384
  try {
@@ -56091,7 +56172,7 @@ ${h2.head}`);
56091
56172
  const resp = await executeChat([
56092
56173
  { role: "system", content: system },
56093
56174
  { role: "user", content: user }
56094
- ]);
56175
+ ], { provider: "google", model: "gemini-2.5-flash-lite" });
56095
56176
  const raw = (resp?.output || "").trim();
56096
56177
  const jsonText = extractJsonSafe(raw, "array") || raw;
56097
56178
  const arr = JSON.parse(jsonText);
@@ -56108,7 +56189,7 @@ ${h2.head}`);
56108
56189
  return [];
56109
56190
  }
56110
56191
  }
56111
- async function llmMapBlocksBatch(root, request, blocks, repoFiles) {
56192
+ async function llmMapBlocksBatch(root, request, blocks, repoFiles, opts) {
56112
56193
  try {
56113
56194
  const candidates = repoFiles.filter((p) => /\.(html|css|js|ts|tsx)$/i.test(p)).slice(0, 120);
56114
56195
  const blockSnippets = blocks.slice(0, 20).map((b, i2) => {
@@ -56126,26 +56207,43 @@ ${head2}
56126
56207
  ${h2.head}`);
56127
56208
  }
56128
56209
  const system = [
56129
- "For each provided code block, decide whether to MODIFY an existing repo file or CREATE a new file.",
56130
- 'Return JSON array of objects: [{ "index": number, "action": "modify"|"create", "path": string }].',
56131
- "When action is modify, path MUST be one of the candidate repo-relative paths listed."
56210
+ "You act as a precise file mapper for code edits.",
56211
+ "For each provided code block, decide to MODIFY an existing repo file or CREATE a new file.",
56212
+ "Rules:",
56213
+ "- Prefer MODIFY when an existing file in candidates plausibly matches the block (same technology, same area).",
56214
+ "- If any TargetDirHints are provided, prefer files under those directories when choosing MODIFY targets.",
56215
+ "- Only choose CREATE if NO suitable candidate exists. Do NOT invent frameworks or restructure.",
56216
+ "- When MODIFY, the path MUST be one of the candidate repo-relative paths listed.",
56217
+ 'Return JSON array: [{ "index": number, "action": "modify"|"create", "path": string }].'
56132
56218
  ].join("\n");
56133
56219
  const user = [
56134
56220
  `Request: ${request}`,
56135
56221
  "Blocks:",
56136
56222
  blockSnippets,
56137
56223
  "Candidates:",
56138
- samples.join("\n\n")
56224
+ samples.join("\n\n"),
56225
+ `TargetDirHints: ${(opts?.dirHints || []).join(", ")}`
56139
56226
  ].join("\n\n");
56140
- const spin = new ProcessAnimation();
56141
- spin.start();
56142
- const resp = await executeChat([
56143
- { role: "system", content: system },
56144
- { role: "user", content: user }
56145
- ]);
56227
+ let startedLocalSpinner = false;
56228
+ let spin = null;
56229
+ if (!ProcessAnimation.hasActive()) {
56230
+ spin = new ProcessAnimation();
56231
+ spin.start();
56232
+ startedLocalSpinner = true;
56233
+ }
56234
+ let resp;
56146
56235
  try {
56147
- spin.stop();
56148
- } catch {
56236
+ resp = await executeChat([
56237
+ { role: "system", content: system },
56238
+ { role: "user", content: user }
56239
+ ], { provider: "google", model: "gemini-2.5-flash-lite" });
56240
+ } finally {
56241
+ if (startedLocalSpinner && spin) {
56242
+ try {
56243
+ spin.stop();
56244
+ } catch {
56245
+ }
56246
+ }
56149
56247
  }
56150
56248
  const raw = (resp?.output || "").trim();
56151
56249
  const jsonText = extractJsonSafe(raw, "array") || raw;
@@ -56199,12 +56297,25 @@ function trackCodeFallback(event) {
56199
56297
  }
56200
56298
  }
56201
56299
  async function orchestrate(request, opts) {
56300
+ const dbg = (...a) => {
56301
+ if (process.env.MARIA_DEBUG === "1") {
56302
+ try {
56303
+ console.log("[DEBUG/orchestrator]", ...a);
56304
+ } catch {
56305
+ }
56306
+ }
56307
+ };
56202
56308
  const profile = await scanRepo(opts.root);
56309
+ dbg("start", { root: opts.root, flags: {
56310
+ ...opts.flags
56311
+ /* redact */
56312
+ }, attachedFiles: Array.isArray(opts.attachedFiles) ? opts.attachedFiles.length : 0 });
56203
56313
  const initial = [];
56204
56314
  const fallbackNotices = [];
56205
56315
  const withNotices = (base) => fallbackNotices.length > 0 ? [...fallbackNotices, ...base] : base;
56206
56316
  const explicitFilesRaw = parseExplicitFilenames(request);
56207
56317
  const explicitFiles = explicitFilesRaw.length > 0 ? await resolveExplicitPaths(opts.root, explicitFilesRaw, request) : [];
56318
+ dbg("explicitFiles", { raw: explicitFilesRaw, resolved: explicitFiles });
56208
56319
  const explicitAbsMap = /* @__PURE__ */ Object.create(null);
56209
56320
  if (explicitFiles.length > 0) {
56210
56321
  const pathMod = await import('path');
@@ -56216,12 +56327,14 @@ async function orchestrate(request, opts) {
56216
56327
  explicitFiles,
56217
56328
  attachmentsCount: Array.isArray(opts.attachedFiles) ? opts.attachedFiles.length : 0
56218
56329
  });
56330
+ dbg("intent", { isEditIntent });
56219
56331
  let editTargets = explicitFiles;
56220
56332
  if (isEditIntent && editTargets.length === 0) {
56221
56333
  try {
56222
56334
  const repoFiles = await getRepoFiles(opts.root);
56223
56335
  const llmTargets = await llmSelectEditTargets(opts.root, request, repoFiles);
56224
56336
  editTargets = llmTargets;
56337
+ dbg("llmSelectEditTargets", { editTargets });
56225
56338
  } catch {
56226
56339
  }
56227
56340
  }
@@ -56233,6 +56346,7 @@ async function orchestrate(request, opts) {
56233
56346
  maxAttachments: opts.flags.maxAttachments || 50,
56234
56347
  allowDotfiles: !!opts.flags.allowDotfiles
56235
56348
  });
56349
+ dbg("attachment.map", { mappedCount: mapRes.mapped.length, warnings: mapRes.warnings });
56236
56350
  mapRes.warnings.slice();
56237
56351
  for (const m2 of mapRes.mapped) {
56238
56352
  initial.push({
@@ -56244,7 +56358,7 @@ async function orchestrate(request, opts) {
56244
56358
  });
56245
56359
  }
56246
56360
  }
56247
- const onlyAttached = !!opts.flags.onlyAttached;
56361
+ const onlyAttached = !!(opts.flags.onlyAttached && Array.isArray(opts.attachedFiles) && opts.attachedFiles.length > 0);
56248
56362
  if (!onlyAttached) {
56249
56363
  let codeOutput = "";
56250
56364
  if (process.env.MARIA_E2E_FAKE_CODE === "1") {
@@ -56264,10 +56378,37 @@ async function orchestrate(request, opts) {
56264
56378
  "[BEGIN file: path]\n<content>\n[END]",
56265
56379
  "Do not include any prose before/after; no menus/questions/suggestions; start immediately with ``` or [BEGIN file: ...]."
56266
56380
  ].join("\n");
56381
+ let targetFilesAbs = [];
56382
+ try {
56383
+ const pathMod = await import('path');
56384
+ const fromExplicit = Array.isArray(explicitFiles) && explicitFiles.length > 0 ? explicitFiles.map((rel) => explicitAbsMap[rel] || pathMod.join(opts.root, rel)) : [];
56385
+ const fromEditTargets = Array.isArray(editTargets) && editTargets.length > 0 ? editTargets.map((rel) => pathMod.isAbsolute(rel) ? rel : pathMod.join(opts.root, rel)) : [];
56386
+ const seen = /* @__PURE__ */ new Set();
56387
+ for (const p of [...fromExplicit, ...fromEditTargets]) {
56388
+ const norm = (p || "").replace(/\\/g, "/");
56389
+ if (!norm) continue;
56390
+ if (seen.has(norm)) continue;
56391
+ seen.add(norm);
56392
+ targetFilesAbs.push(norm);
56393
+ }
56394
+ } catch {
56395
+ }
56396
+ const targetDirsAbs = Array.from(new Set(targetFilesAbs.map((p) => p.split("/").slice(0, -1).join("/")).filter(Boolean)));
56397
+ const targetFilesSection = targetFilesAbs.length > 0 ? ["// TARGET FILES (absolute):", ...targetFilesAbs.map((p) => `// - ${p}`)].join("\n") : "";
56398
+ const targetDirsSection = targetDirsAbs.length > 0 ? ["// TARGET DIRECTORIES (absolute):", ...targetDirsAbs.map((p) => `// - ${p}`)].join("\n") : "";
56267
56399
  const requestPreamble = isEditIntent ? [
56268
- "// EDIT MODE: modify existing files, preserve unrelated content.",
56269
- "// Follow existing structure and only touch requested files.",
56270
- editContext ? "// Current file snapshots provided below." : ""
56400
+ "// EDIT MODE RULES:",
56401
+ "// 1) Read the entire target file(s) BEFORE making changes. Assume omitted lines must remain exactly as-is.",
56402
+ "// 2) Preserve unrelated content and formatting (indentation, EOLs, imports order, license headers).",
56403
+ "// 3) Do NOT rename, move, or delete files unless explicitly requested.",
56404
+ "// 4) Apply the MINIMAL necessary change to achieve the request. Avoid broad refactors.",
56405
+ "// 5) When returning a whole file, keep everything identical except for the exact lines you changed.",
56406
+ "// 6) Do NOT introduce unrelated edits or code style churn.",
56407
+ "// 7) Edit ONLY the files listed under TARGET FILES or inside TARGET DIRECTORIES unless explicitly instructed otherwise.",
56408
+ "// 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.",
56409
+ editContext ? "// Current file snapshots provided below." : "",
56410
+ targetFilesSection,
56411
+ targetDirsSection
56271
56412
  ].filter(Boolean).join("\n") : "";
56272
56413
  const enriched = `${FILE_FORMAT_INSTRUCTIONS}
56273
56414
 
@@ -56275,6 +56416,7 @@ ${requestPreamble}
56275
56416
  ${request}
56276
56417
 
56277
56418
  ${editContext}`;
56419
+ dbg("executeCode.prompt.head", enriched.slice(0, 1400));
56278
56420
  const ctxAttachments = Array.isArray(opts.attachedFiles) && opts.attachedFiles.length > 0 ? opts.attachedFiles.map((f3) => ({
56279
56421
  name: f3.originalName,
56280
56422
  path: f3.pathHint,
@@ -56308,6 +56450,68 @@ ${editContext}`;
56308
56450
  } catch {
56309
56451
  }
56310
56452
  }
56453
+ try {
56454
+ const fs52 = await import('fs/promises');
56455
+ const pathMod = await import('path');
56456
+ const dirCandidates = Array.from(new Set(String(request).match(/[A-Za-z]:\\[^\s"']+|\.?\/?[^\s"']+[\/\\][^\s"']*/g) || []));
56457
+ const maxFilesPerDir = 80;
56458
+ for (const raw2 of dirCandidates) {
56459
+ try {
56460
+ const normalized2 = raw2.replace(/^"|"$/g, "").replace(/^'|'$/g, "");
56461
+ const abs = pathMod.isAbsolute(normalized2) ? normalized2 : pathMod.join(opts.root, normalized2);
56462
+ const st = await fs52.stat(abs).catch(() => null);
56463
+ if (!st || !st.isDirectory()) continue;
56464
+ const collected = [];
56465
+ const walk2 = async (d) => {
56466
+ if (collected.length >= maxFilesPerDir) return;
56467
+ let entries = [];
56468
+ try {
56469
+ entries = await fs52.readdir(d, { withFileTypes: true });
56470
+ } catch {
56471
+ return;
56472
+ }
56473
+ for (const e2 of entries) {
56474
+ const name2 = e2.name;
56475
+ if (name2 === ".git" || name2 === "node_modules" || name2 === "dist" || name2 === "build" || name2 === ".maria") continue;
56476
+ const full = pathMod.join(d, name2);
56477
+ if (e2.isDirectory()) {
56478
+ await walk2(full);
56479
+ if (collected.length >= maxFilesPerDir) break;
56480
+ continue;
56481
+ }
56482
+ collected.push(full);
56483
+ if (collected.length >= maxFilesPerDir) break;
56484
+ }
56485
+ };
56486
+ await walk2(abs);
56487
+ for (const f3 of collected) {
56488
+ try {
56489
+ const key = f3.toLowerCase();
56490
+ if (attachedPathSet.has(key)) continue;
56491
+ const buf = await fs52.readFile(f3);
56492
+ const head2 = buf.subarray(0, Math.min(buf.length, 4096));
56493
+ let binaryLike = false;
56494
+ for (let i2 = 0; i2 < head2.length; i2++) {
56495
+ if (head2[i2] === 0) {
56496
+ binaryLike = true;
56497
+ break;
56498
+ }
56499
+ }
56500
+ if (binaryLike) continue;
56501
+ const sample = buf.subarray(0, Math.min(buf.length, 8192)).toString("utf8");
56502
+ const printable = sample.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, "");
56503
+ const ratio = sample.length === 0 ? 0 : printable.length / sample.length;
56504
+ if (ratio < 0.6 && sample.length > 0) continue;
56505
+ pathAttachments.push({ name: pathMod.basename(f3), path: f3, mime: "text/plain", data_base64: buf.toString("base64") });
56506
+ attachedPathSet.add(key);
56507
+ } catch {
56508
+ }
56509
+ }
56510
+ } catch {
56511
+ }
56512
+ }
56513
+ } catch {
56514
+ }
56311
56515
  if (isEditIntent && Array.isArray(editTargets) && editTargets.length > 0) {
56312
56516
  try {
56313
56517
  const fs52 = await import('fs/promises');
@@ -56362,9 +56566,34 @@ ${editContext}`;
56362
56566
  hydratedCtx.push(...ctxAttachments);
56363
56567
  }
56364
56568
  }
56365
- const allAttachments = (hydratedCtx.length ? hydratedCtx : ctxAttachments).concat(pathAttachments);
56569
+ const pathMod2 = await import('path');
56570
+ const normalizeMime = (p, m2) => {
56571
+ if (!p) return m2 || "text/plain";
56572
+ const ext2 = pathMod2.extname(p).toLowerCase();
56573
+ if (m2 && m2 !== "application/octet-stream") return m2;
56574
+ if (ext2 === ".pdf") return "application/pdf";
56575
+ if (ext2 === ".png") return "image/png";
56576
+ if (ext2 === ".jpg" || ext2 === ".jpeg") return "image/jpeg";
56577
+ if (ext2 === ".webp") return "image/webp";
56578
+ if (ext2 === ".gif") return "image/gif";
56579
+ if (ext2 === ".bmp") return "image/bmp";
56580
+ if (ext2 === ".svg") return "image/svg+xml";
56581
+ if (ext2 === ".tif" || ext2 === ".tiff") return "image/tiff";
56582
+ if (ext2 === ".heic") return "image/heic";
56583
+ if (ext2 === ".heif") return "image/heif";
56584
+ return "text/plain";
56585
+ };
56586
+ const allAttachments = (hydratedCtx.length ? hydratedCtx : ctxAttachments).concat(pathAttachments).map((a) => ({
56587
+ ...a,
56588
+ mime: normalizeMime(a.path, a.mime)
56589
+ }));
56366
56590
  const response = await executeCode(allAttachments.length > 0 ? { prompt: enriched, provider: "google", model: "gemini-2.5-flash", attachments: allAttachments } : enriched);
56591
+ try {
56592
+ dbg("executeCode.attachments.meta", { ctx: (hydratedCtx.length ? hydratedCtx : ctxAttachments).length, path: pathAttachments.length });
56593
+ } catch {
56594
+ }
56367
56595
  const raw = (response.output || response?.data?.content || "").trim();
56596
+ dbg("executeCode.output.head", raw.slice(0, 1200));
56368
56597
  if (!raw) {
56369
56598
  return {
56370
56599
  ok: false,
@@ -56402,6 +56631,7 @@ ${editContext}`;
56402
56631
  codeOutput = outcome.data?.output || "";
56403
56632
  }
56404
56633
  const blocks = extractBlocks(codeOutput);
56634
+ dbg("extractBlocks.count", blocks.length);
56405
56635
  if (explicitFiles.length > 0 || isEditIntent && editTargets.length > 0) {
56406
56636
  const mapped = /* @__PURE__ */ new Set();
56407
56637
  const targets = explicitFiles.length > 0 ? explicitFiles : editTargets;
@@ -56439,23 +56669,49 @@ ${editContext}`;
56439
56669
  } else {
56440
56670
  try {
56441
56671
  const repoFiles = await getRepoFiles(opts.root);
56442
- const decisions = await llmMapBlocksBatch(opts.root, request, blocks, repoFiles);
56672
+ let dirHints = [];
56673
+ try {
56674
+ const fs52 = await import('fs/promises');
56675
+ const pathMod = await import('path');
56676
+ const rawDirs = Array.from(new Set(String(request).match(/[A-Za-z]:\\[^\s"']+|\.?\/?[^\s"']+[\/\\][^\s"']*/g) || []));
56677
+ for (const raw of rawDirs) {
56678
+ try {
56679
+ const normalized2 = raw.replace(/^"|"$/g, "").replace(/^'|'$/g, "");
56680
+ const abs = pathMod.isAbsolute(normalized2) ? normalized2 : pathMod.join(opts.root, normalized2);
56681
+ const st = await fs52.stat(abs).catch(() => null);
56682
+ if (!st || !st.isDirectory()) continue;
56683
+ const rel = pathMod.relative(opts.root, abs).replace(/\\/g, "/").replace(/^\/+/, "");
56684
+ if (!rel || rel.startsWith("..")) continue;
56685
+ dirHints.push(rel);
56686
+ } catch {
56687
+ }
56688
+ }
56689
+ if (dirHints.length === 0 && Array.isArray(editTargets) && editTargets.length > 0) {
56690
+ dirHints = Array.from(new Set(editTargets.map((p) => (p || "").replace(/^\/+/, "").split("/").slice(0, -1).join("/")).filter(Boolean)));
56691
+ }
56692
+ } catch {
56693
+ }
56694
+ const decisions = await llmMapBlocksBatch(opts.root, request, blocks, repoFiles, { dirHints });
56695
+ dbg("llmMapBlocksBatch.decisions", decisions);
56443
56696
  for (let i2 = 0; i2 < blocks.length; i2++) {
56444
56697
  const b = blocks[i2];
56445
56698
  const d = decisions[i2] || { action: "create", path: suggestName2(request, b.language, i2) };
56446
- if (d.action === "modify" && repoFiles.includes(d.path)) {
56699
+ const hinted = b.filename && String(b.filename).trim() || extractFilenameHintFromComment(b.code);
56700
+ const finalPath = hinted && hinted.trim() || d.path;
56701
+ if (d.action === "modify" && repoFiles.includes(finalPath)) {
56447
56702
  const lang = languageFromExt(d.path.replace(/^.*(\.[a-z0-9]+)$/i, "$1"));
56448
- initial.push({ path: d.path, kind: "source", action: "modify", description: "Modify existing file", language: lang, preview: b.code });
56703
+ initial.push({ path: finalPath, kind: "source", action: "modify", description: "Modify existing file", language: lang, preview: b.code, noNormalize: true });
56449
56704
  } else {
56450
- const pth = d.path || suggestName2(request, b.language, i2);
56451
- initial.push({ path: pth, kind: "source", action: "create", description: describe2(b.language, ""), language: b.language, preview: b.code });
56705
+ const pth = finalPath && finalPath.trim() || d.path || suggestName2(request, b.language, i2);
56706
+ initial.push({ path: pth, kind: "source", action: "create", description: describe2(b.language, ""), language: b.language, preview: b.code, noNormalize: true });
56452
56707
  }
56453
56708
  }
56454
56709
  } catch {
56455
56710
  for (let i2 = 0; i2 < blocks.length; i2++) {
56456
56711
  const b = blocks[i2];
56457
- const path65 = suggestName2(request, b.language, i2);
56458
- initial.push({ path: path65, kind: "source", action: "create", description: describe2(b.language, ""), language: b.language, preview: b.code });
56712
+ const hinted = b.filename && String(b.filename).trim() || extractFilenameHintFromComment(b.code);
56713
+ const path65 = hinted && hinted.trim() || suggestName2(request, b.language, i2);
56714
+ initial.push({ path: path65, kind: "source", action: "create", description: describe2(b.language, ""), language: b.language, preview: b.code, noNormalize: true });
56459
56715
  }
56460
56716
  }
56461
56717
  }
@@ -56471,13 +56727,13 @@ ${editContext}`;
56471
56727
  }
56472
56728
  }
56473
56729
  }
56474
- if (explicitFiles.length > 0 && initial.filter((f3) => !!f3.preview).length === 0) {
56730
+ if (!isEditIntent && explicitFiles.length > 0 && initial.filter((f3) => !!f3.preview).length === 0) {
56475
56731
  for (const f3 of explicitFiles) {
56476
56732
  initial.push(scaffoldForFilename(f3, explicitFiles));
56477
56733
  }
56478
56734
  }
56479
56735
  const normalized = await normalizePlans(
56480
- initial.map((p) => p.noNormalize ? p : p),
56736
+ initial.map((p) => p.noNormalize ? { ...p } : p),
56481
56737
  { root: opts.root }
56482
56738
  );
56483
56739
  try {
@@ -56502,11 +56758,26 @@ ${editContext}`;
56502
56758
  }
56503
56759
  } catch {
56504
56760
  }
56505
- const validated = await validatePlan(normalized, { root: opts.root, profile, flags: { maxFiles: opts.flags.maxFiles, yes: opts.flags.yes, interactive: !!opts.flags.interactive, ...opts.flags } });
56761
+ let filtered = normalized;
56762
+ if (isEditIntent) {
56763
+ const keepCreates = !!opts.flags.onlyAttached;
56764
+ if (!keepCreates) {
56765
+ filtered = normalized.map((f3) => f3.action === "create" ? { ...f3, action: "skip" } : f3);
56766
+ }
56767
+ }
56768
+ const baseFlags = { ...opts.flags };
56769
+ const effectiveFlags = {
56770
+ ...baseFlags,
56771
+ maxFiles: opts.flags.maxFiles,
56772
+ interactive: !!opts.flags.interactive,
56773
+ // Force yes when editing; if baseFlags.yes is already true, keep it true
56774
+ yes: baseFlags?.yes === true || isEditIntent
56775
+ };
56776
+ const validated = await validatePlan(filtered, { root: opts.root, profile, flags: effectiveFlags });
56506
56777
  const hasAnyModify = validated.files.some((f3) => f3.action === "modify") || isEditIntent;
56507
56778
  const outputMode = !opts.flags.hideCode && hasAnyModify ? "diff" : resolveOutputMode(opts.flags.output, validated.files.length, !!opts.flags.hideCode);
56508
56779
  const skippedSetForLookup = new Set(validated.skipped || []);
56509
- const skippedPlans = normalized.filter((f3) => skippedSetForLookup.has(f3.path));
56780
+ const skippedPlans = filtered.filter((f3) => skippedSetForLookup.has(f3.path));
56510
56781
  const remainingSkipped = (validated.skipped || []).filter((p) => !skippedPlans.some((sp) => sp.path === p)).map((p) => ({ path: p, kind: "source", action: "skip", description: "" }));
56511
56782
  const displayFiles = validated.files.concat(skippedPlans).concat(remainingSkipped);
56512
56783
  const summary = summarizePlan(displayFiles);
@@ -56699,6 +56970,21 @@ function extractBlocks(content) {
56699
56970
  if (blocks.length === 0 && looksLikeCode(content)) blocks.push({ language: detectLanguage(content), code: content.trim() });
56700
56971
  return blocks;
56701
56972
  }
56973
+ function extractFilenameHintFromComment(code) {
56974
+ try {
56975
+ const first = (code.split(/\r?\n/, 1)[0] || "").trim();
56976
+ let m2 = first.match(/^\/\/\s*filename:\s*(.+)$/i);
56977
+ if (m2?.[1]) return m2[1].trim();
56978
+ m2 = first.match(/^#\s*filename:\s*(.+)$/i);
56979
+ if (m2?.[1]) return m2[1].trim();
56980
+ m2 = first.match(/^<!--\s*filename:\s*(.+?)\s*-->$/i);
56981
+ if (m2?.[1]) return m2[1].trim();
56982
+ m2 = first.match(/^\/\*\s*filename:\s*(.+?)\s*\*\/$/i);
56983
+ if (m2?.[1]) return m2[1].trim();
56984
+ } catch {
56985
+ }
56986
+ return void 0;
56987
+ }
56702
56988
  function looksLikeCode(s2) {
56703
56989
  return ["function ", "const ", "let ", "var ", "class ", "def ", "import ", "export "].some((k) => s2.includes(k));
56704
56990
  }
@@ -56856,48 +57142,100 @@ function parseExplicitFilenames(request) {
56856
57142
  return out;
56857
57143
  }
56858
57144
  async function detectEditIntentLLM(root, request, ctx2) {
57145
+ let existingDirs = [];
57146
+ let existingFiles = [];
57147
+ let anyExistingPathMentioned = false;
56859
57148
  try {
57149
+ const fs52 = await import('fs/promises');
57150
+ const pathMod = await import('path');
57151
+ const rawPaths = Array.from(new Set(String(request).match(/[A-Za-z]:\\[^\s"']+|\.?\/?[^\s"']+[\/\\][^\s"']*/g) || []));
57152
+ existingDirs = [];
57153
+ existingFiles = [];
57154
+ for (const raw2 of rawPaths) {
57155
+ try {
57156
+ const normalized = raw2.replace(/^"|"$/g, "").replace(/^'|'$/g, "");
57157
+ const abs = pathMod.isAbsolute(normalized) ? normalized : pathMod.join(root, normalized);
57158
+ const st = await fs52.stat(abs).catch(() => null);
57159
+ if (!st) continue;
57160
+ anyExistingPathMentioned = true;
57161
+ const rel = pathMod.relative(root, abs).replace(/\\/g, "/");
57162
+ if (!rel || rel.startsWith("..")) continue;
57163
+ if (st.isDirectory()) existingDirs.push(rel);
57164
+ else if (st.isFile()) existingFiles.push(rel);
57165
+ } catch {
57166
+ }
57167
+ }
56860
57168
  const repoFiles = await getRepoFiles(root);
56861
- const candidates = repoFiles;
57169
+ let scope = [];
57170
+ if (existingDirs.length > 0) {
57171
+ const dirSet = existingDirs.map((d) => d.replace(/\\/g, "/").replace(/^\/+/, "").toLowerCase());
57172
+ scope = repoFiles.filter((p) => dirSet.some((d) => p.toLowerCase().startsWith(d + "/")));
57173
+ } else {
57174
+ scope = repoFiles;
57175
+ }
56862
57176
  const headSnippets = [];
56863
- for (const p of candidates) {
57177
+ for (const p of scope) {
56864
57178
  const h2 = await readHeadTail(root, p, 5);
56865
57179
  headSnippets.push(`- ${p}
56866
57180
  ${h2.head}`);
56867
57181
  }
56868
57182
  const system = [
56869
- "Classify the user intent for the MARIA code orchestrator.",
56870
- "Decide strictly between EDIT_EXISTING (modify existing files) or CREATE_NEW (generate new project/files).",
56871
- "User is likely to have EDIT_EXITING intent when:",
56872
- "- the coding language is not specified",
56873
- "- it has a clear and/or relevant path of the file/directory exists",
56874
- "- it has attachments",
56875
- "- the wording implies modifying existing files (ex. fix, improve, update, change, patch, make it, clean up, etc.)",
56876
- 'Return JSON: { "intent": "EDIT_EXISTING" | "CREATE_NEW" } only. No commentary.'
57183
+ "You classify the intent for a code operation in an existing repository.",
57184
+ "Choose strictly one: EDIT_EXISTING (modify existing files) or CREATE_NEW (generate a new project/files).",
57185
+ "Decision rules (apply in order):",
57186
+ "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.",
57187
+ "2) Wording like fix/improve/update/change/patch/make it work/enable X implies EDIT_EXISTING.",
57188
+ "3) Wording like create/new project/scaffold/from scratch/template implies CREATE_NEW.",
57189
+ "4) If attachments or explicit file paths are present, that increases likelihood of EDIT_EXISTING.",
57190
+ 'Return ONLY compact JSON: { "intent": "EDIT_EXISTING" | "CREATE_NEW" }.'
56877
57191
  ].join("\n");
57192
+ const evidence = {
57193
+ explicitFilesCount: (ctx2.explicitFiles || []).length,
57194
+ attachmentsCount: ctx2.attachmentsCount || 0,
57195
+ existingDirCount: existingDirs.length,
57196
+ existingFileMentions: existingFiles.length,
57197
+ existingDirs,
57198
+ existingFiles
57199
+ };
56878
57200
  const user = [
56879
57201
  `Request: ${request}`,
56880
- `ExplicitFiles: ${JSON.stringify(ctx2.explicitFiles || [])}`,
56881
- `AttachmentsCount: ${ctx2.attachmentsCount}`,
56882
- "Repo snapshot (paths with file heads):",
57202
+ `Evidence: ${JSON.stringify(evidence)}`,
57203
+ "Repo snapshot (trimmed):",
56883
57204
  headSnippets.join("\n\n")
56884
57205
  ].join("\n\n");
56885
- const spin1 = new ProcessAnimation();
56886
- spin1.start();
56887
- const resp = await executeChat([
56888
- { role: "system", content: system },
56889
- { role: "user", content: user }
56890
- ]);
57206
+ if (existingDirs.length > 0 || existingFiles.length > 0 || ctx2.explicitFiles && ctx2.explicitFiles.length > 0 || ctx2.attachmentsCount > 0) {
57207
+ return true;
57208
+ }
57209
+ let startedLocalSpinner = false;
57210
+ let spin1 = null;
57211
+ if (!ProcessAnimation.hasActive()) {
57212
+ spin1 = new ProcessAnimation();
57213
+ spin1.start();
57214
+ startedLocalSpinner = true;
57215
+ }
57216
+ let resp;
56891
57217
  try {
56892
- spin1.stop();
56893
- } catch {
57218
+ resp = await executeChat([
57219
+ { role: "system", content: system },
57220
+ { role: "user", content: user }
57221
+ ], { provider: "google", model: "gemini-2.5-flash-lite" });
57222
+ } finally {
57223
+ if (startedLocalSpinner && spin1) {
57224
+ try {
57225
+ spin1.stop();
57226
+ } catch {
57227
+ }
57228
+ }
56894
57229
  }
56895
57230
  const raw = (resp?.output || "").trim();
56896
57231
  const jsonText = extractJsonSafe(raw, "object") || raw;
56897
57232
  const parsed = JSON.parse(jsonText);
57233
+ if (existingDirs.length > 0 || existingFiles.length > 0 || anyExistingPathMentioned || ctx2.explicitFiles && ctx2.explicitFiles.length > 0 || ctx2.attachmentsCount > 0) {
57234
+ return true;
57235
+ }
56898
57236
  return parsed?.intent === "EDIT_EXISTING";
56899
57237
  } catch {
56900
- return ctx2.explicitFiles && ctx2.explicitFiles.length > 0 || ctx2.attachmentsCount > 0;
57238
+ return ctx2.explicitFiles && ctx2.explicitFiles.length > 0 || ctx2.attachmentsCount > 0 || existingDirs.length > 0 || existingFiles.length > 0 || anyExistingPathMentioned;
56901
57239
  }
56902
57240
  }
56903
57241
  function sanitizeFolderName(name2) {
@@ -57065,7 +57403,7 @@ async function resolveExplicitPaths(root, files, hintText) {
57065
57403
  const chat = await executeChat([
57066
57404
  { role: "system", content: system },
57067
57405
  { role: "user", content: user }
57068
- ]);
57406
+ ], { provider: "google", model: "gemini-2.5-flash-lite" });
57069
57407
  const raw = (chat.output || "").trim();
57070
57408
  const pick = ranked.find((r2) => r2 === raw) || ranked.find((r2) => raw.includes(r2)) || ranked[0];
57071
57409
  return pick.replace(/^\/+/, "");
@@ -57240,7 +57578,16 @@ var init_code_command = __esm({
57240
57578
  }
57241
57579
  ];
57242
57580
  async execute(commandArgs, context2) {
57581
+ const debug = (...args2) => {
57582
+ if (process.env.MARIA_DEBUG === "1") {
57583
+ try {
57584
+ console.log("[DEBUG/code]", ...args2);
57585
+ } catch {
57586
+ }
57587
+ }
57588
+ };
57243
57589
  const request = await this.ensureLanguageDefaults(commandArgs.raw.join(" ").trim());
57590
+ debug("request", request);
57244
57591
  if (!request) {
57245
57592
  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.");
57246
57593
  }
@@ -57252,6 +57599,7 @@ var init_code_command = __esm({
57252
57599
  const explicitDry = commandArgs.raw.includes("--dry-run");
57253
57600
  const explicitOutput = commandArgs.raw.some((x2) => x2.startsWith("--output") || x2 === "--verbose" || x2 === "-v");
57254
57601
  const explicitPreview = commandArgs.raw.some((x2) => x2.startsWith("--preview-lines"));
57602
+ const explicitOnlyAttached = commandArgs.raw.includes("--only-attached");
57255
57603
  const preSpin = new ProcessAnimation();
57256
57604
  preSpin.start();
57257
57605
  let inferred = {};
@@ -57263,6 +57611,8 @@ var init_code_command = __esm({
57263
57611
  } catch {
57264
57612
  }
57265
57613
  }
57614
+ debug("inferCodeArgs.raw", rawText);
57615
+ debug("inferCodeArgs.result", inferred);
57266
57616
  if (!explicitPlan && !explicitDry) {
57267
57617
  if (typeof inferred.planOnly === "boolean") opts.planOnly = inferred.planOnly;
57268
57618
  if (typeof inferred.dryRun === "boolean") opts.dryRun = inferred.dryRun;
@@ -57273,6 +57623,9 @@ var init_code_command = __esm({
57273
57623
  if (!explicitPreview && typeof inferred.previewLines === "number") {
57274
57624
  opts.previewLines = inferred.previewLines;
57275
57625
  }
57626
+ if (!explicitOnlyAttached && typeof inferred.onlyAttached === "boolean") {
57627
+ opts.onlyAttached = inferred.onlyAttached;
57628
+ }
57276
57629
  } catch {
57277
57630
  }
57278
57631
  if (opts.planOnly) {
@@ -57287,13 +57640,27 @@ var init_code_command = __esm({
57287
57640
  const root = opts.root || process.cwd();
57288
57641
  const { orchestrate: orchestrate2 } = await Promise.resolve().then(() => (init_Orchestrator(), Orchestrator_exports));
57289
57642
  const attachments = await this.collectAttachedFiles(context2).catch(() => []);
57643
+ try {
57644
+ if (process.env.MARIA_DEBUG === "1") {
57645
+ const attView = attachments.map((a) => ({ name: a.originalName, size: a.size, mime: a.mime, pathHint: a.pathHint })).slice(0, 50);
57646
+ console.log("[DEBUG/code] collected.attachments", { count: attachments.length, attachments: attView });
57647
+ }
57648
+ } catch {
57649
+ }
57290
57650
  const abort = new AbortController();
57291
57651
  const onSigint = () => abort.abort();
57292
57652
  process.once("SIGINT", onSigint);
57293
- const spinner = new ProcessAnimation();
57294
- spinner.start();
57653
+ let startedLocalSpinner = false;
57654
+ let spinner = null;
57655
+ if (!ProcessAnimation.hasActive()) {
57656
+ spinner = new ProcessAnimation();
57657
+ spinner.start();
57658
+ startedLocalSpinner = true;
57659
+ }
57295
57660
  try {
57296
- 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 });
57661
+ const effectiveOnlyAttached = opts.onlyAttached && attachments.length > 0;
57662
+ 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 });
57663
+ debug("orchestrate.summaryLines.head", Array.isArray(res?.summaryLines) ? res.summaryLines.slice(0, 10) : []);
57297
57664
  if (opts.planOnly) {
57298
57665
  const fs52 = await import('fs/promises');
57299
57666
  const path65 = await import('path');
@@ -57346,9 +57713,11 @@ var init_code_command = __esm({
57346
57713
  const out = Array.isArray(res?.summaryLines) ? res.summaryLines.join("\n") : "";
57347
57714
  return this.success(out);
57348
57715
  } finally {
57349
- try {
57350
- spinner.stop();
57351
- } catch {
57716
+ if (startedLocalSpinner && spinner) {
57717
+ try {
57718
+ spinner.stop();
57719
+ } catch {
57720
+ }
57352
57721
  }
57353
57722
  process.removeListener("SIGINT", onSigint);
57354
57723
  }
@@ -57471,12 +57840,6 @@ ${pretty}`);
57471
57840
  if (llmLang) {
57472
57841
  const hint2 = (() => {
57473
57842
  const l = llmLang.toLowerCase();
57474
- if (l === "tsx") return "TypeScript (React/TSX)";
57475
- if (l === "jsx") return "JavaScript (React/JSX)";
57476
- if (l === "typescript") return "TypeScript";
57477
- if (l === "javascript") return "JavaScript";
57478
- if (l === "html") return "HTML";
57479
- if (l === "css") return "CSS";
57480
57843
  return llmLang;
57481
57844
  })();
57482
57845
  return raw + ` (Use ${hint2})`;
@@ -57489,7 +57852,7 @@ ${pretty}`);
57489
57852
  preSpin.start();
57490
57853
  const system = [
57491
57854
  "You analyze a user's code-generation request.",
57492
- "Decide if the user explicitly specified a programming language or framework/tooling (e.g., TypeScript, Python, Rust, Java, React, Vue, Node, etc.).",
57855
+ "Decide if the user explicitly specified a programming language or framework/tooling.",
57493
57856
  'Return ONLY compact JSON with shape {"explicitLanguage": boolean, "language"?: string}.',
57494
57857
  "Do not add any commentary."
57495
57858
  ].join("\n");
@@ -57498,7 +57861,7 @@ ${pretty}`);
57498
57861
  method: "POST",
57499
57862
  body: {
57500
57863
  provider: "google",
57501
- model: "gemini-2.5-flash",
57864
+ model: "gemini-2.5-flash-lite",
57502
57865
  taskType: "chat",
57503
57866
  prompt: `${system}
57504
57867
 
@@ -57543,6 +57906,14 @@ ${user}`
57543
57906
  if (parsed && parsed.explicitLanguage) return raw;
57544
57907
  } catch {
57545
57908
  }
57909
+ try {
57910
+ const pathMod = await import('path');
57911
+ const hasPathToken = /[A-Za-z]:\\[^\s"']+|\.?\/?[^\s"']+[\/\\][^\s"']*/.test(raw);
57912
+ if (hasPathToken) {
57913
+ return raw;
57914
+ }
57915
+ } catch {
57916
+ }
57546
57917
  const hint = " (Use TypeScript and React; prefer functional components and node)";
57547
57918
  return raw + hint;
57548
57919
  }
@@ -57552,9 +57923,7 @@ ${user}`
57552
57923
  const system = [
57553
57924
  "You are a programming language classifier.",
57554
57925
  "Given multiple short code excerpts, determine the dominant language across them.",
57555
- "Respond with ONLY a single token language name from this set:",
57556
- "[typescript, tsx, javascript, jsx, python, java, go, rust, php, cpp, c, swift, kotlin, ruby, csharp, html, css, scss, json, yaml, markdown].",
57557
- "If unsure between tsx and jsx, choose tsx if TypeScript types appear, else jsx."
57926
+ "Respond with ONLY language name(s)"
57558
57927
  ].join("\n");
57559
57928
  const joined = samples.slice(0, 20).map((s2, i2) => `// sample ${i2 + 1}
57560
57929
  ${s2}`).join("\n\n");
@@ -57563,7 +57932,7 @@ ${s2}`).join("\n\n");
57563
57932
  method: "POST",
57564
57933
  body: {
57565
57934
  provider: "google",
57566
- model: "gemini-2.5-flash",
57935
+ model: "gemini-2.5-flash-lite",
57567
57936
  taskType: "chat",
57568
57937
  prompt: `${system}
57569
57938
 
@@ -57869,64 +58238,6 @@ ${joined}`
57869
58238
  return false;
57870
58239
  }
57871
58240
  }
57872
- /**
57873
- * Extract code blocks from AI response
57874
- */
57875
- extractCodeBlocks(content) {
57876
- const blocks = [];
57877
- const codeBlockRegex = /```(\w*)\n([\s\S]*?)```/g;
57878
- let match2;
57879
- while ((match2 = codeBlockRegex.exec(content)) !== null) {
57880
- blocks.push({
57881
- language: match2[1] || "javascript",
57882
- code: match2[2].trim()
57883
- });
57884
- }
57885
- if (blocks.length === 0 && this.looksLikeCode(content)) {
57886
- blocks.push({
57887
- code: content.trim(),
57888
- language: this.detectLanguage(content)
57889
- });
57890
- }
57891
- return blocks;
57892
- }
57893
- /**
57894
- * Check if content looks like code
57895
- */
57896
- looksLikeCode(content) {
57897
- const codeIndicators = [
57898
- "function ",
57899
- "const ",
57900
- "let ",
57901
- "var ",
57902
- "class ",
57903
- "def ",
57904
- "import ",
57905
- "export ",
57906
- "{",
57907
- "}",
57908
- ";",
57909
- "//",
57910
- "/*"
57911
- ];
57912
- return codeIndicators.some((indicator) => content.includes(indicator));
57913
- }
57914
- /**
57915
- * Detect programming language from content
57916
- */
57917
- detectLanguage(code) {
57918
- const hasReact = /(^|\s)from\s+['"]react['"]|^\s*import\s+React/m.test(code);
57919
- const hasJSX = /<([A-Za-z][\w:-]*)(\s|>|\/)>?/m.test(code);
57920
- const hasTS = /(\binterface\s+\w+\b|\btype\s+\w+\s*=|:\s*[A-Za-z_][\w<>\[\]| &?:]*)/m.test(code) || /React\.FC\s*</m.test(code);
57921
- if (hasReact || hasJSX) return hasTS ? "tsx" : "jsx";
57922
- if (code.includes("interface ") || code.includes(": string")) return "typescript";
57923
- if (code.includes("def ") || code.includes("print(")) return "python";
57924
- if (code.includes("func ") || code.includes("package main")) return "go";
57925
- if (code.includes("fn ") || code.includes("let mut")) return "rust";
57926
- if (code.includes("<?php")) return "php";
57927
- if (code.includes("#include")) return "cpp";
57928
- return "javascript";
57929
- }
57930
58241
  /**
57931
58242
  * Save code block to file
57932
58243
  */
@@ -63724,11 +64035,10 @@ var init_clients_safe = __esm({
63724
64035
  }
63725
64036
  });
63726
64037
  function createSpinner(text) {
63727
- let frame = 0;
63728
64038
  let active = false;
63729
- let timer;
63730
- const render = () => {
63731
- const f3 = SPINNER_FRAMES[frame = (frame + 1) % SPINNER_FRAMES.length];
64039
+ let frame = 0;
64040
+ const renderOnce = () => {
64041
+ const f3 = SPINNER_FRAMES[frame % SPINNER_FRAMES.length];
63732
64042
  process5__namespace.default.stdout.write(`\r ${chalk14__default.default.cyan(f3)} ${text}`);
63733
64043
  };
63734
64044
  return {
@@ -63738,13 +64048,11 @@ function createSpinner(text) {
63738
64048
  start() {
63739
64049
  if (active) return;
63740
64050
  active = true;
63741
- render();
63742
- timer = setInterval(render, 80);
64051
+ renderOnce();
63743
64052
  },
63744
64053
  stop(symbol = "\u2714") {
63745
64054
  if (!active) return;
63746
64055
  active = false;
63747
- if (timer) clearInterval(timer);
63748
64056
  const finalSymbol = symbol === "\u2714" ? chalk14__default.default.green(symbol) : symbol === "\u26A0" ? chalk14__default.default.yellow(symbol) : symbol === "\u2716" ? chalk14__default.default.red(symbol) : symbol;
63749
64057
  process5__namespace.default.stdout.write(`\r ${finalSymbol} ${text}
63750
64058
  `);