@kontextmind/kxm 0.7.205 → 0.7.206

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.
@@ -11,7 +11,7 @@
11
11
  "name": "kxm",
12
12
  "source": "./plugins/kxm",
13
13
  "description": "Durable workflows, peer agents, and kxm tui",
14
- "version": "0.7.205",
14
+ "version": "0.7.206",
15
15
  "category": "development",
16
16
  "tags": ["kxm", "multi-agent", "workflows", "mcp"]
17
17
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontextmind/kxm",
3
- "version": "0.7.205",
3
+ "version": "0.7.206",
4
4
  "description": "KXM local-first multi-agent orchestration and operator dashboard",
5
5
  "type": "module",
6
6
  "author": "KontextMind",
@@ -77,8 +77,8 @@
77
77
  ]
78
78
  },
79
79
  "dependencies": {
80
- "@anthropic-ai/claude-agent-sdk": "0.3.296",
81
- "@earendil-works/pi-tui": "1.1.0",
80
+ "@anthropic-ai/claude-agent-sdk": "0.3.303",
81
+ "@earendil-works/pi-tui": "1.3.0",
82
82
  "@modelcontextprotocol/sdk": "1.32.1",
83
83
  "yaml": "^2.9.0"
84
84
  },
@@ -95,9 +95,9 @@
95
95
  }
96
96
  },
97
97
  "devDependencies": {
98
- "@earendil-works/pi-coding-agent": "1.1.0",
98
+ "@earendil-works/pi-coding-agent": "1.3.0",
99
99
  "@modelcontextprotocol/sdk": "1.32.1",
100
- "@playwright/test": "1.64.0",
100
+ "@playwright/test": "1.64.1",
101
101
  "@types/node": "26.6.5",
102
102
  "ajv": "8.20.0",
103
103
  "commander": "15.0.0",
@@ -105,7 +105,7 @@
105
105
  "markdownlint-cli2": "0.23.3",
106
106
  "nx": "23.3.0",
107
107
  "tsx": "^4.23.13",
108
- "typebox": "1.3.36",
108
+ "typebox": "1.4.2",
109
109
  "typescript": "7.0.2"
110
110
  },
