@trydecember/cli 0.4.2 → 0.4.4

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.
Files changed (2) hide show
  1. package/dist/december.js +1621 -309
  2. package/package.json +1 -1
package/dist/december.js CHANGED
@@ -65,7 +65,7 @@ var package_default;
65
65
  var init_package = __esm(() => {
66
66
  package_default = {
67
67
  name: "@trydecember/cli",
68
- version: "0.4.2",
68
+ version: "0.4.4",
69
69
  description: "a coding agent for terminal and cloud.",
70
70
  homepage: "https://trydecember.com",
71
71
  repository: {
@@ -11072,10 +11072,13 @@ async function providerFetch(url, init) {
11072
11072
  if (!res.ok) {
11073
11073
  const text = await res.text();
11074
11074
  const normalized = normalizeErrorResponse(text);
11075
+ const headers = new Headers(res.headers);
11076
+ headers.delete("retry-after");
11077
+ headers.delete("retry-after-ms");
11075
11078
  return new Response(normalized, {
11076
11079
  status: res.status,
11077
11080
  statusText: res.statusText,
11078
- headers: res.headers
11081
+ headers
11079
11082
  });
11080
11083
  }
11081
11084
  return res;
@@ -11085,7 +11088,8 @@ function openaiProvider(baseURL, apiKey, defaultHeaders, customClient) {
11085
11088
  baseURL,
11086
11089
  apiKey: apiKey || process.env.OPENAI_API_KEY || "dummy-key",
11087
11090
  defaultHeaders,
11088
- fetch: providerFetch
11091
+ fetch: providerFetch,
11092
+ maxRetries: 0
11089
11093
  });
11090
11094
  return createProvider({
11091
11095
  id: "openai",
@@ -11184,6 +11188,11 @@ function openaiProvider(baseURL, apiKey, defaultHeaders, customClient) {
11184
11188
  }
11185
11189
  const activeToolCalls = new Map;
11186
11190
  for await (const chunk of stream) {
11191
+ if (chunk.error) {
11192
+ const errObj = chunk.error;
11193
+ const msg = typeof errObj === "string" ? errObj : errObj.message || JSON.stringify(errObj);
11194
+ throw new Error(msg);
11195
+ }
11187
11196
  if (chunk.usage) {
11188
11197
  const cachedTokens = chunk.usage.prompt_tokens_details?.cached_tokens;
11189
11198
  yield {
@@ -64374,6 +64383,7 @@ function parseIgnoreLines(content) {
64374
64383
  const trimmed = line.trim();
64375
64384
  if (!trimmed || trimmed.startsWith("#"))
64376
64385
  continue;
64386
+ const isRootAnchored = trimmed.startsWith("/");
64377
64387
  let clean = trimmed;
64378
64388
  if (clean.startsWith("/")) {
64379
64389
  clean = clean.slice(1);
@@ -64386,7 +64396,7 @@ function parseIgnoreLines(content) {
64386
64396
  if (!clean.endsWith("/**")) {
64387
64397
  patterns.push(`${clean}/**`);
64388
64398
  }
64389
- if (!clean.startsWith("**/")) {
64399
+ if (!isRootAnchored && !clean.startsWith("**/")) {
64390
64400
  patterns.push(`**/${clean}`);
64391
64401
  patterns.push(`**/${clean}/**`);
64392
64402
  }
@@ -66471,15 +66481,23 @@ function anthropicProvider(baseURL, apiKey, customClientOrDefaultHeaders, custom
66471
66481
  antMessages.push({ role: "assistant", content });
66472
66482
  }
66473
66483
  } else {
66474
- antMessages.push({
66475
- role: msg.role,
66476
- content: [
66477
- {
66478
- type: "text",
66479
- text: msg.content
66480
- }
66481
- ]
66482
- });
66484
+ const prev = antMessages[antMessages.length - 1];
66485
+ if (prev && prev.role === "user" && Array.isArray(prev.content)) {
66486
+ prev.content.push({
66487
+ type: "text",
66488
+ text: msg.content
66489
+ });
66490
+ } else {
66491
+ antMessages.push({
66492
+ role: "user",
66493
+ content: [
66494
+ {
66495
+ type: "text",
66496
+ text: msg.content
66497
+ }
66498
+ ]
66499
+ });
66500
+ }
66483
66501
  }
66484
66502
  }
66485
66503
  if (antMessages.length >= 4) {
@@ -109443,6 +109461,14 @@ var LIVE_MODEL_CACHE_TTL_MS, liveModelCache, diskCacheLoaded = false, getCurated
109443
109461
  case "aurikoai":
109444
109462
  case "auriko-ai":
109445
109463
  return [
109464
+ {
109465
+ label: "DeepSeek V4 Flash",
109466
+ value: "deepseek-v4-flash"
109467
+ },
109468
+ {
109469
+ label: "Gemini 2.5 Flash",
109470
+ value: "gemini-2.5-flash"
109471
+ },
109446
109472
  {
109447
109473
  label: "Claude Sonnet 4.6",
109448
109474
  value: "claude-sonnet-4-6"
@@ -109455,18 +109481,10 @@ var LIVE_MODEL_CACHE_TTL_MS, liveModelCache, diskCacheLoaded = false, getCurated
109455
109481
  label: "Claude Opus 4.7",
109456
109482
  value: "claude-opus-4-7"
109457
109483
  },
109458
- {
109459
- label: "DeepSeek V4 Flash",
109460
- value: "deepseek-v4-flash"
109461
- },
109462
109484
  {
109463
109485
  label: "DeepSeek V4 Pro",
109464
109486
  value: "deepseek-v4-pro"
109465
109487
  },
109466
- {
109467
- label: "Gemini 2.5 Flash",
109468
- value: "gemini-2.5-flash"
109469
- },
109470
109488
  {
109471
109489
  label: "Gemini 2.5 Pro",
109472
109490
  value: "gemini-2.5-pro"
@@ -111283,9 +111301,15 @@ var LIVE_MODEL_CACHE_TTL_MS, liveModelCache, diskCacheLoaded = false, getCurated
111283
111301
  if (normalized === "anyapi") {
111284
111302
  return "anthropic/claude-sonnet-4-6";
111285
111303
  }
111304
+ if (normalized === "auriko" || normalized === "aurikoai" || normalized === "auriko-ai") {
111305
+ return "deepseek-v4-flash";
111306
+ }
111286
111307
  if (normalized === "bailing") {
111287
111308
  return "Ling-1T";
111288
111309
  }
111310
+ if (normalized === "baseten" || normalized === "basetenco" || normalized === "baseten-co") {
111311
+ return "deepseek-ai/DeepSeek-V4.1-Flash";
111312
+ }
111289
111313
  if (normalized === "berget") {
111290
111314
  return "mistralai/Mistral-Small-3.2-24B-Instruct-2506";
111291
111315
  }
@@ -119420,6 +119444,190 @@ var init_browser = __esm(() => {
119420
119444
  };
119421
119445
  });
119422
119446
 
119447
+ // ../../packages/tools/src/code_intelligence.ts
119448
+ function formatDiagnostics(diags, path) {
119449
+ const target = path ? ` for ${path}` : "";
119450
+ if (!diags) {
119451
+ return `No diagnostic errors found${target}.`;
119452
+ }
119453
+ if (typeof diags === "string") {
119454
+ const trimmed = diags.trim();
119455
+ if (!trimmed) {
119456
+ return `No diagnostic errors found${target}.`;
119457
+ }
119458
+ return `Diagnostic errors found${target}:
119459
+ ${trimmed}`;
119460
+ }
119461
+ if (Array.isArray(diags)) {
119462
+ if (diags.length === 0) {
119463
+ return `No diagnostic errors found${target}.`;
119464
+ }
119465
+ const formatted = diags.map((d) => {
119466
+ const loc = d.line ? `:${d.line}${d.column ? `:${d.column}` : ""}` : "";
119467
+ return `${d.filePath}${loc} - [${d.severity ?? "error"}] ${d.message}${d.source ? ` (${d.source})` : ""}`;
119468
+ }).join(`
119469
+ `);
119470
+ return `Diagnostic errors found${target}:
119471
+ ${formatted}`;
119472
+ }
119473
+ return `No diagnostic errors found${target}.`;
119474
+ }
119475
+ var codeIntelligenceSchema, CodeIntelligenceTool;
119476
+ var init_code_intelligence = __esm(() => {
119477
+ init_esm2();
119478
+ codeIntelligenceSchema = Type2.Object({
119479
+ action: Type2.Union([
119480
+ Type2.Literal("diagnostics"),
119481
+ Type2.Literal("definition"),
119482
+ Type2.Literal("references"),
119483
+ Type2.Literal("outline")
119484
+ ], {
119485
+ description: 'The code intelligence action to perform: "diagnostics", "definition", "references", or "outline".'
119486
+ }),
119487
+ path: Type2.Optional(Type2.String({
119488
+ description: 'Relative or absolute file path or directory to query. Required for "definition", "references", and "outline".'
119489
+ })),
119490
+ line: Type2.Optional(Type2.Integer({
119491
+ description: "1-indexed line number of the symbol for definition or references lookup."
119492
+ })),
119493
+ column: Type2.Optional(Type2.Integer({
119494
+ description: "1-indexed column number of the symbol for definition or references lookup."
119495
+ })),
119496
+ symbol: Type2.Optional(Type2.String({
119497
+ description: "Optional symbol name to search for in the file if line and column are not specified."
119498
+ }))
119499
+ });
119500
+ CodeIntelligenceTool = {
119501
+ name: "code_intelligence",
119502
+ description: "Query on-demand code intelligence, symbol definitions, call site references, document outline, and compiler diagnostics across the workspace or for a specific file.",
119503
+ inputSchema: codeIntelligenceSchema,
119504
+ execute: async ({ action, path, line, column, symbol }, context) => {
119505
+ if (action !== "diagnostics" && action !== "definition" && action !== "references" && action !== "outline") {
119506
+ return `Unsupported action: "${action}". Supported actions: "diagnostics", "definition", "references", "outline".`;
119507
+ }
119508
+ if (action === "diagnostics") {
119509
+ const diagFn = context.operations.diagnostics?.getDiagnostics || context.operations.lsp?.getDiagnostics;
119510
+ if (!diagFn) {
119511
+ return "Error: Diagnostics operations are not supported in the current environment.";
119512
+ }
119513
+ try {
119514
+ const diags = await diagFn(path ?? "");
119515
+ return formatDiagnostics(diags, path);
119516
+ } catch (err) {
119517
+ const message = err instanceof Error ? err.message : String(err);
119518
+ return `Error retrieving diagnostics: ${message}`;
119519
+ }
119520
+ }
119521
+ if (action === "outline") {
119522
+ if (!path) {
119523
+ return 'Error: "path" parameter is required for "outline" action.';
119524
+ }
119525
+ if (!context.operations.lsp?.getOutline) {
119526
+ return "Error: LSP operations are not supported in the current environment.";
119527
+ }
119528
+ try {
119529
+ const symbols = await context.operations.lsp.getOutline(path);
119530
+ if (!symbols || symbols.length === 0) {
119531
+ return `No document symbols found for ${path}.`;
119532
+ }
119533
+ const lines = symbols.map((s) => `- [${s.kind}] ${s.name} (${path}:${s.line}:${s.column})${s.containerName ? ` (in ${s.containerName})` : ""}`);
119534
+ return `Document outline for ${path}:
119535
+ ${lines.join(`
119536
+ `)}`;
119537
+ } catch (err) {
119538
+ const message = err instanceof Error ? err.message : String(err);
119539
+ return `Error retrieving outline: ${message}`;
119540
+ }
119541
+ }
119542
+ if (!path) {
119543
+ return `Error: "path" parameter is required for "${action}" action.`;
119544
+ }
119545
+ let targetLine = line;
119546
+ let targetColumn = column;
119547
+ if ((targetLine === undefined || targetColumn === undefined) && symbol) {
119548
+ try {
119549
+ const content = await context.operations.fs.readFile(path);
119550
+ const fileLines = content.split(`
119551
+ `);
119552
+ let fallbackLine;
119553
+ let fallbackCol;
119554
+ for (let i = 0;i < fileLines.length; i++) {
119555
+ const lineStr = fileLines[i];
119556
+ const idx = lineStr.indexOf(symbol);
119557
+ if (idx !== -1) {
119558
+ if (/^\s*import\b/.test(lineStr)) {
119559
+ if (fallbackLine === undefined) {
119560
+ fallbackLine = i + 1;
119561
+ fallbackCol = idx + 1;
119562
+ }
119563
+ } else {
119564
+ targetLine = i + 1;
119565
+ targetColumn = idx + 1;
119566
+ break;
119567
+ }
119568
+ }
119569
+ }
119570
+ if (targetLine === undefined) {
119571
+ targetLine = fallbackLine;
119572
+ targetColumn = fallbackCol;
119573
+ }
119574
+ if (targetLine === undefined || targetColumn === undefined) {
119575
+ return `Symbol "${symbol}" not found in ${path}.`;
119576
+ }
119577
+ } catch (err) {
119578
+ const message = err instanceof Error ? err.message : String(err);
119579
+ return `Error reading file ${path}: ${message}`;
119580
+ }
119581
+ }
119582
+ if (targetLine === undefined || targetColumn === undefined) {
119583
+ return `Error: "line" and "column" (or "symbol") are required for "${action}" action.`;
119584
+ }
119585
+ if (action === "definition") {
119586
+ if (!context.operations.lsp?.getDefinition) {
119587
+ return "Error: LSP operations are not supported in the current environment.";
119588
+ }
119589
+ try {
119590
+ const raw = await context.operations.lsp.getDefinition(path, targetLine, targetColumn);
119591
+ const locs = Array.isArray(raw) ? raw : raw ? [raw] : [];
119592
+ if (locs.length === 0) {
119593
+ return `No definition found for symbol at ${path}:${targetLine}:${targetColumn}.`;
119594
+ }
119595
+ if (locs.length === 1) {
119596
+ const loc = locs[0];
119597
+ return `Definition found at ${loc.filePath}:${loc.line}:${loc.column}`;
119598
+ }
119599
+ const list = locs.map((l) => `- ${l.filePath}:${l.line}:${l.column}`).join(`
119600
+ `);
119601
+ return `Definition(s) found:
119602
+ ${list}`;
119603
+ } catch (err) {
119604
+ const message = err instanceof Error ? err.message : String(err);
119605
+ return `Error retrieving definition: ${message}`;
119606
+ }
119607
+ }
119608
+ if (action === "references") {
119609
+ if (!context.operations.lsp?.getReferences) {
119610
+ return "Error: LSP operations are not supported in the current environment.";
119611
+ }
119612
+ try {
119613
+ const refs = await context.operations.lsp.getReferences(path, targetLine, targetColumn);
119614
+ if (!refs || refs.length === 0) {
119615
+ return `No references found for symbol at ${path}:${targetLine}:${targetColumn}.`;
119616
+ }
119617
+ const list = refs.map((r) => `- ${r.filePath}:${r.line}:${r.column}`).join(`
119618
+ `);
119619
+ return `Found ${refs.length} reference(s) for symbol at ${path}:${targetLine}:${targetColumn}:
119620
+ ${list}`;
119621
+ } catch (err) {
119622
+ const message = err instanceof Error ? err.message : String(err);
119623
+ return `Error retrieving references: ${message}`;
119624
+ }
119625
+ }
119626
+ return `Unsupported action: "${action}".`;
119627
+ }
119628
+ };
119629
+ });
119630
+
119423
119631
  // ../../packages/tools/src/deferred-registry.ts
119424
119632
  class DeferredToolRegistry {
119425
119633
  tools = new Map;
@@ -121273,6 +121481,264 @@ var init_ls = __esm(() => {
121273
121481
  };
121274
121482
  });
121275
121483
 
121484
+ // ../../packages/tools/src/manage_memory.ts
121485
+ import path20 from "node:path";
121486
+ function resolveWriteTarget(cwd, target, category) {
121487
+ const isRules = target === "rules" || target === "RULES.md" || !target && category === "rules";
121488
+ if (isRules) {
121489
+ if (target === "RULES.md") {
121490
+ return { relativePath: "RULES.md", isRules: true };
121491
+ }
121492
+ return { relativePath: "RULES.md", isRules: true };
121493
+ }
121494
+ if (target === "MEMORY.md") {
121495
+ return { relativePath: "MEMORY.md", isRules: false };
121496
+ }
121497
+ return { relativePath: path20.join(".december", "memory.md"), isRules: false };
121498
+ }
121499
+ function resolveReadCandidates(target) {
121500
+ if (target === "rules" || target === "RULES.md") {
121501
+ return ["RULES.md", path20.join(".december", "rules.md"), path20.join(".december", "RULES.md")];
121502
+ }
121503
+ if (target === "memory" || target === "MEMORY.md") {
121504
+ return [
121505
+ path20.join(".december", "memory.md"),
121506
+ "MEMORY.md",
121507
+ path20.join(".december", "MEMORY.md")
121508
+ ];
121509
+ }
121510
+ return [
121511
+ path20.join(".december", "memory.md"),
121512
+ "MEMORY.md",
121513
+ "RULES.md",
121514
+ path20.join(".december", "rules.md")
121515
+ ];
121516
+ }
121517
+ function normalizeEntry(text) {
121518
+ return text.replace(/^[-*•]\s+/, "").trim();
121519
+ }
121520
+ var manageMemorySchema, ManageMemoryTool;
121521
+ var init_manage_memory = __esm(() => {
121522
+ init_esm2();
121523
+ manageMemorySchema = Type2.Object({
121524
+ action: Type2.Union([Type2.Literal("record"), Type2.Literal("read"), Type2.Literal("forget")], {
121525
+ description: 'The action to perform: "record" to store an entry, "read" to inspect entries, or "forget" to prune an entry.'
121526
+ }),
121527
+ entry: Type2.Optional(Type2.String({
121528
+ description: "The memory or rule entry text to record or forget."
121529
+ })),
121530
+ category: Type2.Optional(Type2.Union([
121531
+ Type2.Literal("build_and_test"),
121532
+ Type2.Literal("conventions"),
121533
+ Type2.Literal("architecture"),
121534
+ Type2.Literal("quirks"),
121535
+ Type2.Literal("rules"),
121536
+ Type2.Literal("general")
121537
+ ], {
121538
+ description: "Category header: build_and_test, conventions, architecture, quirks, rules, or general."
121539
+ })),
121540
+ target: Type2.Optional(Type2.Union([
121541
+ Type2.Literal("memory"),
121542
+ Type2.Literal("rules"),
121543
+ Type2.Literal("MEMORY.md"),
121544
+ Type2.Literal("RULES.md")
121545
+ ], {
121546
+ description: 'Target file: "memory" (.december/memory.md or MEMORY.md) or "rules" (RULES.md or .december/rules.md). Defaults to memory for general/memory entries, or rules if category is "rules".'
121547
+ })),
121548
+ query: Type2.Optional(Type2.String({
121549
+ description: "Optional query keyword to filter entries when reading."
121550
+ }))
121551
+ });
121552
+ ManageMemoryTool = {
121553
+ name: "manage_memory",
121554
+ description: "Use this tool to record, read, or forget persistent notes, conventions, build/test commands, quirks, and rules across sessions in workspace memory (.december/memory.md, MEMORY.md, RULES.md).",
121555
+ inputSchema: manageMemorySchema,
121556
+ execute: async ({ action, entry, category, target, query }, context) => {
121557
+ const cwd = context.operations.env.cwd();
121558
+ switch (action) {
121559
+ case "record": {
121560
+ if (!entry || !entry.trim()) {
121561
+ return 'Error: "entry" parameter is required for action "record".';
121562
+ }
121563
+ const cleanEntry = normalizeEntry(entry);
121564
+ if (!cleanEntry) {
121565
+ return 'Error: "entry" parameter cannot be empty.';
121566
+ }
121567
+ const { relativePath, isRules } = resolveWriteTarget(cwd, target, category);
121568
+ const fullPath = path20.isAbsolute(relativePath) ? relativePath : path20.join(cwd, relativePath);
121569
+ const cat = category || (isRules ? "rules" : "conventions");
121570
+ let existingContent = "";
121571
+ try {
121572
+ existingContent = await context.operations.fs.readFile(fullPath);
121573
+ } catch {}
121574
+ const lines = existingContent.split(`
121575
+ `);
121576
+ for (const line of lines) {
121577
+ const lineContent = normalizeEntry(line);
121578
+ if (lineContent && lineContent.toLowerCase() === cleanEntry.toLowerCase()) {
121579
+ return `Entry already exists in ${relativePath} under "${cat}". Skipped duplicate.`;
121580
+ }
121581
+ }
121582
+ const headerRegex = new RegExp(`^##\\s+${cat}\\b`, "im");
121583
+ const headerMatch = existingContent.match(headerRegex);
121584
+ let updatedContent = "";
121585
+ if (headerMatch && headerMatch.index !== undefined) {
121586
+ const headerIndex = headerMatch.index;
121587
+ const rest = existingContent.slice(headerIndex);
121588
+ const nextHeaderMatch = rest.slice(headerMatch[0].length).match(/\n##\s+/i);
121589
+ if (nextHeaderMatch && nextHeaderMatch.index !== undefined) {
121590
+ const insertPos = headerIndex + headerMatch[0].length + nextHeaderMatch.index;
121591
+ updatedContent = `${existingContent.slice(0, insertPos)}
121592
+ - ${cleanEntry}${existingContent.slice(insertPos)}`;
121593
+ } else {
121594
+ updatedContent = `${existingContent.trimEnd()}
121595
+ - ${cleanEntry}
121596
+ `;
121597
+ }
121598
+ } else {
121599
+ const prefix = existingContent.trim() ? `${existingContent.trimEnd()}
121600
+
121601
+ ` : "";
121602
+ updatedContent = `${prefix}## ${cat}
121603
+ - ${cleanEntry}
121604
+ `;
121605
+ }
121606
+ try {
121607
+ await context.operations.fs.writeFile(fullPath, updatedContent);
121608
+ return `Recorded entry under "${cat}" in ${relativePath}.`;
121609
+ } catch (err) {
121610
+ return `Failed to write memory file: ${err.message}`;
121611
+ }
121612
+ }
121613
+ case "read": {
121614
+ const candidates = resolveReadCandidates(target);
121615
+ const contents = [];
121616
+ for (const candidate of candidates) {
121617
+ const fullPath = path20.isAbsolute(candidate) ? candidate : path20.join(cwd, candidate);
121618
+ try {
121619
+ const text = await context.operations.fs.readFile(fullPath);
121620
+ if (text && text.trim()) {
121621
+ contents.push({ file: candidate, text: text.trim() });
121622
+ }
121623
+ } catch {}
121624
+ }
121625
+ if (contents.length === 0) {
121626
+ return "No memory or rules entries found.";
121627
+ }
121628
+ if (category) {
121629
+ const matchedSections = [];
121630
+ const catLower = category.toLowerCase();
121631
+ for (const item of contents) {
121632
+ const lines = item.text.split(`
121633
+ `);
121634
+ let inCategory = false;
121635
+ const sectionLines = [];
121636
+ for (const line of lines) {
121637
+ if (line.startsWith("## ")) {
121638
+ const headerName = line.slice(3).trim().toLowerCase();
121639
+ if (headerName === catLower) {
121640
+ inCategory = true;
121641
+ sectionLines.push(line);
121642
+ } else {
121643
+ inCategory = false;
121644
+ }
121645
+ } else if (inCategory) {
121646
+ sectionLines.push(line);
121647
+ }
121648
+ }
121649
+ if (sectionLines.length > 0) {
121650
+ matchedSections.push(`### Source: ${item.file}
121651
+ ${sectionLines.join(`
121652
+ `).trim()}`);
121653
+ }
121654
+ }
121655
+ if (matchedSections.length === 0) {
121656
+ return `No entries found under category "${category}".`;
121657
+ }
121658
+ return matchedSections.join(`
121659
+
121660
+ `);
121661
+ }
121662
+ if (query) {
121663
+ const queryLower = query.toLowerCase();
121664
+ const matchedLines = [];
121665
+ for (const item of contents) {
121666
+ const lines = item.text.split(`
121667
+ `);
121668
+ let currentHeader = "";
121669
+ const fileMatches = [];
121670
+ for (const line of lines) {
121671
+ if (line.startsWith("## ")) {
121672
+ currentHeader = line;
121673
+ } else if (line.toLowerCase().includes(queryLower)) {
121674
+ if (currentHeader && !fileMatches.includes(currentHeader)) {
121675
+ fileMatches.push(currentHeader);
121676
+ }
121677
+ fileMatches.push(line);
121678
+ }
121679
+ }
121680
+ if (fileMatches.length > 0) {
121681
+ matchedLines.push(`### Source: ${item.file}
121682
+ ${fileMatches.join(`
121683
+ `)}`);
121684
+ }
121685
+ }
121686
+ if (matchedLines.length === 0) {
121687
+ return `No memory entries found matching "${query}".`;
121688
+ }
121689
+ return matchedLines.join(`
121690
+
121691
+ `);
121692
+ }
121693
+ return contents.map((c) => `### Source: ${c.file}
121694
+ ${c.text}`).join(`
121695
+
121696
+ `);
121697
+ }
121698
+ case "forget": {
121699
+ if (!entry || !entry.trim()) {
121700
+ return 'Error: "entry" parameter is required for action "forget".';
121701
+ }
121702
+ const targetClean = normalizeEntry(entry).toLowerCase();
121703
+ const candidates = resolveReadCandidates(target);
121704
+ let removed = false;
121705
+ for (const candidate of candidates) {
121706
+ const fullPath = path20.isAbsolute(candidate) ? candidate : path20.join(cwd, candidate);
121707
+ try {
121708
+ const text = await context.operations.fs.readFile(fullPath);
121709
+ const lines = text.split(`
121710
+ `);
121711
+ const newLines = [];
121712
+ let fileModified = false;
121713
+ for (const line of lines) {
121714
+ const lineClean = normalizeEntry(line).toLowerCase();
121715
+ if (lineClean && (lineClean === targetClean || lineClean.includes(targetClean))) {
121716
+ fileModified = true;
121717
+ removed = true;
121718
+ continue;
121719
+ }
121720
+ newLines.push(line);
121721
+ }
121722
+ if (fileModified) {
121723
+ await context.operations.fs.writeFile(fullPath, newLines.join(`
121724
+ `).trimEnd() + `
121725
+ `);
121726
+ }
121727
+ } catch {}
121728
+ }
121729
+ if (removed) {
121730
+ return "Removed entry from memory.";
121731
+ }
121732
+ return "Entry not found in memory.";
121733
+ }
121734
+ default: {
121735
+ return "Invalid action. Supported actions: record, read, forget.";
121736
+ }
121737
+ }
121738
+ }
121739
+ };
121740
+ });
121741
+
121276
121742
  // ../../packages/tools/src/manage_task.ts
121277
121743
  var manageTaskSchema, ManageTaskTool;
121278
121744
  var init_manage_task = __esm(() => {
@@ -162241,7 +162707,7 @@ ${formatted}`;
162241
162707
 
162242
162708
  // ../../packages/tools/src/bash-operations.ts
162243
162709
  import { spawn as spawn2 } from "node:child_process";
162244
- import path20 from "node:path";
162710
+ import path21 from "node:path";
162245
162711
  function trackDetachedChildPid(pid) {
162246
162712
  trackedDetachedChildPids.add(pid);
162247
162713
  }
@@ -162251,7 +162717,7 @@ function untrackDetachedChildPid(pid) {
162251
162717
  function killProcessGroup(pid) {
162252
162718
  if (process.platform === "win32") {
162253
162719
  try {
162254
- const child = spawn2(path20.join(process.env.SystemRoot ?? "C:\\Windows", "System32", "taskkill.exe"), ["/F", "/T", "/PID", String(pid)], {
162720
+ const child = spawn2(path21.join(process.env.SystemRoot ?? "C:\\Windows", "System32", "taskkill.exe"), ["/F", "/T", "/PID", String(pid)], {
162255
162721
  stdio: "ignore",
162256
162722
  detached: true,
162257
162723
  windowsHide: true
@@ -162369,12 +162835,14 @@ var init_src4 = __esm(() => {
162369
162835
  init_ask_question();
162370
162836
  init_bash();
162371
162837
  init_browser();
162838
+ init_code_intelligence();
162372
162839
  init_deferred_registry();
162373
162840
  init_edit();
162374
162841
  init_edit_diff();
162375
162842
  init_find();
162376
162843
  init_grep();
162377
162844
  init_ls();
162845
+ init_manage_memory();
162378
162846
  init_manage_task();
162379
162847
  init_read();
162380
162848
  init_search_tools();
@@ -162392,11 +162860,13 @@ var init_src4 = __esm(() => {
162392
162860
  init_grep();
162393
162861
  init_ask_question();
162394
162862
  init_manage_task();
162863
+ init_manage_memory();
162395
162864
  init_browser();
162396
162865
  init_web_search();
162397
162866
  init_diff_preview();
162398
162867
  init_deferred_registry();
162399
162868
  init_search_tools();
162869
+ init_code_intelligence();
162400
162870
  CORE_TOOLS = [
162401
162871
  BashTool,
162402
162872
  ReadFileTool,
@@ -162407,7 +162877,9 @@ var init_src4 = __esm(() => {
162407
162877
  FindFilesTool,
162408
162878
  GrepSearchTool,
162409
162879
  AskQuestionTool,
162410
- SearchToolsTool
162880
+ SearchToolsTool,
162881
+ CodeIntelligenceTool,
162882
+ ManageMemoryTool
162411
162883
  ];
162412
162884
  DEFERRED_TOOLS = [ManageTaskTool, BrowserTool, WebSearchTool];
162413
162885
  defaultDeferredRegistry.register(BrowserTool, {
@@ -162422,6 +162894,10 @@ var init_src4 = __esm(() => {
162422
162894
  category: "task",
162423
162895
  keywords: ["task", "background", "kill", "status", "process", "pid", "manage_task"]
162424
162896
  });
162897
+ defaultDeferredRegistry.register(ManageMemoryTool, {
162898
+ category: "memory",
162899
+ keywords: ["memory", "rules", "conventions", "quirks", "manage_memory", "remember", "forget"]
162900
+ });
162425
162901
  });
162426
162902
 
162427
162903
  // ../../node_modules/.bun/uuid@14.0.1/node_modules/uuid/dist-node/rng.js
@@ -162626,6 +163102,64 @@ function estimateTokens(messages) {
162626
163102
  return acc + Math.ceil((text.length + toolInput.length) / 4);
162627
163103
  }, 0);
162628
163104
  }
163105
+ function isSafeCompactionBoundary(messages, splitIndex, startIndex = 0) {
163106
+ if (splitIndex <= startIndex || splitIndex >= messages.length) {
163107
+ return false;
163108
+ }
163109
+ if (messages[splitIndex]?.role === "tool") {
163110
+ return false;
163111
+ }
163112
+ const preCallIds = new Set;
163113
+ for (let i = startIndex;i < splitIndex; i++) {
163114
+ const msg = messages[i];
163115
+ if (msg?.toolCalls) {
163116
+ for (const tc of msg.toolCalls) {
163117
+ if (tc.id)
163118
+ preCallIds.add(tc.id);
163119
+ }
163120
+ }
163121
+ }
163122
+ for (let i = splitIndex;i < messages.length; i++) {
163123
+ const msg = messages[i];
163124
+ if (msg?.role === "tool" && msg.toolCallId && preCallIds.has(msg.toolCallId)) {
163125
+ return false;
163126
+ }
163127
+ }
163128
+ const postCallIds = new Set;
163129
+ for (let i = splitIndex;i < messages.length; i++) {
163130
+ const msg = messages[i];
163131
+ if (msg?.toolCalls) {
163132
+ for (const tc of msg.toolCalls) {
163133
+ if (tc.id)
163134
+ postCallIds.add(tc.id);
163135
+ }
163136
+ }
163137
+ }
163138
+ for (let i = startIndex;i < splitIndex; i++) {
163139
+ const msg = messages[i];
163140
+ if (msg?.role === "tool" && msg.toolCallId && postCallIds.has(msg.toolCallId)) {
163141
+ return false;
163142
+ }
163143
+ }
163144
+ return true;
163145
+ }
163146
+ function findSafeCompactionBoundary(messages, targetIndex, startIndex = 1) {
163147
+ if (isSafeCompactionBoundary(messages, targetIndex, startIndex)) {
163148
+ return targetIndex;
163149
+ }
163150
+ const maxDelta = messages.length;
163151
+ for (let delta = 1;delta < maxDelta; delta++) {
163152
+ const left = targetIndex - delta;
163153
+ if (left > startIndex && isSafeCompactionBoundary(messages, left, startIndex)) {
163154
+ return left;
163155
+ }
163156
+ const right = targetIndex + delta;
163157
+ if (right < messages.length && isSafeCompactionBoundary(messages, right, startIndex)) {
163158
+ return right;
163159
+ }
163160
+ }
163161
+ return targetIndex;
163162
+ }
162629
163163
  async function compactContextIfNeeded(messages, llm, maxTokens, modelOptions, signal) {
162630
163164
  let limit = maxTokens;
162631
163165
  if (!limit) {
@@ -162644,10 +163178,17 @@ async function compactContextIfNeeded(messages, llm, maxTokens, modelOptions, si
162644
163178
  if (messages.length <= PROTECTED_TAIL + 1) {
162645
163179
  return messages;
162646
163180
  }
162647
- const systemPrompt = messages[0];
162648
- const middleHistory = messages.slice(1, messages.length - PROTECTED_TAIL);
162649
- const recentHistory = messages.slice(messages.length - PROTECTED_TAIL);
162650
- const hasPreviousSummary = middleHistory.length > 0 && middleHistory[0].role === "system" && middleHistory[0].content.includes("[COMPACTED HISTORY SUMMARY]");
163181
+ const hasSystemPrompt = messages[0]?.role === "system";
163182
+ const systemPrompt = hasSystemPrompt ? messages[0] : undefined;
163183
+ const historyStartIndex = hasSystemPrompt ? 1 : 0;
163184
+ const targetIndex = messages.length - PROTECTED_TAIL;
163185
+ const safeSplitIndex = findSafeCompactionBoundary(messages, targetIndex, historyStartIndex);
163186
+ if (safeSplitIndex <= historyStartIndex || safeSplitIndex >= messages.length) {
163187
+ return messages;
163188
+ }
163189
+ const middleHistory = messages.slice(historyStartIndex, safeSplitIndex);
163190
+ const recentHistory = messages.slice(safeSplitIndex);
163191
+ const hasPreviousSummary = middleHistory.length > 0 && (middleHistory[0].role === "system" || middleHistory[0].role === "user") && middleHistory[0].content.includes("[COMPACTED HISTORY SUMMARY]");
162651
163192
  let previousSummaryText = "";
162652
163193
  let messagesToSummarize = middleHistory;
162653
163194
  if (hasPreviousSummary) {
@@ -162655,6 +163196,9 @@ async function compactContextIfNeeded(messages, llm, maxTokens, modelOptions, si
162655
163196
  `, "");
162656
163197
  messagesToSummarize = middleHistory.slice(1);
162657
163198
  }
163199
+ if (messagesToSummarize.length === 0) {
163200
+ return messages;
163201
+ }
162658
163202
  const fileManifests = extractFileManifests(messagesToSummarize, previousSummaryText);
162659
163203
  const historyText = messagesToSummarize.map((m) => {
162660
163204
  let txt = `[${m.role.toUpperCase()}]: ${m.content}`;
@@ -162792,7 +163336,7 @@ ${fileManifests.modifiedFiles.join(`
162792
163336
  content: `[COMPACTED HISTORY SUMMARY]
162793
163337
  ${finalSummary}`
162794
163338
  };
162795
- return [systemPrompt, summaryMessage, ...recentHistory];
163339
+ return systemPrompt ? [systemPrompt, summaryMessage, ...recentHistory] : [summaryMessage, ...recentHistory];
162796
163340
  }
162797
163341
  var DEFAULT_MAX_TOKENS = 32000, PRUNE_PROTECT_TOKENS = 40000, PRUNE_MINIMUM_SAVINGS = 15000, PRUNE_PROTECT_STEPS = 3, PRUNE_PROTECTED_TOOLS;
162798
163342
  var init_compaction = __esm(() => {
@@ -162801,10 +163345,144 @@ var init_compaction = __esm(() => {
162801
163345
  });
162802
163346
 
162803
163347
  // ../../packages/agent/src/conversation-manager.ts
163348
+ import fs19 from "node:fs";
163349
+ import path22 from "node:path";
163350
+
162804
163351
  class ConversationManager {
162805
163352
  _messages = [];
162806
- constructor(initialMessages = []) {
163353
+ workspaceRoot;
163354
+ constructor(initialMessagesOrOptions, options) {
163355
+ let initialMessages = [];
163356
+ let opts;
163357
+ if (Array.isArray(initialMessagesOrOptions)) {
163358
+ initialMessages = initialMessagesOrOptions;
163359
+ opts = options;
163360
+ } else if (initialMessagesOrOptions && typeof initialMessagesOrOptions === "object") {
163361
+ opts = initialMessagesOrOptions;
163362
+ }
162807
163363
  this._messages = initialMessages;
163364
+ if (opts?.workspaceRoot) {
163365
+ this.workspaceRoot = opts.workspaceRoot;
163366
+ this.initSession({
163367
+ workspaceRoot: opts.workspaceRoot,
163368
+ systemPrompt: opts.systemPrompt
163369
+ });
163370
+ }
163371
+ }
163372
+ initSession(optionsOrWorkspaceRoot, systemPromptArg) {
163373
+ let workspaceRoot;
163374
+ let systemPrompt;
163375
+ if (typeof optionsOrWorkspaceRoot === "string") {
163376
+ workspaceRoot = optionsOrWorkspaceRoot;
163377
+ systemPrompt = systemPromptArg;
163378
+ } else if (optionsOrWorkspaceRoot && typeof optionsOrWorkspaceRoot === "object") {
163379
+ workspaceRoot = optionsOrWorkspaceRoot.workspaceRoot;
163380
+ systemPrompt = optionsOrWorkspaceRoot.systemPrompt;
163381
+ } else {
163382
+ workspaceRoot = this.workspaceRoot;
163383
+ systemPrompt = systemPromptArg;
163384
+ }
163385
+ if (workspaceRoot) {
163386
+ this.workspaceRoot = workspaceRoot;
163387
+ }
163388
+ const existingSystemIndex = this._messages.findIndex((m) => m.role === "system");
163389
+ let prompt = systemPrompt;
163390
+ if (prompt === undefined) {
163391
+ if (existingSystemIndex !== -1) {
163392
+ prompt = this._messages[existingSystemIndex].content;
163393
+ } else {
163394
+ prompt = "";
163395
+ }
163396
+ }
163397
+ const memoryContent = this.readWorkspaceMemory(workspaceRoot);
163398
+ let finalPrompt = prompt;
163399
+ if (memoryContent) {
163400
+ finalPrompt = this.injectMemoryBlock(prompt, memoryContent);
163401
+ }
163402
+ if (existingSystemIndex !== -1) {
163403
+ this._messages[existingSystemIndex].content = finalPrompt;
163404
+ } else if (finalPrompt) {
163405
+ this._messages.unshift({
163406
+ role: "system",
163407
+ content: finalPrompt,
163408
+ id: v4_default2(),
163409
+ timestamp: Date.now()
163410
+ });
163411
+ }
163412
+ return finalPrompt;
163413
+ }
163414
+ readWorkspaceMemory(workspaceRoot) {
163415
+ if (!workspaceRoot)
163416
+ return "";
163417
+ const memoryCandidates = [
163418
+ path22.join(workspaceRoot, ".december", "memory.md"),
163419
+ path22.join(workspaceRoot, "MEMORY.md"),
163420
+ path22.join(workspaceRoot, ".december", "MEMORY.md"),
163421
+ path22.join(workspaceRoot, "memory.md")
163422
+ ];
163423
+ const rulesCandidates = [
163424
+ path22.join(workspaceRoot, "RULES.md"),
163425
+ path22.join(workspaceRoot, ".december", "rules.md"),
163426
+ path22.join(workspaceRoot, ".december", "RULES.md"),
163427
+ path22.join(workspaceRoot, "rules.md")
163428
+ ];
163429
+ let memoryText = "";
163430
+ for (const candidate of memoryCandidates) {
163431
+ try {
163432
+ if (fs19.existsSync(candidate) && fs19.statSync(candidate).isFile()) {
163433
+ const content = fs19.readFileSync(candidate, "utf8").trim();
163434
+ if (content) {
163435
+ memoryText = content;
163436
+ break;
163437
+ }
163438
+ }
163439
+ } catch {}
163440
+ }
163441
+ let rulesText = "";
163442
+ for (const candidate of rulesCandidates) {
163443
+ try {
163444
+ if (fs19.existsSync(candidate) && fs19.statSync(candidate).isFile()) {
163445
+ const content = fs19.readFileSync(candidate, "utf8").trim();
163446
+ if (content) {
163447
+ rulesText = content;
163448
+ break;
163449
+ }
163450
+ }
163451
+ } catch {}
163452
+ }
163453
+ const parts = [];
163454
+ if (memoryText)
163455
+ parts.push(memoryText);
163456
+ if (rulesText && rulesText !== memoryText)
163457
+ parts.push(rulesText);
163458
+ return parts.join(`
163459
+
163460
+ `).trim();
163461
+ }
163462
+ injectMemoryBlock(prompt, memoryContent) {
163463
+ if (!memoryContent.trim())
163464
+ return prompt;
163465
+ if (prompt.includes("<project_memory>"))
163466
+ return prompt;
163467
+ const block = `<project_memory>
163468
+ ${memoryContent.trim()}
163469
+ </project_memory>`;
163470
+ if (!prompt)
163471
+ return block;
163472
+ if (prompt.includes(`
163473
+
163474
+ Current date:`)) {
163475
+ return prompt.replace(`
163476
+
163477
+ Current date:`, `
163478
+
163479
+ ${block}
163480
+
163481
+ Current date:`);
163482
+ }
163483
+ return `${prompt}
163484
+
163485
+ ${block}`;
162808
163486
  }
162809
163487
  get messages() {
162810
163488
  return this._messages;
@@ -163559,10 +164237,10 @@ class LangfuseMedia {
163559
164237
  this._contentType = contentType;
163560
164238
  this._source = "bytes";
163561
164239
  } else if (filePath && contentType) {
163562
- if (!fs19) {
164240
+ if (!fs20) {
163563
164241
  throw new Error("File system support is not available in this environment");
163564
164242
  }
163565
- if (!fs19.existsSync(filePath)) {
164243
+ if (!fs20.existsSync(filePath)) {
163566
164244
  throw new Error(`File at path ${filePath} does not exist`);
163567
164245
  }
163568
164246
  this._contentBytes = this.readFile(filePath);
@@ -163574,10 +164252,10 @@ class LangfuseMedia {
163574
164252
  }
163575
164253
  readFile(filePath) {
163576
164254
  try {
163577
- if (!fs19) {
164255
+ if (!fs20) {
163578
164256
  throw new Error("File system support is not available in this environment");
163579
164257
  }
163580
- return fs19.readFileSync(filePath);
164258
+ return fs20.readFileSync(filePath);
163581
164259
  } catch (error) {
163582
164260
  console.error(`Error reading file at path ${filePath}`, error);
163583
164261
  return;
@@ -164699,7 +165377,7 @@ var DEFAULT_PROMPT_CACHE_TTL_SECONDS = 60, LangfusePersistedProperty, ChatMessag
164699
165377
  }
164700
165378
  });
164701
165379
  return queryParams.toString();
164702
- }, utils, common_release_envs, fs19 = null, cryptoModule = null, dynamicImport = (module) => {
165380
+ }, utils, common_release_envs, fs20 = null, cryptoModule = null, dynamicImport = (module) => {
164703
165381
  return import(module);
164704
165382
  }, MAX_EVENT_SIZE_BYTES, MAX_BATCH_SIZE_BYTES, ENVIRONMENT_PATTERN, WHITELISTED_LANGFUSE_INTERNAL_ENVIRONMENTS, LangfuseFetchHttpError, LangfuseFetchNetworkError, SUPPORT_URL = "https://langfuse.com/support", API_DOCS_URL = "https://api.reference.langfuse.com", RBAC_DOCS_URL = "https://langfuse.com/docs/rbac", INSTALLATION_DOCS_URL = "https://langfuse.com/docs/sdk/typescript/guide", RATE_LIMITS_URL = "https://langfuse.com/faq/all/api-limits", NPM_PACKAGE_URL = "https://www.npmjs.com/package/langfuse", updatePromptResponse, defaultServerErrorPrompt, defaultErrorResponse, errorResponseByCode, LangfuseCore, LangfuseTraceClient, LangfuseObservationClient, LangfuseSpanClient, LangfuseGenerationClient, LangfuseEventClient;
164705
165383
  var init_lib = __esm(() => {
@@ -164878,12 +165556,12 @@ var init_lib = __esm(() => {
164878
165556
  ];
164879
165557
  if (typeof globalThis.Deno !== "undefined") {
164880
165558
  Promise.all([dynamicImport("node:fs"), dynamicImport("node:crypto")]).then(([importedFs, importedCrypto]) => {
164881
- fs19 = importedFs;
165559
+ fs20 = importedFs;
164882
165560
  cryptoModule = importedCrypto;
164883
165561
  }).catch();
164884
165562
  } else if (typeof process !== "undefined" && process.versions?.node) {
164885
165563
  Promise.all([dynamicImport("fs"), dynamicImport("crypto")]).then(([importedFs, importedCrypto]) => {
164886
- fs19 = importedFs;
165564
+ fs20 = importedFs;
164887
165565
  cryptoModule = importedCrypto;
164888
165566
  }).catch();
164889
165567
  } else if (typeof crypto !== "undefined") {
@@ -166386,29 +167064,73 @@ var init_tracer_factory = __esm(() => {
166386
167064
  });
166387
167065
 
166388
167066
  // ../../packages/agent/src/utils/evaporation.ts
166389
- function evaporateStaleToolOutputs(messages, preserveRecentTurns = 3) {
167067
+ function isErrorToolOutput(content) {
167068
+ const lower = content.toLowerCase();
167069
+ return content.includes("[Tool Error]") || lower.includes("error:") || lower.includes("command failed") || lower.includes("exit code 1") || lower.includes("exit code 2") || lower.includes("failed:") || lower.includes("traceback (most recent call last)") || lower.includes("exception:") || lower.includes("fatal:") || lower.includes("panic:") || lower.includes("syntaxerror");
167070
+ }
167071
+ function truncateLargeToolOutput(rawContent, maxBytes = 6 * 1024, headLines = 20, tailLines = 20) {
167072
+ const byteLength = Buffer.byteLength(rawContent, "utf8");
167073
+ if (byteLength <= maxBytes) {
167074
+ return rawContent;
167075
+ }
167076
+ if (isErrorToolOutput(rawContent)) {
167077
+ return rawContent;
167078
+ }
167079
+ const lines = rawContent.split(`
167080
+ `);
167081
+ if (lines.length <= headLines + tailLines) {
167082
+ return rawContent;
167083
+ }
167084
+ const head = lines.slice(0, headLines).join(`
167085
+ `);
167086
+ const tail = lines.slice(lines.length - tailLines).join(`
167087
+ `);
167088
+ const omittedLines = lines.length - headLines - tailLines;
167089
+ const omittedBytes = byteLength - Buffer.byteLength(head, "utf8") - Buffer.byteLength(tail, "utf8");
167090
+ const omittedKb = (omittedBytes / 1024).toFixed(1);
167091
+ const banner = `
167092
+
167093
+ [... ${omittedLines} lines truncated (${omittedKb} KB) ...]
167094
+
167095
+ `;
167096
+ return head + banner + tail;
167097
+ }
167098
+ function evaporateStaleToolOutputs(messages, preserveRecentTurnsOrOptions = 3, preserveRecentStepsArg) {
166390
167099
  if (!messages || messages.length === 0)
166391
167100
  return [];
167101
+ let preserveRecentTurns = 3;
167102
+ let preserveRecentSteps = 2;
167103
+ let maxOutputSize = 6 * 1024;
167104
+ let headLines = 20;
167105
+ let tailLines = 20;
167106
+ if (typeof preserveRecentTurnsOrOptions === "number") {
167107
+ preserveRecentTurns = preserveRecentTurnsOrOptions;
167108
+ if (preserveRecentStepsArg !== undefined) {
167109
+ preserveRecentSteps = preserveRecentStepsArg;
167110
+ }
167111
+ } else if (typeof preserveRecentTurnsOrOptions === "object") {
167112
+ preserveRecentTurns = preserveRecentTurnsOrOptions.preserveRecentTurns ?? 3;
167113
+ preserveRecentSteps = preserveRecentTurnsOrOptions.preserveRecentSteps ?? 2;
167114
+ maxOutputSize = preserveRecentTurnsOrOptions.maxOutputSize ?? 6 * 1024;
167115
+ headLines = preserveRecentTurnsOrOptions.headLines ?? 20;
167116
+ tailLines = preserveRecentTurnsOrOptions.tailLines ?? 20;
167117
+ }
166392
167118
  let userTurnsSeen = 0;
166393
- let cutoffIndex = 0;
167119
+ let agentStepsSeen = 0;
167120
+ const isStale = new Array(messages.length).fill(false);
166394
167121
  for (let i = messages.length - 1;i >= 0; i--) {
166395
167122
  const msg = messages[i];
167123
+ if (!msg)
167124
+ continue;
166396
167125
  if (msg.role === "user" && !msg.isUI) {
166397
167126
  userTurnsSeen++;
166398
- if (userTurnsSeen >= preserveRecentTurns) {
166399
- cutoffIndex = i;
166400
- break;
166401
- }
166402
167127
  }
166403
- }
166404
- if (userTurnsSeen < preserveRecentTurns || cutoffIndex <= 0) {
166405
- return messages.map((m) => ({
166406
- role: m.role,
166407
- content: m.content,
166408
- thinking: m.thinking,
166409
- toolCalls: m.toolCalls,
166410
- toolCallId: m.toolCallId
166411
- }));
167128
+ if (msg.role === "assistant" && (msg.toolCalls?.length || 0) > 0) {
167129
+ agentStepsSeen++;
167130
+ }
167131
+ if (userTurnsSeen >= preserveRecentTurns || agentStepsSeen >= preserveRecentSteps) {
167132
+ isStale[i] = true;
167133
+ }
166412
167134
  }
166413
167135
  return messages.map((msg, idx) => {
166414
167136
  const baseMsg = {
@@ -166418,27 +167140,37 @@ function evaporateStaleToolOutputs(messages, preserveRecentTurns = 3) {
166418
167140
  toolCalls: msg.toolCalls,
166419
167141
  toolCallId: msg.toolCallId
166420
167142
  };
166421
- if (idx >= cutoffIndex) {
167143
+ if (msg.role !== "tool") {
166422
167144
  return baseMsg;
166423
167145
  }
166424
- if (msg.role === "tool") {
166425
- const rawContent = typeof msg.content === "string" ? msg.content : "";
166426
- if (rawContent.length < 200) {
166427
- return baseMsg;
166428
- }
166429
- if (rawContent.includes("[Tool Error]") || rawContent.toLowerCase().includes("error:") || rawContent.toLowerCase().includes("command failed")) {
166430
- return baseMsg;
166431
- }
167146
+ const rawContent = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
167147
+ if (rawContent.length < 200) {
167148
+ return baseMsg;
167149
+ }
167150
+ if (isErrorToolOutput(rawContent)) {
167151
+ return baseMsg;
167152
+ }
167153
+ if (rawContent.startsWith("[Tool Output Evaporated:") || rawContent.startsWith("[tool output microcompacted:") || rawContent.startsWith("[Old tool result content cleared]")) {
167154
+ return baseMsg;
167155
+ }
167156
+ if (isStale[idx]) {
166432
167157
  const lines = rawContent.split(`
166433
167158
  `);
166434
167159
  const lineCount = lines.length;
166435
167160
  const byteSize = (Buffer.byteLength(rawContent, "utf8") / 1024).toFixed(1);
166436
- const tombstone = `[Tool Output Evaporated: ${lineCount} lines (${byteSize} KB). Content previously processed by assistant.]`;
167161
+ const tombstone = `[Tool Output Evaporated: ${lineCount} lines (${byteSize} KB). exit code 0. Content previously processed by assistant.]`;
166437
167162
  return {
166438
167163
  ...baseMsg,
166439
167164
  content: tombstone
166440
167165
  };
166441
167166
  }
167167
+ const truncated = truncateLargeToolOutput(rawContent, maxOutputSize, headLines, tailLines);
167168
+ if (truncated !== rawContent) {
167169
+ return {
167170
+ ...baseMsg,
167171
+ content: truncated
167172
+ };
167173
+ }
166442
167174
  return baseMsg;
166443
167175
  });
166444
167176
  }
@@ -166503,8 +167235,14 @@ class Agent2 {
166503
167235
  this.convertToLlm = config.convertToLlm || this.defaultConvertToLlm;
166504
167236
  this.steeringQueue = new PendingMessageQueue(config.steeringMode || "all");
166505
167237
  this.followUpQueue = new PendingMessageQueue(config.followUpMode || "all");
166506
- this.conversation = new ConversationManager;
166507
167238
  this.workspaceDir = config.workspaceDir;
167239
+ this.conversation = new ConversationManager([], {
167240
+ workspaceRoot: config.workspaceDir
167241
+ });
167242
+ this.systemPrompt = this.conversation.initSession({
167243
+ workspaceRoot: config.workspaceDir,
167244
+ systemPrompt: this.systemPrompt
167245
+ });
166508
167246
  this.logsDir = config.logsDir;
166509
167247
  this.disableLogging = config.disableLogging || false;
166510
167248
  this.deferredRegistry = config.deferredRegistry || new DeferredToolRegistry;
@@ -166522,10 +167260,12 @@ class Agent2 {
166522
167260
  this.deferredRegistry.register(tool);
166523
167261
  }
166524
167262
  }
166525
- this.conversation.addMessage({
166526
- role: "system",
166527
- content: this.systemPrompt
166528
- });
167263
+ if (this.conversation.messages.length === 0) {
167264
+ this.conversation.addMessage({
167265
+ role: "system",
167266
+ content: this.systemPrompt
167267
+ });
167268
+ }
166529
167269
  }
166530
167270
  get messages() {
166531
167271
  return this.conversation.messages;
@@ -166535,16 +167275,26 @@ class Agent2 {
166535
167275
  }
166536
167276
  defaultConvertToLlm(messages) {
166537
167277
  const filtered = messages.filter((m) => !m.isUI);
166538
- const evaporated = evaporateStaleToolOutputs(filtered, 3);
167278
+ const evaporated = evaporateStaleToolOutputs(filtered, {
167279
+ preserveRecentTurns: 3,
167280
+ preserveRecentSteps: 2,
167281
+ maxOutputSize: 6 * 1024
167282
+ });
166539
167283
  const merged = [];
166540
- for (const msg of evaporated) {
167284
+ for (let i = 0;i < evaporated.length; i++) {
167285
+ const msg = evaporated[i];
167286
+ let role = msg.role;
167287
+ const content = msg.content;
167288
+ if (role === "system" && i > 0 && content.includes("[COMPACTED HISTORY SUMMARY]")) {
167289
+ role = "user";
167290
+ }
166541
167291
  const prev = merged[merged.length - 1];
166542
- if (prev && prev.role === "user" && msg.role === "user" && !prev.toolCalls && !msg.toolCalls) {
167292
+ if (prev && prev.role === "user" && role === "user" && !prev.toolCalls && !msg.toolCalls) {
166543
167293
  prev.content = `${prev.content}
166544
167294
 
166545
- ${msg.content}`;
167295
+ ${content}`;
166546
167296
  } else {
166547
- merged.push({ ...msg });
167297
+ merged.push({ ...msg, role, content });
166548
167298
  }
166549
167299
  }
166550
167300
  return merged;
@@ -166858,9 +167608,9 @@ var init_p_retry = __esm(() => {
166858
167608
  });
166859
167609
 
166860
167610
  // ../../packages/agent/src/utils/request-logger.ts
166861
- import fs20 from "node:fs";
167611
+ import fs21 from "node:fs";
166862
167612
  import os11 from "node:os";
166863
- import path21 from "node:path";
167613
+ import path23 from "node:path";
166864
167614
  function createRequestLogEntry(params) {
166865
167615
  const {
166866
167616
  turn,
@@ -166913,11 +167663,11 @@ function getGlobalConfigDir() {
166913
167663
  const home = process.env.HOME || os11.homedir();
166914
167664
  const isTestEnv = !!process.env.VITEST;
166915
167665
  if (isTestEnv && home === os11.homedir()) {
166916
- return path21.join(os11.tmpdir(), "december-test-config", ".december");
167666
+ return path23.join(os11.tmpdir(), "december-test-config", ".december");
166917
167667
  }
166918
- const canonicalDir = path21.join(home, ".december");
166919
- const legacyDir = path21.join(home, ".config", "december");
166920
- if (fs20.existsSync(canonicalDir) || !fs20.existsSync(legacyDir)) {
167668
+ const canonicalDir = path23.join(home, ".december");
167669
+ const legacyDir = path23.join(home, ".config", "december");
167670
+ if (fs21.existsSync(canonicalDir) || !fs21.existsSync(legacyDir)) {
166921
167671
  return canonicalDir;
166922
167672
  }
166923
167673
  return legacyDir;
@@ -166927,7 +167677,7 @@ function getLogsDir(customDir) {
166927
167677
  return customDir;
166928
167678
  if (process.env.DECEMBER_LOGS_DIR)
166929
167679
  return process.env.DECEMBER_LOGS_DIR;
166930
- return path21.join(getGlobalConfigDir(), "logs");
167680
+ return path23.join(getGlobalConfigDir(), "logs");
166931
167681
  }
166932
167682
  function getSessionLogPath(sessionIdOrLogsDir, logsDirOrSessionId) {
166933
167683
  if (!sessionIdOrLogsDir && !logsDirOrSessionId)
@@ -166935,7 +167685,7 @@ function getSessionLogPath(sessionIdOrLogsDir, logsDirOrSessionId) {
166935
167685
  let sessionId;
166936
167686
  let customDir;
166937
167687
  if (logsDirOrSessionId) {
166938
- if (sessionIdOrLogsDir.startsWith("session-") || !sessionIdOrLogsDir.includes(path21.sep) && logsDirOrSessionId.includes(path21.sep)) {
167688
+ if (sessionIdOrLogsDir.startsWith("session-") || !sessionIdOrLogsDir.includes(path23.sep) && logsDirOrSessionId.includes(path23.sep)) {
166939
167689
  sessionId = sessionIdOrLogsDir;
166940
167690
  customDir = logsDirOrSessionId;
166941
167691
  } else {
@@ -166946,7 +167696,7 @@ function getSessionLogPath(sessionIdOrLogsDir, logsDirOrSessionId) {
166946
167696
  sessionId = sessionIdOrLogsDir;
166947
167697
  }
166948
167698
  const cleanSessionId = sessionId.startsWith("session-") ? sessionId : `session-${sessionId}`;
166949
- return path21.join(getLogsDir(customDir), `${cleanSessionId}.jsonl`);
167699
+ return path23.join(getLogsDir(customDir), `${cleanSessionId}.jsonl`);
166950
167700
  }
166951
167701
  async function appendTurnLog(arg1, arg2, arg3) {
166952
167702
  try {
@@ -166969,11 +167719,11 @@ async function appendTurnLog(arg1, arg2, arg3) {
166969
167719
  const logFile = getSessionLogPath(sessionId, logsDir);
166970
167720
  if (!logFile)
166971
167721
  return;
166972
- const dir = path21.dirname(logFile);
166973
- await fs20.promises.mkdir(dir, { recursive: true });
167722
+ const dir = path23.dirname(logFile);
167723
+ await fs21.promises.mkdir(dir, { recursive: true });
166974
167724
  const line = JSON.stringify(entry) + `
166975
167725
  `;
166976
- await fs20.promises.appendFile(logFile, line, "utf8");
167726
+ await fs21.promises.appendFile(logFile, line, "utf8");
166977
167727
  } catch {}
166978
167728
  }
166979
167729
  var init_request_logger = __esm(() => {
@@ -167752,17 +168502,17 @@ var init_agent_loop = __esm(() => {
167752
168502
  ]);
167753
168503
  });
167754
168504
  // ../../packages/agent/src/skills/skill-subagent.ts
167755
- import fs21 from "node:fs";
168505
+ import fs22 from "node:fs";
167756
168506
  async function executeSkillInSubagent(options) {
167757
168507
  const { skill, args = [], task, parentAgent, signal, onEvent } = options;
167758
168508
  let rawBody = skill.content || "";
167759
- if (!rawBody && skill.entryFilePath && fs21.existsSync(skill.entryFilePath)) {
168509
+ if (!rawBody && skill.entryFilePath && fs22.existsSync(skill.entryFilePath)) {
167760
168510
  try {
167761
168511
  const parsed = parseSkillFile(skill.entryFilePath);
167762
168512
  rawBody = parsed.body;
167763
168513
  } catch {
167764
168514
  try {
167765
- rawBody = fs21.readFileSync(skill.entryFilePath, "utf8");
168515
+ rawBody = fs22.readFileSync(skill.entryFilePath, "utf8");
167766
168516
  } catch (err) {
167767
168517
  return {
167768
168518
  success: false,
@@ -167910,9 +168660,9 @@ var init_run_skill_tool = __esm(() => {
167910
168660
  });
167911
168661
 
167912
168662
  // ../../packages/agent/src/harness/agent-harness.ts
167913
- import fs22 from "node:fs";
168663
+ import fs23 from "node:fs";
167914
168664
  import os12 from "node:os";
167915
- import path22 from "node:path";
168665
+ import path24 from "node:path";
167916
168666
  function isReasoningModel(model, thinkingLevel) {
167917
168667
  if (thinkingLevel === "off") {
167918
168668
  return false;
@@ -167937,7 +168687,8 @@ function getModelFamilyPrompt(options) {
167937
168687
  - Proactive Task Breakdown: Break down complex requests into orderly stages, verify each stage empirically, and maintain single-step transitions.`;
167938
168688
  } else if (model.includes("gemini")) {
167939
168689
  roleGuidelines = `### Model Specialization (Gemini)
167940
- - Structured Execution: Follow clear operational phases: 1) Explore and gather context, 2) Formulate targeted edits, 3) Verify thoroughly.
168690
+ - Structured Execution: Follow clear operational phases: 1) Explore and gather context (inspect repo layout in monorepos before deep-diving), 2) Formulate targeted edits, 3) Verify thoroughly.
168691
+ - Efficient Verification: Run verification tests only after modifying files, not during early exploratory discovery. Once targeted tests pass and git diff is verified clean, complete your response immediately without repeating status, diff, or test commands.
167941
168692
  - Absolute File Paths: STRICTLY specify absolute file paths when referencing, viewing, or editing files. NEVER output ambiguous relative paths.`;
167942
168693
  }
167943
168694
  return {
@@ -167973,22 +168724,22 @@ function discoverProjectRules(startDir, rootBoundary) {
167973
168724
  "AGENTS.override.md",
167974
168725
  "AGENTS.md",
167975
168726
  "CLAUDE.md",
167976
- path22.join(".december", "rules.md"),
167977
- path22.join(".december", "AGENTS.md")
168727
+ path24.join(".december", "rules.md"),
168728
+ path24.join(".december", "AGENTS.md")
167978
168729
  ];
167979
168730
  const seen = new Set;
167980
- let current = path22.resolve(startDir);
167981
- const boundary = rootBoundary ? path22.resolve(rootBoundary) : path22.parse(current).root;
168731
+ let current = path24.resolve(startDir);
168732
+ const boundary = rootBoundary ? path24.resolve(rootBoundary) : path24.parse(current).root;
167982
168733
  const collectedFiles = [];
167983
168734
  while (true) {
167984
168735
  for (const name of candidateNames) {
167985
- const candidatePath = path22.join(current, name);
167986
- if (fs22.existsSync(candidatePath)) {
168736
+ const candidatePath = path24.join(current, name);
168737
+ if (fs23.existsSync(candidatePath)) {
167987
168738
  try {
167988
- if (fs22.statSync(candidatePath).isFile()) {
168739
+ if (fs23.statSync(candidatePath).isFile()) {
167989
168740
  if (!seen.has(candidatePath)) {
167990
168741
  seen.add(candidatePath);
167991
- const content = fs22.readFileSync(candidatePath, "utf8").trim();
168742
+ const content = fs23.readFileSync(candidatePath, "utf8").trim();
167992
168743
  if (content) {
167993
168744
  collectedFiles.unshift({ path: candidatePath, content });
167994
168745
  }
@@ -167999,7 +168750,7 @@ function discoverProjectRules(startDir, rootBoundary) {
167999
168750
  }
168000
168751
  if (current === boundary)
168001
168752
  break;
168002
- const parent = path22.dirname(current);
168753
+ const parent = path24.dirname(current);
168003
168754
  if (parent === current)
168004
168755
  break;
168005
168756
  current = parent;
@@ -168007,17 +168758,17 @@ function discoverProjectRules(startDir, rootBoundary) {
168007
168758
  const home = process.env.HOME || process.env.USERPROFILE || os12.homedir();
168008
168759
  const globalCandidateDirs = [
168009
168760
  process.env.DECEMBER_CONFIG_DIR,
168010
- path22.join(home, ".december"),
168011
- path22.join(home, ".config", "december")
168761
+ path24.join(home, ".december"),
168762
+ path24.join(home, ".config", "december")
168012
168763
  ].filter((dir) => !!dir);
168013
168764
  for (const globalDir of globalCandidateDirs) {
168014
168765
  for (const candidate of ["rules.md", "AGENTS.md"]) {
168015
- const globalPath = path22.join(globalDir, candidate);
168016
- if (fs22.existsSync(globalPath) && !seen.has(globalPath)) {
168766
+ const globalPath = path24.join(globalDir, candidate);
168767
+ if (fs23.existsSync(globalPath) && !seen.has(globalPath)) {
168017
168768
  try {
168018
- if (fs22.statSync(globalPath).isFile()) {
168769
+ if (fs23.statSync(globalPath).isFile()) {
168019
168770
  seen.add(globalPath);
168020
- const content = fs22.readFileSync(globalPath, "utf8").trim();
168771
+ const content = fs23.readFileSync(globalPath, "utf8").trim();
168021
168772
  if (content) {
168022
168773
  collectedFiles.unshift({ path: globalPath, content });
168023
168774
  }
@@ -168099,7 +168850,7 @@ You operate across two environments seamlessly: locally via a terminal CLI, and
168099
168850
  `### Core Operating Principles & Guardrails
168100
168851
  1. Inspect Logs & Stack Traces First: NEVER diagnose errors or failures without fetching and reading full un-truncated error logs. Base diagnoses strictly on empirical log evidence.
168101
168852
  2. Root Cause Resolution: NEVER mask symptoms, swallow exceptions silently, use dummy fallbacks, or delete/comment out failing tests. Address the root cause directly.
168102
- 3. Execution & Verification: NEVER claim a task or fix is complete without running build, type-check, or test verification commands to empirically prove it works.
168853
+ 3. Execution & Verification: NEVER claim a task or fix is complete without running build, type-check, or test verification commands to empirically prove it works. Do not run heavy build or test suites prematurely during early discovery before any code is modified. Target verification to the modified component rather than full monorepo rebuilds when possible. Once targeted verification succeeds and git diff confirms clean changes, conclude your turn promptly without redundant re-verification loops.
168103
168854
  4. Preserving Integrity: Always preserve existing docstrings, comments, and public API signatures unless explicitly asked to modify them.
168104
168855
  5. Absolute File Paths: ALWAYS specify absolute file paths when referencing, viewing, or editing files.
168105
168856
  6. Strict Workspace Boundary: All operations (file reads, writes, searches, bash commands) must be strictly confined within the workspace directory (/workspace or current working directory). NEVER inspect, explore, or search system root paths or directories outside the workspace (such as /etc, /root, /bin, /var).
@@ -168273,6 +169024,12 @@ var init_agent_harness = __esm(() => {
168273
169024
  guidelines: [
168274
169025
  "Use 'run_skill' to delegate complex procedural workflows or specialized domain tasks to an isolated subagent, keeping the main conversation context clean."
168275
169026
  ]
169027
+ },
169028
+ manage_memory: {
169029
+ snippet: "Record, read, or prune persistent workspace rules and memories",
169030
+ guidelines: [
169031
+ "Use 'manage_memory' to record learned conventions, build/test commands, architecture notes, and quirks into persistent workspace memory."
169032
+ ]
168276
169033
  }
168277
169034
  };
168278
169035
  });
@@ -168287,10 +169044,383 @@ var init_telemetry = __esm(() => {
168287
169044
  init_tracer_factory();
168288
169045
  });
168289
169046
 
169047
+ // ../../packages/agent/src/lsp-client.ts
169048
+ import { spawn as spawn3 } from "node:child_process";
169049
+ import fs24 from "node:fs";
169050
+ import path25 from "node:path";
169051
+ import { fileURLToPath, pathToFileURL } from "node:url";
169052
+
169053
+ class LspClient {
169054
+ workspaceRoot;
169055
+ binaryPath;
169056
+ requestTimeoutMs;
169057
+ child = null;
169058
+ nextId = 1;
169059
+ pendingRequests = new Map;
169060
+ buffer = Buffer.alloc(0);
169061
+ diagnosticsByUri = new Map;
169062
+ openedDocuments = new Set;
169063
+ initPromise = null;
169064
+ isShutdown = false;
169065
+ constructor(options) {
169066
+ this.workspaceRoot = path25.resolve(options.workspaceRoot);
169067
+ this.binaryPath = options.binaryPath;
169068
+ this.requestTimeoutMs = options.requestTimeoutMs ?? 15000;
169069
+ }
169070
+ async start() {
169071
+ if (this.initPromise) {
169072
+ return this.initPromise;
169073
+ }
169074
+ this.initPromise = this.initInternal();
169075
+ return this.initPromise;
169076
+ }
169077
+ findBinary() {
169078
+ if (this.binaryPath && fs24.existsSync(this.binaryPath)) {
169079
+ return this.binaryPath;
169080
+ }
169081
+ const candidatePaths = [
169082
+ path25.resolve(this.workspaceRoot, "node_modules/.bin/typescript-language-server"),
169083
+ path25.resolve(process.cwd(), "node_modules/.bin/typescript-language-server"),
169084
+ path25.resolve(process.cwd(), "node_modules/typescript-language-server/lib/cli.mjs")
169085
+ ];
169086
+ for (const candidate of candidatePaths) {
169087
+ if (fs24.existsSync(candidate)) {
169088
+ return candidate;
169089
+ }
169090
+ }
169091
+ return "typescript-language-server";
169092
+ }
169093
+ async initInternal() {
169094
+ const bin = this.findBinary();
169095
+ this.child = spawn3(bin, ["--stdio"], {
169096
+ cwd: this.workspaceRoot,
169097
+ env: { ...process.env },
169098
+ stdio: ["pipe", "pipe", "pipe"]
169099
+ });
169100
+ this.child.stdout?.on("data", (chunk) => {
169101
+ this.handleStdout(chunk);
169102
+ });
169103
+ this.child.stderr?.on("data", (_chunk) => {});
169104
+ this.child.on("error", (err) => {
169105
+ this.rejectAllPending(new Error(`LSP process error: ${err.message}`));
169106
+ });
169107
+ this.child.on("exit", (code) => {
169108
+ this.rejectAllPending(new Error(`LSP process exited with code ${code}`));
169109
+ this.child = null;
169110
+ this.initPromise = null;
169111
+ });
169112
+ const rootUri = pathToFileURL(this.workspaceRoot).toString();
169113
+ await this.sendRequest("initialize", {
169114
+ processId: process.pid,
169115
+ rootUri,
169116
+ rootPath: this.workspaceRoot,
169117
+ capabilities: {
169118
+ textDocument: {
169119
+ definition: { dynamicRegistration: false, linkSupport: true },
169120
+ references: { dynamicRegistration: false },
169121
+ documentSymbol: {
169122
+ dynamicRegistration: false,
169123
+ hierarchicalDocumentSymbolSupport: true
169124
+ },
169125
+ synchronization: {
169126
+ dynamicRegistration: false,
169127
+ willSave: false,
169128
+ didSave: true
169129
+ }
169130
+ }
169131
+ }
169132
+ });
169133
+ this.sendNotification("initialized", {});
169134
+ }
169135
+ handleStdout(chunk) {
169136
+ this.buffer = Buffer.concat([this.buffer, chunk]);
169137
+ while (true) {
169138
+ const headerEnd = this.buffer.indexOf(`\r
169139
+ \r
169140
+ `);
169141
+ if (headerEnd === -1)
169142
+ break;
169143
+ const headerStr = this.buffer.subarray(0, headerEnd).toString("utf8");
169144
+ const match = headerStr.match(/Content-Length:\s*(\d+)/i);
169145
+ if (!match) {
169146
+ this.buffer = this.buffer.subarray(headerEnd + 4);
169147
+ continue;
169148
+ }
169149
+ const contentLength = parseInt(match[1], 10);
169150
+ const totalMsgLength = headerEnd + 4 + contentLength;
169151
+ if (this.buffer.length < totalMsgLength)
169152
+ break;
169153
+ const body = this.buffer.subarray(headerEnd + 4, totalMsgLength).toString("utf8");
169154
+ this.buffer = this.buffer.subarray(totalMsgLength);
169155
+ try {
169156
+ const msg = JSON.parse(body);
169157
+ if (msg.id !== undefined && this.pendingRequests.has(msg.id)) {
169158
+ const pending = this.pendingRequests.get(msg.id);
169159
+ clearTimeout(pending.timeout);
169160
+ this.pendingRequests.delete(msg.id);
169161
+ if (msg.error) {
169162
+ pending.reject(new Error(msg.error.message || "LSP error"));
169163
+ } else {
169164
+ pending.resolve(msg.result);
169165
+ }
169166
+ } else if (msg.method === "textDocument/publishDiagnostics") {
169167
+ this.handlePublishDiagnostics(msg.params);
169168
+ }
169169
+ } catch {}
169170
+ }
169171
+ }
169172
+ handlePublishDiagnostics(params) {
169173
+ if (!params || !params.uri || !Array.isArray(params.diagnostics))
169174
+ return;
169175
+ const uri = params.uri;
169176
+ const filePath = fileURLToPath(uri);
169177
+ const items = params.diagnostics.map((d) => {
169178
+ const sevNum = d.severity ?? 1;
169179
+ const severity = sevNum === 1 ? "error" : sevNum === 2 ? "warning" : "info";
169180
+ return {
169181
+ filePath,
169182
+ line: (d.range?.start?.line ?? 0) + 1,
169183
+ column: (d.range?.start?.character ?? 0) + 1,
169184
+ message: d.message || "",
169185
+ severity,
169186
+ source: d.source || "typescript"
169187
+ };
169188
+ });
169189
+ this.diagnosticsByUri.set(uri, items);
169190
+ }
169191
+ sendRequest(method, params) {
169192
+ if (!this.child || !this.child.stdin || this.isShutdown) {
169193
+ return Promise.reject(new Error("LSP client is not running"));
169194
+ }
169195
+ const id = this.nextId++;
169196
+ return new Promise((resolve, reject) => {
169197
+ const timeout = setTimeout(() => {
169198
+ this.pendingRequests.delete(id);
169199
+ reject(new Error(`LSP request "${method}" timed out after ${this.requestTimeoutMs}ms`));
169200
+ }, this.requestTimeoutMs);
169201
+ this.pendingRequests.set(id, { resolve, reject, timeout });
169202
+ const payload = JSON.stringify({ jsonrpc: "2.0", id, method, params });
169203
+ const header = `Content-Length: ${Buffer.byteLength(payload, "utf8")}\r
169204
+ \r
169205
+ `;
169206
+ this.child?.stdin?.write(header + payload);
169207
+ });
169208
+ }
169209
+ sendNotification(method, params) {
169210
+ if (!this.child || !this.child.stdin || this.isShutdown)
169211
+ return;
169212
+ const payload = JSON.stringify({ jsonrpc: "2.0", method, params });
169213
+ const header = `Content-Length: ${Buffer.byteLength(payload, "utf8")}\r
169214
+ \r
169215
+ `;
169216
+ this.child.stdin.write(header + payload);
169217
+ }
169218
+ async openDocument(filePath, content) {
169219
+ const absPath = path25.isAbsolute(filePath) ? filePath : path25.resolve(this.workspaceRoot, filePath);
169220
+ const uri = pathToFileURL(absPath).toString();
169221
+ if (this.openedDocuments.has(uri)) {
169222
+ return uri;
169223
+ }
169224
+ let fileContent = content;
169225
+ if (fileContent === undefined) {
169226
+ try {
169227
+ fileContent = fs24.readFileSync(absPath, "utf8");
169228
+ } catch {
169229
+ fileContent = "";
169230
+ }
169231
+ }
169232
+ this.sendNotification("textDocument/didOpen", {
169233
+ textDocument: {
169234
+ uri,
169235
+ languageId: "typescript",
169236
+ version: 1,
169237
+ text: fileContent
169238
+ }
169239
+ });
169240
+ this.openedDocuments.add(uri);
169241
+ return uri;
169242
+ }
169243
+ async getDefinition(filePath, line, column) {
169244
+ const queryRawDefinition = async (targetFile, targetLine, targetCol) => {
169245
+ await this.start();
169246
+ const uri = await this.openDocument(targetFile);
169247
+ const raw = await this.sendRequest("textDocument/definition", {
169248
+ textDocument: { uri },
169249
+ position: {
169250
+ line: Math.max(0, targetLine - 1),
169251
+ character: Math.max(0, targetCol - 1)
169252
+ }
169253
+ });
169254
+ if (!raw)
169255
+ return [];
169256
+ const array = Array.isArray(raw) ? raw : [raw];
169257
+ const locations = [];
169258
+ for (const loc of array) {
169259
+ const targetUri = loc.targetUri || loc.uri;
169260
+ if (!targetUri)
169261
+ continue;
169262
+ const targetRange = loc.targetSelectionRange || loc.targetRange || loc.range;
169263
+ const targetFilePath = fileURLToPath(targetUri);
169264
+ locations.push({
169265
+ filePath: targetFilePath,
169266
+ line: (targetRange?.start?.line ?? 0) + 1,
169267
+ column: (targetRange?.start?.character ?? 0) + 1
169268
+ });
169269
+ }
169270
+ return locations;
169271
+ };
169272
+ const initial = await queryRawDefinition(filePath, line, column);
169273
+ const resolved = [];
169274
+ for (const loc of initial) {
169275
+ try {
169276
+ const content = fs24.readFileSync(loc.filePath, "utf8");
169277
+ const lines = content.split(`
169278
+ `);
169279
+ const lineContent = lines[loc.line - 1] || "";
169280
+ if (/^\s*import\b/.test(lineContent)) {
169281
+ const secondHop = await queryRawDefinition(loc.filePath, loc.line, loc.column);
169282
+ if (secondHop.length > 0 && !(secondHop.length === 1 && secondHop[0].filePath === loc.filePath && secondHop[0].line === loc.line)) {
169283
+ resolved.push(...secondHop);
169284
+ continue;
169285
+ }
169286
+ }
169287
+ } catch {}
169288
+ resolved.push(loc);
169289
+ }
169290
+ return resolved;
169291
+ }
169292
+ async getReferences(filePath, line, column) {
169293
+ await this.start();
169294
+ const uri = await this.openDocument(filePath);
169295
+ const raw = await this.sendRequest("textDocument/references", {
169296
+ textDocument: { uri },
169297
+ position: {
169298
+ line: Math.max(0, line - 1),
169299
+ character: Math.max(0, column - 1)
169300
+ },
169301
+ context: { includeDeclaration: true }
169302
+ });
169303
+ if (!raw || !Array.isArray(raw))
169304
+ return [];
169305
+ const locations = [];
169306
+ for (const loc of raw) {
169307
+ if (!loc.uri)
169308
+ continue;
169309
+ const targetFilePath = fileURLToPath(loc.uri);
169310
+ locations.push({
169311
+ filePath: targetFilePath,
169312
+ line: (loc.range?.start?.line ?? 0) + 1,
169313
+ column: (loc.range?.start?.character ?? 0) + 1
169314
+ });
169315
+ }
169316
+ return locations;
169317
+ }
169318
+ async getOutline(filePath) {
169319
+ await this.start();
169320
+ const uri = await this.openDocument(filePath);
169321
+ const raw = await this.sendRequest("textDocument/documentSymbol", {
169322
+ textDocument: { uri }
169323
+ });
169324
+ if (!raw || !Array.isArray(raw))
169325
+ return [];
169326
+ const items = [];
169327
+ const extractSymbols = (symbols, containerName) => {
169328
+ for (const sym of symbols) {
169329
+ const kindStr = SYMBOL_KINDS[sym.kind] || "symbol";
169330
+ const range = sym.range || sym.location?.range;
169331
+ const line = (range?.start?.line ?? 0) + 1;
169332
+ const column = (range?.start?.character ?? 0) + 1;
169333
+ items.push({
169334
+ name: sym.name || "anonymous",
169335
+ kind: kindStr,
169336
+ line,
169337
+ column,
169338
+ containerName: sym.containerName || containerName
169339
+ });
169340
+ if (Array.isArray(sym.children) && sym.children.length > 0) {
169341
+ extractSymbols(sym.children, sym.name);
169342
+ }
169343
+ }
169344
+ };
169345
+ extractSymbols(raw);
169346
+ return items;
169347
+ }
169348
+ getDiagnostics(filePath) {
169349
+ if (filePath) {
169350
+ const absPath = path25.isAbsolute(filePath) ? filePath : path25.resolve(this.workspaceRoot, filePath);
169351
+ const uri = pathToFileURL(absPath).toString();
169352
+ return this.diagnosticsByUri.get(uri) || [];
169353
+ }
169354
+ const all = [];
169355
+ for (const items of this.diagnosticsByUri.values()) {
169356
+ all.push(...items);
169357
+ }
169358
+ return all;
169359
+ }
169360
+ async shutdown() {
169361
+ if (this.isShutdown)
169362
+ return;
169363
+ this.isShutdown = true;
169364
+ if (this.child) {
169365
+ try {
169366
+ await Promise.race([
169367
+ this.sendRequest("shutdown", {}),
169368
+ new Promise((r) => setTimeout(r, 500))
169369
+ ]);
169370
+ this.sendNotification("exit", {});
169371
+ } catch {}
169372
+ try {
169373
+ this.child.kill("SIGTERM");
169374
+ } catch {}
169375
+ this.child = null;
169376
+ }
169377
+ this.rejectAllPending(new Error("LSP client shutdown"));
169378
+ }
169379
+ rejectAllPending(err) {
169380
+ for (const [id, pending] of this.pendingRequests.entries()) {
169381
+ clearTimeout(pending.timeout);
169382
+ pending.reject(err);
169383
+ this.pendingRequests.delete(id);
169384
+ }
169385
+ }
169386
+ }
169387
+ var SYMBOL_KINDS;
169388
+ var init_lsp_client = __esm(() => {
169389
+ SYMBOL_KINDS = {
169390
+ 1: "file",
169391
+ 2: "module",
169392
+ 3: "namespace",
169393
+ 4: "package",
169394
+ 5: "class",
169395
+ 6: "method",
169396
+ 7: "property",
169397
+ 8: "field",
169398
+ 9: "constructor",
169399
+ 10: "enum",
169400
+ 11: "interface",
169401
+ 12: "function",
169402
+ 13: "variable",
169403
+ 14: "constant",
169404
+ 15: "string",
169405
+ 16: "number",
169406
+ 17: "boolean",
169407
+ 18: "array",
169408
+ 19: "object",
169409
+ 20: "key",
169410
+ 21: "null",
169411
+ 22: "enumMember",
169412
+ 23: "struct",
169413
+ 24: "event",
169414
+ 25: "operator",
169415
+ 26: "typeParameter"
169416
+ };
169417
+ });
169418
+
168290
169419
  // ../../packages/agent/src/index.ts
168291
169420
  var exports_src3 = {};
168292
169421
  __export(exports_src3, {
168293
169422
  AgentHarness: () => AgentHarness,
169423
+ LspClient: () => LspClient,
168294
169424
  runAgentLoop: () => runAgentLoop
168295
169425
  });
168296
169426
  var init_src5 = __esm(() => {
@@ -168303,6 +169433,7 @@ var init_src5 = __esm(() => {
168303
169433
  init_telemetry();
168304
169434
  init_skill_subagent();
168305
169435
  init_run_skill_tool();
169436
+ init_lsp_client();
168306
169437
  });
168307
169438
 
168308
169439
  // src/headless-runner.ts
@@ -168580,8 +169711,8 @@ var exports_file_session_repository = {};
168580
169711
  __export(exports_file_session_repository, {
168581
169712
  FileSessionRepository: () => FileSessionRepository
168582
169713
  });
168583
- import fs23 from "node:fs/promises";
168584
- import path23 from "node:path";
169714
+ import fs25 from "node:fs/promises";
169715
+ import path26 from "node:path";
168585
169716
 
168586
169717
  class FileSessionRepository {
168587
169718
  sessionDir;
@@ -168591,22 +169722,22 @@ class FileSessionRepository {
168591
169722
  if (sessionDir) {
168592
169723
  this.sessionDir = sessionDir;
168593
169724
  } else {
168594
- this.sessionDir = path23.join(getConfigDir(), "sessions");
168595
- const legacyDir = path23.join(getLegacyConfigDir(), "sessions");
169725
+ this.sessionDir = path26.join(getConfigDir(), "sessions");
169726
+ const legacyDir = path26.join(getLegacyConfigDir(), "sessions");
168596
169727
  if (legacyDir !== this.sessionDir) {
168597
169728
  this.legacySessionDir = legacyDir;
168598
169729
  }
168599
169730
  }
168600
169731
  }
168601
169732
  async saveContext(sessionId, messages) {
168602
- await fs23.mkdir(this.sessionDir, { recursive: true });
168603
- const historyPath = path23.join(this.sessionDir, `${sessionId}.jsonl`);
169733
+ await fs25.mkdir(this.sessionDir, { recursive: true });
169734
+ const historyPath = path26.join(this.sessionDir, `${sessionId}.jsonl`);
168604
169735
  let existing = this.sessionCache[sessionId];
168605
169736
  if (!existing) {
168606
169737
  existing = {};
168607
169738
  this.sessionCache[sessionId] = existing;
168608
169739
  try {
168609
- const data = await fs23.readFile(historyPath, "utf-8");
169740
+ const data = await fs25.readFile(historyPath, "utf-8");
168610
169741
  const lines = data.split(`
168611
169742
  `).filter((l) => l.trim().length > 0);
168612
169743
  for (const l of lines) {
@@ -168630,19 +169761,19 @@ class FileSessionRepository {
168630
169761
  }
168631
169762
  }
168632
169763
  if (appendContent) {
168633
- await fs23.appendFile(historyPath, appendContent, "utf-8");
169764
+ await fs25.appendFile(historyPath, appendContent, "utf-8");
168634
169765
  }
168635
169766
  }
168636
169767
  async loadContext(sessionId) {
168637
- let historyPath = path23.join(this.sessionDir, `${sessionId}.jsonl`);
169768
+ let historyPath = path26.join(this.sessionDir, `${sessionId}.jsonl`);
168638
169769
  try {
168639
169770
  let data;
168640
169771
  try {
168641
- data = await fs23.readFile(historyPath, "utf-8");
169772
+ data = await fs25.readFile(historyPath, "utf-8");
168642
169773
  } catch (err) {
168643
169774
  if (this.legacySessionDir) {
168644
- const legacyPath = path23.join(this.legacySessionDir, `${sessionId}.jsonl`);
168645
- data = await fs23.readFile(legacyPath, "utf-8");
169775
+ const legacyPath = path26.join(this.legacySessionDir, `${sessionId}.jsonl`);
169776
+ data = await fs25.readFile(legacyPath, "utf-8");
168646
169777
  historyPath = legacyPath;
168647
169778
  } else {
168648
169779
  throw err;
@@ -168683,7 +169814,7 @@ class FileSessionRepository {
168683
169814
  if (msgs.length < lines.length) {
168684
169815
  const compacted = msgs.map((m) => JSON.stringify(m)).join(`
168685
169816
  `);
168686
- await fs23.writeFile(historyPath, compacted, "utf-8").catch(() => {});
169817
+ await fs25.writeFile(historyPath, compacted, "utf-8").catch(() => {});
168687
169818
  }
168688
169819
  const cacheObj = {};
168689
169820
  for (const msg of msgs) {
@@ -168697,12 +169828,12 @@ class FileSessionRepository {
168697
169828
  }
168698
169829
  async listSessions() {
168699
169830
  try {
168700
- await fs23.mkdir(this.sessionDir, { recursive: true });
169831
+ await fs25.mkdir(this.sessionDir, { recursive: true });
168701
169832
  const sessions = [];
168702
169833
  const seenIds = new Set;
168703
169834
  const processDirectory = async (dirPath) => {
168704
169835
  try {
168705
- const dirFiles = await fs23.readdir(dirPath);
169836
+ const dirFiles = await fs25.readdir(dirPath);
168706
169837
  for (const file of dirFiles) {
168707
169838
  if (!file.endsWith(".jsonl"))
168708
169839
  continue;
@@ -168710,10 +169841,10 @@ class FileSessionRepository {
168710
169841
  if (seenIds.has(id))
168711
169842
  continue;
168712
169843
  seenIds.add(id);
168713
- const filePath = path23.join(dirPath, file);
169844
+ const filePath = path26.join(dirPath, file);
168714
169845
  try {
168715
- const stat = await fs23.stat(filePath);
168716
- const data = await fs23.readFile(filePath, "utf-8");
169846
+ const stat = await fs25.stat(filePath);
169847
+ const data = await fs25.readFile(filePath, "utf-8");
168717
169848
  const lines = data.split(`
168718
169849
  `).filter((l) => l.trim().length > 0);
168719
169850
  let preview = "";
@@ -168747,20 +169878,20 @@ class FileSessionRepository {
168747
169878
  }
168748
169879
  }
168749
169880
  async deleteSession(sessionId) {
168750
- const historyPath = path23.join(this.sessionDir, `${sessionId}.jsonl`);
169881
+ const historyPath = path26.join(this.sessionDir, `${sessionId}.jsonl`);
168751
169882
  try {
168752
- await fs23.unlink(historyPath);
169883
+ await fs25.unlink(historyPath);
168753
169884
  } catch (e) {}
168754
169885
  if (this.legacySessionDir) {
168755
169886
  try {
168756
- await fs23.unlink(path23.join(this.legacySessionDir, `${sessionId}.jsonl`));
169887
+ await fs25.unlink(path26.join(this.legacySessionDir, `${sessionId}.jsonl`));
168757
169888
  } catch {}
168758
169889
  }
168759
169890
  }
168760
169891
  async renameSession(oldSessionId, newSessionId) {
168761
- const oldPath = path23.join(this.sessionDir, `${oldSessionId}.jsonl`);
168762
- const newPath = path23.join(this.sessionDir, `${newSessionId}.jsonl`);
168763
- await fs23.rename(oldPath, newPath);
169892
+ const oldPath = path26.join(this.sessionDir, `${oldSessionId}.jsonl`);
169893
+ const newPath = path26.join(this.sessionDir, `${newSessionId}.jsonl`);
169894
+ await fs25.rename(oldPath, newPath);
168764
169895
  }
168765
169896
  }
168766
169897
  var init_file_session_repository = __esm(() => {
@@ -174394,21 +175525,53 @@ __export(exports_local_operations, {
174394
175525
  localOperations: () => localOperations,
174395
175526
  setActiveScopeDir: () => setActiveScopeDir
174396
175527
  });
174397
- import { exec as exec4, spawn as spawn3 } from "node:child_process";
174398
- import fs24 from "node:fs/promises";
174399
- import path24 from "node:path";
175528
+ import { exec as exec4, spawn as spawn4 } from "node:child_process";
175529
+ import nodeFs from "node:fs";
175530
+ import fs26 from "node:fs/promises";
175531
+ import path27 from "node:path";
174400
175532
  import { promisify as promisify4 } from "node:util";
174401
175533
  function setActiveScopeDir(dir) {
174402
175534
  activeScopeDir = dir;
174403
175535
  }
174404
175536
  function getScopedCwd() {
174405
175537
  if (activeScopeDir) {
174406
- return path24.resolve(process.cwd(), activeScopeDir);
175538
+ return path27.resolve(process.cwd(), activeScopeDir);
174407
175539
  }
174408
175540
  return process.cwd();
174409
175541
  }
174410
- var import_fast_glob, execAsync2, localBashOps, activeScopeDir, SERVER_READY_REGEX, DEFAULT_BG_TIMEOUT_MS = 20000, DEFAULT_SERVER_READY_DELAY_MS = 2500, localOperations;
175542
+ function isTypeScriptProject(workspaceDir) {
175543
+ return nodeFs.existsSync(path27.join(workspaceDir, "tsconfig.json")) || nodeFs.existsSync(path27.join(workspaceDir, "package.json"));
175544
+ }
175545
+ async function getOrLaunchLspClient(customWorkspaceDir) {
175546
+ const workspaceDir = customWorkspaceDir || getScopedCwd();
175547
+ if (activeLspClient && activeLspWorkspace === workspaceDir) {
175548
+ return activeLspClient;
175549
+ }
175550
+ if (!isTypeScriptProject(workspaceDir)) {
175551
+ return null;
175552
+ }
175553
+ if (activeLspClient) {
175554
+ try {
175555
+ await activeLspClient.shutdown();
175556
+ } catch {}
175557
+ activeLspClient = null;
175558
+ activeLspWorkspace = null;
175559
+ }
175560
+ const client = new LspClient({ workspaceRoot: workspaceDir });
175561
+ activeLspClient = client;
175562
+ activeLspWorkspace = workspaceDir;
175563
+ try {
175564
+ await client.start();
175565
+ } catch {
175566
+ activeLspClient = null;
175567
+ activeLspWorkspace = null;
175568
+ return null;
175569
+ }
175570
+ return activeLspClient;
175571
+ }
175572
+ var import_fast_glob, execAsync2, localBashOps, activeScopeDir, SERVER_READY_REGEX, DEFAULT_BG_TIMEOUT_MS = 20000, DEFAULT_SERVER_READY_DELAY_MS = 2500, localOperations, activeLspClient = null, activeLspWorkspace = null;
174411
175573
  var init_local_operations = __esm(() => {
175574
+ init_src5();
174412
175575
  init_src();
174413
175576
  init_src4();
174414
175577
  init_task_manager();
@@ -174424,7 +175587,7 @@ var init_local_operations = __esm(() => {
174424
175587
  const fallbackTimeoutMs = actualOptions.waitMsBeforeAsync ?? DEFAULT_BG_TIMEOUT_MS;
174425
175588
  const serverDelayMs = actualOptions.serverReadyDelayMs ?? DEFAULT_SERVER_READY_DELAY_MS;
174426
175589
  return new Promise((resolve, reject) => {
174427
- const child = spawn3(command, {
175590
+ const child = spawn4(command, {
174428
175591
  cwd: targetCwd,
174429
175592
  detached: process.platform !== "win32",
174430
175593
  env: actualOptions.env ?? process.env,
@@ -174554,19 +175717,19 @@ Failed to start process: ${err?.message || err}
174554
175717
  fs: {
174555
175718
  readFile: async (filepath) => {
174556
175719
  const root = getScopedCwd();
174557
- const absolutePath = path24.isAbsolute(filepath) ? filepath : path24.resolve(root, filepath);
174558
- return fs24.readFile(absolutePath, "utf8");
175720
+ const absolutePath = path27.isAbsolute(filepath) ? filepath : path27.resolve(root, filepath);
175721
+ return fs26.readFile(absolutePath, "utf8");
174559
175722
  },
174560
175723
  writeFile: async (filepath, content) => {
174561
175724
  const root = getScopedCwd();
174562
- const absolutePath = path24.isAbsolute(filepath) ? filepath : path24.resolve(root, filepath);
174563
- await fs24.mkdir(path24.dirname(absolutePath), { recursive: true });
174564
- await fs24.writeFile(absolutePath, content, "utf8");
175725
+ const absolutePath = path27.isAbsolute(filepath) ? filepath : path27.resolve(root, filepath);
175726
+ await fs26.mkdir(path27.dirname(absolutePath), { recursive: true });
175727
+ await fs26.writeFile(absolutePath, content, "utf8");
174565
175728
  },
174566
175729
  readdir: async (dirPath) => {
174567
175730
  const root = getScopedCwd();
174568
- const absolutePath = path24.isAbsolute(dirPath) ? dirPath : path24.resolve(root, dirPath);
174569
- const entries = await fs24.readdir(absolutePath, { withFileTypes: true });
175731
+ const absolutePath = path27.isAbsolute(dirPath) ? dirPath : path27.resolve(root, dirPath);
175732
+ const entries = await fs26.readdir(absolutePath, { withFileTypes: true });
174570
175733
  const ignores = getWorkspaceIgnores(root);
174571
175734
  return entries.filter((entry) => !isPathIgnored(entry.name, ignores)).map((entry) => {
174572
175735
  const type = entry.isDirectory() ? "DIR " : "FILE";
@@ -174575,14 +175738,14 @@ Failed to start process: ${err?.message || err}
174575
175738
  },
174576
175739
  mkdir: async (dirPath, options) => {
174577
175740
  const root = getScopedCwd();
174578
- const absolutePath = path24.isAbsolute(dirPath) ? dirPath : path24.resolve(root, dirPath);
174579
- await fs24.mkdir(absolutePath, options);
175741
+ const absolutePath = path27.isAbsolute(dirPath) ? dirPath : path27.resolve(root, dirPath);
175742
+ await fs26.mkdir(absolutePath, options);
174580
175743
  },
174581
175744
  exists: async (filepath) => {
174582
175745
  const root = getScopedCwd();
174583
- const absolutePath = path24.isAbsolute(filepath) ? filepath : path24.resolve(root, filepath);
175746
+ const absolutePath = path27.isAbsolute(filepath) ? filepath : path27.resolve(root, filepath);
174584
175747
  try {
174585
- await fs24.access(absolutePath);
175748
+ await fs26.access(absolutePath);
174586
175749
  return true;
174587
175750
  } catch {
174588
175751
  return false;
@@ -174592,7 +175755,7 @@ Failed to start process: ${err?.message || err}
174592
175755
  search: {
174593
175756
  find: async (dirPath, query) => {
174594
175757
  const root = getScopedCwd();
174595
- const targetDir = path24.isAbsolute(dirPath) ? dirPath : path24.resolve(root, dirPath);
175758
+ const targetDir = path27.isAbsolute(dirPath) ? dirPath : path27.resolve(root, dirPath);
174596
175759
  const ignores = getWorkspaceIgnores(root);
174597
175760
  const files = await import_fast_glob.default([query], {
174598
175761
  cwd: targetDir,
@@ -174604,10 +175767,11 @@ Failed to start process: ${err?.message || err}
174604
175767
  },
174605
175768
  grep: async (dirPath, query) => {
174606
175769
  const root = getScopedCwd();
174607
- const targetDir = path24.isAbsolute(dirPath) ? dirPath : path24.resolve(root, dirPath);
175770
+ const targetDir = path27.isAbsolute(dirPath) ? dirPath : path27.resolve(root, dirPath);
174608
175771
  const ignores = getWorkspaceIgnores(root);
174609
175772
  try {
174610
- const cmd = `git grep -nI "${query}" ${targetDir} || grep -rnI --exclude-dir=node_modules --exclude-dir=.git "${query}" ${targetDir}`;
175773
+ const escapedQuery = query.replace(/"/g, "\\\"");
175774
+ const cmd = `git grep -nI "${escapedQuery}" -- ${targetDir} || grep -rnI --exclude-dir=node_modules --exclude-dir=.git "${escapedQuery}" ${targetDir}`;
174611
175775
  const { stdout } = await execAsync2(cmd);
174612
175776
  if (!stdout)
174613
175777
  return "";
@@ -174616,8 +175780,9 @@ Failed to start process: ${err?.message || err}
174616
175780
  const filtered = lines.filter((line) => {
174617
175781
  if (!line.trim())
174618
175782
  return false;
174619
- const filePath = line.split(":")[0];
174620
- return !isPathIgnored(filePath, ignores);
175783
+ const rawPath = line.split(":")[0];
175784
+ const relPath = path27.isAbsolute(rawPath) ? path27.relative(root, rawPath) : rawPath;
175785
+ return !isPathIgnored(relPath, ignores);
174621
175786
  });
174622
175787
  return filtered.join(`
174623
175788
  `);
@@ -174658,8 +175823,56 @@ Failed to start process: ${err?.message || err}
174658
175823
  return { text: "", error: error.message };
174659
175824
  }
174660
175825
  }
175826
+ },
175827
+ diagnostics: {
175828
+ getDiagnostics: async (filePath) => {
175829
+ const client = await getOrLaunchLspClient();
175830
+ if (!client)
175831
+ return [];
175832
+ return client.getDiagnostics(filePath);
175833
+ }
175834
+ },
175835
+ lsp: {
175836
+ getDefinition: async (filePath, line, column) => {
175837
+ const client = await getOrLaunchLspClient();
175838
+ if (!client)
175839
+ return [];
175840
+ return client.getDefinition(filePath, line, column);
175841
+ },
175842
+ getReferences: async (filePath, line, column) => {
175843
+ const client = await getOrLaunchLspClient();
175844
+ if (!client)
175845
+ return [];
175846
+ return client.getReferences(filePath, line, column);
175847
+ },
175848
+ getOutline: async (filePath) => {
175849
+ const client = await getOrLaunchLspClient();
175850
+ if (!client)
175851
+ return [];
175852
+ return client.getOutline(filePath);
175853
+ },
175854
+ getDiagnostics: async (filePath) => {
175855
+ const client = await getOrLaunchLspClient();
175856
+ if (!client)
175857
+ return [];
175858
+ return client.getDiagnostics(filePath);
175859
+ },
175860
+ shutdown: async () => {
175861
+ if (activeLspClient) {
175862
+ await activeLspClient.shutdown();
175863
+ activeLspClient = null;
175864
+ activeLspWorkspace = null;
175865
+ }
175866
+ }
174661
175867
  }
174662
175868
  };
175869
+ process.on("exit", () => {
175870
+ if (activeLspClient) {
175871
+ try {
175872
+ activeLspClient.shutdown();
175873
+ } catch {}
175874
+ }
175875
+ });
174663
175876
  });
174664
175877
 
174665
175878
  // src/utils/provider-factory.ts
@@ -179011,7 +180224,7 @@ var init_wrap_ansi = __esm(() => {
179011
180224
  // ../../node_modules/.bun/terminal-size@4.0.1/node_modules/terminal-size/index.js
179012
180225
  import process3 from "node:process";
179013
180226
  import { execFileSync } from "node:child_process";
179014
- import fs25 from "node:fs";
180227
+ import fs27 from "node:fs";
179015
180228
  import tty from "node:tty";
179016
180229
  function terminalSize() {
179017
180230
  const { env, stdout, stderr } = process3;
@@ -179059,7 +180272,7 @@ var defaultColumns = 80, defaultRows = 24, exec6 = (command, arguments_, { shell
179059
180272
  return true;
179060
180273
  }
179061
180274
  try {
179062
- const statContents = fs25.readFileSync("/proc/self/stat", "utf8");
180275
+ const statContents = fs27.readFileSync("/proc/self/stat", "utf8");
179063
180276
  const closingParenthesisIndex = statContents.lastIndexOf(") ");
179064
180277
  if (closingParenthesisIndex === -1) {
179065
180278
  return false;
@@ -179079,8 +180292,8 @@ var defaultColumns = 80, defaultRows = 24, exec6 = (command, arguments_, { shell
179079
180292
  }
179080
180293
  }, devTty = () => {
179081
180294
  try {
179082
- const flags = process3.platform === "darwin" ? fs25.constants.O_EVTONLY | fs25.constants.O_NONBLOCK : fs25.constants.O_NONBLOCK;
179083
- const { columns, rows } = tty.WriteStream(fs25.openSync("/dev/tty", flags));
180295
+ const flags = process3.platform === "darwin" ? fs27.constants.O_EVTONLY | fs27.constants.O_NONBLOCK : fs27.constants.O_NONBLOCK;
180296
+ const { columns, rows } = tty.WriteStream(fs27.openSync("/dev/tty", flags));
179084
180297
  return { columns, rows };
179085
180298
  } catch {}
179086
180299
  }, tput = () => {
@@ -211282,7 +212495,7 @@ var init_Text = __esm(() => {
211282
212495
  });
211283
212496
 
211284
212497
  // ../../node_modules/.bun/ink@7.1.0+90ffd46201e3176f/node_modules/ink/build/components/ErrorOverview.js
211285
- import * as fs26 from "node:fs";
212498
+ import * as fs28 from "node:fs";
211286
212499
  import { cwd } from "node:process";
211287
212500
  function ErrorOverview({ error }) {
211288
212501
  const stack = error.stack ? error.stack.split(`
@@ -211292,8 +212505,8 @@ function ErrorOverview({ error }) {
211292
212505
  let excerpt;
211293
212506
  let lineWidth = 0;
211294
212507
  const stackLineCounts = new Map;
211295
- if (filePath && origin?.line && fs26.existsSync(filePath)) {
211296
- const sourceCode = fs26.readFileSync(filePath, "utf8");
212508
+ if (filePath && origin?.line && fs28.existsSync(filePath)) {
212509
+ const sourceCode = fs28.readFileSync(filePath, "utf8");
211297
212510
  excerpt = dist_default4(sourceCode, origin.line);
211298
212511
  if (excerpt) {
211299
212512
  for (const { line } of excerpt) {
@@ -212724,7 +213937,7 @@ var init_render_to_string = __esm(async () => {
212724
213937
  });
212725
213938
 
212726
213939
  // ../../node_modules/.bun/ink@7.1.0+90ffd46201e3176f/node_modules/ink/build/components/Static.js
212727
- function Static13(props) {
213940
+ function Static15(props) {
212728
213941
  const { items, children: render, style: customStyle } = props;
212729
213942
  const [index, setIndex] = import_react17.useState(0);
212730
213943
  const itemsToRender = import_react17.useMemo(() => {
@@ -213967,9 +215180,9 @@ var init_paste_manager = __esm(() => {
213967
215180
  });
213968
215181
 
213969
215182
  // ../../packages/tui/src/utils/prompt-history.ts
213970
- import fs27 from "node:fs";
215183
+ import fs29 from "node:fs";
213971
215184
  import os15 from "node:os";
213972
- import path25 from "node:path";
215185
+ import path28 from "node:path";
213973
215186
 
213974
215187
  class PromptHistory {
213975
215188
  historyPath;
@@ -213981,16 +215194,16 @@ class PromptHistory {
213981
215194
  this.historyPath = customPath;
213982
215195
  } else {
213983
215196
  const home = process.env.HOME || process.env.USERPROFILE || os15.homedir();
213984
- const canonicalPath = path25.join(home, ".december", "history");
213985
- const legacyPath = path25.join(home, ".config", "december", "history");
213986
- this.historyPath = fs27.existsSync(canonicalPath) || !fs27.existsSync(legacyPath) ? canonicalPath : legacyPath;
215197
+ const canonicalPath = path28.join(home, ".december", "history");
215198
+ const legacyPath = path28.join(home, ".config", "december", "history");
215199
+ this.historyPath = fs29.existsSync(canonicalPath) || !fs29.existsSync(legacyPath) ? canonicalPath : legacyPath;
213987
215200
  }
213988
215201
  this.load();
213989
215202
  }
213990
215203
  load() {
213991
215204
  try {
213992
- if (fs27.existsSync(this.historyPath)) {
213993
- const raw = fs27.readFileSync(this.historyPath, "utf8");
215205
+ if (fs29.existsSync(this.historyPath)) {
215206
+ const raw = fs29.readFileSync(this.historyPath, "utf8");
213994
215207
  this.entries = raw.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
213995
215208
  }
213996
215209
  } catch {
@@ -214006,11 +215219,11 @@ class PromptHistory {
214006
215219
  this.cursor = this.entries.length;
214007
215220
  this.currentDraft = "";
214008
215221
  try {
214009
- const dir = path25.dirname(this.historyPath);
214010
- if (!fs27.existsSync(dir)) {
214011
- fs27.mkdirSync(dir, { recursive: true });
215222
+ const dir = path28.dirname(this.historyPath);
215223
+ if (!fs29.existsSync(dir)) {
215224
+ fs29.mkdirSync(dir, { recursive: true });
214012
215225
  }
214013
- fs27.appendFileSync(this.historyPath, trimmed + `
215226
+ fs29.appendFileSync(this.historyPath, trimmed + `
214014
215227
  `, "utf8");
214015
215228
  } catch {}
214016
215229
  }
@@ -214074,16 +215287,16 @@ var init_clipboard = () => {};
214074
215287
 
214075
215288
  // ../../packages/tui/src/utils/handoff.ts
214076
215289
  import { exec as exec7 } from "node:child_process";
214077
- import fs28 from "node:fs";
214078
- import path26 from "node:path";
215290
+ import fs30 from "node:fs";
215291
+ import path29 from "node:path";
214079
215292
  import { promisify as promisify5 } from "node:util";
214080
215293
  function getHandoffExcludes(baseDir = process.cwd()) {
214081
215294
  const excludes = [...MANDATORY_HANDOFF_EXCLUDES];
214082
215295
  const readIgnoreFile = (fileName) => {
214083
- const filePath = path26.join(baseDir, fileName);
215296
+ const filePath = path29.join(baseDir, fileName);
214084
215297
  try {
214085
- if (fs28.existsSync(filePath)) {
214086
- const lines = fs28.readFileSync(filePath, "utf8").split(`
215298
+ if (fs30.existsSync(filePath)) {
215299
+ const lines = fs30.readFileSync(filePath, "utf8").split(`
214087
215300
  `);
214088
215301
  for (const line of lines) {
214089
215302
  const trimmed = line.trim();
@@ -214152,9 +215365,9 @@ var init_handoff = __esm(() => {
214152
215365
  });
214153
215366
 
214154
215367
  // ../../packages/tui/src/components/command-menu/commands.tsx
214155
- import fs29 from "node:fs";
215368
+ import fs31 from "node:fs";
214156
215369
  import os17 from "node:os";
214157
- import path27 from "node:path";
215370
+ import path30 from "node:path";
214158
215371
  var COMMANDS;
214159
215372
  var init_commands2 = __esm(() => {
214160
215373
  init_clipboard();
@@ -214258,13 +215471,13 @@ var init_commands2 = __esm(() => {
214258
215471
  const archivePath = ".december-handoff.tar.gz";
214259
215472
  try {
214260
215473
  const home = process.env.HOME || process.env.USERPROFILE || os17.homedir();
214261
- const canonicalConfigPath = path27.join(home, ".december", "config.json");
214262
- const legacyConfigPath = path27.join(home, ".config", "december", "config.json");
214263
- const configPath = fs29.existsSync(canonicalConfigPath) || !fs29.existsSync(legacyConfigPath) ? canonicalConfigPath : legacyConfigPath;
215474
+ const canonicalConfigPath = path30.join(home, ".december", "config.json");
215475
+ const legacyConfigPath = path30.join(home, ".config", "december", "config.json");
215476
+ const configPath = fs31.existsSync(canonicalConfigPath) || !fs31.existsSync(legacyConfigPath) ? canonicalConfigPath : legacyConfigPath;
214264
215477
  let config = {};
214265
215478
  try {
214266
- if (fs29.existsSync(configPath)) {
214267
- config = JSON.parse(fs29.readFileSync(configPath, "utf8"));
215479
+ if (fs31.existsSync(configPath)) {
215480
+ config = JSON.parse(fs31.readFileSync(configPath, "utf8"));
214268
215481
  }
214269
215482
  } catch {}
214270
215483
  if (!config.decemberToken) {
@@ -214299,7 +215512,7 @@ var init_commands2 = __esm(() => {
214299
215512
  headers: { Authorization: `Bearer ${config.decemberToken}` }
214300
215513
  });
214301
215514
  if (!urlRes.ok) {
214302
- await fs29.promises.unlink(archivePath).catch(() => {});
215515
+ await fs31.promises.unlink(archivePath).catch(() => {});
214303
215516
  if (urlRes.status === 401) {
214304
215517
  ctx.toast.show({
214305
215518
  variant: "error",
@@ -214320,7 +215533,7 @@ var init_commands2 = __esm(() => {
214320
215533
  const urlJson = await urlRes.json();
214321
215534
  const { uploadUrl, objectKey } = urlJson.data || urlJson;
214322
215535
  ctx.toast.show({ variant: "info", message: "Uploading to MinIO..." });
214323
- const fileData = await fs29.promises.readFile(archivePath);
215536
+ const fileData = await fs31.promises.readFile(archivePath);
214324
215537
  const uploadRes = await fetch(uploadUrl, {
214325
215538
  method: "PUT",
214326
215539
  body: fileData
@@ -214344,11 +215557,11 @@ var init_commands2 = __esm(() => {
214344
215557
  const errJson = await sessionRes.json().catch(() => ({}));
214345
215558
  throw new Error(errJson.message || await sessionRes.text());
214346
215559
  }
214347
- await fs29.promises.unlink(archivePath).catch(() => {});
215560
+ await fs31.promises.unlink(archivePath).catch(() => {});
214348
215561
  ctx.toast.show({ variant: "success", message: "Handoff complete! Exiting in 3s." });
214349
215562
  setTimeout(() => ctx.exit(), 3000);
214350
215563
  } catch (e) {
214351
- await fs29.promises.unlink(archivePath).catch(() => {});
215564
+ await fs31.promises.unlink(archivePath).catch(() => {});
214352
215565
  ctx.toast.show({ variant: "error", message: `Handoff failed: ${e.message}` });
214353
215566
  }
214354
215567
  }
@@ -215746,6 +216959,8 @@ var init_input_bar = __esm(async () => {
215746
216959
  showExitConfirm ? /* @__PURE__ */ jsx_runtime8.jsx(Box_default, {
215747
216960
  width: "100%",
215748
216961
  justifyContent: "space-between",
216962
+ height: 1,
216963
+ overflow: "hidden",
215749
216964
  children: /* @__PURE__ */ jsx_runtime8.jsx(Box_default, {
215750
216965
  gap: 2,
215751
216966
  alignItems: "center",
@@ -215762,6 +216977,7 @@ var init_input_bar = __esm(async () => {
215762
216977
  ]
215763
216978
  }) : /* @__PURE__ */ jsx_runtime8.jsx(Text3, {
215764
216979
  color: THEME.colors.muted,
216980
+ wrap: "truncate",
215765
216981
  children: "· Press Ctrl+C again to exit"
215766
216982
  })
215767
216983
  })
@@ -215769,6 +216985,8 @@ var init_input_bar = __esm(async () => {
215769
216985
  }) : /* @__PURE__ */ jsx_runtime8.jsxs(Box_default, {
215770
216986
  width: "100%",
215771
216987
  justifyContent: "space-between",
216988
+ height: 1,
216989
+ overflow: "hidden",
215772
216990
  children: [
215773
216991
  /* @__PURE__ */ jsx_runtime8.jsx(Box_default, {
215774
216992
  gap: 2,
@@ -215780,6 +216998,7 @@ var init_input_bar = __esm(async () => {
215780
216998
  children: [
215781
216999
  /* @__PURE__ */ jsx_runtime8.jsxs(Text3, {
215782
217000
  color: THEME.colors.muted,
217001
+ wrap: "truncate",
215783
217002
  children: [
215784
217003
  activeModeTag ? /* @__PURE__ */ jsx_runtime8.jsxs(Text3, {
215785
217004
  color: THEME.colors.brand,
@@ -215793,6 +217012,7 @@ var init_input_bar = __esm(async () => {
215793
217012
  ]
215794
217013
  }),
215795
217014
  totalTasksCount > 0 && /* @__PURE__ */ jsx_runtime8.jsxs(Text3, {
217015
+ wrap: "truncate",
215796
217016
  color: runningTasksCount > 0 ? THEME.colors.warning : THEME.colors.muted,
215797
217017
  children: [
215798
217018
  "·",
@@ -215819,6 +217039,7 @@ var init_input_bar = __esm(async () => {
215819
217039
  marginLeft: 2,
215820
217040
  children: /* @__PURE__ */ jsx_runtime8.jsx(Text3, {
215821
217041
  color: contextColor,
217042
+ wrap: "truncate",
215822
217043
  children: contextLabel
215823
217044
  })
215824
217045
  })
@@ -215849,7 +217070,7 @@ var init_scroll_viewport = __esm(async () => {
215849
217070
  }, ref) {
215850
217071
  const viewportRef = import_react43.useRef(null);
215851
217072
  const contentRef = import_react43.useRef(null);
215852
- const defaultHeight = explicitHeight ?? (typeof process !== "undefined" && process.stdout?.rows ? Math.max(10, process.stdout.rows - 6) : 24);
217073
+ const defaultHeight = explicitHeight ?? (typeof process !== "undefined" && process.stdout?.rows ? Math.max(1, process.stdout.rows - 6) : 24);
215853
217074
  const [viewportHeight, setViewportHeight] = import_react43.useState(defaultHeight);
215854
217075
  const [contentHeight, setContentHeight] = import_react43.useState(0);
215855
217076
  const [internalScrollTop, setInternalScrollTop] = import_react43.useState(0);
@@ -215923,6 +217144,8 @@ var init_scroll_viewport = __esm(async () => {
215923
217144
  const newTop = Math.max(0, currentTop - delta);
215924
217145
  setIsReadingMode(newTop < currentMax);
215925
217146
  setInternalScrollTop(newTop);
217147
+ stateRef.current.isReadingMode = newTop < currentMax;
217148
+ stateRef.current.internalScrollTop = newTop;
215926
217149
  notifyStateChange(newTop);
215927
217150
  }, [notifyStateChange]);
215928
217151
  const handleScrollDown = import_react43.useCallback((delta) => {
@@ -215932,9 +217155,12 @@ var init_scroll_viewport = __esm(async () => {
215932
217155
  if (newTop >= currentMax) {
215933
217156
  setIsReadingMode(false);
215934
217157
  setInternalScrollTop(currentMax);
217158
+ stateRef.current.isReadingMode = false;
217159
+ stateRef.current.internalScrollTop = currentMax;
215935
217160
  notifyStateChange(currentMax);
215936
217161
  } else {
215937
217162
  setInternalScrollTop(newTop);
217163
+ stateRef.current.internalScrollTop = newTop;
215938
217164
  notifyStateChange(newTop);
215939
217165
  }
215940
217166
  }, [notifyStateChange]);
@@ -215942,12 +217168,16 @@ var init_scroll_viewport = __esm(async () => {
215942
217168
  const currentMax = stateRef.current.maxScrollTop;
215943
217169
  setIsReadingMode(currentMax > 0);
215944
217170
  setInternalScrollTop(0);
217171
+ stateRef.current.isReadingMode = currentMax > 0;
217172
+ stateRef.current.internalScrollTop = 0;
215945
217173
  notifyStateChange(0);
215946
217174
  }, [notifyStateChange]);
215947
217175
  const handleScrollToBottom = import_react43.useCallback(() => {
215948
217176
  const currentMax = stateRef.current.maxScrollTop;
215949
217177
  setIsReadingMode(false);
215950
217178
  setInternalScrollTop(currentMax);
217179
+ stateRef.current.isReadingMode = false;
217180
+ stateRef.current.internalScrollTop = currentMax;
215951
217181
  notifyStateChange(currentMax);
215952
217182
  }, [notifyStateChange]);
215953
217183
  import_react43.default.useImperativeHandle(ref, () => ({
@@ -216002,6 +217232,7 @@ var init_scroll_viewport = __esm(async () => {
216002
217232
  height: explicitHeight,
216003
217233
  flexGrow: 1,
216004
217234
  flexShrink: 1,
217235
+ minHeight: 0,
216005
217236
  overflowY: "hidden",
216006
217237
  flexDirection: "column",
216007
217238
  width: "100%",
@@ -216023,7 +217254,7 @@ var init_scroll_viewport = __esm(async () => {
216023
217254
  });
216024
217255
 
216025
217256
  // ../../packages/tui/src/utils/terminal-link.ts
216026
- import path28 from "node:path";
217257
+ import path31 from "node:path";
216027
217258
  function terminalLink(text, uri) {
216028
217259
  if (!uri)
216029
217260
  return text;
@@ -216032,7 +217263,7 @@ function terminalLink(text, uri) {
216032
217263
  function fileLink(displayPath, targetPath, line, col) {
216033
217264
  if (!targetPath)
216034
217265
  return displayPath;
216035
- const absPath = path28.isAbsolute(targetPath) ? targetPath : path28.resolve(process.cwd(), targetPath);
217266
+ const absPath = path31.isAbsolute(targetPath) ? targetPath : path31.resolve(process.cwd(), targetPath);
216036
217267
  const lineFragment = line ? `#L${line}${col ? `:${col}` : ""}` : "";
216037
217268
  const uri = `file://${absPath}${lineFragment}`;
216038
217269
  return terminalLink(displayPath, uri);
@@ -216041,9 +217272,9 @@ function toRelativePath(filePath, cwd = process.cwd()) {
216041
217272
  if (!filePath)
216042
217273
  return "";
216043
217274
  const trimmed = filePath.trim();
216044
- if (!path28.isAbsolute(trimmed))
217275
+ if (!path31.isAbsolute(trimmed))
216045
217276
  return trimmed;
216046
- const rel = path28.relative(cwd, trimmed);
217277
+ const rel = path31.relative(cwd, trimmed);
216047
217278
  if (!rel || rel.startsWith("..")) {
216048
217279
  return trimmed;
216049
217280
  }
@@ -216097,7 +217328,10 @@ var import_react44, jsx_runtime10, DiffGutterView;
216097
217328
  var init_diff_gutter = __esm(async () => {
216098
217329
  init_theme();
216099
217330
  init_terminal_link();
216100
- await init_build2();
217331
+ await __promiseAll([
217332
+ init_build2(),
217333
+ init_use_terminal_columns()
217334
+ ]);
216101
217335
  import_react44 = __toESM(require_react(), 1);
216102
217336
  jsx_runtime10 = __toESM(require_jsx_runtime(), 1);
216103
217337
  DiffGutterView = import_react44.default.memo(function DiffGutterView({
@@ -216116,8 +217350,12 @@ var init_diff_gutter = __esm(async () => {
216116
217350
  const isTruncated = isExpanded && parsedLines.length > maxVisibleLines;
216117
217351
  const maxLineNum = Math.max(...parsedLines.map((l) => Math.max(l.oldLineNumber || 0, l.newLineNumber || 0)), 1);
216118
217352
  const gutterWidth = Math.max(2, String(maxLineNum).length);
216119
- const displayPath = filePath ? filePath.replace(/^\.\//, "") : "";
216120
- const linkedPath = displayPath ? fileLink(displayPath, displayPath, startLine) : "";
217353
+ const columns = useTerminalColumns();
217354
+ const maxPathLen = Math.max(30, columns - 45);
217355
+ const cleanPath = filePath ? filePath.replace(/^\.\//, "") : "";
217356
+ const displayPath = cleanPath ? toRelativePath(cleanPath) : "";
217357
+ const truncatedDisplay = displayPath.length > maxPathLen ? displayPath.substring(0, maxPathLen - 3) + "..." : displayPath;
217358
+ const linkedPath = displayPath ? fileLink(truncatedDisplay, filePath || displayPath, startLine) : "";
216121
217359
  return /* @__PURE__ */ jsx_runtime10.jsxs(Box_default, {
216122
217360
  flexDirection: "column",
216123
217361
  marginY: 0,
@@ -216127,14 +217365,18 @@ var init_diff_gutter = __esm(async () => {
216127
217365
  gap: 1,
216128
217366
  alignItems: "center",
216129
217367
  children: [
216130
- /* @__PURE__ */ jsx_runtime10.jsx(Text3, {
216131
- color: THEME.colors.warning,
216132
- children: `${THEME.glyphs.status} `
216133
- }),
216134
- /* @__PURE__ */ jsx_runtime10.jsx(Text3, {
216135
- color: THEME.colors.warning,
216136
- bold: true,
216137
- children: actionLabel
217368
+ /* @__PURE__ */ jsx_runtime10.jsxs(Text3, {
217369
+ children: [
217370
+ /* @__PURE__ */ jsx_runtime10.jsx(Text3, {
217371
+ color: THEME.colors.warning,
217372
+ children: `${THEME.glyphs.status} `
217373
+ }),
217374
+ /* @__PURE__ */ jsx_runtime10.jsx(Text3, {
217375
+ color: THEME.colors.warning,
217376
+ bold: true,
217377
+ children: actionLabel
217378
+ })
217379
+ ]
216138
217380
  }),
216139
217381
  displayPath && /* @__PURE__ */ jsx_runtime10.jsx(Text3, {
216140
217382
  color: THEME.colors.text,
@@ -216161,6 +217403,7 @@ var init_diff_gutter = __esm(async () => {
216161
217403
  }),
216162
217404
  /* @__PURE__ */ jsx_runtime10.jsxs(Text3, {
216163
217405
  color: THEME.colors.muted,
217406
+ wrap: "truncate",
216164
217407
  children: [
216165
217408
  "(",
216166
217409
  isExpanded ? "ctrl+o to collapse" : "ctrl+o to view",
@@ -261585,17 +262828,19 @@ function CollapsibleThought({
261585
262828
  });
261586
262829
  }
261587
262830
  function StyledCommand({ command, truncate = true }) {
262831
+ const columns = useTerminalColumns();
262832
+ const maxArgLength = Math.max(40, columns - 38);
261588
262833
  const match = command.match(/^([A-Za-z_]+)\(([\s\S]*)\)$/);
261589
262834
  if (match) {
261590
262835
  const args = (match[2] || "").replace(/\r?\n/g, " ");
261591
262836
  const toolName = match[1];
261592
262837
  let displayArgs = args;
261593
- if (truncate && displayArgs.length > 80) {
261594
- displayArgs = displayArgs.substring(0, 80) + "...";
261595
- }
261596
262838
  if ((toolName === "read_file" || toolName === "view_file" || toolName === "Read") && args && !args.includes(" ")) {
261597
- const rel = toRelativePath(displayArgs);
261598
- displayArgs = fileLink(rel, args);
262839
+ const rel = toRelativePath(args);
262840
+ const truncatedRel = truncate && rel.length > maxArgLength ? rel.substring(0, maxArgLength - 3) + "..." : rel;
262841
+ displayArgs = fileLink(truncatedRel, args);
262842
+ } else if (truncate && displayArgs.length > maxArgLength) {
262843
+ displayArgs = displayArgs.substring(0, maxArgLength - 3) + "...";
261599
262844
  }
261600
262845
  const cmdColor = THEME.colors.warning;
261601
262846
  return /* @__PURE__ */ jsx_runtime17.jsxs(Text3, {
@@ -261621,8 +262866,8 @@ function StyledCommand({ command, truncate = true }) {
261621
262866
  });
261622
262867
  }
261623
262868
  let displayCmd = command.replace(/\r?\n/g, " ");
261624
- if (truncate && displayCmd.length > 80) {
261625
- displayCmd = displayCmd.substring(0, 80) + "...";
262869
+ if (truncate && displayCmd.length > maxArgLength) {
262870
+ displayCmd = displayCmd.substring(0, maxArgLength - 3) + "...";
261626
262871
  }
261627
262872
  return /* @__PURE__ */ jsx_runtime17.jsx(Text3, {
261628
262873
  color: THEME.colors.text,
@@ -261633,17 +262878,19 @@ function StyledRunningCommand({
261633
262878
  command,
261634
262879
  truncate = true
261635
262880
  }) {
262881
+ const columns = useTerminalColumns();
262882
+ const maxArgLength = Math.max(40, columns - 38);
261636
262883
  const match = command.match(/^([A-Za-z_]+)\(([\s\S]*)\)$/);
261637
262884
  if (match) {
261638
262885
  const args = (match[2] || "").replace(/\r?\n/g, " ");
261639
262886
  const toolName = match[1];
261640
262887
  let displayArgs = args;
261641
- if (truncate && displayArgs.length > 80) {
261642
- displayArgs = displayArgs.substring(0, 80) + "...";
261643
- }
261644
262888
  if ((toolName === "read_file" || toolName === "view_file" || toolName === "Read") && args && !args.includes(" ")) {
261645
- const rel = toRelativePath(displayArgs);
261646
- displayArgs = fileLink(rel, args);
262889
+ const rel = toRelativePath(args);
262890
+ const truncatedRel = truncate && rel.length > maxArgLength ? rel.substring(0, maxArgLength - 3) + "..." : rel;
262891
+ displayArgs = fileLink(truncatedRel, args);
262892
+ } else if (truncate && displayArgs.length > maxArgLength) {
262893
+ displayArgs = displayArgs.substring(0, maxArgLength - 3) + "...";
261647
262894
  }
261648
262895
  const cmdColor = THEME.colors.warning;
261649
262896
  return /* @__PURE__ */ jsx_runtime17.jsxs(Text3, {
@@ -261665,8 +262912,8 @@ function StyledRunningCommand({
261665
262912
  });
261666
262913
  }
261667
262914
  let displayCmd = command.replace(/\r?\n/g, " ");
261668
- if (truncate && displayCmd.length > 80) {
261669
- displayCmd = displayCmd.substring(0, 80) + "...";
262915
+ if (truncate && displayCmd.length > maxArgLength) {
262916
+ displayCmd = displayCmd.substring(0, maxArgLength - 3) + "...";
261670
262917
  }
261671
262918
  return /* @__PURE__ */ jsx_runtime17.jsx(Text3, {
261672
262919
  color: THEME.colors.text,
@@ -261696,6 +262943,7 @@ function CollapsibleCommandOutput({
261696
262943
  }),
261697
262944
  lines.length > 0 && /* @__PURE__ */ jsx_runtime17.jsxs(Text3, {
261698
262945
  color: THEME.colors.muted,
262946
+ wrap: "truncate",
261699
262947
  children: [
261700
262948
  "(",
261701
262949
  isExpanded ? "ctrl+o to collapse" : "ctrl+o to expand",
@@ -261762,22 +263010,56 @@ function getReadBlockPath(block) {
261762
263010
  const rel = toRelativePath(rawPath);
261763
263011
  return { display: rel || rawPath, target: rawPath };
261764
263012
  }
263013
+ function formatConsolidatedReadArgs(paths, maxArgLength) {
263014
+ const count = paths.length;
263015
+ if (count === 0)
263016
+ return "";
263017
+ if (count === 1) {
263018
+ const p0 = paths[0];
263019
+ const label = p0.display.length > maxArgLength ? p0.display.substring(0, Math.max(10, maxArgLength - 3)) + "..." : p0.display;
263020
+ return fileLink(label, p0.target);
263021
+ }
263022
+ if (count === 2) {
263023
+ const p0 = paths[0];
263024
+ const p1 = paths[1];
263025
+ if (p0.display.length + 2 + p1.display.length <= maxArgLength) {
263026
+ return `${fileLink(p0.display, p0.target)}, ${fileLink(p1.display, p1.target)}`;
263027
+ }
263028
+ const remainingForP1 = maxArgLength - (p0.display.length + 2);
263029
+ if (remainingForP1 >= 8) {
263030
+ const truncatedP1 = p1.display.substring(0, remainingForP1 - 3) + "...";
263031
+ return `${fileLink(p0.display, p0.target)}, ${fileLink(truncatedP1, p1.target)}`;
263032
+ }
263033
+ const truncatedP0 = p0.display.length > maxArgLength - 6 ? p0.display.substring(0, Math.max(10, maxArgLength - 9)) + "..." : p0.display;
263034
+ return `${fileLink(truncatedP0, p0.target)}, ...`;
263035
+ }
263036
+ const p0 = paths[0];
263037
+ const p1 = paths[1];
263038
+ const suffixTwo = `, +${count - 2} more`;
263039
+ if (p0.display.length + 2 + p1.display.length + suffixTwo.length <= maxArgLength) {
263040
+ return `${fileLink(p0.display, p0.target)}, ${fileLink(p1.display, p1.target)}${suffixTwo}`;
263041
+ }
263042
+ const remainingForP1 = maxArgLength - (p0.display.length + 2 + suffixTwo.length);
263043
+ if (remainingForP1 >= 8) {
263044
+ const truncatedP1 = p1.display.substring(0, remainingForP1 - 3) + "...";
263045
+ return `${fileLink(p0.display, p0.target)}, ${fileLink(truncatedP1, p1.target)}${suffixTwo}`;
263046
+ }
263047
+ const suffixOne = `, +${count - 1} more`;
263048
+ const remainingForP0 = maxArgLength - suffixOne.length;
263049
+ const label0 = p0.display.length > remainingForP0 ? p0.display.substring(0, Math.max(10, remainingForP0 - 3)) + "..." : p0.display;
263050
+ return `${fileLink(label0, p0.target)}${suffixOne}`;
263051
+ }
261765
263052
  function ConsolidatedReadsView({
261766
263053
  blocks,
261767
263054
  forceExpanded,
261768
263055
  isLastBlock
261769
263056
  }) {
261770
263057
  const isExpanded = forceExpanded ?? false;
263058
+ const columns = useTerminalColumns();
263059
+ const maxArgLength = Math.max(40, columns - 38);
261771
263060
  const paths = blocks.map(getReadBlockPath);
261772
263061
  const count = paths.length;
261773
- let inlineArgs = "";
261774
- if (count === 2) {
261775
- inlineArgs = `${fileLink(paths[0].display, paths[0].target)}, ${fileLink(paths[1].display, paths[1].target)}`;
261776
- } else if (count > 2) {
261777
- inlineArgs = `${fileLink(paths[0].display, paths[0].target)}, ${fileLink(paths[1].display, paths[1].target)}, +${count - 2} more`;
261778
- } else if (count === 1) {
261779
- inlineArgs = fileLink(paths[0].display, paths[0].target);
261780
- }
263062
+ const inlineArgs = formatConsolidatedReadArgs(paths, maxArgLength);
261781
263063
  const cmdColor = THEME.colors.warning;
261782
263064
  const isRunning = blocks.some((b) => b.status === "running");
261783
263065
  if (isRunning) {
@@ -261822,11 +263104,19 @@ function ConsolidatedReadsView({
261822
263104
  alignItems: "center",
261823
263105
  gap: 1,
261824
263106
  children: [
261825
- fileRunning ? /* @__PURE__ */ jsx_runtime17.jsx(Spinner2, {}) : /* @__PURE__ */ jsx_runtime17.jsx(Text3, {
261826
- color: cmdColor,
261827
- children: `${THEME.glyphs.status} `
263107
+ fileRunning ? /* @__PURE__ */ jsx_runtime17.jsx(Spinner2, {}) : /* @__PURE__ */ jsx_runtime17.jsxs(Text3, {
263108
+ children: [
263109
+ /* @__PURE__ */ jsx_runtime17.jsx(Text3, {
263110
+ color: cmdColor,
263111
+ children: `${THEME.glyphs.status} `
263112
+ }),
263113
+ /* @__PURE__ */ jsx_runtime17.jsx(Text3, {
263114
+ color: THEME.colors.muted,
263115
+ children: fileLink(p.display, p.target)
263116
+ })
263117
+ ]
261828
263118
  }),
261829
- /* @__PURE__ */ jsx_runtime17.jsx(Text3, {
263119
+ fileRunning && /* @__PURE__ */ jsx_runtime17.jsx(Text3, {
261830
263120
  color: THEME.colors.muted,
261831
263121
  children: fileLink(p.display, p.target)
261832
263122
  })
@@ -261868,6 +263158,7 @@ function ConsolidatedReadsView({
261868
263158
  }),
261869
263159
  /* @__PURE__ */ jsx_runtime17.jsxs(Text3, {
261870
263160
  color: THEME.colors.muted,
263161
+ wrap: "truncate",
261871
263162
  children: [
261872
263163
  "(",
261873
263164
  isExpanded ? "ctrl+o to collapse" : "ctrl+o to expand",
@@ -261951,6 +263242,7 @@ var init_bot_message = __esm(async () => {
261951
263242
  init_terminal_link();
261952
263243
  await __promiseAll([
261953
263244
  init_build2(),
263245
+ init_use_terminal_columns(),
261954
263246
  init_diff_gutter(),
261955
263247
  init_inline_tip(),
261956
263248
  init_spinner(),
@@ -262300,14 +263592,18 @@ ${r}`;
262300
263592
  gap: 1,
262301
263593
  alignItems: "center",
262302
263594
  children: [
262303
- /* @__PURE__ */ jsx_runtime17.jsx(Text3, {
262304
- color: THEME.colors.warning,
262305
- children: `${THEME.glyphs.status} `
262306
- }),
262307
- /* @__PURE__ */ jsx_runtime17.jsx(Text3, {
262308
- color: THEME.colors.warning,
262309
- bold: true,
262310
- children: actionLabel
263595
+ /* @__PURE__ */ jsx_runtime17.jsxs(Text3, {
263596
+ children: [
263597
+ /* @__PURE__ */ jsx_runtime17.jsx(Text3, {
263598
+ color: THEME.colors.warning,
263599
+ children: `${THEME.glyphs.status} `
263600
+ }),
263601
+ /* @__PURE__ */ jsx_runtime17.jsx(Text3, {
263602
+ color: THEME.colors.warning,
263603
+ bold: true,
263604
+ children: actionLabel
263605
+ })
263606
+ ]
262311
263607
  }),
262312
263608
  /* @__PURE__ */ jsx_runtime17.jsx(Text3, {
262313
263609
  color: THEME.colors.muted,
@@ -262346,27 +263642,27 @@ ${r}`;
262346
263642
  });
262347
263643
 
262348
263644
  // ../../packages/tui/src/components/header.tsx
262349
- import fs30 from "node:fs";
263645
+ import fs32 from "node:fs";
262350
263646
  import { homedir } from "node:os";
262351
- import path29 from "node:path";
263647
+ import path32 from "node:path";
262352
263648
  function getGitBranch(workspaceRoot = process.cwd()) {
262353
263649
  if (cachedBranch !== undefined) {
262354
263650
  return cachedBranch;
262355
263651
  }
262356
263652
  try {
262357
- const gitPath = path29.resolve(workspaceRoot, ".git");
262358
- if (fs30.existsSync(gitPath)) {
262359
- const stat = fs30.statSync(gitPath);
262360
- let headPath = path29.resolve(gitPath, "HEAD");
263653
+ const gitPath = path32.resolve(workspaceRoot, ".git");
263654
+ if (fs32.existsSync(gitPath)) {
263655
+ const stat = fs32.statSync(gitPath);
263656
+ let headPath = path32.resolve(gitPath, "HEAD");
262361
263657
  if (stat.isFile()) {
262362
- const content = fs30.readFileSync(gitPath, "utf-8").trim();
263658
+ const content = fs32.readFileSync(gitPath, "utf-8").trim();
262363
263659
  if (content.startsWith("gitdir:")) {
262364
263660
  const gitDir = content.slice(7).trim();
262365
- headPath = path29.resolve(workspaceRoot, gitDir, "HEAD");
263661
+ headPath = path32.resolve(workspaceRoot, gitDir, "HEAD");
262366
263662
  }
262367
263663
  }
262368
- if (fs30.existsSync(headPath)) {
262369
- const headContent = fs30.readFileSync(headPath, "utf-8").trim();
263664
+ if (fs32.existsSync(headPath)) {
263665
+ const headContent = fs32.readFileSync(headPath, "utf-8").trim();
262370
263666
  if (headContent.startsWith("ref: refs/heads/")) {
262371
263667
  cachedBranch = headContent.replace("ref: refs/heads/", "").trim();
262372
263668
  return cachedBranch;
@@ -262640,11 +263936,11 @@ var init_message_list = __esm(async () => {
262640
263936
  flexDirection: "column",
262641
263937
  width: "100%",
262642
263938
  children: [
262643
- staticMessages.length > 0 && /* @__PURE__ */ jsx_runtime20.jsx(Static13, {
263939
+ staticMessages.length > 0 && /* @__PURE__ */ jsx_runtime20.jsx(Static15, {
262644
263940
  items: staticMessages,
262645
263941
  style: { width: "100%" },
262646
263942
  children: (msg, index) => renderSingleMessage(msg, index, staticMessages, cliVersion, latestVersion, userEmail, expandCommands, announcement)
262647
- }, `${staticKey}-${expandCommands}`),
263943
+ }, staticKey),
262648
263944
  activeMessages.map((msg, index) => renderSingleMessage(msg, staticMessages.length + index, [...staticMessages, ...activeMessages], cliVersion, latestVersion, userEmail, expandCommands, announcement))
262649
263945
  ]
262650
263946
  });
@@ -267729,17 +269025,17 @@ var init_global_shortcuts = __esm(async () => {
267729
269025
 
267730
269026
  // ../../packages/tui/src/utils/pager.ts
267731
269027
  import * as cp3 from "node:child_process";
267732
- import fs31 from "node:fs";
269028
+ import fs33 from "node:fs";
267733
269029
  import os18 from "node:os";
267734
- import path30 from "node:path";
269030
+ import path33 from "node:path";
267735
269031
  function openPlanInPager(planText, title = "december-plan") {
267736
269032
  if (Boolean(process.env.BUN_TEST) || Boolean(process.env.VITEST)) {
267737
269033
  return Promise.resolve();
267738
269034
  }
267739
269035
  return new Promise((resolve) => {
267740
269036
  try {
267741
- const tmpFile = path30.join(os18.tmpdir(), `${title}-${Date.now()}.md`);
267742
- fs31.writeFileSync(tmpFile, planText, "utf8");
269037
+ const tmpFile = path33.join(os18.tmpdir(), `${title}-${Date.now()}.md`);
269038
+ fs33.writeFileSync(tmpFile, planText, "utf8");
267743
269039
  const pager = process.env.PAGER || process.env.EDITOR || (os18.platform() === "win32" ? "notepad" : "less -R");
267744
269040
  const child = cp3.spawn(pager, [tmpFile], {
267745
269041
  stdio: "inherit",
@@ -267747,7 +269043,7 @@ function openPlanInPager(planText, title = "december-plan") {
267747
269043
  });
267748
269044
  child.on("exit", () => {
267749
269045
  try {
267750
- fs31.unlinkSync(tmpFile);
269046
+ fs33.unlinkSync(tmpFile);
267751
269047
  } catch {}
267752
269048
  resolve();
267753
269049
  });
@@ -267823,6 +269119,19 @@ function ChatApp({
267823
269119
  }
267824
269120
  };
267825
269121
  }, []);
269122
+ import_react69.useEffect(() => {
269123
+ if (!process.stdout?.isTTY || isInline)
269124
+ return;
269125
+ const handleResize = () => {
269126
+ try {
269127
+ process.stdout.write("\x1B[2J\x1B[H");
269128
+ } catch {}
269129
+ };
269130
+ process.stdout.on("resize", handleResize);
269131
+ return () => {
269132
+ process.stdout.off("resize", handleResize);
269133
+ };
269134
+ }, [isInline]);
267826
269135
  use_input_default((input, key) => {
267827
269136
  if (key.ctrl && input === "c") {
267828
269137
  handleInterrupt();
@@ -268304,8 +269613,8 @@ var init_store = __esm(() => {
268304
269613
 
268305
269614
  // src/store/interceptors.ts
268306
269615
  import { execSync as execSync2 } from "node:child_process";
268307
- import fs32 from "node:fs/promises";
268308
- import path31 from "node:path";
269616
+ import fs34 from "node:fs/promises";
269617
+ import path34 from "node:path";
268309
269618
  async function computeToolCallDiff(toolCall) {
268310
269619
  if (toolCall.input?.diff) {
268311
269620
  return toolCall.input.diff;
@@ -268314,11 +269623,11 @@ async function computeToolCallDiff(toolCall) {
268314
269623
  if (!targetPath)
268315
269624
  return;
268316
269625
  const cwd = process.cwd();
268317
- const resolved = path31.resolve(cwd, targetPath);
268318
- const relative = path31.relative(cwd, resolved);
269626
+ const resolved = path34.resolve(cwd, targetPath);
269627
+ const relative = path34.relative(cwd, resolved);
268319
269628
  let oldContent = "";
268320
269629
  try {
268321
- oldContent = await fs32.readFile(resolved, "utf-8");
269630
+ oldContent = await fs34.readFile(resolved, "utf-8");
268322
269631
  } catch {}
268323
269632
  if (toolCall.name === "write_to_file" || toolCall.name === "write") {
268324
269633
  const newContent = toolCall.input?.CodeContent ?? toolCall.input?.content ?? "";
@@ -268382,9 +269691,9 @@ function setupAgentInterceptors(agent, storeState) {
268382
269691
  ];
268383
269692
  if (fileTools.includes(toolCall.name)) {
268384
269693
  const cwd = process.cwd();
268385
- const resolved = path31.resolve(cwd, targetPath);
268386
- const relative = path31.relative(cwd, resolved);
268387
- const isOutside = relative.startsWith("..") || path31.isAbsolute(relative);
269694
+ const resolved = path34.resolve(cwd, targetPath);
269695
+ const relative = path34.relative(cwd, resolved);
269696
+ const isOutside = relative.startsWith("..") || path34.isAbsolute(relative);
268388
269697
  if (!config.nonWorkspaceAccess && isOutside) {
268389
269698
  return {
268390
269699
  block: true,
@@ -268448,8 +269757,8 @@ function setupAgentInterceptors(agent, storeState) {
268448
269757
  const targetPath = toolCall.input?.TargetFile || toolCall.input?.AbsolutePath || toolCall.input?.filePath || toolCall.input?.path;
268449
269758
  if (targetPath) {
268450
269759
  const cwd = process.cwd();
268451
- const resolved = path31.resolve(cwd, targetPath);
268452
- const relative = path31.relative(cwd, resolved);
269760
+ const resolved = path34.resolve(cwd, targetPath);
269761
+ const relative = path34.relative(cwd, resolved);
268453
269762
  try {
268454
269763
  execSync2(`git ls-files --error-unmatch "${relative}"`, {
268455
269764
  cwd,
@@ -268476,7 +269785,7 @@ var init_interceptors = __esm(() => {
268476
269785
  });
268477
269786
 
268478
269787
  // src/utils/direct-shell.ts
268479
- import { spawn as spawn5 } from "node:child_process";
269788
+ import { spawn as spawn6 } from "node:child_process";
268480
269789
  function startDirectCommand(command, options = {}) {
268481
269790
  const { timeoutMs = 20000, serverReadyDelayMs = 2500, onData, onBackground } = options;
268482
269791
  let output = "";
@@ -268486,7 +269795,7 @@ function startDirectCommand(command, options = {}) {
268486
269795
  const isWindows = process.platform === "win32";
268487
269796
  const shellCmd = isWindows ? "cmd.exe" : "/bin/bash";
268488
269797
  const shellArgs = isWindows ? ["/d", "/s", "/c", command] : ["-c", command];
268489
- const child = spawn5(shellCmd, shellArgs, {
269798
+ const child = spawn6(shellCmd, shellArgs, {
268490
269799
  cwd: process.cwd(),
268491
269800
  env: process.env,
268492
269801
  stdio: ["pipe", "pipe", "pipe"],
@@ -268638,15 +269947,15 @@ var init_direct_shell = __esm(() => {
268638
269947
  });
268639
269948
 
268640
269949
  // src/utils/usage-tracker.ts
268641
- import fs33 from "node:fs/promises";
268642
- import path32 from "node:path";
269950
+ import fs35 from "node:fs/promises";
269951
+ import path35 from "node:path";
268643
269952
  function getUsageFilePath() {
268644
- return path32.join(getConfigDir(), "usage.json");
269953
+ return path35.join(getConfigDir(), "usage.json");
268645
269954
  }
268646
269955
  async function loadUsageLedger() {
268647
269956
  const filePath = getUsageFilePath();
268648
269957
  try {
268649
- const raw = await fs33.readFile(filePath, "utf-8");
269958
+ const raw = await fs35.readFile(filePath, "utf-8");
268650
269959
  const parsed = JSON.parse(raw);
268651
269960
  if (parsed && typeof parsed === "object" && parsed.version === 1 && parsed.days) {
268652
269961
  return parsed;
@@ -268659,8 +269968,8 @@ async function loadUsageLedger() {
268659
269968
  }
268660
269969
  async function saveUsageLedger(ledger) {
268661
269970
  const configDir = getConfigDir();
268662
- const filePath = path32.join(configDir, "usage.json");
268663
- const tmpPath = path32.join(configDir, `usage.json.tmp.${Date.now()}`);
269971
+ const filePath = path35.join(configDir, "usage.json");
269972
+ const tmpPath = path35.join(configDir, `usage.json.tmp.${Date.now()}`);
268664
269973
  const cutoffDate = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString().slice(0, 10);
268665
269974
  const prunedDays = {};
268666
269975
  for (const [dateKey, dayRecord] of Object.entries(ledger.days)) {
@@ -268670,12 +269979,12 @@ async function saveUsageLedger(ledger) {
268670
269979
  }
268671
269980
  ledger.days = prunedDays;
268672
269981
  try {
268673
- await fs33.mkdir(configDir, { recursive: true });
268674
- await fs33.writeFile(tmpPath, JSON.stringify(ledger, null, 2), "utf-8");
268675
- await fs33.rename(tmpPath, filePath);
269982
+ await fs35.mkdir(configDir, { recursive: true });
269983
+ await fs35.writeFile(tmpPath, JSON.stringify(ledger, null, 2), "utf-8");
269984
+ await fs35.rename(tmpPath, filePath);
268676
269985
  } catch {
268677
269986
  try {
268678
- await fs33.unlink(tmpPath);
269987
+ await fs35.unlink(tmpPath);
268679
269988
  } catch {}
268680
269989
  }
268681
269990
  }
@@ -268873,7 +270182,7 @@ var init_usage_rates2 = __esm(() => {
268873
270182
  // src/utils/error-parser.ts
268874
270183
  import util5 from "util";
268875
270184
  function isCreditOrBalanceError(combined) {
268876
- return combined.includes("insufficient balance") || combined.includes("insufficient credits") || combined.includes("insufficient_balance") || combined.includes("insufficient_credits") || combined.includes("insufficient_quota") || combined.includes("balance is insufficient") || combined.includes("balance insufficient") || combined.includes("balance exhausted") || combined.includes("out of credits") || combined.includes("credit balance is too low") || combined.includes("credits exhausted") || combined.includes("requires more credits") || combined.includes("can only afford") || combined.includes("payment required") || combined.includes("quota exceeded") || combined.includes("quota_exhausted") || combined.includes("402") || combined.includes("1008") || combined.includes("20009") || (combined.includes("credits") || combined.includes("balance")) && (combined.includes("insufficient") || combined.includes("exhausted") || combined.includes("empty") || combined.includes("zero") || combined.includes("low") || combined.includes("top up") || combined.includes("recharge") || combined.includes("afford") || combined.includes("add credits"));
270185
+ return combined.includes("insufficient balance") || combined.includes("insufficient credits") || combined.includes("insufficient_balance") || combined.includes("insufficient_credits") || combined.includes("insufficient_quota") || combined.includes("balance is insufficient") || combined.includes("balance insufficient") || combined.includes("balance exhausted") || combined.includes("out of credits") || combined.includes("credit balance is too low") || combined.includes("credits exhausted") || combined.includes("requires more credits") || combined.includes("can only afford") || combined.includes("payment required") || combined.includes("quota exceeded") || combined.includes("quota_exhausted") || combined.includes("budget_exhausted") || combined.includes("budget exhausted") || combined.includes("budget exceeded") || combined.includes("budget_exceeded") || combined.includes("402") || combined.includes("1008") || combined.includes("20009") || (combined.includes("credits") || combined.includes("balance")) && (combined.includes("insufficient") || combined.includes("exhausted") || combined.includes("empty") || combined.includes("zero") || combined.includes("low") || combined.includes("top up") || combined.includes("recharge") || combined.includes("afford") || combined.includes("add credits"));
268877
270186
  }
268878
270187
  function normalizeProviderAlias(provider) {
268879
270188
  if (!provider)
@@ -269257,7 +270566,8 @@ function parseErrorMessage(err, context) {
269257
270566
  }
269258
270567
  const contextStr = `${context?.provider || ""} ${context?.model || ""}`.toLowerCase().trim();
269259
270568
  const lowerStr = (errMsg + " " + (finalResult || "") + (contextStr ? " " + contextStr : "")).toLowerCase();
269260
- const isRateLimit = lowerStr.includes("429") || lowerStr.includes("quota") || lowerStr.includes("rate limit") || lowerStr.includes("rate_limit") || lowerStr.includes("resource_exhausted") || lowerStr.includes("generativelanguage.googleapis.com");
270569
+ const isBudgetExhausted = lowerStr.includes("budget_exhausted") || lowerStr.includes("budget exhausted") || lowerStr.includes("budget_exceeded") || lowerStr.includes("budget exceeded");
270570
+ const isRateLimit = !isBudgetExhausted && (lowerStr.includes("429") || lowerStr.includes("quota") || lowerStr.includes("rate limit") || lowerStr.includes("rate_limit") || lowerStr.includes("resource_exhausted") || lowerStr.includes("generativelanguage.googleapis.com"));
269261
270571
  if (isRateLimit && !finalResult.includes("Rate limit or quota exhausted")) {
269262
270572
  const rateDetails = resolveRateLimitDetails(lowerStr, context);
269263
270573
  return rateDetails.notice + finalResult;
@@ -269298,7 +270608,8 @@ function parseError(err, context) {
269298
270608
  const contextStr = `${context?.provider || ""} ${context?.model || ""}`.toLowerCase().trim();
269299
270609
  const fullMessage = parseErrorMessage(err, context);
269300
270610
  const combined = `${fullMessage} ${explicitCause || ""} ${contextStr ? " " + contextStr : ""}`.toLowerCase();
269301
- const isRateLimit = combined.includes("429") || combined.includes("quota") || combined.includes("rate limit") || combined.includes("rate_limit") || combined.includes("resource_exhausted") || combined.includes("generativelanguage.googleapis.com");
270611
+ const isBudgetExhausted = combined.includes("budget_exhausted") || combined.includes("budget exhausted") || combined.includes("budget_exceeded") || combined.includes("budget exceeded");
270612
+ const isRateLimit = !isBudgetExhausted && (combined.includes("429") || combined.includes("quota") || combined.includes("rate limit") || combined.includes("rate_limit") || combined.includes("resource_exhausted") || combined.includes("generativelanguage.googleapis.com"));
269302
270613
  if (isRateLimit) {
269303
270614
  const parts = fullMessage.split(`
269304
270615
  `);
@@ -269940,7 +271251,7 @@ var init_error_parser = __esm(() => {
269940
271251
  {
269941
271252
  id: "auriko",
269942
271253
  name: "Auriko",
269943
- match: (str) => (str.includes("auriko.ai") || str.includes("api.auriko.ai") || str.includes("auriko")) && (isCreditOrBalanceError(str) || str.includes("credits") || str.includes("402")),
271254
+ match: (str) => (str.includes("auriko.ai") || str.includes("api.auriko.ai") || str.includes("auriko")) && (isCreditOrBalanceError(str) || str.includes("credits") || str.includes("budget") || str.includes("402")),
269944
271255
  notice: `Insufficient credits in your Auriko account. Please check your account at https://www.auriko.ai/dashboard
269945
271256
  `,
269946
271257
  message: "Insufficient credits in your Auriko account.",
@@ -270110,14 +271421,14 @@ function extractJsonArray(rawText) {
270110
271421
  }
270111
271422
 
270112
271423
  // src/utils/project-context.ts
270113
- import fs34 from "node:fs";
270114
- import path33 from "node:path";
271424
+ import fs36 from "node:fs";
271425
+ import path36 from "node:path";
270115
271426
  function getProjectContext(workspaceDir = process.cwd()) {
270116
271427
  const parts = [];
270117
271428
  try {
270118
- const pkgPath = path33.join(workspaceDir, "package.json");
270119
- if (fs34.existsSync(pkgPath)) {
270120
- const pkg = JSON.parse(fs34.readFileSync(pkgPath, "utf8"));
271429
+ const pkgPath = path36.join(workspaceDir, "package.json");
271430
+ if (fs36.existsSync(pkgPath)) {
271431
+ const pkg = JSON.parse(fs36.readFileSync(pkgPath, "utf8"));
270121
271432
  const deps = Object.keys(pkg.dependencies || {}).slice(0, 15).join(", ");
270122
271433
  const devDeps = Object.keys(pkg.devDependencies || {}).slice(0, 10).join(", ");
270123
271434
  parts.push(`Project: ${pkg.name || "Unnamed"}`);
@@ -270126,13 +271437,13 @@ function getProjectContext(workspaceDir = process.cwd()) {
270126
271437
  if (devDeps)
270127
271438
  parts.push(`DevDependencies: ${devDeps}`);
270128
271439
  }
270129
- const agentsPath = path33.join(workspaceDir, "AGENTS.md");
270130
- if (fs34.existsSync(agentsPath)) {
270131
- const content = fs34.readFileSync(agentsPath, "utf8");
271440
+ const agentsPath = path36.join(workspaceDir, "AGENTS.md");
271441
+ if (fs36.existsSync(agentsPath)) {
271442
+ const content = fs36.readFileSync(agentsPath, "utf8");
270132
271443
  parts.push(`Guidelines (from AGENTS.md):
270133
271444
  ${content.slice(0, 500)}`);
270134
271445
  }
270135
- const entries = fs34.readdirSync(workspaceDir, { withFileTypes: true }).filter((e) => !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.isDirectory() ? `${e.name}/` : e.name).slice(0, 20);
271446
+ const entries = fs36.readdirSync(workspaceDir, { withFileTypes: true }).filter((e) => !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.isDirectory() ? `${e.name}/` : e.name).slice(0, 20);
270136
271447
  if (entries.length > 0) {
270137
271448
  parts.push(`Workspace layout: ${entries.join(", ")}`);
270138
271449
  }
@@ -270287,14 +271598,14 @@ async function fetchProviderBalance(provider, apiKey, timeoutMs = 2000) {
270287
271598
  }
270288
271599
 
270289
271600
  // src/utils/formatters.ts
270290
- import path34 from "node:path";
271601
+ import path37 from "node:path";
270291
271602
  function toRelativePath2(filePath, cwd = process.cwd()) {
270292
271603
  if (!filePath)
270293
271604
  return "";
270294
271605
  const trimmed = filePath.trim();
270295
- if (!path34.isAbsolute(trimmed))
271606
+ if (!path37.isAbsolute(trimmed))
270296
271607
  return trimmed;
270297
- const rel = path34.relative(cwd, trimmed);
271608
+ const rel = path37.relative(cwd, trimmed);
270298
271609
  if (!rel || rel.startsWith("..")) {
270299
271610
  return trimmed;
270300
271611
  }
@@ -271034,7 +272345,7 @@ ${formatSubscriptionCard(bundle, targetModel)}`
271034
272345
  const probeAbortController = new AbortController;
271035
272346
  const probeTimeout = setTimeout(() => {
271036
272347
  probeAbortController.abort(new Error("Connection timed out while verifying API key."));
271037
- }, 1e4);
272348
+ }, 8000);
271038
272349
  try {
271039
272350
  const stream = testProvider.stream([{ role: "user", content: "Hi" }], undefined, undefined, {
271040
272351
  model: testModel,
@@ -271051,15 +272362,19 @@ ${formatSubscriptionCard(bundle, targetModel)}`
271051
272362
  }
271052
272363
  } catch (probeErr) {
271053
272364
  lastProbeErr = probeErr;
271054
- const errText = (probeErr?.message || String(probeErr)).toLowerCase();
272365
+ const errText = `${probeErr?.message || ""} ${probeErr?.code || ""} ${probeErr?.error?.code || ""} ${String(probeErr)}`.toLowerCase();
271055
272366
  if (errText.includes("401") || errText.includes("unauthorized") || errText.includes("invalid api key") || errText.includes("incorrect api key")) {
271056
272367
  throw probeErr;
271057
272368
  }
271058
- if (errText.includes("402") || errText.includes("credits") || errText.includes("payment") || errText.includes("afford") || errText.includes("insufficient_quota")) {
272369
+ if (errText.includes("402") || errText.includes("credits") || errText.includes("payment") || errText.includes("afford") || errText.includes("budget") || errText.includes("balance") || errText.includes("insufficient_quota")) {
271059
272370
  if (!lowCreditErr) {
271060
272371
  lowCreditErr = probeErr;
271061
272372
  lowCreditModel = testModel;
271062
272373
  }
272374
+ break;
272375
+ }
272376
+ if (errText.includes("timed out") || errText.includes("timeout") || probeErr?.name === "AbortError") {
272377
+ break;
271063
272378
  }
271064
272379
  }
271065
272380
  }
@@ -271099,10 +272414,10 @@ ${formatSubscriptionCard(bundle, targetModel)}`
271099
272414
  setApiKey("");
271100
272415
  addToast(MESSAGES.AUTH.API_KEY_SAVED(selectedProvider), "success");
271101
272416
  } catch (err) {
271102
- const errStr = (err?.message || JSON.stringify(err) || String(err)).toLowerCase();
272417
+ const errStr = `${err?.message || ""} ${err?.code || ""} ${err?.error?.code || ""} ${String(err)}`.toLowerCase();
271103
272418
  const isTimeout = err?.name === "AbortError" || errStr.includes("aborted") || errStr.includes("timed out") || errStr.includes("timeout");
271104
- const isLowCredit = errStr.includes("402") || errStr.includes("credits") || errStr.includes("payment") || errStr.includes("afford");
271105
- if (errStr.includes("429") || errStr.includes("quota") || errStr.includes("rate limit") || errStr.includes("404") || errStr.includes("not found") || errStr.includes("503") || errStr.includes("无可用渠道") || errStr.includes("no available channel") || isLowCredit) {
272419
+ const isLowCredit = errStr.includes("402") || errStr.includes("credits") || errStr.includes("payment") || errStr.includes("afford") || errStr.includes("budget") || errStr.includes("balance") || errStr.includes("insufficient_quota");
272420
+ if (errStr.includes("429") || errStr.includes("quota") || errStr.includes("rate limit") || errStr.includes("404") || errStr.includes("not found") || errStr.includes("503") || errStr.includes("无可用渠道") || errStr.includes("no available channel") || errStr.includes("platform_keys_unavailable") || errStr.includes("byok_keys_required") || errStr.includes("tier_opt_in_required") || errStr.includes("no_provider_available") || isLowCredit) {
271106
272421
  await Promise.resolve().then(() => init_models5());
271107
272422
  const finalModel = testModel || getDefaultModelForProvider(selectedProvider);
271108
272423
  const config = await loadConfig();
@@ -271290,7 +272605,6 @@ ${formatSubscriptionCard(bundle, targetModel)}`
271290
272605
  try {
271291
272606
  await agent.loadContext(item.value);
271292
272607
  const resumedMessages = convertAgentMessagesToTuiMessages(agent.messages);
271293
- console.clear();
271294
272608
  setStaticMessages([{ id: "header", role: "header" }, ...resumedMessages]);
271295
272609
  setStaticKey((k) => k + 1);
271296
272610
  setActiveMessages([]);
@@ -271683,7 +272997,6 @@ function useAgentSession({
271683
272997
  } = state;
271684
272998
  const [expandCommands, setExpandCommands] = import_react71.useState(false);
271685
272999
  const toggleExpandCommands = import_react71.useCallback(() => {
271686
- console.clear();
271687
273000
  setExpandCommands((prev) => !prev);
271688
273001
  }, []);
271689
273002
  const [detectedSubscriptions, setDetectedSubscriptions] = import_react71.useState({});
@@ -271728,7 +273041,6 @@ function useAgentSession({
271728
273041
  }
271729
273042
  const resumed = convertAgentMessagesToTuiMessages(agent.messages);
271730
273043
  if (resumed.length > 0) {
271731
- console.clear();
271732
273044
  setStaticMessages([{ id: "header", role: "header" }, ...resumed]);
271733
273045
  setStaticKey((k) => k + 1);
271734
273046
  }
@@ -273696,7 +275008,7 @@ var init_use_agent_session = __esm(async () => {
273696
275008
 
273697
275009
  // src/index.ts
273698
275010
  init_package();
273699
- import path35 from "node:path";
275011
+ import path38 from "node:path";
273700
275012
 
273701
275013
  // src/args.ts
273702
275014
  import { parseArgs } from "node:util";
@@ -273834,12 +275146,12 @@ Options:
273834
275146
  }
273835
275147
  // src/constants/release-highlights.json
273836
275148
  var release_highlights_default = {
273837
- version: "0.4.2",
273838
- title: "What's New in v0.4.2",
275149
+ version: "0.4.4",
275150
+ title: "What's New in v0.4.4",
273839
275151
  bullets: [
273840
- "*(tui)* Enable mouse wheel, pageup/down, and arrow scrolling",
273841
- "*(docs)* Add youtube and x social links to footer and creator navigation schema",
273842
- "*(tui)* Remove empty line between tool calls and active loader"
275152
+ "*(agent)* Passive workspace memory ingestion and manage_memory tool",
275153
+ "*(agent)* Lsp client integration for definition and reference navigation",
275154
+ "*(tools)* Prefer non-import lines when resolving symbol definition in code_intelligence"
273843
275155
  ],
273844
275156
  url: "https://trydecember.com/docs/changelog"
273845
275157
  };
@@ -274040,7 +275352,7 @@ To define project-specific instructions, create an AGENTS.md file in your projec
274040
275352
  sessionRepository,
274041
275353
  sessionId,
274042
275354
  runtime: "cli",
274043
- workspaceDir: parsedArgs.scope ? path35.resolve(process.cwd(), parsedArgs.scope) : process.cwd(),
275355
+ workspaceDir: parsedArgs.scope ? path38.resolve(process.cwd(), parsedArgs.scope) : process.cwd(),
274044
275356
  thinkingLevel: config.thinkingLevel || "auto",
274045
275357
  steeringMode: config.steeringMode || "all",
274046
275358
  followUpMode: config.followUpMode || "all"
@@ -274144,7 +275456,7 @@ Executing Headless Task: "${parsedArgs.prompt}"
274144
275456
  sessionRepository,
274145
275457
  sessionId,
274146
275458
  runtime: "cli",
274147
- workspaceDir: parsedArgs.scope ? path35.resolve(process.cwd(), parsedArgs.scope) : process.cwd(),
275459
+ workspaceDir: parsedArgs.scope ? path38.resolve(process.cwd(), parsedArgs.scope) : process.cwd(),
274148
275460
  thinkingLevel: config.thinkingLevel || "auto",
274149
275461
  steeringMode: config.steeringMode || "all",
274150
275462
  followUpMode: config.followUpMode || "all"