@creative-dswork/dscode 0.2.3 → 0.2.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.
package/dist/dscode.mjs CHANGED
@@ -214348,7 +214348,7 @@ function generateRecoveryTimelineHTML(recoveryArcs) {
214348
214348
  <span style="background:${COLORS.warn};color:#000;padding:4px 10px;border-radius:4px;font-size:12px;font-weight:600;white-space:nowrap;">\u{1F50D} Step ${arc.detectionStep}</span>
214349
214349
  <span style="color:${COLORS.textMuted};font-size:14px;">\u2192</span>
214350
214350
  <span style="background:${effectiveColor};color:#000;padding:4px 10px;border-radius:4px;font-size:12px;font-weight:600;white-space:nowrap;">${effectiveIcon} Step ${arc.correctionStep}</span>
214351
- <span style="background:rgba(88,166,255,0.15);padding:2px 8px;border-radius:4px;font-size:11px;color:${COLORS.accent};">${detLabel}</span>
214351
+ <span style="background:rgba(212,151,8,0.15);padding:2px 8px;border-radius:4px;font-size:11px;color:${COLORS.accent};">${detLabel}</span>
214352
214352
  <span style="font-size:11px;color:${COLORS.textMuted};">${arc.stepsToRecover} steps</span>
214353
214353
  ${arc.misdiagnosisCount > 0 ? `<span style="background:rgba(210,153,29,0.15);padding:2px 8px;border-radius:4px;font-size:11px;color:${COLORS.warn};">${arc.misdiagnosisCount} \u6B21\u8BEF\u5224</span>` : ""}
214354
214354
  </div>
@@ -214356,7 +214356,7 @@ function generateRecoveryTimelineHTML(recoveryArcs) {
214356
214356
  <span style="color:${COLORS.danger};">${escapeHtml(arc.errorAgent)}</span>: ${escapeHtml(arc.errorSummary)}
214357
214357
  \u2192 <span style="color:${effectiveColor};">${escapeHtml(arc.correctionAgent)}</span>: ${escapeHtml(arc.correctionSummary)}
214358
214358
  </div>
214359
- <div style="font-size:12px;color:${COLORS.accent};font-style:italic;padding:8px 12px;background:rgba(88,166,255,0.08);border-radius:4px;border-left:3px solid ${COLORS.accent};margin-top:8px;">
214359
+ <div style="font-size:12px;color:${COLORS.accent};font-style:italic;padding:8px 12px;background:rgba(212,151,8,0.08);border-radius:4px;border-left:3px solid ${COLORS.accent};margin-top:8px;">
214360
214360
  \u{1F4A1} \u6839\u56E0\u5047\u8BF4: ${escapeHtml(arc.rootCauseHypothesis)}
214361
214361
  </div>
214362
214362
  </div>`;
@@ -214444,7 +214444,7 @@ function generateRuleChainHTML(attribution, rulesApplied) {
214444
214444
  "Rule3": "Irrecoverable Point \u2014 identifies the FIRST step that made the correct path unrecoverable (not necessarily the first error)"
214445
214445
  };
214446
214446
  const ruleItems = rulesApplied.map((r) => `
214447
- <div style="padding:8px 12px;margin:4px 0;background:${r === "Rule3" ? "rgba(248,81,73,0.15)" : "rgba(88,166,255,0.1)"};border-left:3px solid ${r === "Rule3" ? COLORS.danger : COLORS.accent};border-radius:4px;">
214447
+ <div style="padding:8px 12px;margin:4px 0;background:${r === "Rule3" ? "rgba(224,85,83,0.15)" : "rgba(212,151,8,0.1)"};border-left:3px solid ${r === "Rule3" ? COLORS.danger : COLORS.accent};border-radius:4px;">
214448
214448
  <strong style="color:${COLORS.accent};font-size:13px;">${escapeHtml(r)}</strong>
214449
214449
  <div style="font-size:12px;color:${COLORS.textMuted};margin-top:2px;">${escapeHtml(ruleDescriptions[r] ?? "")}</div>
214450
214450
  </div>`).join("");
@@ -214471,7 +214471,7 @@ function generateCascadePathHTML(cascadePath) {
214471
214471
  <span style="font-family:monospace;font-size:13px;color:${COLORS.accent};">${escapeHtml(e.fromZoneId)}:${e.fromStepId}</span>
214472
214472
  <span style="color:${COLORS.textMuted};font-size:18px;">\u2192</span>
214473
214473
  <span style="font-family:monospace;font-size:13px;">${escapeHtml(e.toZoneId)}:${e.toStepId}</span>
214474
- <span style="background:rgba(88,166,255,0.15);padding:2px 8px;border-radius:4px;font-size:11px;color:${COLORS.accent};">${mechanismLabel}</span>
214474
+ <span style="background:rgba(212,151,8,0.15);padding:2px 8px;border-radius:4px;font-size:11px;color:${COLORS.accent};">${mechanismLabel}</span>
214475
214475
  <span style="font-size:11px;color:${COLORS.textMuted};">\u6570\u636E: ${escapeHtml(e.dataItem)}</span>
214476
214476
  </div>`;
214477
214477
  }).join("");
@@ -214503,9 +214503,9 @@ body {
214503
214503
  max-width: 1200px;
214504
214504
  margin: 0 auto;
214505
214505
  }
