gsd-pi 2.36.0-dev.d612764 → 2.36.0-dev.f887f4e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/resources/extensions/gsd/auto-dashboard.js +104 -334
  2. package/dist/resources/extensions/gsd/auto-loop.js +0 -11
  3. package/dist/resources/extensions/gsd/auto.js +0 -16
  4. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
  5. package/dist/resources/extensions/gsd/commands.js +1 -51
  6. package/dist/resources/extensions/gsd/docs/preferences-reference.md +0 -25
  7. package/dist/resources/extensions/gsd/index.js +0 -5
  8. package/dist/resources/extensions/gsd/notifications.js +1 -10
  9. package/dist/resources/extensions/gsd/preferences-types.js +0 -2
  10. package/dist/resources/extensions/gsd/preferences-validation.js +0 -29
  11. package/dist/resources/extensions/gsd/preferences.js +0 -3
  12. package/dist/resources/extensions/gsd/prompts/research-milestone.md +3 -4
  13. package/dist/resources/extensions/gsd/prompts/research-slice.md +2 -3
  14. package/dist/resources/extensions/gsd/templates/preferences.md +0 -6
  15. package/dist/resources/extensions/search-the-web/native-search.js +4 -45
  16. package/dist/resources/extensions/shared/terminal.js +0 -5
  17. package/dist/resources/extensions/subagent/index.js +60 -180
  18. package/package.json +1 -1
  19. package/packages/pi-tui/dist/terminal-image.d.ts.map +1 -1
  20. package/packages/pi-tui/dist/terminal-image.js +0 -4
  21. package/packages/pi-tui/dist/terminal-image.js.map +1 -1
  22. package/packages/pi-tui/src/terminal-image.ts +0 -5
  23. package/src/resources/extensions/gsd/auto-dashboard.ts +116 -363
  24. package/src/resources/extensions/gsd/auto-loop.ts +0 -42
  25. package/src/resources/extensions/gsd/auto.ts +0 -21
  26. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
  27. package/src/resources/extensions/gsd/commands.ts +1 -54
  28. package/src/resources/extensions/gsd/docs/preferences-reference.md +0 -25
  29. package/src/resources/extensions/gsd/index.ts +0 -8
  30. package/src/resources/extensions/gsd/notifications.ts +1 -10
  31. package/src/resources/extensions/gsd/preferences-types.ts +0 -13
  32. package/src/resources/extensions/gsd/preferences-validation.ts +0 -26
  33. package/src/resources/extensions/gsd/preferences.ts +0 -4
  34. package/src/resources/extensions/gsd/prompts/research-milestone.md +3 -4
  35. package/src/resources/extensions/gsd/prompts/research-slice.md +2 -3
  36. package/src/resources/extensions/gsd/templates/preferences.md +0 -6
  37. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +0 -2
  38. package/src/resources/extensions/gsd/tests/preferences.test.ts +0 -23
  39. package/src/resources/extensions/search-the-web/native-search.ts +4 -50
  40. package/src/resources/extensions/shared/terminal.ts +0 -5
  41. package/src/resources/extensions/subagent/index.ts +79 -236
  42. package/dist/resources/extensions/cmux/index.js +0 -321
  43. package/dist/resources/extensions/gsd/commands-cmux.js +0 -120
  44. package/src/resources/extensions/cmux/index.ts +0 -384
  45. package/src/resources/extensions/gsd/commands-cmux.ts +0 -143
  46. package/src/resources/extensions/gsd/tests/cmux.test.ts +0 -98
@@ -24,8 +24,6 @@ import { formatTokenCount } from "../shared/mod.js";
24
24
  import { discoverAgents } from "./agents.js";
25
25
  import { createIsolation, mergeDeltaPatches, readIsolationMode, } from "./isolation.js";
26
26
  import { registerWorker, updateWorker } from "./worker-registry.js";
27
- import { loadEffectiveGSDPreferences } from "../gsd/preferences.js";
28
- import { CmuxClient, shellEscape } from "../cmux/index.js";
29
27
  const MAX_PARALLEL_TASKS = 8;
30
28
  const MAX_CONCURRENCY = 4;
31
29
  const COLLAPSED_ITEM_COUNT = 10;
@@ -193,66 +191,6 @@ function writePromptToTempFile(agentName, prompt) {
193
191
  fs.writeFileSync(filePath, prompt, { encoding: "utf-8", mode: 0o600 });
194
192
  return { dir: tmpDir, filePath };
195
193
  }
