kimiflare 0.83.0 → 0.84.1

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/index.js CHANGED
@@ -3727,7 +3727,7 @@ function raceWithSignal(promise, signal) {
3727
3727
  async function runAgentTurn(opts2) {
3728
3728
  const turnStart = performance.now();
3729
3729
  logger.info("turn:start", { sessionId: opts2.sessionId, codeMode: opts2.codeMode ?? false });
3730
- const max = opts2.maxToolIterations ?? 50;
3730
+ const max = opts2.maxToolIterations ?? 200;
3731
3731
  const codeMode = opts2.codeMode ?? false;
3732
3732
  const fireStopHook = async () => {
3733
3733
  if (opts2.signal.aborted) return;
@@ -13999,6 +13999,301 @@ var init_deploy_commute = __esm({
13999
13999
  }
14000
14000
  });
14001
14001
 
14002
+ // src/util/image.ts
14003
+ import { readFile as readFile15 } from "fs/promises";
14004
+ import { basename as basename3 } from "path";
14005
+ async function encodeImageFile(filePath) {
14006
+ const buf = await readFile15(filePath);
14007
+ if (buf.byteLength > MAX_IMAGE_BYTES) {
14008
+ throw new Error(
14009
+ `image too large (${(buf.byteLength / 1024 / 1024).toFixed(1)} MB); max is ${MAX_IMAGE_BYTES / 1024 / 1024} MB`
14010
+ );
14011
+ }
14012
+ const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
14013
+ const mime = EXT_TO_MIME[ext] ?? "image/jpeg";
14014
+ const b64 = buf.toString("base64");
14015
+ return {
14016
+ filename: basename3(filePath),
14017
+ mime,
14018
+ dataUrl: `data:${mime};base64,${b64}`
14019
+ };
14020
+ }
14021
+ function isImagePath(path) {
14022
+ const ext = path.slice(path.lastIndexOf(".")).toLowerCase();
14023
+ return ext in EXT_TO_MIME;
14024
+ }
14025
+ var MAX_IMAGE_BYTES, EXT_TO_MIME;
14026
+ var init_image = __esm({
14027
+ "src/util/image.ts"() {
14028
+ "use strict";
14029
+ MAX_IMAGE_BYTES = 5 * 1024 * 1024;
14030
+ EXT_TO_MIME = {
14031
+ ".png": "image/png",
14032
+ ".jpg": "image/jpeg",
14033
+ ".jpeg": "image/jpeg",
14034
+ ".gif": "image/gif",
14035
+ ".webp": "image/webp",
14036
+ ".bmp": "image/bmp"
14037
+ };
14038
+ }
14039
+ });
14040
+
14041
+ // src/ui/app-helpers.ts
14042
+ import { execSync as execSync3, spawn as spawn5 } from "child_process";
14043
+ import { existsSync as existsSync4, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
14044
+ import { join as join24 } from "path";
14045
+ import { platform as platform3 } from "os";
14046
+ function buildFilePickerIgnoreList(cwd) {
14047
+ const hardcoded = [
14048
+ // Dependencies
14049
+ "**/node_modules/**",
14050
+ "**/vendor/**",
14051
+ "**/.bundle/**",
14052
+ "**/bower_components/**",
14053
+ // Version control
14054
+ "**/.git/**",
14055
+ "**/.svn/**",
14056
+ "**/.hg/**",
14057
+ // Build / output directories
14058
+ "**/dist/**",
14059
+ "**/build/**",
14060
+ "**/out/**",
14061
+ "**/public/**",
14062
+ "**/.next/**",
14063
+ "**/.nuxt/**",
14064
+ "**/.svelte-kit/**",
14065
+ "**/.vercel/**",
14066
+ "**/.netlify/**",
14067
+ "**/target/**",
14068
+ "**/bin/**",
14069
+ "**/obj/**",
14070
+ "**/Debug/**",
14071
+ "**/Release/**",
14072
+ "**/.gradle/**",
14073
+ // Caches
14074
+ "**/.cache/**",
14075
+ "**/.parcel-cache/**",
14076
+ "**/.turbo/**",
14077
+ "**/.eslintcache",
14078
+ "**/.stylelintcache",
14079
+ "**/.rpt2_cache/**",
14080
+ "**/.rts2_cache/**",
14081
+ // Temporary
14082
+ "**/tmp/**",
14083
+ "**/temp/**",
14084
+ "**/*.tmp",
14085
+ // Coverage
14086
+ "**/coverage/**",
14087
+ "**/.nyc_output/**",
14088
+ // OS files
14089
+ "**/.DS_Store",
14090
+ "**/Thumbs.db",
14091
+ // Logs
14092
+ "**/*.log",
14093
+ "**/logs/**",
14094
+ // Lock files (auto-generated, usually huge)
14095
+ "**/package-lock.json",
14096
+ "**/yarn.lock",
14097
+ "**/pnpm-lock.yaml",
14098
+ "**/bun.lockb",
14099
+ "**/Cargo.lock",
14100
+ "**/Gemfile.lock",
14101
+ "**/composer.lock",
14102
+ "**/Pipfile.lock",
14103
+ "**/poetry.lock",
14104
+ "**/go.sum",
14105
+ // Minified / source maps
14106
+ "**/*.min.js",
14107
+ "**/*.min.css",
14108
+ "**/*.map",
14109
+ // kimiflare internal
14110
+ "**/.kimiflare/**",
14111
+ // IDE (usually not relevant to mention)
14112
+ "**/.idea/**"
14113
+ ];
14114
+ const gitignorePatterns = [];
14115
+ try {
14116
+ const gitignorePath = join24(cwd, ".gitignore");
14117
+ const stats = statSync4(gitignorePath);
14118
+ if (stats.size > MAX_GITIGNORE_SIZE) {
14119
+ return hardcoded;
14120
+ }
14121
+ const content = readFileSync4(gitignorePath, "utf-8");
14122
+ for (const line of content.split(/\r?\n/)) {
14123
+ const trimmed = line.trim();
14124
+ if (!trimmed || trimmed.startsWith("#")) continue;
14125
+ if (trimmed.startsWith("!")) continue;
14126
+ let pattern = trimmed;
14127
+ const isAnchored = pattern.startsWith("/");
14128
+ const isDir = pattern.endsWith("/");
14129
+ if (isAnchored) pattern = pattern.slice(1);
14130
+ if (isDir) pattern = pattern.slice(0, -1);
14131
+ if (!pattern) continue;
14132
+ if (isAnchored) {
14133
+ gitignorePatterns.push(isDir ? pattern + "/**" : pattern);
14134
+ } else {
14135
+ gitignorePatterns.push(isDir ? "**/" + pattern + "/**" : "**/" + pattern);
14136
+ }
14137
+ }
14138
+ } catch {
14139
+ }
14140
+ return [...hardcoded, ...gitignorePatterns];
14141
+ }
14142
+ function gatewayFromConfig(cfg) {
14143
+ if (process.env.KIMIFLARE_DISABLE_AI_GATEWAY === "1") return void 0;
14144
+ if (!cfg.aiGatewayId) return void 0;
14145
+ return {
14146
+ id: cfg.aiGatewayId,
14147
+ cacheTtl: cfg.aiGatewayCacheTtl,
14148
+ skipCache: cfg.aiGatewaySkipCache,
14149
+ collectLogPayload: cfg.aiGatewayCollectLogPayload,
14150
+ metadata: cfg.aiGatewayMetadata
14151
+ };
14152
+ }
14153
+ function gatewayUsageLookupFromConfig(cfg, meta) {
14154
+ if (process.env.KIMIFLARE_DISABLE_AI_GATEWAY === "1") return void 0;
14155
+ if (!cfg.aiGatewayId || !meta) return void 0;
14156
+ return {
14157
+ accountId: cfg.accountId,
14158
+ apiToken: cfg.apiToken,
14159
+ gatewayId: cfg.aiGatewayId,
14160
+ meta
14161
+ };
14162
+ }
14163
+ function openBrowser(url) {
14164
+ const cmd = platform3() === "darwin" ? "open" : platform3() === "win32" ? "start" : "xdg-open";
14165
+ const child = spawn5(cmd, [url], { detached: true, stdio: "ignore" });
14166
+ child.unref();
14167
+ }
14168
+ function detectGitHubRepo(cachedRepo) {
14169
+ if (cachedRepo) {
14170
+ const parts = cachedRepo.split("/");
14171
+ if (parts.length === 2) return { owner: parts[0], name: parts[1] };
14172
+ }
14173
+ try {
14174
+ const remoteUrl = execSync3("git remote get-url origin", { cwd: process.cwd(), encoding: "utf8" }).trim();
14175
+ const httpsMatch = remoteUrl.match(/github\.com\/([^\/]+)\/([^\/]+?)(?:\.git)?$/);
14176
+ if (httpsMatch) return { owner: httpsMatch[1], name: httpsMatch[2] };
14177
+ const sshMatch = remoteUrl.match(/github\.com:([^\/]+)\/([^\/]+?)(?:\.git)?$/);
14178
+ if (sshMatch) return { owner: sshMatch[1], name: sshMatch[2] };
14179
+ } catch {
14180
+ }
14181
+ return null;
14182
+ }
14183
+ function detectGitBranch() {
14184
+ try {
14185
+ return execSync3("git branch --show-current", { cwd: process.cwd(), encoding: "utf8" }).trim() || null;
14186
+ } catch {
14187
+ return null;
14188
+ }
14189
+ }
14190
+ function formatTokens(n) {
14191
+ if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
14192
+ if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
14193
+ return String(n);
14194
+ }
14195
+ function trackRecentFile(ref, path, max = 10) {
14196
+ ref.current.set(path, Date.now());
14197
+ if (ref.current.size > max) {
14198
+ let oldest = null;
14199
+ let oldestTime = Infinity;
14200
+ for (const [p, t] of ref.current) {
14201
+ if (t < oldestTime) {
14202
+ oldestTime = t;
14203
+ oldest = p;
14204
+ }
14205
+ }
14206
+ if (oldest) ref.current.delete(oldest);
14207
+ }
14208
+ }
14209
+ function capEvents(prev) {
14210
+ if (prev.length <= MAX_EVENTS) return prev;
14211
+ return prev.slice(prev.length - MAX_EVENTS);
14212
+ }
14213
+ function compactEventsVisual(prev, keepLastTurns) {
14214
+ let seen = 0;
14215
+ let cutoff = -1;
14216
+ for (let i = prev.length - 1; i >= 0; i--) {
14217
+ if (prev[i].kind === "user") {
14218
+ seen++;
14219
+ if (seen === keepLastTurns + 1) {
14220
+ cutoff = i;
14221
+ break;
14222
+ }
14223
+ }
14224
+ }
14225
+ if (cutoff <= 0) return prev;
14226
+ const kept = prev.slice(cutoff);
14227
+ return [
14228
+ { kind: "info", key: mkKey(), text: `\xB7\xB7\xB7 ${cutoff} earlier messages compacted \xB7\xB7\xB7` },
14229
+ ...kept
14230
+ ];
14231
+ }
14232
+ function makePrefixMessages(cacheStable, model, mode, tools) {
14233
+ if (cacheStable) {
14234
+ return buildSystemMessages({ cwd: process.cwd(), tools, model, mode });
14235
+ }
14236
+ return [
14237
+ {
14238
+ role: "system",
14239
+ content: buildSystemPrompt({ cwd: process.cwd(), tools, model, mode })
14240
+ }
14241
+ ];
14242
+ }
14243
+ function rebuildSystemPromptForMode(messages, cacheStable, model, mode, tools) {
14244
+ if (cacheStable) {
14245
+ const rebuilt = buildSystemMessages({ cwd: process.cwd(), tools, model, mode });
14246
+ messages[0] = rebuilt[0];
14247
+ if (rebuilt[1]) {
14248
+ messages[1] = rebuilt[1];
14249
+ }
14250
+ } else {
14251
+ messages[0] = {
14252
+ role: "system",
14253
+ content: buildSystemPrompt({ cwd: process.cwd(), tools, model, mode })
14254
+ };
14255
+ }
14256
+ }
14257
+ function findImagePaths(text) {
14258
+ const paths = [];
14259
+ const quotedRegex = /"([^"]+)"|'([^']+)'/g;
14260
+ let match;
14261
+ while ((match = quotedRegex.exec(text)) !== null) {
14262
+ const path = match[1] ?? match[2];
14263
+ if (path && isImagePath(path) && existsSync4(path)) {
14264
+ paths.push(path);
14265
+ }
14266
+ }
14267
+ const remaining = text.replace(/"[^"]+"|'[^']+'/g, "");
14268
+ const ESCAPED_SPACE = "\0";
14269
+ const processed = remaining.replace(/\\ /g, ESCAPED_SPACE);
14270
+ for (const token of processed.split(/\s+/)) {
14271
+ const clean = token.replace(new RegExp(ESCAPED_SPACE, "g"), " ").replace(/^["']|["',;:!?]$/g, "").replace(/[.,;:!?]$/, "");
14272
+ if (clean && isImagePath(clean) && existsSync4(clean) && !paths.includes(clean)) {
14273
+ paths.push(clean);
14274
+ }
14275
+ }
14276
+ return paths;
14277
+ }
14278
+ var MAX_GITIGNORE_SIZE, CONTEXT_LIMIT, AUTO_COMPACT_THRESHOLD, MAX_EVENTS, MAX_IMAGES_PER_MESSAGE, FEEDBACK_WORKER_URL, nextKey, mkKey, nextAssistantId, mkAssistantId;
14279
+ var init_app_helpers = __esm({
14280
+ "src/ui/app-helpers.ts"() {
14281
+ "use strict";
14282
+ init_system_prompt();
14283
+ init_image();
14284
+ MAX_GITIGNORE_SIZE = 1 * 1024 * 1024;
14285
+ CONTEXT_LIMIT = 262e3;
14286
+ AUTO_COMPACT_THRESHOLD = 0.8;
14287
+ MAX_EVENTS = 500;
14288
+ MAX_IMAGES_PER_MESSAGE = 10;
14289
+ FEEDBACK_WORKER_URL = "https://hello.kimiflare.com";
14290
+ nextKey = 1;
14291
+ mkKey = () => `evt_${nextKey++}`;
14292
+ nextAssistantId = 1;
14293
+ mkAssistantId = () => nextAssistantId++;
14294
+ }
14295
+ });
14296
+
14002
14297
  // src/commands/builtins.ts
14003
14298
  var BUILTIN_COMMANDS, BUILTIN_COMMAND_NAMES;
14004
14299
  var init_builtins = __esm({
@@ -15021,24 +15316,24 @@ var init_theme = __esm({
15021
15316
  });
15022
15317
 
15023
15318
  // src/util/clipboard.ts
15024
- import { execSync as execSync3 } from "child_process";
15025
- import { platform as platform3 } from "os";
15319
+ import { execSync as execSync4 } from "child_process";
15320
+ import { platform as platform4 } from "os";
15026
15321
  function writeToClipboard(text) {
15027
- const os2 = platform3();
15322
+ const os2 = platform4();
15028
15323
  try {
15029
15324
  if (os2 === "darwin") {
15030
- execSync3("pbcopy", { input: text, timeout: 5e3 });
15325
+ execSync4("pbcopy", { input: text, timeout: 5e3 });
15031
15326
  return { success: true, message: "Copied to clipboard" };
15032
15327
  }
15033
15328
  if (os2 === "win32") {
15034
- execSync3("clip", { input: text, timeout: 5e3 });
15329
+ execSync4("clip", { input: text, timeout: 5e3 });
15035
15330
  return { success: true, message: "Copied to clipboard" };
15036
15331
  }
15037
15332
  try {
15038
- execSync3("xclip -selection clipboard", { input: text, timeout: 5e3 });
15333
+ execSync4("xclip -selection clipboard", { input: text, timeout: 5e3 });
15039
15334
  return { success: true, message: "Copied to clipboard" };
15040
15335
  } catch {
15041
- execSync3("xsel --clipboard --input", { input: text, timeout: 5e3 });
15336
+ execSync4("xsel --clipboard --input", { input: text, timeout: 5e3 });
15042
15337
  return { success: true, message: "Copied to clipboard" };
15043
15338
  }
15044
15339
  } catch {
@@ -15359,8 +15654,8 @@ var init_frontmatter = __esm({
15359
15654
  });
15360
15655
 
15361
15656
  // src/skills/loader.ts
15362
- import { readFile as readFile15, readdir as readdir6, stat as stat6 } from "fs/promises";
15363
- import { join as join24, extname } from "path";
15657
+ import { readFile as readFile16, readdir as readdir6, stat as stat6 } from "fs/promises";
15658
+ import { join as join25, extname } from "path";
15364
15659
  function normalizeManifest(raw, filePath) {
15365
15660
  const name = typeof raw.name === "string" ? raw.name : "";
15366
15661
  const description = typeof raw.description === "string" ? raw.description : "";
@@ -15374,7 +15669,7 @@ function normalizeManifest(raw, filePath) {
15374
15669
  return { name, description, match, scope, priority, enabled };
15375
15670
  }
15376
15671
  async function loadSkillFile(filePath) {
15377
- const raw = await readFile15(filePath, "utf-8");
15672
+ const raw = await readFile16(filePath, "utf-8");
15378
15673
  const parsed = parseFrontmatter(raw);
15379
15674
  const manifest = normalizeManifest(parsed.data, filePath);
15380
15675
  const body = parsed.content.trim();
@@ -15396,7 +15691,7 @@ async function loadSkillsFromDir(dirPath) {
15396
15691
  const entries = await readdir6(dirPath);
15397
15692
  const files = [];
15398
15693
  for (const entry of entries) {
15399
- const full = join24(dirPath, entry);
15694
+ const full = join25(dirPath, entry);
15400
15695
  const s = await stat6(full);
15401
15696
  if (s.isFile() && extname(entry) === ".md") {
15402
15697
  files.push(full);
@@ -15424,12 +15719,12 @@ var init_loader = __esm({
15424
15719
  });
15425
15720
 
15426
15721
  // src/skills/manager.ts
15427
- import { mkdir as mkdir10, writeFile as writeFile11, unlink as unlink2, readFile as readFile16 } from "fs/promises";
15428
- import { join as join25 } from "path";
15722
+ import { mkdir as mkdir10, writeFile as writeFile11, unlink as unlink2, readFile as readFile17 } from "fs/promises";
15723
+ import { join as join26 } from "path";
15429
15724
  function getSkillDirs(cwd) {
15430
15725
  return {
15431
- projectDir: join25(cwd, ".kimiflare", "skills"),
15432
- globalDir: join25(process.env.HOME ?? "", ".config", "kimiflare", "skills")
15726
+ projectDir: join26(cwd, ".kimiflare", "skills"),
15727
+ globalDir: join26(process.env.HOME ?? "", ".config", "kimiflare", "skills")
15433
15728
  };
15434
15729
  }
15435
15730
  async function listAllSkills(cwd) {
@@ -15443,7 +15738,7 @@ async function listAllSkills(cwd) {
15443
15738
  async function createSkill(opts2) {
15444
15739
  const dirs = getSkillDirs(opts2.cwd);
15445
15740
  const dir = opts2.scope === "project" ? dirs.projectDir : dirs.globalDir;
15446
- const filepath = join25(dir, `${opts2.name}.md`);
15741
+ const filepath = join26(dir, `${opts2.name}.md`);
15447
15742
  const frontmatter = {
15448
15743
  name: opts2.name,
15449
15744
  enabled: true,
@@ -15479,7 +15774,7 @@ async function setSkillEnabled(name, enabled, cwd) {
15479
15774
  const all = await listAllSkills(cwd);
15480
15775
  const skill = all.project.find((s) => s.name === name) ?? all.global.find((s) => s.name === name);
15481
15776
  if (!skill) throw new Error(`skill "${name}" not found`);
15482
- const raw = await readFile16(skill.filePath, "utf-8");
15777
+ const raw = await readFile17(skill.filePath, "utf-8");
15483
15778
  const parsed = parseFrontmatter(raw);
15484
15779
  parsed.data.enabled = enabled;
15485
15780
  const yaml = Object.entries(parsed.data).map(([k, v]) => {
@@ -15564,13 +15859,13 @@ var init_frontmatter2 = __esm({
15564
15859
  // src/commands/loader.ts
15565
15860
  import { open, realpath as realpath2 } from "fs/promises";
15566
15861
  import { homedir as homedir14 } from "os";
15567
- import { join as join26, relative as relative5, sep as sep2 } from "path";
15862
+ import { join as join27, relative as relative5, sep as sep2 } from "path";
15568
15863
  function projectCommandsDir(cwd = process.cwd()) {
15569
- return join26(cwd, ".kimiflare", "commands");
15864
+ return join27(cwd, ".kimiflare", "commands");
15570
15865
  }
15571
15866
  function globalCommandsDir() {
15572
- const xdg = process.env.XDG_CONFIG_HOME || join26(homedir14(), ".config");
15573
- return join26(xdg, "kimiflare", "commands");
15867
+ const xdg = process.env.XDG_CONFIG_HOME || join27(homedir14(), ".config");
15868
+ return join27(xdg, "kimiflare", "commands");
15574
15869
  }
15575
15870
  async function loadCustomCommands(cwd = process.cwd()) {
15576
15871
  const warnings = [];
@@ -15844,13 +16139,13 @@ var init_worker_client = __esm({
15844
16139
  });
15845
16140
 
15846
16141
  // src/init/context-generator.ts
15847
- import { existsSync as existsSync4, statSync as statSync4 } from "fs";
15848
- import { join as join27 } from "path";
16142
+ import { existsSync as existsSync5, statSync as statSync5 } from "fs";
16143
+ import { join as join28 } from "path";
15849
16144
  function detectFlavor(cwd) {
15850
16145
  for (const [flavor, signatures] of Object.entries(FLAVOR_SIGNATURES)) {
15851
16146
  if (flavor === "generic") continue;
15852
16147
  for (const sig of signatures) {
15853
- const path = join27(cwd, sig);
16148
+ const path = join28(cwd, sig);
15854
16149
  if (sig.includes("*")) {
15855
16150
  try {
15856
16151
  const parts = sig.split("*");
@@ -15862,7 +16157,7 @@ function detectFlavor(cwd) {
15862
16157
  }
15863
16158
  } catch {
15864
16159
  }
15865
- } else if (existsSync4(path)) {
16160
+ } else if (existsSync5(path)) {
15866
16161
  return flavor;
15867
16162
  }
15868
16163
  }
@@ -15871,16 +16166,16 @@ function detectFlavor(cwd) {
15871
16166
  }
15872
16167
  function findFile(cwd, candidates) {
15873
16168
  for (const c of candidates) {
15874
- if (existsSync4(join27(cwd, c))) return c;
16169
+ if (existsSync5(join28(cwd, c))) return c;
15875
16170
  }
15876
16171
  return null;
15877
16172
  }
15878
16173
  function findSourceRoots(cwd) {
15879
16174
  const roots = [];
15880
16175
  for (const r of SOURCE_ROOT_CANDIDATES) {
15881
- const p = join27(cwd, r);
16176
+ const p = join28(cwd, r);
15882
16177
  try {
15883
- const s = statSync4(p);
16178
+ const s = statSync5(p);
15884
16179
  if (s.isDirectory()) roots.push(r);
15885
16180
  } catch {
15886
16181
  }
@@ -15889,9 +16184,9 @@ function findSourceRoots(cwd) {
15889
16184
  }
15890
16185
  function findCiConfig(cwd) {
15891
16186
  for (const c of CI_PATHS) {
15892
- if (existsSync4(join27(cwd, c))) {
16187
+ if (existsSync5(join28(cwd, c))) {
15893
16188
  try {
15894
- const s = statSync4(join27(cwd, c));
16189
+ const s = statSync5(join28(cwd, c));
15895
16190
  return s.isDirectory() ? c : c;
15896
16191
  } catch {
15897
16192
  }
@@ -16028,7 +16323,7 @@ function analyzeProject(cwd) {
16028
16323
  ciConfig: findCiConfig(cwd),
16029
16324
  readme: findFile(cwd, ["README.md", "README.rst", "README.txt", "Readme.md"]),
16030
16325
  sourceRoots: findSourceRoots(cwd),
16031
- hasGit: existsSync4(join27(cwd, ".git"))
16326
+ hasGit: existsSync5(join28(cwd, ".git"))
16032
16327
  };
16033
16328
  }
16034
16329
  function bashDiscoveryCommands(profile) {
@@ -16193,7 +16488,7 @@ Aim for 100\u2013200 lines total. Use markdown tables where they save space.
16193
16488
  }
16194
16489
  function buildInitPrompt(cwd) {
16195
16490
  const existingName = ["KIMI.md", "KIMIFLARE.md", "AGENT.md"].find(
16196
- (n) => existsSync4(join27(cwd, n))
16491
+ (n) => existsSync5(join28(cwd, n))
16197
16492
  );
16198
16493
  const isRefresh = existingName !== void 0;
16199
16494
  const targetFilename = existingName ?? "KIMI.md";
@@ -16281,11 +16576,11 @@ __export(ui_mode_exports, {
16281
16576
  runCamouflageOnboarding: () => runCamouflageOnboarding,
16282
16577
  runUiMode: () => runUiMode
16283
16578
  });
16284
- import { execSync as execSync4, spawn as spawn5 } from "child_process";
16579
+ import { execSync as execSync5, spawn as spawn6 } from "child_process";
16285
16580
  import { appendFileSync, openSync } from "fs";
16286
16581
  import { readdir as readdir7, unlink as unlink4 } from "fs/promises";
16287
- import { join as join28, relative as relative6 } from "path";
16288
- import { platform as platform4 } from "os";
16582
+ import { join as join29, relative as relative6 } from "path";
16583
+ import { platform as platform5 } from "os";
16289
16584
  function kimiLog(payload) {
16290
16585
  if (!KIMI_LOG_PATH) return;
16291
16586
  try {
@@ -16360,6 +16655,7 @@ ${err instanceof Error ? err.message : err}`);
16360
16655
  let currentThemeName = "everforest-dark";
16361
16656
  const branch = tryGitBranch2();
16362
16657
  let currentSessionFilePath = null;
16658
+ let sessionPlan = null;
16363
16659
  const startupCfg = await loadConfig().catch(() => null);
16364
16660
  let multiAgentEnabled = (startupCfg?.multiAgentEnabled ?? false) || /^(1|true|yes|on)$/i.test(process.env.KIMIFLARE_MULTI_AGENT_ENABLED ?? "");
16365
16661
  const multiAgentSupervisor = new TurnSupervisor();
@@ -16915,7 +17211,7 @@ Executor opened PR: ${prUrl}` : plan });
16915
17211
  onToolLimitReached: async () => {
16916
17212
  const r = await confirm(cam, {
16917
17213
  id: `lim-${Date.now()}`,
16918
- prompt: "Tool-call limit reached (50). Continue running?",
17214
+ prompt: "Tool-call limit reached (200). Continue running?",
16919
17215
  yes_label: "Continue",
16920
17216
  no_label: "Stop turn",
16921
17217
  default: "no",
@@ -17028,6 +17324,12 @@ Executor opened PR: ${prUrl}` : plan });
17028
17324
  setPhase("idle");
17029
17325
  cam.send("StatusUpdate", { segments: { elapsed: "" } });
17030
17326
  kimiLog({ dir: "turn", phase: "end" });
17327
+ if (currentMode === "plan" && !currentController?.signal.aborted) {
17328
+ const plan = distillSessionPlan(messages);
17329
+ if (plan) {
17330
+ sessionPlan = plan;
17331
+ }
17332
+ }
17031
17333
  if (planOptionsRef.current && !currentController?.signal.aborted) {
17032
17334
  const options = planOptionsRef.current;
17033
17335
  planOptionsRef.current = null;
@@ -17055,10 +17357,19 @@ Executor opened PR: ${prUrl}` : plan });
17055
17357
  promptTokens = 0;
17056
17358
  cachedTokens = 0;
17057
17359
  completionTokens = 0;
17360
+ sessionPlan = null;
17058
17361
  cam.send("TranscriptCleared", {});
17059
17362
  cam.send("StatusUpdate", {
17060
17363
  segments: { tokens: "in 0", cost: "$0.00", elapsed: "" }
17061
17364
  });
17365
+ rebuildSystemPromptForMode(
17366
+ messages,
17367
+ false,
17368
+ // Camouflage UI always uses single system message
17369
+ opts2.model,
17370
+ currentMode,
17371
+ ALL_TOOLS
17372
+ );
17062
17373
  messages.push({ role: "user", content: selected.plan });
17063
17374
  cam.send("UserMessageCreated", { text: selected.plan });
17064
17375
  cam.send("ShowToast", {
@@ -17121,7 +17432,7 @@ Executor opened PR: ${prUrl}` : plan });
17121
17432
  allow_cancel: true
17122
17433
  });
17123
17434
  if (pick3.cancelled || !pick3.value) return;
17124
- openBrowser(
17435
+ openBrowser2(
17125
17436
  `${URL2}/inbox?u=${encodeURIComponent(handle)}&s=${encodeURIComponent(secret)}&m=${encodeURIComponent(pick3.value)}`
17126
17437
  );
17127
17438
  cam.send("ShowToast", { text: "opened in browser", kind: "success", ttl_ms: 1500 });
@@ -17625,6 +17936,7 @@ Executor opened PR: ${prUrl}` : plan });
17625
17936
  promptTokens = 0;
17626
17937
  cachedTokens = 0;
17627
17938
  completionTokens = 0;
17939
+ sessionPlan = null;
17628
17940
  cam.send("TranscriptCleared", {});
17629
17941
  cam.send("StatusUpdate", {
17630
17942
  segments: { tokens: "in 0", cost: "$0.00", elapsed: "" }
@@ -18237,7 +18549,7 @@ Executor opened PR: ${prUrl}` : plan });
18237
18549
  return true;
18238
18550
  }
18239
18551
  case "hello":
18240
- openBrowser("https://hello.kimiflare.com");
18552
+ openBrowser2("https://hello.kimiflare.com");
18241
18553
  cam.send("ShowToast", { text: "opened hello.kimiflare.com \u2014 leave the creator a voice note", kind: "info", ttl_ms: 3500 });
18242
18554
  return true;
18243
18555
  case "inbox":
@@ -18255,7 +18567,7 @@ Executor opened PR: ${prUrl}` : plan });
18255
18567
  cam.send("ShowToast", { text: "can't /fresh while model is running \u2014 press Esc to interrupt first", kind: "warn", ttl_ms: 2500 });
18256
18568
  return true;
18257
18569
  }
18258
- const plan = distillSessionPlan(messages);
18570
+ const plan = sessionPlan ?? distillSessionPlan(messages);
18259
18571
  if (!plan) {
18260
18572
  cam.send("ShowToast", { text: "No plan found to start fresh with.", kind: "error", ttl_ms: 2500 });
18261
18573
  return true;
@@ -18268,10 +18580,19 @@ Executor opened PR: ${prUrl}` : plan });
18268
18580
  promptTokens = 0;
18269
18581
  cachedTokens = 0;
18270
18582
  completionTokens = 0;
18583
+ sessionPlan = null;
18271
18584
  cam.send("TranscriptCleared", {});
18272
18585
  cam.send("StatusUpdate", {
18273
18586
  segments: { tokens: "in 0", cost: "$0.00", elapsed: "" }
18274
18587
  });
18588
+ rebuildSystemPromptForMode(
18589
+ messages,
18590
+ false,
18591
+ // Camouflage UI always uses single system message
18592
+ opts2.model,
18593
+ currentMode,
18594
+ ALL_TOOLS
18595
+ );
18275
18596
  messages.push({ role: "user", content: plan });
18276
18597
  cam.send("ShowToast", {
18277
18598
  text: clipResult.success ? "Plan copied to clipboard. Starting fresh session with plan only\u2026" : "Clipboard unavailable. Starting fresh session with plan only\u2026",
@@ -18345,7 +18666,7 @@ async function registerMentions(cam, recents) {
18345
18666
  for (const e of entries) {
18346
18667
  if (collected.length >= 200) return;
18347
18668
  if (e.name.startsWith(".") || SKIP.has(e.name)) continue;
18348
- const full = join28(dir, e.name);
18669
+ const full = join29(dir, e.name);
18349
18670
  if (e.isDirectory()) {
18350
18671
  await walk2(full, depth + 1);
18351
18672
  } else if (e.isFile()) {
@@ -18394,9 +18715,9 @@ function formatShortDate(iso) {
18394
18715
  return iso;
18395
18716
  }
18396
18717
  }
18397
- function openBrowser(url) {
18398
- const cmd = platform4() === "darwin" ? "open" : platform4() === "win32" ? "start" : "xdg-open";
18399
- const child = spawn5(cmd, [url], { detached: true, stdio: "ignore" });
18718
+ function openBrowser2(url) {
18719
+ const cmd = platform5() === "darwin" ? "open" : platform5() === "win32" ? "start" : "xdg-open";
18720
+ const child = spawn6(cmd, [url], { detached: true, stdio: "ignore" });
18400
18721
  child.unref();
18401
18722
  }
18402
18723
  function formatUsd(n) {
@@ -18415,7 +18736,7 @@ function formatElapsed2(secs) {
18415
18736
  }
18416
18737
  function tryGitBranch2() {
18417
18738
  try {
18418
- const out = execSync4("git rev-parse --abbrev-ref HEAD 2>/dev/null", {
18739
+ const out = execSync5("git rev-parse --abbrev-ref HEAD 2>/dev/null", {
18419
18740
  encoding: "utf8",
18420
18741
  timeout: 200
18421
18742
  }).trim();
@@ -18520,6 +18841,7 @@ var init_ui_mode = __esm({
18520
18841
  init_classify();
18521
18842
  init_deploy_commute();
18522
18843
  init_system_prompt();
18844
+ init_app_helpers();
18523
18845
  init_executor();
18524
18846
  init_errors();
18525
18847
  init_builtins();
@@ -21007,7 +21329,7 @@ var init_text_input = __esm({
21007
21329
  // src/ui/permission.tsx
21008
21330
  import { useState as useState4, useCallback } from "react";
21009
21331
  import { Box as Box7, Text as Text8, useInput as useInput2 } from "ink";
21010
- import { platform as platform5 } from "os";
21332
+ import { platform as platform6 } from "os";
21011
21333
  import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
21012
21334
  function formatSelection(label, shortcut) {
21013
21335
  return `${label} [${MOD_KEY}+${shortcut}]`;
@@ -21171,7 +21493,7 @@ var init_permission = __esm({
21171
21493
  { value: { decision: "deny", scope: "once" }, label: "Something else", key: 3 }
21172
21494
  ];
21173
21495
  DENY = { decision: "deny", scope: "once" };
21174
- MOD_KEY = platform5() === "darwin" ? "\u2325" : "Alt";
21496
+ MOD_KEY = platform6() === "darwin" ? "\u2325" : "Alt";
21175
21497
  }
21176
21498
  });
21177
21499
 
@@ -21609,7 +21931,7 @@ function TaskList({ tasks, startedAt, tokensDelta }) {
21609
21931
  const allDone = done === total;
21610
21932
  const header = active ? active.title : allDone ? `${total} tasks done` : `${done}/${total}`;
21611
21933
  const elapsed = startedAt ? formatElapsed5(now2 - startedAt) : null;
21612
- const headerStats = [elapsed, tokensDelta > 0 ? `\u2191 ${formatTokens(tokensDelta)} tokens` : null].filter(Boolean).join(" \xB7 ");
21934
+ const headerStats = [elapsed, tokensDelta > 0 ? `\u2191 ${formatTokens2(tokensDelta)} tokens` : null].filter(Boolean).join(" \xB7 ");
21613
21935
  const visibleTasks = tasks.slice(0, MAX_VISIBLE);
21614
21936
  const hiddenPending = Math.max(0, tasks.length - visibleTasks.length);
21615
21937
  return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", marginBottom: 1, children: [
@@ -21661,7 +21983,7 @@ function formatElapsed5(ms) {
21661
21983
  if (m === 0) return `${s}s`;
21662
21984
  return `${m}m ${s}s`;
21663
21985
  }
21664
- function formatTokens(n) {
21986
+ function formatTokens2(n) {
21665
21987
  if (n < 1e3) return String(n);
21666
21988
  return `${(n / 1e3).toFixed(1)}k`;
21667
21989
  }
@@ -23201,53 +23523,14 @@ function Welcome() {
23201
23523
  });
23202
23524
  return /* @__PURE__ */ jsxs18(Box18, { flexDirection: "column", marginBottom: 1, children: [
23203
23525
  /* @__PURE__ */ jsx20(Box18, { marginBottom: 1, children: /* @__PURE__ */ jsx20(Text19, { bold: true, color: theme.accent, children: headline }) }),
23204
- /* @__PURE__ */ jsx20(Box18, { flexDirection: "column", children: /* @__PURE__ */ jsx20(Text19, { color: theme.info.color, dimColor: true, children: "Type / for commands" }) })
23205
- ] });
23206
- }
23207
- var init_welcome = __esm({
23208
- "src/ui/welcome.tsx"() {
23209
- "use strict";
23210
- init_theme_context();
23211
- init_greetings();
23212
- }
23213
- });
23214
-
23215
- // src/util/image.ts
23216
- import { readFile as readFile17 } from "fs/promises";
23217
- import { basename as basename4 } from "path";
23218
- async function encodeImageFile(filePath) {
23219
- const buf = await readFile17(filePath);
23220
- if (buf.byteLength > MAX_IMAGE_BYTES) {
23221
- throw new Error(
23222
- `image too large (${(buf.byteLength / 1024 / 1024).toFixed(1)} MB); max is ${MAX_IMAGE_BYTES / 1024 / 1024} MB`
23223
- );
23224
- }
23225
- const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
23226
- const mime = EXT_TO_MIME[ext] ?? "image/jpeg";
23227
- const b64 = buf.toString("base64");
23228
- return {
23229
- filename: basename4(filePath),
23230
- mime,
23231
- dataUrl: `data:${mime};base64,${b64}`
23232
- };
23233
- }
23234
- function isImagePath(path) {
23235
- const ext = path.slice(path.lastIndexOf(".")).toLowerCase();
23236
- return ext in EXT_TO_MIME;
23526
+ /* @__PURE__ */ jsx20(Box18, { flexDirection: "column", children: /* @__PURE__ */ jsx20(Text19, { color: theme.info.color, dimColor: true, children: "Type / for commands" }) })
23527
+ ] });
23237
23528
  }
23238
- var MAX_IMAGE_BYTES, EXT_TO_MIME;
23239
- var init_image = __esm({
23240
- "src/util/image.ts"() {
23529
+ var init_welcome = __esm({
23530
+ "src/ui/welcome.tsx"() {
23241
23531
  "use strict";
23242
- MAX_IMAGE_BYTES = 5 * 1024 * 1024;
23243
- EXT_TO_MIME = {
23244
- ".png": "image/png",
23245
- ".jpg": "image/jpeg",
23246
- ".jpeg": "image/jpeg",
23247
- ".gif": "image/gif",
23248
- ".webp": "image/webp",
23249
- ".bmp": "image/bmp"
23250
- };
23532
+ init_theme_context();
23533
+ init_greetings();
23251
23534
  }
23252
23535
  });
23253
23536
 
@@ -23484,10 +23767,10 @@ var init_wcag = __esm({
23484
23767
 
23485
23768
  // src/ui/theme-loader.ts
23486
23769
  import { readFile as readFile18, readdir as readdir8 } from "fs/promises";
23487
- import { join as join29 } from "path";
23770
+ import { join as join30 } from "path";
23488
23771
  import { homedir as homedir15 } from "os";
23489
23772
  function projectThemesDir(cwd = process.cwd()) {
23490
- return join29(cwd, ".kimiflare", "themes");
23773
+ return join30(cwd, ".kimiflare", "themes");
23491
23774
  }
23492
23775
  function isHexColor(c) {
23493
23776
  return /^#[0-9a-fA-F]{6}$/.test(c);
@@ -23576,7 +23859,7 @@ async function loadThemesFromDir(dir, source) {
23576
23859
  return { themes, errors };
23577
23860
  }
23578
23861
  for (const file of files.filter((f) => f.endsWith(".json"))) {
23579
- const path = join29(dir, file);
23862
+ const path = join30(dir, file);
23580
23863
  let raw;
23581
23864
  try {
23582
23865
  raw = await readFile18(path, "utf-8");
@@ -23725,8 +24008,8 @@ var init_theme_loader = __esm({
23725
24008
  "use strict";
23726
24009
  init_wcag();
23727
24010
  init_theme();
23728
- USER_THEMES_DIR = join29(
23729
- process.env.XDG_CONFIG_HOME || join29(homedir15(), ".config"),
24011
+ USER_THEMES_DIR = join30(
24012
+ process.env.XDG_CONFIG_HOME || join30(homedir15(), ".config"),
23730
24013
  "kimiflare",
23731
24014
  "themes"
23732
24015
  );
@@ -24813,7 +25096,7 @@ var init_command_list = __esm({
24813
25096
  import { useState as useState18 } from "react";
24814
25097
  import { Box as Box25, Text as Text26 } from "ink";
24815
25098
  import SelectInput10 from "ink-select-input";
24816
- import { spawn as spawn6 } from "child_process";
25099
+ import { spawn as spawn7 } from "child_process";
24817
25100
  import { jsx as jsx27, jsxs as jsxs25 } from "react/jsx-runtime";
24818
25101
  function LspWizard({ servers, currentScope, hasProjectDir, onDone, onSave }) {
24819
25102
  const theme = useTheme();
@@ -24827,7 +25110,7 @@ function LspWizard({ servers, currentScope, hasProjectDir, onDone, onSave }) {
24827
25110
  const runInstall = (command) => {
24828
25111
  setInstallState({ status: "running", output: "Installing..." });
24829
25112
  const { shell, args } = getShellCommand();
24830
- const child = spawn6(shell, [...args, command], {
25113
+ const child = spawn7(shell, [...args, command], {
24831
25114
  env: process.env
24832
25115
  });
24833
25116
  let stdout = "";
@@ -25853,7 +26136,7 @@ function formatSessionLine(s) {
25853
26136
  const ago = formatAgo(new Date(s.updatedAt));
25854
26137
  const prompt = s.prompt.slice(0, 30) + (s.prompt.length > 30 ? "\u2026" : "");
25855
26138
  const outcome = s.prUrl ? `PR ${s.prUrl.split("/").pop()}` : s.status;
25856
- const cost = s.tokensUsed && s.tokensBudget ? ` (${formatTokens2(s.tokensUsed)}/${formatTokens2(s.tokensBudget)})` : s.tokensUsed ? ` (${formatTokens2(s.tokensUsed)})` : "";
26139
+ const cost = s.tokensUsed && s.tokensBudget ? ` (${formatTokens3(s.tokensUsed)}/${formatTokens3(s.tokensBudget)})` : s.tokensUsed ? ` (${formatTokens3(s.tokensUsed)})` : "";
25857
26140
  return `${icon} ${prompt} \u2192 ${outcome} ${ago}${cost}`;
25858
26141
  }
25859
26142
  function formatAgo(date) {
@@ -25866,7 +26149,7 @@ function formatAgo(date) {
25866
26149
  if (minutes > 0) return `${minutes}m ago`;
25867
26150
  return "just now";
25868
26151
  }
25869
- function formatTokens2(n) {
26152
+ function formatTokens3(n) {
25870
26153
  if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
25871
26154
  if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
25872
26155
  return String(n);
@@ -25927,8 +26210,8 @@ function RemoteSessionDetail({
25927
26210
  ] }),
25928
26211
  session.tokensUsed !== void 0 && /* @__PURE__ */ jsxs33(Text34, { children: [
25929
26212
  "Tokens: ",
25930
- formatTokens2(session.tokensUsed),
25931
- session.tokensBudget ? ` / ${formatTokens2(session.tokensBudget)}` : ""
26213
+ formatTokens3(session.tokensUsed),
26214
+ session.tokensBudget ? ` / ${formatTokens3(session.tokensBudget)}` : ""
25932
26215
  ] }),
25933
26216
  /* @__PURE__ */ jsxs33(Text34, { children: [
25934
26217
  "Created: ",
@@ -25972,7 +26255,7 @@ function InboxModal({ onDone, onOpen }) {
25972
26255
  setError(null);
25973
26256
  try {
25974
26257
  const res = await fetch(
25975
- `${FEEDBACK_WORKER_URL}/inbox/check?u=${encodeURIComponent(u)}&s=${encodeURIComponent(s)}`
26258
+ `${FEEDBACK_WORKER_URL2}/inbox/check?u=${encodeURIComponent(u)}&s=${encodeURIComponent(s)}`
25976
26259
  );
25977
26260
  if (!res.ok) {
25978
26261
  throw new Error(`Server returned ${res.status}`);
@@ -26018,7 +26301,7 @@ function InboxModal({ onDone, onOpen }) {
26018
26301
  if (messages.length === 0) return;
26019
26302
  const msg = messages[selectedIndex];
26020
26303
  if (!msg) return;
26021
- const url = `${FEEDBACK_WORKER_URL}/inbox?u=${encodeURIComponent(twitter)}&s=${encodeURIComponent(secret)}&m=${encodeURIComponent(msg.id)}`;
26304
+ const url = `${FEEDBACK_WORKER_URL2}/inbox?u=${encodeURIComponent(twitter)}&s=${encodeURIComponent(secret)}&m=${encodeURIComponent(msg.id)}`;
26022
26305
  onOpen(url);
26023
26306
  onDone();
26024
26307
  }, [messages, selectedIndex, twitter, secret, onOpen, onDone]);
@@ -26117,255 +26400,13 @@ function InboxModal({ onDone, onOpen }) {
26117
26400
  ] })
26118
26401
  ] });
26119
26402
  }
26120
- var FEEDBACK_WORKER_URL;
26403
+ var FEEDBACK_WORKER_URL2;
26121
26404
  var init_inbox_modal = __esm({
26122
26405
  "src/ui/inbox-modal.tsx"() {
26123
26406
  "use strict";
26124
26407
  init_text_input();
26125
26408
  init_theme_context();
26126
- FEEDBACK_WORKER_URL = "https://hello.kimiflare.com";
26127
- }
26128
- });
26129
-
26130
- // src/ui/app-helpers.ts
26131
- import { execSync as execSync5, spawn as spawn7 } from "child_process";
26132
- import { existsSync as existsSync5, readFileSync as readFileSync4, statSync as statSync5 } from "fs";
26133
- import { join as join30 } from "path";
26134
- import { platform as platform6 } from "os";
26135
- function buildFilePickerIgnoreList(cwd) {
26136
- const hardcoded = [
26137
- // Dependencies
26138
- "**/node_modules/**",
26139
- "**/vendor/**",
26140
- "**/.bundle/**",
26141
- "**/bower_components/**",
26142
- // Version control
26143
- "**/.git/**",
26144
- "**/.svn/**",
26145
- "**/.hg/**",
26146
- // Build / output directories
26147
- "**/dist/**",
26148
- "**/build/**",
26149
- "**/out/**",
26150
- "**/public/**",
26151
- "**/.next/**",
26152
- "**/.nuxt/**",
26153
- "**/.svelte-kit/**",
26154
- "**/.vercel/**",
26155
- "**/.netlify/**",
26156
- "**/target/**",
26157
- "**/bin/**",
26158
- "**/obj/**",
26159
- "**/Debug/**",
26160
- "**/Release/**",
26161
- "**/.gradle/**",
26162
- // Caches
26163
- "**/.cache/**",
26164
- "**/.parcel-cache/**",
26165
- "**/.turbo/**",
26166
- "**/.eslintcache",
26167
- "**/.stylelintcache",
26168
- "**/.rpt2_cache/**",
26169
- "**/.rts2_cache/**",
26170
- // Temporary
26171
- "**/tmp/**",
26172
- "**/temp/**",
26173
- "**/*.tmp",
26174
- // Coverage
26175
- "**/coverage/**",
26176
- "**/.nyc_output/**",
26177
- // OS files
26178
- "**/.DS_Store",
26179
- "**/Thumbs.db",
26180
- // Logs
26181
- "**/*.log",
26182
- "**/logs/**",
26183
- // Lock files (auto-generated, usually huge)
26184
- "**/package-lock.json",
26185
- "**/yarn.lock",
26186
- "**/pnpm-lock.yaml",
26187
- "**/bun.lockb",
26188
- "**/Cargo.lock",
26189
- "**/Gemfile.lock",
26190
- "**/composer.lock",
26191
- "**/Pipfile.lock",
26192
- "**/poetry.lock",
26193
- "**/go.sum",
26194
- // Minified / source maps
26195
- "**/*.min.js",
26196
- "**/*.min.css",
26197
- "**/*.map",
26198
- // kimiflare internal
26199
- "**/.kimiflare/**",
26200
- // IDE (usually not relevant to mention)
26201
- "**/.idea/**"
26202
- ];
26203
- const gitignorePatterns = [];
26204
- try {
26205
- const gitignorePath = join30(cwd, ".gitignore");
26206
- const stats = statSync5(gitignorePath);
26207
- if (stats.size > MAX_GITIGNORE_SIZE) {
26208
- return hardcoded;
26209
- }
26210
- const content = readFileSync4(gitignorePath, "utf-8");
26211
- for (const line of content.split(/\r?\n/)) {
26212
- const trimmed = line.trim();
26213
- if (!trimmed || trimmed.startsWith("#")) continue;
26214
- if (trimmed.startsWith("!")) continue;
26215
- let pattern = trimmed;
26216
- const isAnchored = pattern.startsWith("/");
26217
- const isDir = pattern.endsWith("/");
26218
- if (isAnchored) pattern = pattern.slice(1);
26219
- if (isDir) pattern = pattern.slice(0, -1);
26220
- if (!pattern) continue;
26221
- if (isAnchored) {
26222
- gitignorePatterns.push(isDir ? pattern + "/**" : pattern);
26223
- } else {
26224
- gitignorePatterns.push(isDir ? "**/" + pattern + "/**" : "**/" + pattern);
26225
- }
26226
- }
26227
- } catch {
26228
- }
26229
- return [...hardcoded, ...gitignorePatterns];
26230
- }
26231
- function gatewayFromConfig(cfg) {
26232
- if (process.env.KIMIFLARE_DISABLE_AI_GATEWAY === "1") return void 0;
26233
- if (!cfg.aiGatewayId) return void 0;
26234
- return {
26235
- id: cfg.aiGatewayId,
26236
- cacheTtl: cfg.aiGatewayCacheTtl,
26237
- skipCache: cfg.aiGatewaySkipCache,
26238
- collectLogPayload: cfg.aiGatewayCollectLogPayload,
26239
- metadata: cfg.aiGatewayMetadata
26240
- };
26241
- }
26242
- function gatewayUsageLookupFromConfig(cfg, meta) {
26243
- if (process.env.KIMIFLARE_DISABLE_AI_GATEWAY === "1") return void 0;
26244
- if (!cfg.aiGatewayId || !meta) return void 0;
26245
- return {
26246
- accountId: cfg.accountId,
26247
- apiToken: cfg.apiToken,
26248
- gatewayId: cfg.aiGatewayId,
26249
- meta
26250
- };
26251
- }
26252
- function openBrowser2(url) {
26253
- const cmd = platform6() === "darwin" ? "open" : platform6() === "win32" ? "start" : "xdg-open";
26254
- const child = spawn7(cmd, [url], { detached: true, stdio: "ignore" });
26255
- child.unref();
26256
- }
26257
- function detectGitHubRepo(cachedRepo) {
26258
- if (cachedRepo) {
26259
- const parts = cachedRepo.split("/");
26260
- if (parts.length === 2) return { owner: parts[0], name: parts[1] };
26261
- }
26262
- try {
26263
- const remoteUrl = execSync5("git remote get-url origin", { cwd: process.cwd(), encoding: "utf8" }).trim();
26264
- const httpsMatch = remoteUrl.match(/github\.com\/([^\/]+)\/([^\/]+?)(?:\.git)?$/);
26265
- if (httpsMatch) return { owner: httpsMatch[1], name: httpsMatch[2] };
26266
- const sshMatch = remoteUrl.match(/github\.com:([^\/]+)\/([^\/]+?)(?:\.git)?$/);
26267
- if (sshMatch) return { owner: sshMatch[1], name: sshMatch[2] };
26268
- } catch {
26269
- }
26270
- return null;
26271
- }
26272
- function detectGitBranch() {
26273
- try {
26274
- return execSync5("git branch --show-current", { cwd: process.cwd(), encoding: "utf8" }).trim() || null;
26275
- } catch {
26276
- return null;
26277
- }
26278
- }
26279
- function formatTokens3(n) {
26280
- if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
26281
- if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
26282
- return String(n);
26283
- }
26284
- function trackRecentFile(ref, path, max = 10) {
26285
- ref.current.set(path, Date.now());
26286
- if (ref.current.size > max) {
26287
- let oldest = null;
26288
- let oldestTime = Infinity;
26289
- for (const [p, t] of ref.current) {
26290
- if (t < oldestTime) {
26291
- oldestTime = t;
26292
- oldest = p;
26293
- }
26294
- }
26295
- if (oldest) ref.current.delete(oldest);
26296
- }
26297
- }
26298
- function capEvents(prev) {
26299
- if (prev.length <= MAX_EVENTS) return prev;
26300
- return prev.slice(prev.length - MAX_EVENTS);
26301
- }
26302
- function compactEventsVisual(prev, keepLastTurns) {
26303
- let seen = 0;
26304
- let cutoff = -1;
26305
- for (let i = prev.length - 1; i >= 0; i--) {
26306
- if (prev[i].kind === "user") {
26307
- seen++;
26308
- if (seen === keepLastTurns + 1) {
26309
- cutoff = i;
26310
- break;
26311
- }
26312
- }
26313
- }
26314
- if (cutoff <= 0) return prev;
26315
- const kept = prev.slice(cutoff);
26316
- return [
26317
- { kind: "info", key: mkKey(), text: `\xB7\xB7\xB7 ${cutoff} earlier messages compacted \xB7\xB7\xB7` },
26318
- ...kept
26319
- ];
26320
- }
26321
- function makePrefixMessages(cacheStable, model, mode, tools) {
26322
- if (cacheStable) {
26323
- return buildSystemMessages({ cwd: process.cwd(), tools, model, mode });
26324
- }
26325
- return [
26326
- {
26327
- role: "system",
26328
- content: buildSystemPrompt({ cwd: process.cwd(), tools, model, mode })
26329
- }
26330
- ];
26331
- }
26332
- function findImagePaths(text) {
26333
- const paths = [];
26334
- const quotedRegex = /"([^"]+)"|'([^']+)'/g;
26335
- let match;
26336
- while ((match = quotedRegex.exec(text)) !== null) {
26337
- const path = match[1] ?? match[2];
26338
- if (path && isImagePath(path) && existsSync5(path)) {
26339
- paths.push(path);
26340
- }
26341
- }
26342
- const remaining = text.replace(/"[^"]+"|'[^']+'/g, "");
26343
- const ESCAPED_SPACE = "\0";
26344
- const processed = remaining.replace(/\\ /g, ESCAPED_SPACE);
26345
- for (const token of processed.split(/\s+/)) {
26346
- const clean = token.replace(new RegExp(ESCAPED_SPACE, "g"), " ").replace(/^["']|["',;:!?]$/g, "").replace(/[.,;:!?]$/, "");
26347
- if (clean && isImagePath(clean) && existsSync5(clean) && !paths.includes(clean)) {
26348
- paths.push(clean);
26349
- }
26350
- }
26351
- return paths;
26352
- }
26353
- var MAX_GITIGNORE_SIZE, CONTEXT_LIMIT, AUTO_COMPACT_THRESHOLD, MAX_EVENTS, MAX_IMAGES_PER_MESSAGE, FEEDBACK_WORKER_URL2, nextKey, mkKey, nextAssistantId, mkAssistantId;
26354
- var init_app_helpers = __esm({
26355
- "src/ui/app-helpers.ts"() {
26356
- "use strict";
26357
- init_system_prompt();
26358
- init_image();
26359
- MAX_GITIGNORE_SIZE = 1 * 1024 * 1024;
26360
- CONTEXT_LIMIT = 262e3;
26361
- AUTO_COMPACT_THRESHOLD = 0.8;
26362
- MAX_EVENTS = 500;
26363
- MAX_IMAGES_PER_MESSAGE = 10;
26364
26409
  FEEDBACK_WORKER_URL2 = "https://hello.kimiflare.com";
26365
- nextKey = 1;
26366
- mkKey = () => `evt_${nextKey++}`;
26367
- nextAssistantId = 1;
26368
- mkAssistantId = () => nextAssistantId++;
26369
26410
  }
26370
26411
  });
26371
26412
 
@@ -26546,7 +26587,7 @@ function MultiAgentModal({ initial, onSave, onDone, remoteWorkerUrl, remoteAuthS
26546
26587
  if (deployFailed) {
26547
26588
  if (input === "o" || input === "O") {
26548
26589
  const url = deployLog.map((l) => l.match(/https:\/\/dash\.cloudflare\.com\/[^\s)]+/)?.[0]).find((u) => !!u) ?? "https://dash.cloudflare.com/profile/api-tokens";
26549
- openBrowser2(url);
26590
+ openBrowser(url);
26550
26591
  return;
26551
26592
  }
26552
26593
  if (input === "r" || input === "R") {
@@ -28454,6 +28495,14 @@ function executeFreshStart(ctx, planText) {
28454
28495
  ctx.clearTaskTracking();
28455
28496
  ctx.compactSuggestedRef.current = false;
28456
28497
  ctx.updateNudgedRef.current = false;
28498
+ ctx.sessionPlanRef.current = null;
28499
+ rebuildSystemPromptForMode(
28500
+ ctx.messagesRef.current,
28501
+ ctx.cacheStableRef.current,
28502
+ ctx.cfg?.model ?? "@cf/moonshotai/kimi-k2.6",
28503
+ ctx.mode,
28504
+ [...ALL_TOOLS, ...ctx.mcpToolsRef.current, ...ctx.lspToolsRef.current]
28505
+ );
28457
28506
  ctx.messagesRef.current.push({ role: "user", content: planText });
28458
28507
  const newSessionId = ctx.ensureSessionId();
28459
28508
  if (oldSessionId) {
@@ -28486,6 +28535,7 @@ var init_slash_commands = __esm({
28486
28535
  init_manager4();
28487
28536
  init_sessions();
28488
28537
  init_session_state();
28538
+ init_executor();
28489
28539
  init_recommended();
28490
28540
  init_settings();
28491
28541
  init_types2();
@@ -28535,6 +28585,7 @@ var init_slash_commands = __esm({
28535
28585
  ctx.clearTaskTracking();
28536
28586
  ctx.compactSuggestedRef.current = false;
28537
28587
  ctx.updateNudgedRef.current = false;
28588
+ ctx.sessionPlanRef.current = null;
28538
28589
  return true;
28539
28590
  };
28540
28591
  handleFresh = (ctx) => {
@@ -28546,7 +28597,7 @@ var init_slash_commands = __esm({
28546
28597
  ]);
28547
28598
  return true;
28548
28599
  }
28549
- const plan = distillSessionPlan(ctx.messagesRef.current);
28600
+ const plan = ctx.sessionPlanRef.current ?? distillSessionPlan(ctx.messagesRef.current);
28550
28601
  if (!plan) {
28551
28602
  setEvents((e) => [
28552
28603
  ...e,
@@ -29543,8 +29594,8 @@ project: ${projectSettingsPath(cwd)}`
29543
29594
  handleHello = (ctx) => {
29544
29595
  const { setEvents, mkKey: mkKey2 } = ctx;
29545
29596
  const session = crypto.randomUUID();
29546
- const url = `${FEEDBACK_WORKER_URL2}/?s=${session}&v=${getAppVersion()}`;
29547
- openBrowser2(url);
29597
+ const url = `${FEEDBACK_WORKER_URL}/?s=${session}&v=${getAppVersion()}`;
29598
+ openBrowser(url);
29548
29599
  void (async () => {
29549
29600
  try {
29550
29601
  const qr = await QRCode.toString(url, { type: "terminal", small: true });
@@ -29697,7 +29748,7 @@ project: ${projectSettingsPath(cwd)}`
29697
29748
  setEvents((e) => [
29698
29749
  ...e,
29699
29750
  { kind: "info", key: mkKey2(), text: `Starting remote session for ${repo.owner}/${repo.name}...` },
29700
- { kind: "info", key: mkKey2(), text: `Budget: ${formatTokens3(budget)} tokens. TTL: ${ttl} min.` }
29751
+ { kind: "info", key: mkKey2(), text: `Budget: ${formatTokens(budget)} tokens. TTL: ${ttl} min.` }
29701
29752
  ]);
29702
29753
  try {
29703
29754
  const data = await startRemoteSession({
@@ -31208,6 +31259,7 @@ ${wcagWarnings.join("\n")}` }
31208
31259
  }, []);
31209
31260
  const sessionStartRecallRef = useRef7(null);
31210
31261
  const kimiMdStaleNudgedRef = useRef7(false);
31262
+ const sessionPlanRef = useRef7(null);
31211
31263
  const sessionMgr = useSessionManager({
31212
31264
  cfg,
31213
31265
  messagesRef,
@@ -31885,7 +31937,7 @@ ${wcagWarnings.join("\n")}` }
31885
31937
  (picked) => {
31886
31938
  setShowPlanCompletePicker(false);
31887
31939
  if (!picked || picked === "continue") return;
31888
- const plan = distillSessionPlan(messagesRef.current);
31940
+ const plan = sessionPlanRef.current ?? distillSessionPlan(messagesRef.current);
31889
31941
  if (!plan) {
31890
31942
  setEvents((e) => [
31891
31943
  ...e,
@@ -31919,6 +31971,7 @@ ${wcagWarnings.join("\n")}` }
31919
31971
  clearTaskTracking();
31920
31972
  compactSuggestedRef.current = false;
31921
31973
  updateNudgedRef.current = false;
31974
+ sessionPlanRef.current = null;
31922
31975
  setEvents((e) => [
31923
31976
  ...e,
31924
31977
  {
@@ -31932,6 +31985,13 @@ ${wcagWarnings.join("\n")}` }
31932
31985
  }
31933
31986
  setMode(picked);
31934
31987
  modeRef.current = picked;
31988
+ rebuildSystemPromptForMode(
31989
+ messagesRef.current,
31990
+ cacheStableRef.current,
31991
+ cfg?.model ?? DEFAULT_MODEL,
31992
+ picked,
31993
+ [...ALL_TOOLS, ...mcpToolsRef.current, ...lspToolsRef.current]
31994
+ );
31935
31995
  submitRef.current(plan);
31936
31996
  },
31937
31997
  [mkKey, setShowPlanCompletePicker, setMode, setEvents, setUsage, setSessionUsage, setGatewayMeta, clearTaskTracking, resetSession, submitRef]
@@ -32145,7 +32205,8 @@ ${wcagWarnings.join("\n")}` }
32145
32205
  sessionStateRef,
32146
32206
  compiledContextRef,
32147
32207
  lastApiErrorRef,
32148
- activeScopeRef
32208
+ activeScopeRef,
32209
+ sessionPlanRef
32149
32210
  }), [
32150
32211
  exit,
32151
32212
  busy,
@@ -32583,7 +32644,7 @@ ${conflicts.join("\n")}` }
32583
32644
  askPermission: askForPermission,
32584
32645
  onToolLimitReached: () => new Promise((resolve5) => {
32585
32646
  limitResolveRef.current = resolve5;
32586
- setLimitModal({ limit: 50, resolve: resolve5 });
32647
+ setLimitModal({ limit: 200, resolve: resolve5 });
32587
32648
  }),
32588
32649
  onLoopDetected: () => new Promise((resolve5) => {
32589
32650
  loopResolveRef.current = resolve5;
@@ -32805,6 +32866,7 @@ ${conflicts.join("\n")}` }
32805
32866
  if (modeRef.current === "plan") {
32806
32867
  const plan = distillSessionPlan(messagesRef.current);
32807
32868
  if (plan) {
32869
+ sessionPlanRef.current = plan;
32808
32870
  setShowPlanCompletePicker(true);
32809
32871
  }
32810
32872
  }
@@ -33002,7 +33064,7 @@ ${conflicts.join("\n")}` }
33002
33064
  selectedRemoteSession,
33003
33065
  onSelectRemoteSession: setSelectedRemoteSession,
33004
33066
  onCancelRemoteSession: handleRemoteCancel2,
33005
- onInboxOpen: openBrowser2,
33067
+ onInboxOpen: openBrowser,
33006
33068
  multiAgentSettings: cfg ? {
33007
33069
  multiAgentEnabled: cfg.multiAgentEnabled,
33008
33070
  workerEndpoint: cfg.workerEndpoint,
@@ -33520,7 +33582,7 @@ function renderLogo(version) {
33520
33582
 
33521
33583
  // src/index.tsx
33522
33584
  var program = new Command2();
33523
- program.name("kimiflare").description("Terminal coding agent powered by Kimi-K2.6 on Cloudflare Workers AI.").version(getAppVersion()).option("-p, --print <prompt>", "one-shot mode: send prompt, stream reply to stdout, exit").option("-m, --model <id>", "model id (defaults to @cf/moonshotai/kimi-k2.6)").option("--dangerously-allow-all", "auto-approve every permission prompt (print mode only)").option("--reasoning", "include reasoning in stdout (print mode only)").option("--continue-on-limit", "reset tool-call counter and continue when the 50-call limit is hit (print mode only)").option("--max-input-tokens <n>", "cumulative prompt token budget; exits 42 when exhausted (print mode only)", (v) => parseInt(v, 10)).option("--emit-events", "emit Camouflage NDJSON events to stdout; requires -p (for initial prompt)").option("--multi-turn", "with --emit-events: keep reading stdin for UserInputSubmitted follow-ups after the initial turn").option("--ui <name>", "render UI with the given engine: `ink` (default, stable) or `camouflage` (experimental Rust TUI). Can also be set via the KIMIFLARE_UI environment variable.").option("--camouflage-bin <path>", "with --ui camouflage: path to the camouflage-tui binary (defaults to PATH lookup)").option("--mode <mode>", "run mode: interactive (default), print, rpc");
33585
+ program.name("kimiflare").description("Terminal coding agent powered by Kimi-K2.6 on Cloudflare Workers AI.").version(getAppVersion()).option("-p, --print <prompt>", "one-shot mode: send prompt, stream reply to stdout, exit").option("-m, --model <id>", "model id (defaults to @cf/moonshotai/kimi-k2.6)").option("--dangerously-allow-all", "auto-approve every permission prompt (print mode only)").option("--reasoning", "include reasoning in stdout (print mode only)").option("--continue-on-limit", "reset tool-call counter and continue when the 200-call limit is hit (print mode only)").option("--max-input-tokens <n>", "cumulative prompt token budget; exits 42 when exhausted (print mode only)", (v) => parseInt(v, 10)).option("--emit-events", "emit Camouflage NDJSON events to stdout; requires -p (for initial prompt)").option("--multi-turn", "with --emit-events: keep reading stdin for UserInputSubmitted follow-ups after the initial turn").option("--ui <name>", "render UI with the given engine: `ink` (default, stable) or `camouflage` (experimental Rust TUI). Can also be set via the KIMIFLARE_UI environment variable.").option("--camouflage-bin <path>", "with --ui camouflage: path to the camouflage-tui binary (defaults to PATH lookup)").option("--mode <mode>", "run mode: interactive (default), print, rpc");
33524
33586
  program.command("cost").description("Show cost attribution by task type (requires costAttribution enabled)").option("-w, --week", "last 7 days (default)").option("-m, --month", "last 30 days").option("-d, --day", "today only").option("-s, --session <id>", "single session detail").option("-c, --category <name>", "filter by category").option("--json", "machine-readable output").option("--reclassify", "re-run classification on all sessions").option("--local-only", "skip Cloudflare reconciliation").action(async (cmdOpts) => {
33525
33587
  const cfg = await loadConfig();
33526
33588
  const enabled = cfg?.costAttribution ?? false;