214506
- h1 { font-size: 28px; font-weight: 600; margin-bottom: 4px; color: #f0f6fc; }
214507
- h2 { font-size: 20px; font-weight: 600; margin: 32px 0 16px; color: #f0f6fc; border-bottom: 1px solid ${COLORS.border}; padding-bottom: 8px; }
214508
- h3 { font-size: 16px; font-weight: 600; margin-bottom: 8px; color: #e6edf3; }
214506
+ h1 { font-size: 28px; font-weight: 600; margin-bottom: 4px; color: #e8e4dd; }
214507
+ h2 { font-size: 20px; font-weight: 600; margin: 32px 0 16px; color: #e8e4dd; border-bottom: 1px solid ${COLORS.border}; padding-bottom: 8px; }
214508
+ h3 { font-size: 16px; font-weight: 600; margin-bottom: 8px; color: #e8e4dd; }
214509
214509
  .header { background: ${COLORS.card}; border: 1px solid ${COLORS.border}; border-radius: 8px; padding: 24px; margin-bottom: 24px; }
214510
214510
  .header-meta { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; margin-top: 16px; }
214511
214511
  .meta-item { }
@@ -214686,7 +214686,7 @@ ${(() => {
214686
214686
  grouped[cat].push(r);
214687
214687
  }
214688
214688
  return Object.entries(grouped).map(([cat, catRules]) => `
214689
- <h3 style="font-size:15px;font-weight:600;margin:24px 0 12px;color:#e6edf3;">${escapeHtml(catLabels[cat] ?? cat)}</h3>
214689
+ <h3 style="font-size:15px;font-weight:600;margin:24px 0 12px;color:#e8e4dd;">${escapeHtml(catLabels[cat] ?? cat)}</h3>
214690
214690
  ${catRules.map((r) => {
214691
214691
  const sevLabel = r.severity >= 1 ? "ERROR" : r.severity >= 0.6 ? "WARN" : "INFO";
214692
214692
  const sevColor = r.severity >= 1 ? COLORS.danger : r.severity >= 0.6 ? COLORS.warn : COLORS.accent;
@@ -214707,7 +214707,7 @@ ${catRules.map((r) => {
214707
214707
  ${r.rawDescription ? `
214708
214708
  <details style="margin-bottom:8px;">
214709
214709
  <summary style="font-size:11px;color:${COLORS.accent};cursor:pointer;">\u{1F4DD} \u8BE6\u7EC6\u63CF\u8FF0</summary>
214710
- <p style="font-size:12px;color:${COLORS.textMuted};margin:4px 0;padding:8px;background:rgba(88,166,255,0.05);border-radius:4px;white-space:pre-wrap;">${escapeHtml(r.rawDescription)}</p>
214710
+ <p style="font-size:12px;color:${COLORS.textMuted};margin:4px 0;padding:8px;background:rgba(212,151,8,0.05);border-radius:4px;white-space:pre-wrap;">${escapeHtml(r.rawDescription)}</p>
214711
214711
  </details>` : ""}
214712
214712
  ${r.severity >= 1 ? `<div style="font-size:11px;color:${COLORS.danger};margin-bottom:6px;">\u26A0 \u5EFA\u8BAE\u6301\u4E45\u5316\u5230 Agent \u914D\u7F6E (${escapeHtml(r.targetLayer)})</div>` : ""}
214713
214713
  <div style="background:${COLORS.card};border-radius:4px;padding:10px 12px;">
@@ -214771,15 +214771,15 @@ var init_dashboard = __esm({
214771
214771
  "src/eval/dashboard.ts"() {
214772
214772
  "use strict";
214773
214773
  COLORS = {
214774
- ok: "#3fb950",
214775
- warn: "#d2991d",
214776
- danger: "#f85149",
214777
- bg: "#0d1117",
214778
- card: "#161b22",
214779
- border: "#30363d",
214780
- text: "#c9d1d9",
214781
- textMuted: "#8b949e",
214782
- accent: "#58a6ff"
214774
+ ok: "#5ca860",
214775
+ warn: "#d4a017",
214776
+ danger: "#e05553",
214777
+ bg: "#1e1c19",
214778
+ card: "#282622",
214779
+ border: "#3a3732",
214780
+ text: "#e8e4dd",
214781
+ textMuted: "#8a8580",
214782
+ accent: "#d49708"
214783
214783
  };
214784
214784
  CASCADE_MECHANISM_LABELS = {
214785
214785
  data_contamination: "\u6570\u636E\u6C61\u67D3",
@@ -220764,6 +220764,7 @@ import { createServer, request as httpRequest2 } from "node:http";
220764
220764
  import { readFileSync as readFileSync23, existsSync as existsSync25 } from "node:fs";
220765
220765
  import { join as join28, extname as extname2, resolve as resolve10 } from "node:path";
220766
220766
  import { fileURLToPath as fileURLToPath3 } from "node:url";
220767
+ import { streamSimple as streamSimple3 } from "@mariozechner/pi-ai";
220767
220768
  function extractImagesFromToolResult(result) {
220768
220769
  if (!result || typeof result !== "object") return void 0;
220769
220770
  const r = result;
@@ -220816,6 +220817,7 @@ var init_web_backend = __esm({
220816
220817
  // Context window broadcast throttling
220817
220818
  contextWindowThrottleTimer = null;
220818
220819
  contextWindowThrottlePending = false;
220820
+ lastArtifactHtml = "";
220819
220821
  isAssistantTurn = false;
220820
220822
  constructor(options) {
220821
220823
  this.port = options.port;
@@ -221034,6 +221036,7 @@ var init_web_backend = __esm({
221034
221036
  projectPath: s.projectPath || "",
221035
221037
  preview: s.preview || "",
221036
221038
  totalActiveMs: s.id === currentId2 ? sm2.getTotalActiveMs() : s.totalActiveMs ?? 0,
221039
+ contentHash: s.contentHash ?? "",
221037
221040
  pendingPermission: s.pendingPermission || void 0
221038
221041
  }))
221039
221042
  });
@@ -221355,6 +221358,10 @@ var init_web_backend = __esm({
221355
221358
  client.send({ type: "file_list_result", prefix: cmd.prefix, items });
221356
221359
  break;
221357
221360
  }
221361
+ case "artifact": {
221362
+ await this.handleArtifact(client, cmd);
221363
+ break;
221364
+ }
221358
221365
  case "mcp": {
221359
221366
  this.handleMcp(client, cmd);
221360
221367
  break;
@@ -221412,6 +221419,7 @@ var init_web_backend = __esm({
221412
221419
  projectPath: s.projectPath || "",
221413
221420
  preview: s.preview || "",
221414
221421
  totalActiveMs: s.id === currentId ? sessionManager.getTotalActiveMs() : s.totalActiveMs ?? 0,
221422
+ contentHash: s.contentHash ?? "",
221415
221423
  pendingPermission: s.pendingPermission || void 0
221416
221424
  }))
221417
221425
  });
@@ -221442,6 +221450,7 @@ var init_web_backend = __esm({
221442
221450
  projectPath: s.projectPath || "",
221443
221451
  preview: s.preview || "",
221444
221452
  totalActiveMs: s.id === currentId ? sm.getTotalActiveMs() : s.totalActiveMs ?? 0,
221453
+ contentHash: s.contentHash ?? "",
221445
221454
  pendingPermission: s.pendingPermission || void 0
221446
221455
  }))
221447
221456
  });
@@ -221549,7 +221558,8 @@ var init_web_backend = __esm({
221549
221558
  modelId: s.modelId,
221550
221559
  projectPath: s.projectPath || "",
221551
221560
  preview: s.preview || "",
221552
- totalActiveMs: s.totalActiveMs ?? 0
221561
+ totalActiveMs: s.totalActiveMs ?? 0,
221562
+ contentHash: s.contentHash ?? ""
221553
221563
  }))
221554
221564
  });
221555
221565
  }
@@ -221583,7 +221593,8 @@ var init_web_backend = __esm({
221583
221593
  modelId: s.modelId,
221584
221594
  projectPath: s.projectPath || "",
221585
221595
  preview: s.preview || "",
221586
- totalActiveMs: s.totalActiveMs ?? 0
221596
+ totalActiveMs: s.totalActiveMs ?? 0,
221597
+ contentHash: s.contentHash ?? ""
221587
221598
  }));
221588
221599
  client.send({ type: "sessions", data });
221589
221600
  break;
@@ -221609,7 +221620,8 @@ var init_web_backend = __esm({
221609
221620
  modelId: s.modelId,
221610
221621
  projectPath: s.projectPath || "",
221611
221622
  preview: s.preview || "",
221612
- totalActiveMs: s.totalActiveMs ?? 0
221623
+ totalActiveMs: s.totalActiveMs ?? 0,
221624
+ contentHash: s.contentHash ?? ""
221613
221625
  }))
221614
221626
  });
221615
221627
  break;
@@ -221738,7 +221750,8 @@ ${matchList}` });
221738
221750
  modelId: s.modelId,
221739
221751
  projectPath: s.projectPath || "",
221740
221752
  preview: s.preview || "",
221741
- totalActiveMs: s.totalActiveMs ?? 0
221753
+ totalActiveMs: s.totalActiveMs ?? 0,
221754
+ contentHash: s.contentHash ?? ""
221742
221755
  }))
221743
221756
  });
221744
221757
  break;
@@ -221939,6 +221952,224 @@ ${matchList}` });
221939
221952
  res.end("Not Found");
221940
221953
  }
221941
221954
  }
221955
+ async handleArtifact(client, cmd) {
221956
+ try {
221957
+ let styleConstraints = "";
221958
+ try {
221959
+ const skillPath = join28(this.config.projectPath, ".dscode", "html_output_skill");
221960
+ if (existsSync25(skillPath)) {
221961
+ styleConstraints = readFileSync23(skillPath, "utf-8");
221962
+ }
221963
+ } catch {
221964
+ }
221965
+ const designColors = `DESIGN SYSTEM COLORS (MUST use these exact hex values for EVERY color in your HTML):
221966
+ - Background: #f8f7f5
221967
+ - Surface: #f3f2ef
221968
+ - Border: #e6e4e0
221969
+ - Text: #2d2a26
221970
+ - Muted Text: #8a8580
221971
+ - Accent: #ca8a04 (warm amber/gold \u2014 use for highlights, progress bars, headings)
221972
+ - Success: #347539 text on #edf4ed background
221973
+ - Error: #9f2f2d text on #fdebec background
221974
+ - Warning: #956400 text on #fbf3db background
221975
+ - User Bubble: #ca8a04 background, #ffffff text`;
221976
+ const defaultStyleConstraints = `CRITICAL STYLE RULES:
221977
+ - Use the exact hex colors from DESIGN SYSTEM COLORS above \u2014 do NOT substitute with other greens, reds, or blues
221978
+ - Flat design: 1px solid var(--border) borders, no box-shadow, no gradients
221979
+ - Border-radius: 8px for cards/panels, 6px for buttons, 12px for large containers
221980
+ - Typography: system-ui, -apple-system, sans-serif for labels; monospace for data values
221981
+ - Emoji for visual markers, CSS conic-gradient or inline SVG for chart-like elements
221982
+ - Background MUST be #f8f7f5, never white (#fff) or transparent
221983
+ - Text MUST be #2d2a26, never pure black (#000) or cold gray
221984
+ - The accent color MUST be #ca8a04 (warm amber), never blue`;
221985
+ const systemPrompt = `You are an expert HTML dashboard designer. Generate a single, self-contained HTML file.
221986
+
221987
+ CRITICAL RULES:
221988
+ - Output ONLY valid HTML starting with <!DOCTYPE html>
221989
+ - All CSS MUST be inlined in <style> tags within <head>
221990
+ - NO external resources (fonts, images, scripts, CDN links)
221991
+ - Use system-ui, -apple-system, sans-serif for labels and monospace for data values
221992
+ - Use emoji icons for visual markers
221993
+ - Use CSS conic-gradient or inline SVG for chart-like elements
221994
+ - Make it visually rich and data-dense
221995
+ - Self-contained, single HTML document
221996
+
221997
+ ${designColors}
221998
+
221999
+ ${styleConstraints ? `ARTIFACT STYLE RULES (from .dscode/html_output_skill):
222000
+ ${styleConstraints}
222001
+ ` : defaultStyleConstraints}
222002
+
222003
+ Respond ONLY with the raw HTML starting with <!DOCTYPE html>. DO NOT wrap the output in markdown code fences (no \`\`\`html). DO NOT add any explanatory text before or after the HTML. Just output the HTML directly.`;
222004
+ let sessionSummary = "";
222005
+ if (cmd.context === "session_dashboard" || cmd.action === "generate") {
222006
+ sessionSummary = this.buildSessionSummary();
222007
+ }
222008
+ let userPrompt;
222009
+ if (cmd.action === "generate") {
222010
+ userPrompt = `Create a rich visual dashboard for this coding session. Use the data below to build a comprehensive, beautiful dashboard:
222011
+
222012
+ ${sessionSummary}
222013
+
222014
+ Make it visually stunning with emojis, progress bars, color-coded metrics, and CSS charts.`;
222015
+ } else {
222016
+ const existingHtml = this.lastArtifactHtml || "";
222017
+ userPrompt = `Here is the current dashboard HTML:
222018
+
222019
+ ${existingHtml.slice(0, 5e3)}
222020
+
222021
+ User instruction: ${cmd.instruction || "update the dashboard"}
222022
+
222023
+ Modify the HTML to fulfill the user's request. Output the complete modified HTML.`;
222024
+ }
222025
+ const model = resolveModel(this.config.provider, this.config.modelId);
222026
+ client.send({ type: "artifact_start" });
222027
+ let fullHtml = "";
222028
+ const stream = streamSimple3(model, {
222029
+ systemPrompt,
222030
+ messages: [{ role: "user", content: userPrompt, timestamp: Date.now() }]
222031
+ }, {
222032
+ apiKey: this.config.apiKey,
222033
+ maxTokens: this.config.maxTokens,
222034
+ timeoutMs: 12e4,
222035
+ maxRetries: 1
222036
+ });
222037
+ for await (const event of stream) {
222038
+ if (event.type === "text_delta") {
222039
+ fullHtml += event.delta;
222040
+ client.send({ type: "artifact_delta", delta: event.delta });
222041
+ } else if (event.type === "error") {
222042
+ client.send({ type: "error", text: `Artifact generation error: ${event.errorMessage || "Unknown error"}` });
222043
+ }
222044
+ }
222045
+ fullHtml = this.stripArtifactFences(fullHtml);
222046
+ this.lastArtifactHtml = fullHtml;
222047
+ client.send({ type: "artifact_end" });
222048
+ } catch (err) {
222049
+ client.send({ type: "artifact_end" });
222050
+ client.send({
222051
+ type: "error",
222052
+ text: `Artifact generation failed: ${err instanceof Error ? err.message : String(err)}`
222053
+ });
222054
+ }
222055
+ }
222056
+ stripArtifactFences(html) {
222057
+ let result = html.trim();
222058
+ const fencePattern = /^```(?:html)?\s*\n/;
222059
+ result = result.replace(fencePattern, "");
222060
+ const endFence = /\n```\s*$/;
222061
+ result = result.replace(endFence, "");
222062
+ if (!result.trimStart().startsWith("<")) {
222063
+ return html;
222064
+ }
222065
+ return result;
222066
+ }
222067
+ buildSessionSummary() {
222068
+ const sm = this.harness.sessionManager;
222069
+ const agent = this.harness.agent;
222070
+ const cm = this.harness.contextManager;
222071
+ const messages = agent.state.messages;
222072
+ const tools = this.currentAssistant?.tools ?? [];
222073
+ const breakdown = cm.getCategoryBreakdown(messages, tools, this.getSkillToolNames());
222074
+ const totalTokens = breakdown.total;
222075
+ const usedTokens = breakdown.used;
222076
+ const freeTokens = breakdown.free;
222077
+ const usagePercent = totalTokens > 0 ? Math.round(usedTokens / totalTokens * 100) : 0;
222078
+ const categories = breakdown.categories;
222079
+ const toolStats = /* @__PURE__ */ new Map();
222080
+ for (const msg of messages) {
222081
+ const m = msg;
222082
+ if (m.role === "assistant" && Array.isArray(m.content)) {
222083
+ for (const block of m.content) {
222084
+ if (block.type === "toolCall") {
222085
+ const name = block.name || "unknown";
222086
+ const existing = toolStats.get(name) || { calls: 0, errors: 0 };
222087
+ existing.calls++;
222088
+ toolStats.set(name, existing);
222089
+ }
222090
+ }
222091
+ }
222092
+ if (m.role === "toolResult" && m.isError) {
222093
+ const name = m.toolName || "unknown";
222094
+ const existing = toolStats.get(name);
222095
+ if (existing) {
222096
+ existing.errors++;
222097
+ } else {
222098
+ toolStats.set(name, { calls: 0, errors: 1 });
222099
+ }
222100
+ }
222101
+ }
222102
+ const toolEntries = Array.from(toolStats.entries()).map(([name, stats]) => ({
222103
+ name,
222104
+ callCount: stats.calls,
222105
+ errorCount: stats.errors,
222106
+ successCount: stats.calls - stats.errors,
222107
+ successRate: stats.calls > 0 ? Math.round((stats.calls - stats.errors) / stats.calls * 100) : 100
222108
+ })).sort((a, b) => b.callCount - a.callCount);
222109
+ const totalToolCalls = toolEntries.reduce((sum, t) => sum + t.callCount, 0);
222110
+ const totalToolErrors = toolEntries.reduce((sum, t) => sum + t.errorCount, 0);
222111
+ const sessionActiveMs = sm?.getTotalActiveMs?.() ?? 0;
222112
+ const turnCount = messages.filter((m) => m.role === "user").length;
222113
+ const avgTurnMs = turnCount > 0 ? Math.round(sessionActiveMs / turnCount) : 0;
222114
+ const pressureScore = totalTokens > 0 ? Math.round(usedTokens / totalTokens * 100) : 0;
222115
+ let pressureLabel = "low";
222116
+ if (pressureScore > 95) pressureLabel = "critical";
222117
+ else if (pressureScore > 80) pressureLabel = "high";
222118
+ else if (pressureScore > 50) pressureLabel = "moderate";
222119
+ const topTools = toolEntries.slice(0, 3).map((t) => ({
222120
+ name: t.name,
222121
+ successRate: t.successRate,
222122
+ warning: t.errorCount > 0 && t.errorCount / t.callCount > 0.2 ? "\u26A0\uFE0F" : ""
222123
+ }));
222124
+ function formatMs(ms) {
222125
+ if (ms < 1e3) return `${ms}ms`;
222126
+ const sec = Math.floor(ms / 1e3);
222127
+ if (sec < 60) return `${sec}s`;
222128
+ const min = Math.floor(sec / 60);
222129
+ const remainSec = sec % 60;
222130
+ if (min < 60) return `${min}m ${remainSec}s`;
222131
+ const hrs = Math.floor(min / 60);
222132
+ const remainMin = min % 60;
222133
+ return `${hrs}h ${remainMin}m`;
222134
+ }
222135
+ return JSON.stringify({
222136
+ tokenUsage: {
222137
+ total: totalTokens,
222138
+ used: usedTokens,
222139
+ free: freeTokens,
222140
+ usagePercent,
222141
+ categories: {
222142
+ system: categories.system ?? null,
222143
+ rules: categories.rules ?? null,
222144
+ user: categories.user ?? null,
222145
+ thinking: categories.thinking ?? null,
222146
+ readwrite: categories.readwrite ?? null,
222147
+ edit: categories.edit ?? null,
222148
+ shell: categories.shell ?? null,
222149
+ skill: categories.skill ?? null,
222150
+ mcp: categories.mcp ?? null,
222151
+ other: categories.other ?? null
222152
+ }
222153
+ },
222154
+ toolStatistics: {
222155
+ tools: toolEntries,
222156
+ totalToolsCalled: totalToolCalls,
222157
+ totalErrors: totalToolErrors
222158
+ },
222159
+ timing: {
222160
+ sessionActiveMs,
222161
+ sessionActiveFormatted: formatMs(sessionActiveMs),
222162
+ turnCount,
222163
+ avgTurnMs,
222164
+ avgTurnFormatted: formatMs(avgTurnMs)
222165
+ },
222166
+ contextHealth: {
222167
+ pressureScore,
222168
+ pressureLabel
222169
+ },
222170
+ topTools
222171
+ }, null, 2);
222172
+ }
221942
222173
  };