196
- function buildSubagentProcessArgs(agent, task, tmpPromptPath) {
197
- const args = ["--mode", "json", "-p", "--no-session"];
198
- if (agent.model)
199
- args.push("--model", agent.model);
200
- if (agent.tools && agent.tools.length > 0)
201
- args.push("--tools", agent.tools.join(","));
202
- if (tmpPromptPath)
203
- args.push("--append-system-prompt", tmpPromptPath);
204
- args.push(`Task: ${task}`);
205
- return args;
206
- }
207
- function processSubagentEventLine(line, currentResult, emitUpdate) {
208
- if (!line.trim())
209
- return;
210
- let event;
211
- try {
212
- event = JSON.parse(line);
213
- }
214
- catch {
215
- return;
216
- }
217
- if (event.type === "message_end" && event.message) {
218
- const msg = event.message;
219
- currentResult.messages.push(msg);
220
- if (msg.role === "assistant") {
221
- currentResult.usage.turns++;
222
- const usage = msg.usage;
223
- if (usage) {
224
- currentResult.usage.input += usage.input || 0;
225
- currentResult.usage.output += usage.output || 0;
226
- currentResult.usage.cacheRead += usage.cacheRead || 0;
227
- currentResult.usage.cacheWrite += usage.cacheWrite || 0;
228
- currentResult.usage.cost += usage.cost?.total || 0;
229
- currentResult.usage.contextTokens = usage.totalTokens || 0;
230
- }
231
- if (!currentResult.model && msg.model)
232
- currentResult.model = msg.model;
233
- if (msg.stopReason)
234
- currentResult.stopReason = msg.stopReason;
235
- if (msg.errorMessage)
236
- currentResult.errorMessage = msg.errorMessage;
237
- }
238
- emitUpdate();
239
- }
240
- if (event.type === "tool_result_end" && event.message) {
241
- currentResult.messages.push(event.message);
242
- emitUpdate();
243
- }
244
- }
245
- async function waitForFile(filePath, signal, timeoutMs = 30 * 60 * 1000) {
246
- const started = Date.now();
247
- while (Date.now() - started < timeoutMs) {
248
- if (signal?.aborted)
249
- return false;
250
- if (fs.existsSync(filePath))
251
- return true;
252
- await new Promise((resolve) => setTimeout(resolve, 150));
253
- }
254
- return false;
255
- }
256
194
  async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails) {
257
195
  const agent = agents.find((a) => a.name === agentName);
258
196
  if (!agent) {
@@ -268,6 +206,11 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, si
268
206
  step,
269
207
  };
270
208
  }
209
+ const args = ["--mode", "json", "-p", "--no-session"];
210
+ if (agent.model)
211
+ args.push("--model", agent.model);
212
+ if (agent.tools && agent.tools.length > 0)
213
+ args.push("--tools", agent.tools.join(","));
271
214
  let tmpPromptDir = null;
272
215
  let tmpPromptPath = null;
273
216
  const currentResult = {
@@ -294,8 +237,9 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, si
294
237
  const tmp = writePromptToTempFile(agent.name, agent.systemPrompt);
295
238
  tmpPromptDir = tmp.dir;
296
239
  tmpPromptPath = tmp.filePath;
240
+ args.push("--append-system-prompt", tmpPromptPath);
297
241
  }
298
- const args = buildSubagentProcessArgs(agent, task, tmpPromptPath);
242
+ args.push(`Task: ${task}`);
299
243
  let wasAborted = false;