111
111
  "files": [
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "kxm",
4
4
  "displayName": "KXM",
5
- "version": "0.7.205",
5
+ "version": "0.7.206",
6
6
  "description": "Headless multi-agent orchestration, durable workflows, and a live operator dashboard for Pi and Claude Code",
7
7
  "author": {
8
8
  "name": "KontextMind",
@@ -44219,7 +44219,7 @@ function extractSegments(line, beforeEnd, afterStart, afterLen, strictAfter = fa
44219
44219
  pooledStyleTracker.process(ansi.code);
44220
44220
  if (currentCol < beforeEnd) {
44221
44221
  pendingAnsiBefore += ansi.code;
44222
- } else if (currentCol >= afterStart && currentCol < afterEnd && afterStarted) {
44222
+ } else if (currentCol >= afterStart && currentCol < afterEnd && (afterStarted || ansi.code.startsWith("\x1B_"))) {
44223
44223
  after += ansi.code;
44224
44224
  }
44225
44225
  i += ansi.length;
@@ -44400,6 +44400,8 @@ var SYMBOL_KEYS = /* @__PURE__ */ new Set([
44400
44400
  ">",
44401
44401
  "?"
44402
44402
  ]);
44403
+ var SHIFTED_SYMBOL_KEYS = new Set("~!@#$%^&*()_+|{}:<>?");
44404
+ var MODIFIER_NAMES = ["shift", "alt", "ctrl", "super"];
44403
44405
  var MODIFIERS = {
44404
44406
  shift: 1,
44405
44407
  alt: 2,
@@ -44486,6 +44488,14 @@ function normalizeShiftedLetterIdentityCodepoint(codepoint, modifier) {
44486
44488
  }
44487
44489
  return codepoint;
44488
44490
  }
44491
+ function getLogicalShiftedSymbolCandidate(codepoint, modifier, allowedSymbols) {
44492
+ if (codepoint === void 0 || (modifier & MODIFIERS.shift) === 0)
44493
+ return void 0;
44494
+ const normalizedCodepoint = normalizeKittyFunctionalCodepoint(codepoint);
44495
+ if (!allowedSymbols.has(String.fromCharCode(normalizedCodepoint)))
44496
+ return void 0;
44497
+ return { codepoint: normalizedCodepoint, modifier: modifier & ~MODIFIERS.shift };
44498
+ }
44489
44499
  var LEGACY_KEY_SEQUENCES = {
44490
44500
  up: ["\x1B[A", "\x1BOA"],
44491
44501
  down: ["\x1B[B", "\x1BOB"],
@@ -44681,12 +44691,16 @@ function matchesKittySequence(data, expectedCodepoint, expectedModifier) {
44681
44691
  return false;
44682
44692
  const actualMod = parsed.modifier & ~LOCK_MASK;
44683
44693
  const expectedMod = expectedModifier & ~LOCK_MASK;
44684
- if (actualMod !== expectedMod)
44685
- return false;
44686
44694
  const normalizedCodepoint = normalizeShiftedLetterIdentityCodepoint(normalizeKittyFunctionalCodepoint(parsed.codepoint), parsed.modifier);
44687
44695
  const normalizedExpectedCodepoint = normalizeShiftedLetterIdentityCodepoint(normalizeKittyFunctionalCodepoint(expectedCodepoint), expectedModifier);
44688
- if (normalizedCodepoint === normalizedExpectedCodepoint)
44696
+ if (actualMod === expectedMod && normalizedCodepoint === normalizedExpectedCodepoint)
44697
+ return true;
44698
+ const logicalShiftedSymbol = getLogicalShiftedSymbolCandidate(parsed.shiftedKey, actualMod, SYMBOL_KEYS);
44699
+ if (logicalShiftedSymbol && (logicalShiftedSymbol.modifier & ~LOCK_MASK) === expectedMod && logicalShiftedSymbol.codepoint === normalizeKittyFunctionalCodepoint(expectedCodepoint)) {
44689
44700
  return true;
44701
+ }
44702
+ if (actualMod !== expectedMod)
44703
+ return false;
44690
44704
  if (parsed.baseLayoutKey !== void 0 && parsed.baseLayoutKey === expectedCodepoint) {
44691
44705
  const cp = normalizedCodepoint;
44692
44706
  const isLatinLetter = cp >= 97 && cp <= 122;
@@ -44735,12 +44749,14 @@ function isDigitKey(key) {
44735
44749
  return key >= "0" && key <= "9";
44736
44750
  }
44737
44751
  function matchesPrintableModifyOtherKeys(data, expectedKeycode, expectedModifier) {
44738
- if (expectedModifier === 0)
44739
- return false;
44740
44752
  const parsed = parseModifyOtherKeysSequence(data);
44741
- if (!parsed || parsed.modifier !== expectedModifier)
44753
+ if (!parsed)
44742
44754
  return false;
44743
- return normalizeShiftedLetterIdentityCodepoint(parsed.codepoint, parsed.modifier) === normalizeShiftedLetterIdentityCodepoint(expectedKeycode, expectedModifier);
44755
+ const physicalMatch = parsed.modifier === expectedModifier && normalizeShiftedLetterIdentityCodepoint(parsed.codepoint, parsed.modifier) === normalizeShiftedLetterIdentityCodepoint(expectedKeycode, expectedModifier);
44756
+ if (physicalMatch)
44757
+ return true;
44758
+ const logicalShiftedSymbol = getLogicalShiftedSymbolCandidate(parsed.codepoint, parsed.modifier, SHIFTED_SYMBOL_KEYS);
44759
+ return logicalShiftedSymbol?.modifier === expectedModifier && logicalShiftedSymbol.codepoint === expectedKeycode;
44744
44760
  }
44745
44761
  function formatKeyNameWithModifiers(keyName, modifier) {
44746
44762
  const mods = [];
@@ -44759,16 +44775,25 @@ function formatKeyNameWithModifiers(keyName, modifier) {
44759
44775
  return mods.length > 0 ? `${mods.join("+")}+${keyName}` : keyName;
44760
44776
  }
44761
44777
  function parseKeyId(keyId) {
44762
- const parts = keyId.toLowerCase().split("+");
44763
- const key = parts[parts.length - 1];
44764
- if (!key)
44778
+ let key = keyId.toLowerCase();
44779
+ const modifiers = /* @__PURE__ */ new Set();
44780
+ while (true) {
44781
+ const modifier = MODIFIER_NAMES.find((name) => key.startsWith(`${name}+`));
44782
+ if (!modifier)
44783
+ break;
44784
+ if (modifiers.has(modifier))
44785
+ return null;
44786
+ modifiers.add(modifier);
44787
+ key = key.slice(modifier.length + 1);
44788
+ }
44789
+ if (!key || key.includes("+") && key !== "+")
44765
44790
  return null;
44766
44791
  return {
44767
44792
  key,
44768
- ctrl: parts.includes("ctrl"),
44769
- shift: parts.includes("shift"),
44770
- alt: parts.includes("alt"),
44771
- super: parts.includes("super")
44793
+ ctrl: modifiers.has("ctrl"),
44794
+ shift: modifiers.has("shift"),
44795
+ alt: modifiers.has("alt"),
44796
+ super: modifiers.has("super")
44772
44797
  };
44773
44798
  }
44774
44799
  function matchesKey(data, keyId) {
@@ -45010,7 +45035,7 @@ function matchesKey(data, keyId) {
45010
45035
  if (modifier !== 0) {
45011
45036
  return matchesKittySequence(data, codepoint, modifier) || matchesPrintableModifyOtherKeys(data, codepoint, modifier);
45012
45037
  }
45013
- return data === key || matchesKittySequence(data, codepoint, 0);
45038
+ return data === key || matchesKittySequence(data, codepoint, 0) || matchesPrintableModifyOtherKeys(data, codepoint, 0);
45014
45039
  }
45015
45040
  return false;
45016
45041
  }
@@ -45065,11 +45090,13 @@ function formatParsedKey(codepoint, modifier, baseLayoutKey) {
45065
45090
  function parseKey(data) {
45066
45091
  const kitty = parseKittySequence(data);
45067
45092
  if (kitty) {
45068
- return formatParsedKey(kitty.codepoint, kitty.modifier, kitty.baseLayoutKey);
45093
+ const logicalShiftedSymbol = getLogicalShiftedSymbolCandidate(kitty.shiftedKey, kitty.modifier, SYMBOL_KEYS);
45094
+ return formatParsedKey(logicalShiftedSymbol?.codepoint ?? kitty.codepoint, logicalShiftedSymbol?.modifier ?? kitty.modifier, kitty.baseLayoutKey);
45069
45095
  }
45070
45096
  const modifyOtherKeys = parseModifyOtherKeysSequence(data);
45071
45097
  if (modifyOtherKeys) {
45072
- return formatParsedKey(modifyOtherKeys.codepoint, modifyOtherKeys.modifier);
45098
+ const logicalShiftedSymbol = getLogicalShiftedSymbolCandidate(modifyOtherKeys.codepoint, modifyOtherKeys.modifier, SHIFTED_SYMBOL_KEYS);
45099
+ return formatParsedKey(logicalShiftedSymbol?.codepoint ?? modifyOtherKeys.codepoint, logicalShiftedSymbol?.modifier ?? modifyOtherKeys.modifier);
45073
45100
  }
45074
45101
  if (_kittyProtocolActive) {
45075
45102
  if (data === "\x1B\r" || data === "\n")
@@ -45282,7 +45309,7 @@ function detectCapabilitiesFromEnvironment(tmuxForwardsHyperlink) {
45282
45309
  return { images: null, trueColor: hasTrueColorHint, hyperlinks: false };
45283
45310
  }
45284
45311
  if (termProgram === "herdr") {
45285
- return { images: null, trueColor: hasTrueColorHint, hyperlinks: true };
45312
+ return { images: "kitty", trueColor: hasTrueColorHint, hyperlinks: true };
45286
45313
  }
45287
45314
  if (process.env.KITTY_WINDOW_ID || termProgram === "kitty") {
45288
45315
  return { images: "kitty", trueColor: true, hyperlinks: true };
@@ -45512,6 +45539,12 @@ function isFocusable(component) {
45512
45539
  return component !== null && "focused" in component;
45513
45540
  }
45514
45541
  var CURSOR_MARKER = "\x1B_pi:c\x07";
45542
+ var FAKE_CURSOR_START = "\x1B_pi:fc\x07";
45543
+ var FAKE_CURSOR_END = "\x1B_pi:/fc\x07";
45544
+ var FOCUSED_FAKE_CURSOR = /(\x1b_pi:c\x07)\x1b_pi:fc\x07(.*?)(?:\x1b_pi:\/fc\x07|$)/s;
45545
+ function renderFakeCursor(text) {
45546
+ return `${FAKE_CURSOR_START}${text}${FAKE_CURSOR_END}`;
45547
+ }
45515
45548
  function parseSizeValue(value, referenceSize) {
45516
45549
  if (value === void 0)
45517
45550
  return void 0;
@@ -46314,7 +46347,7 @@ var TuiBase = class _TuiBase extends Container {
46314
46347
  for (const entry of visibleEntries) {
46315
46348
  const { component, options } = entry;
46316
46349
  const { width, maxHeight } = this.resolveOverlayLayout(options, 0, termWidth, termHeight);
46317
- let overlayLines = component.render(width);
46350
+ let overlayLines = this.resolveFakeCursors(component.render(width));
46318
46351
  if (maxHeight !== void 0 && overlayLines.length > maxHeight) {
46319
46352
  overlayLines = overlayLines.slice(0, maxHeight);
46320
46353
  }
@@ -46381,6 +46414,19 @@ var TuiBase = class _TuiBase extends Container {
46381
46414
  }
46382
46415
  return null;
46383
46416
  }
46417
+ /**
46418
+ * Resolve fake cursor markers to reverse video. When the hardware cursor is shown, a fake cursor
46419
+ * directly after CURSOR_MARKER is dropped because the terminal cursor is drawn there. Fake cursors
46420
+ * of components without focus have no CURSOR_MARKER and stay visible.
46421
+ */
46422
+ resolveFakeCursors(lines) {
46423
+ return lines.map((line) => {
46424
+ if (!line.includes(FAKE_CURSOR_START))
46425
+ return line;
46426
+ const resolved = this.showHardwareCursor ? line.replace(FOCUSED_FAKE_CURSOR, "$1$2") : line;
46427
+ return resolved.replaceAll(FAKE_CURSOR_START, "\x1B[7m").replaceAll(FAKE_CURSOR_END, "\x1B[27m");
46428
+ });
46429
+ }
46384
46430
  /**
46385
46431
  * Query the terminal's theme colors: the default foreground (OSC 10), the default background
46386
46432
  * (OSC 11), and ANSI colors 0-15 (OSC 4), followed by a DA1 request that marks the end of the
@@ -47430,7 +47476,7 @@ var Input = class {
47430
47476
  const atCursor2 = graphemes2[0]?.segment ?? " ";
47431
47477
  const afterCursor2 = placeholder.slice(atCursor2.length);
47432
47478
  const marker2 = this.focused ? CURSOR_MARKER : "";
47433
- const cursorChar2 = `\x1B[7m${this.placeholderStyle(atCursor2)}\x1B[27m`;
47479
+ const cursorChar2 = renderFakeCursor(this.placeholderStyle(atCursor2));
47434
47480
  const textWithCursor2 = marker2 + cursorChar2 + this.placeholderStyle(afterCursor2);
47435
47481
  const padding2 = " ".repeat(Math.max(0, availableWidth - visibleWidth(textWithCursor2)));
47436
47482
  return [this.prompt + textWithCursor2 + padding2];
@@ -47469,7 +47515,7 @@ var Input = class {
47469
47515
  const atCursor = cursorGrapheme?.segment ?? " ";
47470
47516
  const afterCursor = visibleText.slice(cursorDisplay + atCursor.length);
47471
47517
  const marker = this.focused ? CURSOR_MARKER : "";
47472
- const cursorChar = `\x1B[7m${atCursor}\x1B[27m`;
47518
+ const cursorChar = renderFakeCursor(atCursor);
47473
47519
  const textWithCursor = beforeCursor + marker + cursorChar + afterCursor;
47474
47520
  const visualLength = visibleWidth(textWithCursor);
47475
47521
  const padding = " ".repeat(Math.max(0, availableWidth - visualLength));
@@ -47941,6 +47987,10 @@ var DEFAULT_SEQUENCE_TIMEOUT_MS = 50;
47941
47987
  var DEFAULT_ESCAPE_TIMEOUT_MS = 10;
47942
47988
  var BRACKETED_PASTE_START = "\x1B[200~";
47943
47989
  var BRACKETED_PASTE_END = "\x1B[201~";
47990
+ var TERMINAL_REPLY_HEADER_PATTERN = /^\x1b(?:\](?:1[01];|4;\d{1,3};|7501;\?)|P>\||_G(?:[a-z]=\d+,)*[a-z]=\d+;)/;
47991
+ function isControlString(data) {
47992
+ return data.startsWith(`${ESC}]`) || data.startsWith(`${ESC}P`) || data.startsWith(`${ESC}_`);
47993
+ }
47944
47994
  function isCompleteSequence(data) {
47945
47995
  if (!data.startsWith(ESC)) {
47946
47996
  return "not-escape";
@@ -48040,8 +48090,20 @@ function extractCompleteSequences(buffer) {
48040
48090
  while (pos < buffer.length) {
48041
48091
  const remaining = buffer.slice(pos);
48042
48092
  if (remaining.startsWith(ESC)) {
48093
+ const terminalReply = isControlString(remaining) && TERMINAL_REPLY_HEADER_PATTERN.test(remaining);
48043
48094
  let seqEnd = 1;
48044
48095
  while (seqEnd <= remaining.length) {
48096
+ if (terminalReply && seqEnd > 2) {
48097
+ const char = remaining[seqEnd - 1];
48098
+ if (char === "" || char === "") {
48099
+ pos += seqEnd;
48100
+ break;
48101
+ }
48102
+ if (char === ESC && seqEnd < remaining.length && remaining[seqEnd] !== "\\") {
48103
+ pos += seqEnd - 1;
48104
+ break;
48105
+ }
48106
+ }
48045
48107
  const candidate = remaining.slice(0, seqEnd);
48046
48108
  const status = isCompleteSequence(candidate);
48047
48109
  if (status === "complete") {
@@ -48163,11 +48225,18 @@ var StdinBuffer = class extends EventEmitter2 {
48163
48225
  this.emitDataSequence(sequence);
48164
48226
  }
48165
48227
  if (this.buffer.length > 0) {
48228
+ if (TERMINAL_REPLY_HEADER_PATTERN.test(this.buffer))
48229
+ return;
48166
48230
  const timeoutMs = this.buffer === ESC ? this.escapeTimeoutMs : this.timeoutMs;
48167
48231
  this.timeout = setTimeout(() => {
48168
48232
  const flushed = this.flush();
48169
48233
  for (const sequence of flushed) {
48170
- this.emitDataSequence(sequence);
48234
+ if (sequence.length > 2 && isControlString(sequence)) {
48235
+ this.emitDataSequence(sequence.slice(0, 2));
48236
+ this.process(sequence.slice(2));
48237
+ } else {
48238
+ this.emitDataSequence(sequence);
48239
+ }
48171
48240
  }
48172
48241
  }, timeoutMs);
48173
48242
  }
@@ -49525,7 +49594,7 @@ var TuiAltScreen = class extends TuiBase {
49525
49594
  this.terminal.write(`${BEGIN_SYNCHRONIZED_OUTPUT}${EXIT_ALT_SCREEN}\x1B[?25h${END_SYNCHRONIZED_OUTPUT}`);
49526
49595
  } else {
49527
49596
  const width = Math.max(1, this.terminal.columns);
49528
- const documentLines = this.render(width).map((line) => line.replace(OSC133_ZONE_PREFIX2, ""));
49597
+ const documentLines = this.resolveFakeCursors(this.render(width)).map((line) => line.replace(OSC133_ZONE_PREFIX2, ""));
49529
49598
  this.lastDocument = this.applyLineResets(documentLines.map((line) => line.replaceAll(CURSOR_MARKER, ""))).map((line) => isImageLine(line) || visibleWidth(line) <= width ? line : sliceByColumn(line, 0, width, true));
49530
49599
  let buffer = `${BEGIN_SYNCHRONIZED_OUTPUT}${EXIT_ALT_SCREEN}${DISABLE_AUTOWRAP}`;
49531
49600
  for (let row = 0; row < this.lastDocument.length; row++) {
@@ -49810,8 +49879,6 @@ var TuiAltScreen = class extends TuiBase {
49810
49879
  this.requestRender();
49811
49880
  return { consume: true };
49812
49881
  }
49813
- if (this.shouldDeferViewportInputToOverlay())
49814
- return void 0;
49815
49882
  this.routeWheel(wheelEvent, wheelDelta);
49816
49883
  return { consume: true };
49817
49884
  }
@@ -50682,7 +50749,7 @@ var TuiAltScreen = class extends TuiBase {
50682
50749
  if (this.refreshSearch(nextLayout)) {
50683
50750
  nextLayout = renderLayoutFrame(root, width, height, () => this.requestRender());
50684
50751
  }
50685
- let screen = nextLayout.lines.map((line) => line.replace(OSC133_ZONE_PREFIX2, ""));
50752
+ let screen = this.resolveFakeCursors(nextLayout.lines.map((line) => line.replace(OSC133_ZONE_PREFIX2, "")));
50686
50753
  screen = this.applySearchHighlights(screen, nextLayout);
50687
50754
  screen = this.compositeScrollToEndIndicator(screen, nextLayout, width);
50688
50755
  screen = this.compositeOverlays(screen, width, height);
@@ -40794,7 +40794,7 @@ function extractSegments(line, beforeEnd, afterStart, afterLen, strictAfter = fa
40794
40794
  pooledStyleTracker.process(ansi.code);
40795
40795
  if (currentCol < beforeEnd) {
40796
40796
  pendingAnsiBefore += ansi.code;
40797
- } else if (currentCol >= afterStart && currentCol < afterEnd && afterStarted) {
40797
+ } else if (currentCol >= afterStart && currentCol < afterEnd && (afterStarted || ansi.code.startsWith("\x1B_"))) {
40798
40798
  after += ansi.code;
40799
40799
  }
40800
40800
  i += ansi.length;
@@ -40972,6 +40972,8 @@ var SYMBOL_KEYS = /* @__PURE__ */ new Set([
40972
40972
  ">",
40973
40973
  "?"
40974
40974
  ]);
40975
+ var SHIFTED_SYMBOL_KEYS = new Set("~!@#$%^&*()_+|{}:<>?");
40976
+ var MODIFIER_NAMES = ["shift", "alt", "ctrl", "super"];
40975
40977
  var MODIFIERS = {
40976
40978
  shift: 1,
40977
40979
  alt: 2,
@@ -41058,6 +41060,14 @@ function normalizeShiftedLetterIdentityCodepoint(codepoint, modifier) {
41058
41060
  }
41059
41061
  return codepoint;
41060
41062
  }
41063
+ function getLogicalShiftedSymbolCandidate(codepoint, modifier, allowedSymbols) {
41064
+ if (codepoint === void 0 || (modifier & MODIFIERS.shift) === 0)
41065
+ return void 0;
41066
+ const normalizedCodepoint = normalizeKittyFunctionalCodepoint(codepoint);
41067
+ if (!allowedSymbols.has(String.fromCharCode(normalizedCodepoint)))
41068
+ return void 0;
41069
+ return { codepoint: normalizedCodepoint, modifier: modifier & ~MODIFIERS.shift };
41070
+ }
41061
41071
  var LEGACY_KEY_SEQUENCES = {
41062
41072
  up: ["\x1B[A", "\x1BOA"],
41063
41073
  down: ["\x1B[B", "\x1BOB"],
@@ -41253,12 +41263,16 @@ function matchesKittySequence(data, expectedCodepoint, expectedModifier) {
41253
41263
  return false;
41254
41264
  const actualMod = parsed.modifier & ~LOCK_MASK;
41255
41265
  const expectedMod = expectedModifier & ~LOCK_MASK;
41256
- if (actualMod !== expectedMod)
41257
- return false;
41258
41266
  const normalizedCodepoint = normalizeShiftedLetterIdentityCodepoint(normalizeKittyFunctionalCodepoint(parsed.codepoint), parsed.modifier);
41259
41267
  const normalizedExpectedCodepoint = normalizeShiftedLetterIdentityCodepoint(normalizeKittyFunctionalCodepoint(expectedCodepoint), expectedModifier);
41260
- if (normalizedCodepoint === normalizedExpectedCodepoint)
41268
+ if (actualMod === expectedMod && normalizedCodepoint === normalizedExpectedCodepoint)
41261
41269
  return true;
41270
+ const logicalShiftedSymbol = getLogicalShiftedSymbolCandidate(parsed.shiftedKey, actualMod, SYMBOL_KEYS);
41271
+ if (logicalShiftedSymbol && (logicalShiftedSymbol.modifier & ~LOCK_MASK) === expectedMod && logicalShiftedSymbol.codepoint === normalizeKittyFunctionalCodepoint(expectedCodepoint)) {
41272
+ return true;
41273
+ }
41274
+ if (actualMod !== expectedMod)
41275
+ return false;
41262
41276
  if (parsed.baseLayoutKey !== void 0 && parsed.baseLayoutKey === expectedCodepoint) {
41263
41277
  const cp = normalizedCodepoint;
41264
41278
  const isLatinLetter = cp >= 97 && cp <= 122;
@@ -41307,12 +41321,14 @@ function isDigitKey(key) {
41307
41321
  return key >= "0" && key <= "9";
41308
41322
  }
41309
41323
  function matchesPrintableModifyOtherKeys(data, expectedKeycode, expectedModifier) {
41310
- if (expectedModifier === 0)
41311
- return false;
41312
41324
  const parsed = parseModifyOtherKeysSequence(data);
41313
- if (!parsed || parsed.modifier !== expectedModifier)
41325
+ if (!parsed)
41314
41326
  return false;
41315
- return normalizeShiftedLetterIdentityCodepoint(parsed.codepoint, parsed.modifier) === normalizeShiftedLetterIdentityCodepoint(expectedKeycode, expectedModifier);
41327
+ const physicalMatch = parsed.modifier === expectedModifier && normalizeShiftedLetterIdentityCodepoint(parsed.codepoint, parsed.modifier) === normalizeShiftedLetterIdentityCodepoint(expectedKeycode, expectedModifier);
41328
+ if (physicalMatch)
41329
+ return true;
41330
+ const logicalShiftedSymbol = getLogicalShiftedSymbolCandidate(parsed.codepoint, parsed.modifier, SHIFTED_SYMBOL_KEYS);
41331
+ return logicalShiftedSymbol?.modifier === expectedModifier && logicalShiftedSymbol.codepoint === expectedKeycode;
41316
41332
  }
41317
41333
  function formatKeyNameWithModifiers(keyName, modifier) {
41318
41334
  const mods = [];
@@ -41331,16 +41347,25 @@ function formatKeyNameWithModifiers(keyName, modifier) {
41331
41347
  return mods.length > 0 ? `${mods.join("+")}+${keyName}` : keyName;
41332
41348
  }
41333
41349
  function parseKeyId(keyId) {
41334
- const parts = keyId.toLowerCase().split("+");
41335
- const key = parts[parts.length - 1];
41336
- if (!key)
41350
+ let key = keyId.toLowerCase();
41351
+ const modifiers = /* @__PURE__ */ new Set();
41352
+ while (true) {
41353
+ const modifier = MODIFIER_NAMES.find((name) => key.startsWith(`${name}+`));
41354
+ if (!modifier)
41355
+ break;
41356
+ if (modifiers.has(modifier))
41357
+ return null;
41358
+ modifiers.add(modifier);
41359
+ key = key.slice(modifier.length + 1);
41360
+ }
41361
+ if (!key || key.includes("+") && key !== "+")
41337
41362
  return null;
41338
41363
  return {
41339
41364
  key,
41340
- ctrl: parts.includes("ctrl"),
41341
- shift: parts.includes("shift"),
41342
- alt: parts.includes("alt"),
41343
- super: parts.includes("super")
41365
+ ctrl: modifiers.has("ctrl"),
41366
+ shift: modifiers.has("shift"),
41367
+ alt: modifiers.has("alt"),
41368
+ super: modifiers.has("super")
41344
41369
  };
41345
41370
  }
41346
41371
  function matchesKey(data, keyId) {
@@ -41582,7 +41607,7 @@ function matchesKey(data, keyId) {
41582
41607
  if (modifier !== 0) {
41583
41608
  return matchesKittySequence(data, codepoint, modifier) || matchesPrintableModifyOtherKeys(data, codepoint, modifier);
41584
41609
  }
41585
- return data === key || matchesKittySequence(data, codepoint, 0);
41610
+ return data === key || matchesKittySequence(data, codepoint, 0) || matchesPrintableModifyOtherKeys(data, codepoint, 0);
41586
41611
  }
41587
41612
  return false;
41588
41613
  }
@@ -41637,11 +41662,13 @@ function formatParsedKey(codepoint, modifier, baseLayoutKey) {
41637
41662
  function parseKey(data) {
41638
41663
  const kitty = parseKittySequence(data);
41639
41664
  if (kitty) {
41640
- return formatParsedKey(kitty.codepoint, kitty.modifier, kitty.baseLayoutKey);
41665
+ const logicalShiftedSymbol = getLogicalShiftedSymbolCandidate(kitty.shiftedKey, kitty.modifier, SYMBOL_KEYS);
41666
+ return formatParsedKey(logicalShiftedSymbol?.codepoint ?? kitty.codepoint, logicalShiftedSymbol?.modifier ?? kitty.modifier, kitty.baseLayoutKey);
41641
41667
  }
41642
41668
  const modifyOtherKeys = parseModifyOtherKeysSequence(data);
41643
41669
  if (modifyOtherKeys) {
41644
- return formatParsedKey(modifyOtherKeys.codepoint, modifyOtherKeys.modifier);
41670
+ const logicalShiftedSymbol = getLogicalShiftedSymbolCandidate(modifyOtherKeys.codepoint, modifyOtherKeys.modifier, SHIFTED_SYMBOL_KEYS);
41671
+ return formatParsedKey(logicalShiftedSymbol?.codepoint ?? modifyOtherKeys.codepoint, logicalShiftedSymbol?.modifier ?? modifyOtherKeys.modifier);
41645
41672
  }
41646
41673
  if (_kittyProtocolActive) {
41647
41674
  if (data === "\x1B\r" || data === "\n")
@@ -41854,7 +41881,7 @@ function detectCapabilitiesFromEnvironment(tmuxForwardsHyperlink) {
41854
41881
  return { images: null, trueColor: hasTrueColorHint, hyperlinks: false };
41855
41882
  }
41856
41883
  if (termProgram === "herdr") {
41857
- return { images: null, trueColor: hasTrueColorHint, hyperlinks: true };
41884
+ return { images: "kitty", trueColor: hasTrueColorHint, hyperlinks: true };
41858
41885
  }
41859
41886
  if (process.env.KITTY_WINDOW_ID || termProgram === "kitty") {
41860
41887
  return { images: "kitty", trueColor: true, hyperlinks: true };
@@ -42084,6 +42111,12 @@ function isFocusable(component) {
42084
42111
  return component !== null && "focused" in component;
42085
42112
  }
42086
42113
  var CURSOR_MARKER = "\x1B_pi:c\x07";
42114
+ var FAKE_CURSOR_START = "\x1B_pi:fc\x07";
42115
+ var FAKE_CURSOR_END = "\x1B_pi:/fc\x07";
42116
+ var FOCUSED_FAKE_CURSOR = /(\x1b_pi:c\x07)\x1b_pi:fc\x07(.*?)(?:\x1b_pi:\/fc\x07|$)/s;
42117
+ function renderFakeCursor(text) {
42118
+ return `${FAKE_CURSOR_START}${text}${FAKE_CURSOR_END}`;
42119
+ }
42087
42120
  function parseSizeValue(value, referenceSize) {
42088
42121
  if (value === void 0)
42089
42122
  return void 0;
@@ -42886,7 +42919,7 @@ var TuiBase = class _TuiBase extends Container {
42886
42919
  for (const entry of visibleEntries) {
42887
42920
  const { component, options } = entry;
42888
42921
  const { width, maxHeight } = this.resolveOverlayLayout(options, 0, termWidth, termHeight);
42889
- let overlayLines = component.render(width);
42922
+ let overlayLines = this.resolveFakeCursors(component.render(width));
42890
42923
  if (maxHeight !== void 0 && overlayLines.length > maxHeight) {
42891
42924
  overlayLines = overlayLines.slice(0, maxHeight);
42892
42925
  }
@@ -42953,6 +42986,19 @@ var TuiBase = class _TuiBase extends Container {
42953
42986
  }
42954
42987
  return null;
42955
42988
  }
42989
+ /**
42990
+ * Resolve fake cursor markers to reverse video. When the hardware cursor is shown, a fake cursor
42991
+ * directly after CURSOR_MARKER is dropped because the terminal cursor is drawn there. Fake cursors
42992
+ * of components without focus have no CURSOR_MARKER and stay visible.
42993
+ */
42994
+ resolveFakeCursors(lines) {
42995
+ return lines.map((line) => {
42996
+ if (!line.includes(FAKE_CURSOR_START))
42997
+ return line;
42998
+ const resolved = this.showHardwareCursor ? line.replace(FOCUSED_FAKE_CURSOR, "$1$2") : line;
42999
+ return resolved.replaceAll(FAKE_CURSOR_START, "\x1B[7m").replaceAll(FAKE_CURSOR_END, "\x1B[27m");
43000
+ });
43001
+ }
42956
43002
  /**
42957
43003
  * Query the terminal's theme colors: the default foreground (OSC 10), the default background
42958
43004
  * (OSC 11), and ANSI colors 0-15 (OSC 4), followed by a DA1 request that marks the end of the
@@ -44002,7 +44048,7 @@ var Input = class {
44002
44048
  const atCursor2 = graphemes2[0]?.segment ?? " ";
44003
44049
  const afterCursor2 = placeholder.slice(atCursor2.length);
44004
44050
  const marker2 = this.focused ? CURSOR_MARKER : "";
44005
- const cursorChar2 = `\x1B[7m${this.placeholderStyle(atCursor2)}\x1B[27m`;
44051
+ const cursorChar2 = renderFakeCursor(this.placeholderStyle(atCursor2));
44006
44052
  const textWithCursor2 = marker2 + cursorChar2 + this.placeholderStyle(afterCursor2);
44007
44053
  const padding2 = " ".repeat(Math.max(0, availableWidth - visibleWidth(textWithCursor2)));
44008
44054
  return [this.prompt + textWithCursor2 + padding2];
@@ -44041,7 +44087,7 @@ var Input = class {
44041
44087
  const atCursor = cursorGrapheme?.segment ?? " ";
44042
44088
  const afterCursor = visibleText.slice(cursorDisplay + atCursor.length);
44043
44089
  const marker = this.focused ? CURSOR_MARKER : "";
44044
- const cursorChar = `\x1B[7m${atCursor}\x1B[27m`;
44090
+ const cursorChar = renderFakeCursor(atCursor);
44045
44091
  const textWithCursor = beforeCursor + marker + cursorChar + afterCursor;
44046
44092
  const visualLength = visibleWidth(textWithCursor);
44047
44093
  const padding = " ".repeat(Math.max(0, availableWidth - visualLength));
@@ -45306,7 +45352,7 @@ var TuiAltScreen = class extends TuiBase {
45306
45352
  this.terminal.write(`${BEGIN_SYNCHRONIZED_OUTPUT}${EXIT_ALT_SCREEN}\x1B[?25h${END_SYNCHRONIZED_OUTPUT}`);
45307
45353
  } else {
45308
45354
  const width = Math.max(1, this.terminal.columns);
45309
- const documentLines = this.render(width).map((line) => line.replace(OSC133_ZONE_PREFIX2, ""));
45355
+ const documentLines = this.resolveFakeCursors(this.render(width)).map((line) => line.replace(OSC133_ZONE_PREFIX2, ""));
45310
45356
  this.lastDocument = this.applyLineResets(documentLines.map((line) => line.replaceAll(CURSOR_MARKER, ""))).map((line) => isImageLine(line) || visibleWidth(line) <= width ? line : sliceByColumn(line, 0, width, true));
45311
45357
  let buffer = `${BEGIN_SYNCHRONIZED_OUTPUT}${EXIT_ALT_SCREEN}${DISABLE_AUTOWRAP}`;
45312
45358
  for (let row = 0; row < this.lastDocument.length; row++) {
@@ -45591,8 +45637,6 @@ var TuiAltScreen = class extends TuiBase {
45591
45637
  this.requestRender();
45592
45638
  return { consume: true };
45593
45639
  }
45594
- if (this.shouldDeferViewportInputToOverlay())
45595
- return void 0;
45596
45640
  this.routeWheel(wheelEvent, wheelDelta);
45597
45641
  return { consume: true };
45598
45642
  }
@@ -46463,7 +46507,7 @@ var TuiAltScreen = class extends TuiBase {
46463
46507
  if (this.refreshSearch(nextLayout)) {
46464
46508
  nextLayout = renderLayoutFrame(root, width, height, () => this.requestRender());
46465
46509
  }
46466
- let screen = nextLayout.lines.map((line) => line.replace(OSC133_ZONE_PREFIX2, ""));
46510
+ let screen = this.resolveFakeCursors(nextLayout.lines.map((line) => line.replace(OSC133_ZONE_PREFIX2, "")));
46467
46511
  screen = this.applySearchHighlights(screen, nextLayout);
46468
46512
  screen = this.compositeScrollToEndIndicator(screen, nextLayout, width);
46469
46513
  screen = this.compositeOverlays(screen, width, height);
@@ -25462,7 +25462,7 @@ function sessionTokenFixHint(policy) {
25462
25462
  }
25463
25463
 
25464
25464
  // plugins/kxm/src/mcp-server.ts
25465
- var VERSION = "0.7.205";
25465
+ var VERSION = "0.7.206";
25466
25466
  var CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
25467
25467
  var inbox = /* @__PURE__ */ new Map();
25468
25468
  var notifiedInbox = /* @__PURE__ */ new Set();
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-plugin",
3
- "version": "0.7.205",
3
+ "version": "0.7.206",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "engines": {
@@ -11,7 +11,7 @@ import { deliverInboxNotification } from "./inbox.ts";
11
11
  import type { HubEvent, MessageRecord } from "./protocol.ts";
12
12
  import { sessionTokenFixHint } from "./session-token-hint.ts";
13
13
 
14
- const VERSION = "0.7.205";
14
+ const VERSION = "0.7.206";
15
15
  const CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
16
16
  const inbox = new Map<string, MessageRecord>();
17
17
  const notifiedInbox = new Set<string>();