221943
222174
  }
221944
222175
  });
@@ -222181,6 +222412,7 @@ var SessionStore = class {
222181
222412
 
222182
222413
  // src/session/manager.ts
222183
222414
  import { existsSync as existsSync5, readFileSync as readFileSync4, readdirSync as readdirSync2 } from "node:fs";
222415
+ import { createHash as createHash3 } from "node:crypto";
222184
222416
  import { join as join5 } from "node:path";
222185
222417
  function ulid() {
222186
222418
  const t = Date.now().toString(36).padStart(10, "0");
@@ -222274,6 +222506,7 @@ var SessionManager = class {
222274
222506
  preview: "",
222275
222507
  hasImages: false,
222276
222508
  imageCount: 0,
222509
+ contentHash: "",
222277
222510
  totalActiveMs: 0
222278
222511
  };
222279
222512
  this.accumulatedMs = 0;
@@ -222330,6 +222563,8 @@ var SessionManager = class {
222330
222563
  if (!this.current.preview) {
222331
222564
  this.current.preview = extractFirstUserMessage(messages);
222332
222565
  }
222566
+ const contentHash2 = createHash3("sha256").update(messages.map((m) => `${m.role}:${String(m.content ?? "").slice(0, 200)}`).join("|")).digest("hex").slice(0, 12);
222567
+ this.current.contentHash = contentHash2;
222333
222568
  this.current.projectPath = this.projectPath;
222334
222569
  let totalImages = 0;
222335
222570
  let hasImages = false;
@@ -222651,14 +222886,14 @@ var ContextManager = class {
222651
222886
 
222652
222887
  // src/memory/store.ts
222653
222888
  import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
222654
- import { createHash as createHash3 } from "node:crypto";
222889
+ import { createHash as createHash4 } from "node:crypto";
222655
222890
  import { join as join6 } from "node:path";
222656
222891
  var MemoryStore = class {
222657
222892
  dataDir;
222658
222893
  projectHash;
222659
222894
  constructor(dataDir, projectPath) {
222660
222895
  this.dataDir = join6(dataDir, "memory");
222661
- this.projectHash = createHash3("sha256").update(projectPath).digest("hex").slice(0, 12);
222896
+ this.projectHash = createHash4("sha256").update(projectPath).digest("hex").slice(0, 12);
222662
222897
  if (!existsSync6(this.dataDir)) {
222663
222898
  mkdirSync5(this.dataDir, { recursive: true });
222664
222899
  }
@@ -222773,16 +223008,16 @@ import { join as join9, resolve as resolve2, dirname as dirname2 } from "node:pa
222773
223008
  import { Type as Type2 } from "@mariozechner/pi-ai";
222774
223009
 
222775
223010
  // src/drivers/edit/hash.ts
222776
- import { createHash as createHash4 } from "node:crypto";
223011
+ import { createHash as createHash5 } from "node:crypto";
222777
223012
  import { Type } from "@mariozechner/pi-ai";
222778
223013
  function computeLineHash(line) {
222779
- return createHash4("md5").update(line.trim()).digest("hex").slice(0, 6);
223014
+ return createHash5("md5").update(line.trim()).digest("hex").slice(0, 6);
222780
223015
  }
222781
223016
  function computeResolutionHash(line) {
222782
- return createHash4("md5").update(line.trim()).digest("hex").slice(0, 8);
223017
+ return createHash5("md5").update(line.trim()).digest("hex").slice(0, 8);
222783
223018
  }
222784
223019
  function computeFileVersion(content) {
222785
- return "fv_" + createHash4("sha256").update(content).digest("hex").slice(0, 8);
223020
+ return "fv_" + createHash5("sha256").update(content).digest("hex").slice(0, 8);
222786
223021
  }
222787
223022
  function hashLines(lines) {
222788
223023
  const resolutionMap = /* @__PURE__ */ new Map();
@@ -222835,7 +223070,7 @@ function computeContextHash(lines, lineIdx) {
222835
223070
  const prev = findNonEmptyBefore(lines, lineIdx);
222836
223071
  const curr = lines[lineIdx].trim();
222837
223072
  const next = findNonEmptyAfter(lines, lineIdx);
222838
- return createHash4("md5").update(`${prev}
223073
+ return createHash5("md5").update(`${prev}
222839
223074
  ${curr}
222840
223075
  ${next}`).digest("hex").slice(0, 8);
222841
223076
  }
@@ -223434,7 +223669,7 @@ function generateLocalDiff(oldLines, newLines, ops, ctx) {
223434
223669
  // src/checkpoint/index.ts
223435
223670
  import { homedir as homedir4 } from "node:os";
223436
223671
  import { join as join8 } from "node:path";
223437
- import { createHash as createHash5 } from "node:crypto";
223672
+ import { createHash as createHash6 } from "node:crypto";
223438
223673
 
223439
223674
  // src/checkpoint/checkpoint-manager.ts
223440
223675
  import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, unlinkSync as unlinkSync2 } from "node:fs";
@@ -223740,7 +223975,7 @@ var _snapshotStore = null;
223740
223975
  var _checkpointManager = null;
223741
223976
  var _fileWriteTracker = null;
223742
223977
  function initCheckpointSystem(projectPath, sessionId) {
223743
- const projectHash = createHash5("sha256").update(projectPath).digest("hex").slice(0, 12);
223978
+ const projectHash = createHash6("sha256").update(projectPath).digest("hex").slice(0, 12);
223744
223979
  const baseDir = join8(homedir4(), ".dscode", "checkpoints", "per-project", projectHash, sessionId);
223745
223980
  _snapshotStore = new SnapshotStore();
223746
223981
  const store = new FileSystemCheckpointStore(baseDir);
@@ -224343,7 +224578,7 @@ import { resolve as resolve4 } from "node:path";
224343
224578
  import { Type as Type5 } from "@mariozechner/pi-ai";
224344
224579
 
224345
224580
  // src/drivers/edit/recovery.ts
224346
- import { createHash as createHash6 } from "node:crypto";
224581
+ import { createHash as createHash7 } from "node:crypto";
224347
224582
  function recoverBySnapshot(snapshotLines, currentLines, operations, snapshotCtx) {
224348
224583
  let expectedLines;
224349
224584
  try {
@@ -224523,7 +224758,7 @@ function computeContextHash2(lines, lineIdx) {
224523
224758
  const prev = findNonEmptyBefore2(lines, lineIdx);
224524
224759
  const curr = lines[lineIdx].trim();
224525
224760
  const next = findNonEmptyAfter2(lines, lineIdx);
224526
- return createHash6("md5").update(`${prev}
224761
+ return createHash7("md5").update(`${prev}
224527
224762
  ${curr}
224528
224763
  ${next}`).digest("hex").slice(0, 8);
224529
224764
  }
@@ -228442,6 +228677,7 @@ var ConversationView = class {
228442
228677
  this.thinkingBuffer = "";
228443
228678
  this.toolEntries = [];
228444
228679
  this.renderedToolCount = 0;
228680
+ this.tui.requestRender(true);
228445
228681
  }
228446
228682
  thinkingDelta(delta) {
228447
228683
  this.thinkingBuffer += delta;
@@ -228793,7 +229029,7 @@ var ConversationView = class {
228793
229029
  this.box.addChild(permText);
228794
229030
  this.liveComponents.push(permText);
228795
229031
  }
228796
- this.tui.requestRender(true);
229032
+ this.tui.requestRender(false);
228797
229033
  }
228798
229034
  };
228799
229035
 
@@ -228991,6 +229227,23 @@ var HybridAutocompleteProvider = class {
228991
229227
  return true;
228992
229228
  }
228993
229229
  };
229230
+ function formatTokens(n) {
229231
+ if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
229232
+ if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
229233
+ return String(n);
229234
+ }
229235
+ function formatContextPercent(estimated, contextWindow) {
229236
+ if (contextWindow <= 0 || !isFinite(estimated) || Number.isNaN(estimated)) return null;
229237
+ const pct = Math.round(estimated / contextWindow * 100);
229238
+ const text = `\u2593\u2593 ${pct}%`;
229239
+ if (pct > 95) return c.red(text);
229240
+ if (pct > 80) return c.yellow(text);
229241
+ return c.dim(text);
229242
+ }
229243
+ function formatCost(total) {
229244
+ if (total >= 0.01) return `$${total.toFixed(4)}`;
229245
+ return `\xA2${(total * 100).toFixed(2)}`;
229246
+ }
228994
229247
  var TuiApp = class {
228995
229248
  deps;
228996
229249
  terminal;
@@ -229660,13 +229913,27 @@ var TuiApp = class {
229660
229913
  this.markActivity();
229661
229914
  this.conversation.toolEnd(name, result, isError);
229662
229915
  }
229663
- finishAssistantMessage() {
229916
+ finishAssistantMessage(usage) {
229664
229917
  this.finalizeIdleSegment();
229665
229918
  this.conversation.finishAssistantMessage();
229919
+ const parts = [];
229666
229920
  if (this.totalWaitMs >= 1e3) {
229667
- this.conversation.addInfo(
229668
- c.dim(`\u23F1 total wait: ${this.formatElapsed(this.totalWaitMs)} (${this.waitSegments.length} segment${this.waitSegments.length > 1 ? "s" : ""})`)
229669
- );
229921
+ parts.push(`\u23F1 ${this.formatElapsed(this.totalWaitMs)}`);
229922
+ }
229923
+ if (usage && usage.input > 0 && usage.output > 0) {
229924
+ parts.push(`\u{1F4CA} ${formatTokens(usage.input)}\u2193 ${formatTokens(usage.output)}\u2191`);
229925
+ }
229926
+ {
229927
+ const estimatedTokens = this.deps.contextManager.getEstimatedTokens(this.deps.agent.state.messages);
229928
+ const contextWindow = this.deps.contextManager.getContextWindow();
229929
+ const ctxStr = formatContextPercent(estimatedTokens, contextWindow);
229930
+ if (ctxStr) parts.push(ctxStr);
229931
+ }
229932
+ if (usage && usage.cost.total > 0) {
229933
+ parts.push(`\u{1F4B0} ${formatCost(usage.cost.total)}`);
229934
+ }
229935
+ if (parts.length > 0) {
229936
+ this.conversation.addInfo(c.dim(parts.join(" \xB7 ")));
229670
229937
  }
229671
229938
  }
229672
229939
  addInfo(text, _display) {
@@ -229950,8 +230217,8 @@ var TuiBackend = class {
229950
230217
  deps.events.on("turn:streaming:start", () => {
229951
230218
  this.tui.startAssistantMessage();
229952
230219
  });
229953
- deps.events.on("turn:end", () => {
229954
- this.tui.finishAssistantMessage();
230220
+ deps.events.on("turn:end", (e) => {
230221
+ this.tui.finishAssistantMessage(e.usage);
229955
230222
  });
229956
230223
  deps.events.on("message:user", (e) => {
229957
230224
  this.tui.addUserMessage(e.text);
@@ -231199,7 +231466,8 @@ You have a \`skill\` tool available. When you decide to use a skill from the lis
231199
231466
  if (event.type === "turn_end") {
231200
231467
  this.sessionManager.trySaveSession(this.agent);
231201
231468
  const turnEndMsg = event.message;
231202
- this.events.emit({ type: "turn:end", stopReason: turnEndMsg?.stopReason, usage: turnEndMsg?.usage });
231469
+ const rawUsage = turnEndMsg?.usage;
231470
+ this.events.emit({ type: "turn:end", stopReason: turnEndMsg?.stopReason, usage: rawUsage ? { input: rawUsage.input, output: rawUsage.output, cacheRead: rawUsage.cacheRead, cacheWrite: rawUsage.cacheWrite, total: rawUsage.totalTokens, cost: { total: rawUsage.cost.total } } : void 0 });
231203
231471
  const msg = turnEndMsg;
231204
231472
  if (msg?.stopReason === "length") {
231205
231473
  this.events.emit({ type: "ui:info", text: "Output truncated (hit max_tokens). Continue from where you left off." });
@@ -0,0 +1 @@
1
+ *,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Geist Sans,system-ui,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:Geist Mono,JetBrains Mono,Fira Code,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}*{scrollbar-width:thin;scrollbar-color:var(--color-border) transparent;transition:background-color .3s ease,border-color .3s ease,color .3s ease}*::-webkit-scrollbar{width:6px}*::-webkit-scrollbar-track{background:transparent}*::-webkit-scrollbar-thumb{background-color:var(--color-border);border-radius:3px}body{font-family:Geist Sans,system-ui,sans-serif;background-color:var(--color-bg);color:var(--color-text)}.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.btn{padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem;font-weight:500;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.btn:focus{outline:2px solid transparent;outline-offset:2px}.btn:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-offset-width: 1px}.btn:disabled{cursor:not-allowed;opacity:.5}.btn{border-radius:6px;border:1px solid transparent}.btn-primary{padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem;font-weight:500;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.btn-primary:focus{outline:2px solid transparent;outline-offset:2px}.btn-primary:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-offset-width: 1px}.btn-primary:disabled{cursor:not-allowed;opacity:.5}.btn-primary{border-radius:6px;border:1px solid transparent;background-color:var(--color-accent);color:#fff;border-color:var(--color-accent)}.btn-primary:hover{background-color:var(--color-accent-hover);border-color:var(--color-accent-hover)}.btn-secondary{padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem;font-weight:500;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.btn-secondary:focus{outline:2px solid transparent;outline-offset:2px}.btn-secondary:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-offset-width: 1px}.btn-secondary:disabled{cursor:not-allowed;opacity:.5}.btn-secondary{border-radius:6px;border:1px solid transparent;background-color:var(--color-surface);color:var(--color-text);border-color:var(--color-border)}.btn-secondary:hover{background-color:var(--color-surface-hover)}.btn-danger{padding:.5rem 1rem;font-size:.875rem;line-height:1.25rem;font-weight:500;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.btn-danger:focus{outline:2px solid transparent;outline-offset:2px}.btn-danger:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000);--tw-ring-offset-width: 1px}.btn-danger:disabled{cursor:not-allowed;opacity:.5}.btn-danger{border-radius:6px;border:1px solid transparent;background-color:var(--color-error);color:var(--color-error-text);border-color:var(--color-error-text)}.btn-danger:hover{filter:brightness(.95)}.input{width:100%;padding:.625rem 1rem;font-size:.875rem;line-height:1.25rem;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.2s}.input::-moz-placeholder{color:var(--color-text-muted)}.input::placeholder{color:var(--color-text-muted)}.input:focus{outline:2px solid transparent;outline-offset:2px}.input{border-radius:12px;border:1px solid var(--color-border);background-color:var(--color-surface);color:var(--color-text)}.input:focus{border-color:var(--color-accent)}.card{border-radius:8px;border:1px solid var(--color-border);background-color:var(--color-surface);padding:1rem}.tool-card{border-radius:8px;border:1px solid var(--color-border);background-color:var(--color-surface);padding:1rem;margin-bottom:.5rem}.resize-handle{position:absolute;right:0;top:0;bottom:0;width:4px;cursor:col-resize;z-index:10;transition:background-color .15s ease}.resize-handle:hover{background-color:var(--color-border)}.resize-handle:active{background-color:var(--color-accent)}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.inset-y-0{top:0;bottom:0}.-right-1\.5{right:-.375rem}.-top-1\.5{top:-.375rem}.bottom-full{bottom:100%}.left-0{left:0}.left-1\/2{left:50%}.left-4{left:1rem}.right-4{right:1rem}.top-4{top:1rem}.top-full{top:100%}.z-40{z-index:40}.z-50{z-index:50}.z-\[100\]{z-index:100}.mx-auto{margin-left:auto;margin-right:auto}.my-2{margin-top:.5rem;margin-bottom:.5rem}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-auto{margin-left:auto}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-1\.5{margin-top:.375rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.table-cell{display:table-cell}.hidden{display:none}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-screen{height:100vh}.max-h-24{max-height:6rem}.max-h-32{max-height:8rem}.max-h-40{max-height:10rem}.max-h-48{max-height:12rem}.max-h-60{max-height:15rem}.max-h-\[200px\]{max-height:200px}.max-h-\[300px\]{max-height:300px}.max-h-\[400px\]{max-height:400px}.max-h-\[70vh\]{max-height:70vh}.min-h-0{min-height:0px}.min-h-\[40px\]{min-height:40px}.w-1\.5{width:.375rem}.w-10{width:2.5rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-5{width:1.25rem}.w-72{width:18rem}.w-80{width:20rem}.w-full{width:100%}.min-w-0{min-width:0px}.min-w-full{min-width:100%}.max-w-4xl{max-width:56rem}.max-w-\[200px\]{max-width:200px}.max-w-\[300px\]{max-width:300px}.max-w-\[85\%\]{max-width:85%}.max-w-full{max-width:100%}.max-w-md{max-width:28rem}.max-w-none{max-width:none}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-translate-x-1\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-full{--tw-translate-x: -100%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes bounce{0%,to{transform:translateY(-25%);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;animation-timing-function:cubic-bezier(0,0,.2,1)}}.animate-bounce{animation:bounce 1s infinite}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.animate-slide-in{animation:slide-in .3s ease-out forwards}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-btn{border-radius:6px}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-sm{border-radius:.125rem}.border{border-width:1px}.border-0{border-width:0px}.border-b{border-bottom-width:1px}.bg-black\/30{background-color:#0000004d}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pl-3{padding-left:.75rem}.pt-16{padding-top:4rem}.pt-2{padding-top:.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:Geist Mono,JetBrains Mono,Fira Code,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\[10px\]{font-size:10px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.capitalize{text-transform:capitalize}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.underline{text-decoration-line:underline}.\!opacity-0{opacity:0!important}.opacity-0{opacity:0}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline{outline-style:solid}.ring{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-\[filter\]{transition-property:filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}:root{--color-bg: #f8f7f5;--color-surface: #f3f2ef;--color-surface-hover: #ebe9e5;--color-border: #e6e4e0;--color-text: #2d2a26;--color-text-muted: #8a8580;--color-accent: #ca8a04;--color-accent-hover: #a07004;--color-accent-bg: rgba(202, 138, 4, .12);--color-success: #edf4ed;--color-success-text: #347539;--color-error: #fdebec;--color-error-text: #9f2f2d;--color-warning: #fbf3db;--color-warning-text: #956400;--color-user-bubble: #ca8a04;--color-user-bubble-text: #ffffff;--cw-system: #8a8580;--cw-rules: #a0908b;--cw-user: #ca8a04;--cw-thinking: #a78bfa;--cw-readwrite: #eab308;--cw-edit: #3b82f6;--cw-shell: #ef4444;--cw-skill: #059669;--cw-mcp: #8b5cf6;--cw-other: #6b7280}.dark{--color-bg: #1e1c19;--color-surface: #282622;--color-surface-hover: #322e2a;--color-border: #3a3732;--color-text: #e8e4dd;--color-text-muted: #8a8580;--cw-system: #8a8580;--cw-rules: #b0a09b;--cw-user: #d49708;--cw-thinking: #7c6bb0;--cw-readwrite: #ca8a04;--cw-edit: #60a5fa;--cw-shell: #f87171;--cw-skill: #10b981;--cw-mcp: #a78bfa;--cw-other: #9ca3af;--color-accent: #d49708;--color-accent-bg: rgba(212, 151, 8, .15);--color-accent-hover: #e5a50a;--color-success: #1a2e1a;--color-success-text: #5ca860;--color-error: #3a1a1a;--color-error-text: #e05553;--color-warning: #3a2e10;--color-warning-text: #d4a017;--color-user-bubble: #d49708;--color-user-bubble-text: #1e1c19}@media (max-width: 767px){.resize-handle{display:none}}@keyframes fade-up{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}@keyframes slide-in{0%{opacity:0;transform:translate(100%)}to{opacity:1;transform:translate(0)}}@keyframes spin{to{transform:rotate(360deg)}}.animate-fade-up{animation:fade-up .6s cubic-bezier(.16,1,.3,1) forwards}.animate-slide-in{@keyframes view-fade-in{0%{opacity:0}to{opacity:1}}animation:slide-in .3s ease-out forwards}.animate-slide-in .view-fade-enter{animation:view-fade-in .3s ease-out forwards}@media (prefers-reduced-motion: reduce){.animate-fade-up,.animate-slide-in{animation:none}*{transition:none!important}}.hover\:opacity-70:hover{opacity:.7}.hover\:opacity-90:hover{opacity:.9}.hover\:brightness-95:hover{--tw-brightness: brightness(.95);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.group:hover .group-hover\:opacity-100{opacity:1}@media (min-width: 640px){.sm\:inline{display:inline}}@media (min-width: 768px){.md\:static{position:static}.md\:block{display:block}.md\:flex{display:flex}.md\:hidden{display:none}.md\:max-w-\[75\%\]{max-width:75%}.md\:translate-x-0{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}}