300
244
  const exitCode = await new Promise((resolve) => {
301
245
  const bundledPaths = (process.env.GSD_BUNDLED_EXTENSION_PATHS ?? "").split(path.delimiter).map(s => s.trim()).filter(Boolean);
@@ -303,12 +247,50 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, si
303
247
  const proc = spawn(process.execPath, [process.env.GSD_BIN_PATH, ...extensionArgs, ...args], { cwd: cwd ?? defaultCwd, shell: false, stdio: ["ignore", "pipe", "pipe"] });
304
248
  liveSubagentProcesses.add(proc);
305
249
  let buffer = "";
250
+ const processLine = (line) => {
251
+ if (!line.trim())
252
+ return;
253
+ let event;
254
+ try {
255
+ event = JSON.parse(line);
256
+ }
257
+ catch {
258
+ return;
259
+ }
260
+ if (event.type === "message_end" && event.message) {
261
+ const msg = event.message;
262
+ currentResult.messages.push(msg);
263
+ if (msg.role === "assistant") {
264
+ currentResult.usage.turns++;
265
+ const usage = msg.usage;
266
+ if (usage) {
267
+ currentResult.usage.input += usage.input || 0;
268
+ currentResult.usage.output += usage.output || 0;
269
+ currentResult.usage.cacheRead += usage.cacheRead || 0;
270
+ currentResult.usage.cacheWrite += usage.cacheWrite || 0;
271
+ currentResult.usage.cost += usage.cost?.total || 0;
272
+ currentResult.usage.contextTokens = usage.totalTokens || 0;
273
+ }
274
+ if (!currentResult.model && msg.model)
275
+ currentResult.model = msg.model;
276
+ if (msg.stopReason)
277
+ currentResult.stopReason = msg.stopReason;
278
+ if (msg.errorMessage)
279
+ currentResult.errorMessage = msg.errorMessage;
280
+ }
281
+ emitUpdate();
282
+ }
283
+ if (event.type === "tool_result_end" && event.message) {
284
+ currentResult.messages.push(event.message);
285
+ emitUpdate();
286
+ }
287
+ };
306
288
  proc.stdout.on("data", (data) => {
307
289
  buffer += data.toString();
308
290
  const lines = buffer.split("\n");
309
291
  buffer = lines.pop() || "";
310
292
  for (const line of lines)
311
- processSubagentEventLine(line, currentResult, emitUpdate);
293
+ processLine(line);
312
294
  });
313
295
  proc.stderr.on("data", (data) => {
314
296
  currentResult.stderr += data.toString();
@@ -316,7 +298,7 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, si
316
298
  proc.on("close", (code) => {
317
299
  liveSubagentProcesses.delete(proc);
318
300
  if (buffer.trim())
319
- processSubagentEventLine(buffer, currentResult, emitUpdate);
301
+ processLine(buffer);
320
302
  resolve(code ?? 0);
321
303
  });
322
304
  proc.on("error", () => {
@@ -360,103 +342,6 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, si
360
342
  }
361
343
  }
362
344
  }
363
- async function runSingleAgentInCmuxSplit(cmuxClient, direction, defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails) {
364
- const agent = agents.find((a) => a.name === agentName);
365
- if (!agent) {
366
- return runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails);
367
- }
368
- let tmpPromptDir = null;
369
- let tmpPromptPath = null;
370
- let tmpOutputDir = null;
371
- const currentResult = {
372
- agent: agentName,
373
- agentSource: agent.source,
374
- task,
375
- exitCode: 0,
376
- messages: [],
377
- stderr: "",
378
- usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
379
- model: agent.model,
380
- step,
381
- };
382
- const emitUpdate = () => {
383
- if (onUpdate) {
384
- onUpdate({
385
- content: [{ type: "text", text: getFinalOutput(currentResult.messages) || "(running...)" }],
386
- details: makeDetails([currentResult]),
387
- });
388
- }
389
- };
390
- try {
391
- if (agent.systemPrompt.trim()) {
392
- const tmp = writePromptToTempFile(agent.name, agent.systemPrompt);
393
- tmpPromptDir = tmp.dir;
394
- tmpPromptPath = tmp.filePath;
395
- }
396
- tmpOutputDir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagent-cmux-"));
397
- const stdoutPath = path.join(tmpOutputDir, "stdout.jsonl");
398
- const stderrPath = path.join(tmpOutputDir, "stderr.log");
399
- const exitPath = path.join(tmpOutputDir, "exit.code");
400
- const cmuxSurfaceId = await cmuxClient.createSplit(direction);
401
- if (!cmuxSurfaceId) {
402
- return runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails);
403
- }
404
- const bundledPaths = (process.env.GSD_BUNDLED_EXTENSION_PATHS ?? "").split(path.delimiter).map((s) => s.trim()).filter(Boolean);
405
- const extensionArgs = bundledPaths.flatMap((p) => ["--extension", p]);
406
- const processArgs = [process.env.GSD_BIN_PATH, ...extensionArgs, ...buildSubagentProcessArgs(agent, task, tmpPromptPath)];
407
- const innerScript = [
408
- `cd ${shellEscape(cwd ?? defaultCwd)}`,
409
- "set -o pipefail",
410
- `${shellEscape(process.execPath)} ${processArgs.map(shellEscape).join(" ")} 2> >(tee ${shellEscape(stderrPath)} >&2) | tee ${shellEscape(stdoutPath)}`,
411
- "status=${PIPESTATUS[0]}",
412
- `printf '%s' "$status" > ${shellEscape(exitPath)}`,
413
- ].join("; ");
414
- const sent = await cmuxClient.sendSurface(cmuxSurfaceId, `bash -lc ${shellEscape(innerScript)}`);
415
- if (!sent) {
416
- return runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails);
417
- }
418
- const finished = await waitForFile(exitPath, signal);
419
- if (!finished) {
420
- currentResult.exitCode = 1;
421
- currentResult.stderr = "cmux split execution timed out or was aborted";
422
- return currentResult;
423
- }
424
- if (fs.existsSync(stdoutPath)) {
425
- const stdout = fs.readFileSync(stdoutPath, "utf-8");
426
- for (const line of stdout.split("\n")) {
427
- processSubagentEventLine(line, currentResult, emitUpdate);
428
- }
429
- }
430
- if (fs.existsSync(stderrPath)) {
431
- currentResult.stderr = fs.readFileSync(stderrPath, "utf-8");
432
- }
433
- currentResult.exitCode = Number.parseInt(fs.readFileSync(exitPath, "utf-8").trim() || "1", 10) || 0;
434
- return currentResult;
435
- }
436
- finally {
437
- if (tmpPromptPath)
438
- try {
439
- fs.unlinkSync(tmpPromptPath);
440
- }
441
- catch {
442
- /* ignore */
443
- }
444
- if (tmpPromptDir)
445
- try {
446
- fs.rmdirSync(tmpPromptDir);
447
- }
448
- catch {
449
- /* ignore */
450
- }
451
- if (tmpOutputDir)
452
- try {
453
- fs.rmSync(tmpOutputDir, { recursive: true, force: true });
454
- }
455
- catch {
456
- /* ignore */
457
- }
458
- }
459
- }
460
345
  const TaskItem = Type.Object({
461
346
  agent: Type.String({ description: "Name of the agent to invoke" }),
462
347
  task: Type.String({ description: "Task to delegate to the agent" }),
@@ -527,8 +412,6 @@ export default function (pi) {
527
412
  const discovery = discoverAgents(ctx.cwd, agentScope);
528
413
  const agents = discovery.agents;
529
414
  const confirmProjectAgents = params.confirmProjectAgents ?? false;
530
- const cmuxClient = CmuxClient.fromPreferences(loadEffectiveGSDPreferences()?.preferences);
531
- const cmuxSplitsEnabled = cmuxClient.getConfig().splits;
532
415
  // Resolve isolation mode
533
416
  const isolationMode = readIsolationMode();
534
417
  const useIsolation = Boolean(params.isolated) && isolationMode !== "none";
@@ -658,24 +541,23 @@ export default function (pi) {
658
541
  const batchSize = params.tasks.length;
659
542
  const results = await mapWithConcurrencyLimit(params.tasks, MAX_CONCURRENCY, async (t, index) => {
660
543
  const workerId = registerWorker(t.agent, t.task, index, batchSize, batchId);
661
- const runTask = () => cmuxSplitsEnabled
662
- ? runSingleAgentInCmuxSplit(cmuxClient, index % 2 === 0 ? "right" : "down", ctx.cwd, agents, t.agent, t.task, t.cwd, undefined, signal, (partial) => {
663
- if (partial.details?.results[0]) {
664
- allResults[index] = partial.details.results[0];
665
- emitParallelUpdate();
666
- }
667
- }, makeDetails("parallel"))
668
- : runSingleAgent(ctx.cwd, agents, t.agent, t.task, t.cwd, undefined, signal, (partial) => {
544
+ let result = await runSingleAgent(ctx.cwd, agents, t.agent, t.task, t.cwd, undefined, signal,
545
+ // Per-task update callback
546
+ (partial) => {
547
+ if (partial.details?.results[0]) {
548
+ allResults[index] = partial.details.results[0];
549
+ emitParallelUpdate();
550
+ }
551
+ }, makeDetails("parallel"));
552
+ // Auto-retry failed tasks (likely API rate limit or transient error)
553
+ const isFailed = result.exitCode !== 0 || (result.messages.length === 0 && !signal?.aborted);
554
+ if (isFailed && MAX_RETRIES > 0 && !signal?.aborted) {
555
+ result = await runSingleAgent(ctx.cwd, agents, t.agent, t.task, t.cwd, undefined, signal, (partial) => {
669
556
  if (partial.details?.results[0]) {
670
557
  allResults[index] = partial.details.results[0];
671
558
  emitParallelUpdate();
672
559
  }
673
560
  }, makeDetails("parallel"));
674
- let result = await runTask();
675
- // Auto-retry failed tasks (likely API rate limit or transient error)
676
- const isFailed = result.exitCode !== 0 || (result.messages.length === 0 && !signal?.aborted);
677
- if (isFailed && MAX_RETRIES > 0 && !signal?.aborted) {
678
- result = await runTask();
679
561
  }
680
562
  updateWorker(workerId, result.exitCode === 0 ? "completed" : "failed");
681
563
  allResults[index] = result;
@@ -709,9 +591,7 @@ export default function (pi) {
709
591
  const taskId = crypto.randomUUID();
710
592
  isolation = await createIsolation(effectiveCwd, taskId, isolationMode);
711
593
  }
712
- const result = cmuxSplitsEnabled
713
- ? await runSingleAgentInCmuxSplit(cmuxClient, "right", ctx.cwd, agents, params.agent, params.task, isolation ? isolation.workDir : params.cwd, undefined, signal, onUpdate, makeDetails("single"))
714
- : await runSingleAgent(ctx.cwd, agents, params.agent, params.task, isolation ? isolation.workDir : params.cwd, undefined, signal, onUpdate, makeDetails("single"));
594
+ const result = await runSingleAgent(ctx.cwd, agents, params.agent, params.task, isolation ? isolation.workDir : params.cwd, undefined, signal, onUpdate, makeDetails("single"));
715
595
  // Capture and merge delta if isolated
716
596
  if (isolation) {
717
597
  const patches = await isolation.captureDelta();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gsd-pi",
3
- "version": "2.36.0-dev.d612764",
3
+ "version": "2.36.0-dev.f887f4e",
4
4
  "description": "GSD — Get Shit Done coding agent",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1 +1 @@
1
- {"version":3,"file":"terminal-image.d.ts","sourceRoot":"","sources":["../src/terminal-image.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,IAAI,CAAC;AAEtD,MAAM,WAAW,oBAAoB;IACpC,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAOD,wBAAgB,iBAAiB,IAAI,cAAc,CAElD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAE5D;AAED,wBAAgB,kBAAkB,IAAI,oBAAoB,CAoCzD;AAED,wBAAgB,eAAe,IAAI,oBAAoB,CAKtD;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAKD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOjD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAGxC;AAED,wBAAgB,WAAW,CAC1B,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE;IACR,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CACZ,GACJ,MAAM,CAkCR;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,wBAAgB,YAAY,CAC3B,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE;IACR,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;CACZ,GACJ,MAAM,CAcR;AAED,wBAAgB,kBAAkB,CACjC,eAAe,EAAE,eAAe,EAChC,gBAAgB,EAAE,MAAM,EACxB,cAAc,GAAE,cAA6C,GAC3D,MAAM,CAMR;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAS5F;AAED,wBAAgB,WAAW,CAC1B,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,eAAe,EAChC,OAAO,GAAE,kBAAuB,GAC9B;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA0B7D;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAMvG"}
1
+ {"version":3,"file":"terminal-image.d.ts","sourceRoot":"","sources":["../src/terminal-image.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,IAAI,CAAC;AAEtD,MAAM,WAAW,oBAAoB;IACpC,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,gFAAgF;IAChF,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAOD,wBAAgB,iBAAiB,IAAI,cAAc,CAElD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAE5D;AAED,wBAAgB,kBAAkB,IAAI,oBAAoB,CA+BzD;AAED,wBAAgB,eAAe,IAAI,oBAAoB,CAKtD;AAED,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAKD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOjD;AAED;;;;GAIG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAGxC;AAED,wBAAgB,WAAW,CAC1B,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE;IACR,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CACZ,GACJ,MAAM,CAkCR;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,wBAAgB,YAAY,CAC3B,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE;IACR,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;CACZ,GACJ,MAAM,CAcR;AAED,wBAAgB,kBAAkB,CACjC,eAAe,EAAE,eAAe,EAChC,gBAAgB,EAAE,MAAM,EACxB,cAAc,GAAE,cAA6C,GAC3D,MAAM,CAMR;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAS5F;AAED,wBAAgB,WAAW,CAC1B,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,eAAe,EAChC,OAAO,GAAE,kBAAuB,GAC9B;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA0B7D;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAMvG"}
@@ -11,13 +11,9 @@ export function detectCapabilities() {
11
11
  const termProgram = process.env.TERM_PROGRAM?.toLowerCase() || "";
12
12
  const term = process.env.TERM?.toLowerCase() || "";
13
13
  const colorTerm = process.env.COLORTERM?.toLowerCase() || "";
14
- const isCmux = Boolean(process.env.CMUX_WORKSPACE_ID && process.env.CMUX_SURFACE_ID);
15
14
  if (process.env.KITTY_WINDOW_ID || termProgram === "kitty") {
16
15
  return { images: "kitty", trueColor: true, hyperlinks: true };
17
16
  }
18
- if (isCmux) {
19
- return { images: "kitty", trueColor: true, hyperlinks: true };
20
- }
21
17
  if (termProgram === "ghostty" || term.includes("ghostty") || process.env.GHOSTTY_RESOURCES_DIR) {
22
18
  return { images: "kitty", trueColor: true, hyperlinks: true };
23
19
  }
@@ -1 +1 @@
1
- {"version":3,"file":"terminal-image.js","sourceRoot":"","sources":["../src/terminal-image.ts"],"names":[],"mappings":"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","sourcesContent":["export type ImageProtocol = \"kitty\" | \"iterm2\" | null;\n\nexport interface TerminalCapabilities {\n\timages: ImageProtocol;\n\ttrueColor: boolean;\n\thyperlinks: boolean;\n}\n\nexport interface CellDimensions {\n\twidthPx: number;\n\theightPx: number;\n}\n\nexport interface ImageDimensions {\n\twidthPx: number;\n\theightPx: number;\n}\n\nexport interface ImageRenderOptions {\n\tmaxWidthCells?: number;\n\tmaxHeightCells?: number;\n\tpreserveAspectRatio?: boolean;\n\t/** Kitty image ID. If provided, reuses/replaces existing image with this ID. */\n\timageId?: number;\n}\n\nlet cachedCapabilities: TerminalCapabilities | null = null;\n\n// Default cell dimensions - updated by TUI when terminal responds to query\nlet cellDimensions: CellDimensions = { widthPx: 9, heightPx: 18 };\n\nexport function getCellDimensions(): CellDimensions {\n\treturn cellDimensions;\n}\n\nexport function setCellDimensions(dims: CellDimensions): void {\n\tcellDimensions = dims;\n}\n\nexport function detectCapabilities(): TerminalCapabilities {\n\tconst termProgram = process.env.TERM_PROGRAM?.toLowerCase() || \"\";\n\tconst term = process.env.TERM?.toLowerCase() || \"\";\n\tconst colorTerm = process.env.COLORTERM?.toLowerCase() || \"\";\n\tconst isCmux = Boolean(process.env.CMUX_WORKSPACE_ID && process.env.CMUX_SURFACE_ID);\n\n\tif (process.env.KITTY_WINDOW_ID || termProgram === \"kitty\") {\n\t\treturn { images: \"kitty\", trueColor: true, hyperlinks: true };\n\t}\n\n\tif (isCmux) {\n\t\treturn { images: \"kitty\", trueColor: true, hyperlinks: true };\n\t}\n\n\tif (termProgram === \"ghostty\" || term.includes(\"ghostty\") || process.env.GHOSTTY_RESOURCES_DIR) {\n\t\treturn { images: \"kitty\", trueColor: true, hyperlinks: true };\n\t}\n\n\tif (process.env.WEZTERM_PANE || termProgram === \"wezterm\") {\n\t\treturn { images: \"kitty\", trueColor: true, hyperlinks: true };\n\t}\n\n\tif (process.env.ITERM_SESSION_ID || termProgram === \"iterm.app\") {\n\t\treturn { images: \"iterm2\", trueColor: true, hyperlinks: true };\n\t}\n\n\tif (termProgram === \"vscode\") {\n\t\treturn { images: null, trueColor: true, hyperlinks: true };\n\t}\n\n\tif (termProgram === \"alacritty\") {\n\t\treturn { images: null, trueColor: true, hyperlinks: true };\n\t}\n\n\tconst trueColor = colorTerm === \"truecolor\" || colorTerm === \"24bit\";\n\treturn { images: null, trueColor, hyperlinks: true };\n}\n\nexport function getCapabilities(): TerminalCapabilities {\n\tif (!cachedCapabilities) {\n\t\tcachedCapabilities = detectCapabilities();\n\t}\n\treturn cachedCapabilities;\n}\n\nexport function resetCapabilitiesCache(): void {\n\tcachedCapabilities = null;\n}\n\nconst KITTY_PREFIX = \"\\x1b_G\";\nconst ITERM2_PREFIX = \"\\x1b]1337;File=\";\n\nexport function isImageLine(line: string): boolean {\n\t// Fast path: sequence at line start (single-row images)\n\tif (line.startsWith(KITTY_PREFIX) || line.startsWith(ITERM2_PREFIX)) {\n\t\treturn true;\n\t}\n\t// Slow path: sequence elsewhere (multi-row images have cursor-up prefix)\n\treturn line.includes(KITTY_PREFIX) || line.includes(ITERM2_PREFIX);\n}\n\n/**\n * Generate a random image ID for Kitty graphics protocol.\n * Uses random IDs to avoid collisions between different module instances\n * (e.g., main app vs extensions).\n */\nexport function allocateImageId(): number {\n\t// Use random ID in range [1, 0xffffffff] to avoid collisions\n\treturn Math.floor(Math.random() * 0xfffffffe) + 1;\n}\n\nexport function encodeKitty(\n\tbase64Data: string,\n\toptions: {\n\t\tcolumns?: number;\n\t\trows?: number;\n\t\timageId?: number;\n\t} = {},\n): string {\n\tconst CHUNK_SIZE = 4096;\n\n\tconst params: string[] = [\"a=T\", \"f=100\", \"q=2\"];\n\n\tif (options.columns) params.push(`c=${options.columns}`);\n\tif (options.rows) params.push(`r=${options.rows}`);\n\tif (options.imageId) params.push(`i=${options.imageId}`);\n\n\tif (base64Data.length <= CHUNK_SIZE) {\n\t\treturn `\\x1b_G${params.join(\",\")};${base64Data}\\x1b\\\\`;\n\t}\n\n\tconst chunks: string[] = [];\n\tlet offset = 0;\n\tlet isFirst = true;\n\n\twhile (offset < base64Data.length) {\n\t\tconst chunk = base64Data.slice(offset, offset + CHUNK_SIZE);\n\t\tconst isLast = offset + CHUNK_SIZE >= base64Data.length;\n\n\t\tif (isFirst) {\n\t\t\tchunks.push(`\\x1b_G${params.join(\",\")},m=1;${chunk}\\x1b\\\\`);\n\t\t\tisFirst = false;\n\t\t} else if (isLast) {\n\t\t\tchunks.push(`\\x1b_Gm=0;${chunk}\\x1b\\\\`);\n\t\t} else {\n\t\t\tchunks.push(`\\x1b_Gm=1;${chunk}\\x1b\\\\`);\n\t\t}\n\n\t\toffset += CHUNK_SIZE;\n\t}\n\n\treturn chunks.join(\"\");\n}\n\n/**\n * Delete a Kitty graphics image by ID.\n * Uses uppercase 'I' to also free the image data.\n */\nexport function deleteKittyImage(imageId: number): string {\n\treturn `\\x1b_Ga=d,d=I,i=${imageId}\\x1b\\\\`;\n}\n\n/**\n * Delete all visible Kitty graphics images.\n * Uses uppercase 'A' to also free the image data.\n */\nexport function deleteAllKittyImages(): string {\n\treturn `\\x1b_Ga=d,d=A\\x1b\\\\`;\n}\n\nexport function encodeITerm2(\n\tbase64Data: string,\n\toptions: {\n\t\twidth?: number | string;\n\t\theight?: number | string;\n\t\tname?: string;\n\t\tpreserveAspectRatio?: boolean;\n\t\tinline?: boolean;\n\t} = {},\n): string {\n\tconst params: string[] = [`inline=${options.inline !== false ? 1 : 0}`];\n\n\tif (options.width !== undefined) params.push(`width=${options.width}`);\n\tif (options.height !== undefined) params.push(`height=${options.height}`);\n\tif (options.name) {\n\t\tconst nameBase64 = Buffer.from(options.name).toString(\"base64\");\n\t\tparams.push(`name=${nameBase64}`);\n\t}\n\tif (options.preserveAspectRatio === false) {\n\t\tparams.push(\"preserveAspectRatio=0\");\n\t}\n\n\treturn `\\x1b]1337;File=${params.join(\";\")}:${base64Data}\\x07`;\n}\n\nexport function calculateImageRows(\n\timageDimensions: ImageDimensions,\n\ttargetWidthCells: number,\n\tcellDimensions: CellDimensions = { widthPx: 9, heightPx: 18 },\n): number {\n\tconst targetWidthPx = targetWidthCells * cellDimensions.widthPx;\n\tconst scale = targetWidthPx / imageDimensions.widthPx;\n\tconst scaledHeightPx = imageDimensions.heightPx * scale;\n\tconst rows = Math.ceil(scaledHeightPx / cellDimensions.heightPx);\n\treturn Math.max(1, rows);\n}\n\n/**\n * Parse image dimensions using the native Rust image module.\n * Auto-detects format from byte content (PNG, JPEG, GIF, WebP).\n */\nexport async function getImageDimensions(base64Data: string): Promise<ImageDimensions | null> {\n\tconst { parseImage: parse } = await import(\"@gsd/native/image\");\n\ttry {\n\t\tconst bytes = new Uint8Array(Buffer.from(base64Data, \"base64\"));\n\t\tconst handle = await parse(bytes);\n\t\treturn { widthPx: handle.width, heightPx: handle.height };\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function renderImage(\n\tbase64Data: string,\n\timageDimensions: ImageDimensions,\n\toptions: ImageRenderOptions = {},\n): { sequence: string; rows: number; imageId?: number } | null {\n\tconst caps = getCapabilities();\n\n\tif (!caps.images) {\n\t\treturn null;\n\t}\n\n\tconst maxWidth = options.maxWidthCells ?? 80;\n\tconst rows = calculateImageRows(imageDimensions, maxWidth, getCellDimensions());\n\n\tif (caps.images === \"kitty\") {\n\t\t// Only use imageId if explicitly provided - static images don't need IDs\n\t\tconst sequence = encodeKitty(base64Data, { columns: maxWidth, rows, imageId: options.imageId });\n\t\treturn { sequence, rows, imageId: options.imageId };\n\t}\n\n\tif (caps.images === \"iterm2\") {\n\t\tconst sequence = encodeITerm2(base64Data, {\n\t\t\twidth: maxWidth,\n\t\t\theight: \"auto\",\n\t\t\tpreserveAspectRatio: options.preserveAspectRatio ?? true,\n\t\t});\n\t\treturn { sequence, rows };\n\t}\n\n\treturn null;\n}\n\nexport function imageFallback(mimeType: string, dimensions?: ImageDimensions, filename?: string): string {\n\tconst parts: string[] = [];\n\tif (filename) parts.push(filename);\n\tparts.push(`[${mimeType}]`);\n\tif (dimensions) parts.push(`${dimensions.widthPx}x${dimensions.heightPx}`);\n\treturn `[Image: ${parts.join(\" \")}]`;\n}\n"]}
1
+ {"version":3,"file":"terminal-image.js","sourceRoot":"","sources":["../src/terminal-image.ts"],"names":[],"mappings":"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","sourcesContent":["export type ImageProtocol = \"kitty\" | \"iterm2\" | null;\n\nexport interface TerminalCapabilities {\n\timages: ImageProtocol;\n\ttrueColor: boolean;\n\thyperlinks: boolean;\n}\n\nexport interface CellDimensions {\n\twidthPx: number;\n\theightPx: number;\n}\n\nexport interface ImageDimensions {\n\twidthPx: number;\n\theightPx: number;\n}\n\nexport interface ImageRenderOptions {\n\tmaxWidthCells?: number;\n\tmaxHeightCells?: number;\n\tpreserveAspectRatio?: boolean;\n\t/** Kitty image ID. If provided, reuses/replaces existing image with this ID. */\n\timageId?: number;\n}\n\nlet cachedCapabilities: TerminalCapabilities | null = null;\n\n// Default cell dimensions - updated by TUI when terminal responds to query\nlet cellDimensions: CellDimensions = { widthPx: 9, heightPx: 18 };\n\nexport function getCellDimensions(): CellDimensions {\n\treturn cellDimensions;\n}\n\nexport function setCellDimensions(dims: CellDimensions): void {\n\tcellDimensions = dims;\n}\n\nexport function detectCapabilities(): TerminalCapabilities {\n\tconst termProgram = process.env.TERM_PROGRAM?.toLowerCase() || \"\";\n\tconst term = process.env.TERM?.toLowerCase() || \"\";\n\tconst colorTerm = process.env.COLORTERM?.toLowerCase() || \"\";\n\n\tif (process.env.KITTY_WINDOW_ID || termProgram === \"kitty\") {\n\t\treturn { images: \"kitty\", trueColor: true, hyperlinks: true };\n\t}\n\n\tif (termProgram === \"ghostty\" || term.includes(\"ghostty\") || process.env.GHOSTTY_RESOURCES_DIR) {\n\t\treturn { images: \"kitty\", trueColor: true, hyperlinks: true };\n\t}\n\n\tif (process.env.WEZTERM_PANE || termProgram === \"wezterm\") {\n\t\treturn { images: \"kitty\", trueColor: true, hyperlinks: true };\n\t}\n\n\tif (process.env.ITERM_SESSION_ID || termProgram === \"iterm.app\") {\n\t\treturn { images: \"iterm2\", trueColor: true, hyperlinks: true };\n\t}\n\n\tif (termProgram === \"vscode\") {\n\t\treturn { images: null, trueColor: true, hyperlinks: true };\n\t}\n\n\tif (termProgram === \"alacritty\") {\n\t\treturn { images: null, trueColor: true, hyperlinks: true };\n\t}\n\n\tconst trueColor = colorTerm === \"truecolor\" || colorTerm === \"24bit\";\n\treturn { images: null, trueColor, hyperlinks: true };\n}\n\nexport function getCapabilities(): TerminalCapabilities {\n\tif (!cachedCapabilities) {\n\t\tcachedCapabilities = detectCapabilities();\n\t}\n\treturn cachedCapabilities;\n}\n\nexport function resetCapabilitiesCache(): void {\n\tcachedCapabilities = null;\n}\n\nconst KITTY_PREFIX = \"\\x1b_G\";\nconst ITERM2_PREFIX = \"\\x1b]1337;File=\";\n\nexport function isImageLine(line: string): boolean {\n\t// Fast path: sequence at line start (single-row images)\n\tif (line.startsWith(KITTY_PREFIX) || line.startsWith(ITERM2_PREFIX)) {\n\t\treturn true;\n\t}\n\t// Slow path: sequence elsewhere (multi-row images have cursor-up prefix)\n\treturn line.includes(KITTY_PREFIX) || line.includes(ITERM2_PREFIX);\n}\n\n/**\n * Generate a random image ID for Kitty graphics protocol.\n * Uses random IDs to avoid collisions between different module instances\n * (e.g., main app vs extensions).\n */\nexport function allocateImageId(): number {\n\t// Use random ID in range [1, 0xffffffff] to avoid collisions\n\treturn Math.floor(Math.random() * 0xfffffffe) + 1;\n}\n\nexport function encodeKitty(\n\tbase64Data: string,\n\toptions: {\n\t\tcolumns?: number;\n\t\trows?: number;\n\t\timageId?: number;\n\t} = {},\n): string {\n\tconst CHUNK_SIZE = 4096;\n\n\tconst params: string[] = [\"a=T\", \"f=100\", \"q=2\"];\n\n\tif (options.columns) params.push(`c=${options.columns}`);\n\tif (options.rows) params.push(`r=${options.rows}`);\n\tif (options.imageId) params.push(`i=${options.imageId}`);\n\n\tif (base64Data.length <= CHUNK_SIZE) {\n\t\treturn `\\x1b_G${params.join(\",\")};${base64Data}\\x1b\\\\`;\n\t}\n\n\tconst chunks: string[] = [];\n\tlet offset = 0;\n\tlet isFirst = true;\n\n\twhile (offset < base64Data.length) {\n\t\tconst chunk = base64Data.slice(offset, offset + CHUNK_SIZE);\n\t\tconst isLast = offset + CHUNK_SIZE >= base64Data.length;\n\n\t\tif (isFirst) {\n\t\t\tchunks.push(`\\x1b_G${params.join(\",\")},m=1;${chunk}\\x1b\\\\`);\n\t\t\tisFirst = false;\n\t\t} else if (isLast) {\n\t\t\tchunks.push(`\\x1b_Gm=0;${chunk}\\x1b\\\\`);\n\t\t} else {\n\t\t\tchunks.push(`\\x1b_Gm=1;${chunk}\\x1b\\\\`);\n\t\t}\n\n\t\toffset += CHUNK_SIZE;\n\t}\n\n\treturn chunks.join(\"\");\n}\n\n/**\n * Delete a Kitty graphics image by ID.\n * Uses uppercase 'I' to also free the image data.\n */\nexport function deleteKittyImage(imageId: number): string {\n\treturn `\\x1b_Ga=d,d=I,i=${imageId}\\x1b\\\\`;\n}\n\n/**\n * Delete all visible Kitty graphics images.\n * Uses uppercase 'A' to also free the image data.\n */\nexport function deleteAllKittyImages(): string {\n\treturn `\\x1b_Ga=d,d=A\\x1b\\\\`;\n}\n\nexport function encodeITerm2(\n\tbase64Data: string,\n\toptions: {\n\t\twidth?: number | string;\n\t\theight?: number | string;\n\t\tname?: string;\n\t\tpreserveAspectRatio?: boolean;\n\t\tinline?: boolean;\n\t} = {},\n): string {\n\tconst params: string[] = [`inline=${options.inline !== false ? 1 : 0}`];\n\n\tif (options.width !== undefined) params.push(`width=${options.width}`);\n\tif (options.height !== undefined) params.push(`height=${options.height}`);\n\tif (options.name) {\n\t\tconst nameBase64 = Buffer.from(options.name).toString(\"base64\");\n\t\tparams.push(`name=${nameBase64}`);\n\t}\n\tif (options.preserveAspectRatio === false) {\n\t\tparams.push(\"preserveAspectRatio=0\");\n\t}\n\n\treturn `\\x1b]1337;File=${params.join(\";\")}:${base64Data}\\x07`;\n}\n\nexport function calculateImageRows(\n\timageDimensions: ImageDimensions,\n\ttargetWidthCells: number,\n\tcellDimensions: CellDimensions = { widthPx: 9, heightPx: 18 },\n): number {\n\tconst targetWidthPx = targetWidthCells * cellDimensions.widthPx;\n\tconst scale = targetWidthPx / imageDimensions.widthPx;\n\tconst scaledHeightPx = imageDimensions.heightPx * scale;\n\tconst rows = Math.ceil(scaledHeightPx / cellDimensions.heightPx);\n\treturn Math.max(1, rows);\n}\n\n/**\n * Parse image dimensions using the native Rust image module.\n * Auto-detects format from byte content (PNG, JPEG, GIF, WebP).\n */\nexport async function getImageDimensions(base64Data: string): Promise<ImageDimensions | null> {\n\tconst { parseImage: parse } = await import(\"@gsd/native/image\");\n\ttry {\n\t\tconst bytes = new Uint8Array(Buffer.from(base64Data, \"base64\"));\n\t\tconst handle = await parse(bytes);\n\t\treturn { widthPx: handle.width, heightPx: handle.height };\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function renderImage(\n\tbase64Data: string,\n\timageDimensions: ImageDimensions,\n\toptions: ImageRenderOptions = {},\n): { sequence: string; rows: number; imageId?: number } | null {\n\tconst caps = getCapabilities();\n\n\tif (!caps.images) {\n\t\treturn null;\n\t}\n\n\tconst maxWidth = options.maxWidthCells ?? 80;\n\tconst rows = calculateImageRows(imageDimensions, maxWidth, getCellDimensions());\n\n\tif (caps.images === \"kitty\") {\n\t\t// Only use imageId if explicitly provided - static images don't need IDs\n\t\tconst sequence = encodeKitty(base64Data, { columns: maxWidth, rows, imageId: options.imageId });\n\t\treturn { sequence, rows, imageId: options.imageId };\n\t}\n\n\tif (caps.images === \"iterm2\") {\n\t\tconst sequence = encodeITerm2(base64Data, {\n\t\t\twidth: maxWidth,\n\t\t\theight: \"auto\",\n\t\t\tpreserveAspectRatio: options.preserveAspectRatio ?? true,\n\t\t});\n\t\treturn { sequence, rows };\n\t}\n\n\treturn null;\n}\n\nexport function imageFallback(mimeType: string, dimensions?: ImageDimensions, filename?: string): string {\n\tconst parts: string[] = [];\n\tif (filename) parts.push(filename);\n\tparts.push(`[${mimeType}]`);\n\tif (dimensions) parts.push(`${dimensions.widthPx}x${dimensions.heightPx}`);\n\treturn `[Image: ${parts.join(\" \")}]`;\n}\n"]}
@@ -41,16 +41,11 @@ export function detectCapabilities(): TerminalCapabilities {
41
41
  const termProgram = process.env.TERM_PROGRAM?.toLowerCase() || "";
42
42
  const term = process.env.TERM?.toLowerCase() || "";
43
43
  const colorTerm = process.env.COLORTERM?.toLowerCase() || "";
44
- const isCmux = Boolean(process.env.CMUX_WORKSPACE_ID && process.env.CMUX_SURFACE_ID);
45
44
 
46
45
  if (process.env.KITTY_WINDOW_ID || termProgram === "kitty") {
47
46
  return { images: "kitty", trueColor: true, hyperlinks: true };
48
47
  }
49
48
 
50
- if (isCmux) {
51
- return { images: "kitty", trueColor: true, hyperlinks: true };
52
- }
53
-
54
49
  if (termProgram === "ghostty" || term.includes("ghostty") || process.env.GHOSTTY_RESOURCES_DIR) {
55
50
  return { images: "kitty", trueColor: true, hyperlinks: true };
56
51
  }