@kernelonpanic/kitcode 1.3.2 → 1.4.0

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
@@ -75,6 +75,9 @@ var configSchema = z.object({
75
75
  theme: themeSchema.default({ accent: "#a78bfa" }),
76
76
  effort: effortSchema.default("xhigh"),
77
77
  thinking: z.boolean().default(true),
78
+ // Manual override for the model's context window. Unset means the window
79
+ // comes from the provider catalogue or the endpoint's model list.
80
+ contextWindow: z.number().int().min(1e3).max(1e7).optional(),
78
81
  maxTokens: z.number().int().positive().max(2e5).default(64e3),
79
82
  budget: budgetSchema.default({
80
83
  maxTokensPerTurn: 1e7,
@@ -3704,12 +3707,12 @@ var SUBAGENT_SYSTEM = [
3704
3707
  ].join("\n");
3705
3708
  function createSubagentRunner(makeConfig, tools) {
3706
3709
  const permissionLane = createExecutionLane();
3707
- const executionLane = createExecutionLane();
3708
- const modelRequestLane = createExecutionLane();
3709
3710
  return {
3710
3711
  async run(request) {
3711
3712
  if (request.signal.aborted) return SUBAGENT_CANCELLED;
3712
3713
  let steps = 0;
3714
+ const executionLane = createExecutionLane();
3715
+ const modelRequestLane = createExecutionLane();
3713
3716
  const base = makeConfig(SUBAGENT_SYSTEM, tools);
3714
3717
  const cfg = {
3715
3718
  ...base,
@@ -3940,7 +3943,7 @@ function clip(value) {
3940
3943
  // package.json
3941
3944
  var package_default = {
3942
3945
  name: "@kernelonpanic/kitcode",
3943
- version: "1.3.2",
3946
+ version: "1.4.0",
3944
3947
  description: "Terminal coding agent with a config you never have to write by hand",
3945
3948
  type: "module",
3946
3949
  license: "MIT",
@@ -3973,6 +3976,7 @@ var package_default = {
3973
3976
  dependencies: {
3974
3977
  "@anthropic-ai/sdk": "^0.115.0",
3975
3978
  "@modelcontextprotocol/sdk": "^1.30.0",
3979
+ build: "^0.1.4",
3976
3980
  chalk: "^6.0.0",
3977
3981
  commander: "^15.0.0",
3978
3982
  "fast-glob": "^3.3.3",
@@ -4008,7 +4012,7 @@ var package_default = {
4008
4012
 
4009
4013
  // src/version.ts
4010
4014
  var KITCODE_VERSION = package_default.version;
4011
- var KITCODE_COMMIT = true ? "993d55335487da4915fa49660911ba6e69aa3685" : "development";
4015
+ var KITCODE_COMMIT = true ? "f5ecc5fb8dac2872eeb1a988705c06552b250129" : "development";
4012
4016
 
4013
4017
  // src/mcp/client.ts
4014
4018
  var clientInfo = { name: "kitcode", version: KITCODE_VERSION };
@@ -6619,6 +6623,7 @@ async function boot(options) {
6619
6623
  const notifyContext = () => {
6620
6624
  for (const listener of contextListeners) listener();
6621
6625
  };
6626
+ const resolveContextWindow = (ref, discovered) => config.contextWindow ?? discovered ?? knownContextWindow(ref);
6622
6627
  const knownContextWindow = (ref) => {
6623
6628
  const parsed = parseModelRef(ref);
6624
6629
  if (!parsed) return null;
@@ -6635,27 +6640,27 @@ async function boot(options) {
6635
6640
  modelRef = ref;
6636
6641
  if (latestRateLimits?.model !== ref) latestRateLimits = void 0;
6637
6642
  contextUsage = restoredContext?.model === ref ? restoredContext : void 0;
6638
- modelContextWindow = discoveredWindow ?? knownContextWindow(ref);
6643
+ modelContextWindow = resolveContextWindow(ref, discoveredWindow);
6639
6644
  notifyContext();
6640
6645
  };
6641
- modelContextWindow = knownContextWindow(modelRef);
6646
+ modelContextWindow = resolveContextWindow(modelRef);
6642
6647
  async function refreshModelContextWindow() {
6643
6648
  const targetRef = modelRef;
6644
6649
  const parsed = parseModelRef(targetRef);
6645
6650
  if (!parsed) {
6646
6651
  if (modelRef === targetRef) {
6647
- modelContextWindow = null;
6652
+ modelContextWindow = config.contextWindow ?? null;
6648
6653
  notifyContext();
6649
6654
  }
6650
6655
  return;
6651
6656
  }
6652
- let next = modelContextWindow ?? knownContextWindow(targetRef);
6657
+ let next = resolveContextWindow(targetRef);
6653
6658
  try {
6654
6659
  const provider = registry.get(parsed.provider);
6655
6660
  const models = await loadModels(provider);
6656
6661
  rememberModels(parsed.provider, models);
6657
6662
  const found = models.find((m) => m.id === parsed.model);
6658
- next = found?.contextWindow ?? next;
6663
+ next = resolveContextWindow(targetRef, found?.contextWindow);
6659
6664
  } catch {
6660
6665
  }
6661
6666
  if (modelRef === targetRef) {
@@ -6999,6 +7004,16 @@ async function boot(options) {
6999
7004
  draft.thinking = enabled;
7000
7005
  });
7001
7006
  },
7007
+ getContextWindowOverride: () => config.contextWindow,
7008
+ async setContextWindowOverride(tokens3) {
7009
+ await persistConfig((draft) => {
7010
+ if (tokens3 === void 0) delete draft.contextWindow;
7011
+ else draft.contextWindow = tokens3;
7012
+ });
7013
+ modelContextWindow = config.contextWindow ?? resolveContextWindow(modelRef);
7014
+ notifyContext();
7015
+ void refreshModelContextWindow();
7016
+ },
7002
7017
  getMaxTokensPerTurn: () => config.budget.maxTokensPerTurn,
7003
7018
  async setMaxTokensPerTurn(tokens3) {
7004
7019
  await persistConfig((draft) => {
@@ -7632,36 +7647,38 @@ function stdioServer(name, command, args) {
7632
7647
  }
7633
7648
 
7634
7649
  // src/ui/commands.ts
7650
+ var BUSY_SAFE = { runsWhileBusy: true };
7635
7651
  var COMMANDS = [
7636
- { name: "model" },
7652
+ { name: "model", ...BUSY_SAFE },
7637
7653
  { name: "login" },
7638
7654
  { name: "provider" },
7639
7655
  { name: "logout", args: "[provider]" },
7640
7656
  { name: "key", args: "[provider]" },
7641
7657
  { name: "refresh", args: "[provider]" },
7642
- { name: "effort" },
7643
- { name: "thinking" },
7644
- { name: "budget", args: "[tokens]" },
7645
- { name: "theme", args: "[color]" },
7646
- { name: "lang" },
7658
+ { name: "effort", ...BUSY_SAFE },
7659
+ { name: "thinking", ...BUSY_SAFE },
7660
+ { name: "context", args: "[tokens|auto]", ...BUSY_SAFE },
7661
+ { name: "budget", args: "[tokens]", ...BUSY_SAFE },
7662
+ { name: "theme", args: "[color]", ...BUSY_SAFE },
7663
+ { name: "lang", ...BUSY_SAFE },
7647
7664
  { name: "prompt", args: "[save <name>|delete <name>]" },
7648
7665
  { name: "skills", args: "[install <source>]" },
7649
7666
  { name: "bypass" },
7650
- { name: "usage" },
7667
+ { name: "usage", ...BUSY_SAFE },
7651
7668
  { name: "mcp", args: "[add|list|delete|enable|disable]" },
7652
7669
  { name: "attach", args: "<path|clipboard|clear>" },
7653
7670
  { name: "memory", args: "[show|set <text>|clear]" },
7654
7671
  { name: "compact" },
7655
7672
  { name: "update" },
7656
- { name: "checker" },
7673
+ { name: "checker", ...BUSY_SAFE },
7657
7674
  { name: "sessions", args: "[list|rename|delete [all]|export]" },
7658
- { name: "config" },
7675
+ { name: "config", ...BUSY_SAFE },
7659
7676
  { name: "resume" },
7660
7677
  { name: "undo" },
7661
7678
  { name: "clear" },
7662
- { name: "help" },
7663
- { name: "exit" },
7664
- { name: "subagents" }
7679
+ { name: "help", ...BUSY_SAFE },
7680
+ { name: "exit", ...BUSY_SAFE },
7681
+ { name: "subagents", ...BUSY_SAFE }
7665
7682
  ];
7666
7683
  var SUBCOMMANDS = [
7667
7684
  { name: "mcp add", args: "<name> <url|-- command>" },
@@ -7675,6 +7692,10 @@ var SUBCOMMANDS = [
7675
7692
  { name: "sessions delete all" },
7676
7693
  { name: "sessions export", args: "[id] [path]" }
7677
7694
  ];
7695
+ function commandRunsWhileBusy(name) {
7696
+ if (name === "quit") return true;
7697
+ return COMMANDS.some((command) => command.name === name && command.runsWhileBusy === true);
7698
+ }
7678
7699
  function matchCommands(line) {
7679
7700
  if (!line.startsWith("/")) return [];
7680
7701
  const token = line.slice(1);
@@ -7977,6 +7998,13 @@ ${url}`,
7977
7998
  undoResult: (restored, removed) => `Undo complete: ${restored} restored, ${removed} newly created removed.`,
7978
7999
  undoConflicts: (paths) => `Kept newer changes in: ${paths.join(", ")}`,
7979
8000
  undoFailed: (failures) => `Could not restore: ${failures.join("; ")}`,
8001
+ titleContext: "Context window",
8002
+ contextCurrent: (tokens3) => `Context window: ${tokens3.toLocaleString()} tokens`,
8003
+ contextSet: (tokens3) => `Context window set to ${tokens3.toLocaleString()} tokens. Auto-compact triggers at 80%.`,
8004
+ contextAuto: (tokens3) => tokens3 === null ? "Context window: unknown \u2014 using whatever the provider reports." : `Context window: auto (${tokens3.toLocaleString()} tokens from the provider).`,
8005
+ contextUnknown: "Context window: unknown. Set it with /context 256k, /context 512k or /context 1m.",
8006
+ contextInvalid: "Usage: /context 256k | 512k | 1m | <tokens> | auto",
8007
+ contextCurrentLabel: "current",
7980
8008
  titleAccent: "Accent colour",
7981
8009
  titleEffort: "Reasoning depth",
7982
8010
  titleModel: "Model",
@@ -7997,6 +8025,7 @@ ${url}`,
7997
8025
  refresh: "reload the model list for a provider",
7998
8026
  effort: "set reasoning depth",
7999
8027
  thinking: "toggle reasoning",
8028
+ context: "set or show the model context window",
8000
8029
  budget: "set token budget per turn",
8001
8030
  theme: "change the accent colour",
8002
8031
  lang: "change the interface language",
@@ -8169,6 +8198,13 @@ ${url}`,
8169
8198
  undoResult: (restored, removed) => `\u041E\u0442\u043A\u0430\u0442 \u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043D: \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u043E ${restored}, \u0443\u0434\u0430\u043B\u0435\u043D\u043E \u043D\u043E\u0432\u044B\u0445 \u0444\u0430\u0439\u043B\u043E\u0432 ${removed}.`,
8170
8199
  undoConflicts: (paths) => `\u0411\u043E\u043B\u0435\u0435 \u0441\u0432\u0435\u0436\u0438\u0435 \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0441\u043E\u0445\u0440\u0430\u043D\u0435\u043D\u044B \u0432: ${paths.join(", ")}`,
8171
8200
  undoFailed: (failures) => `\u041D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u0432\u043E\u0441\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C: ${failures.join("; ")}`,
8201
+ titleContext: "\u041E\u043A\u043D\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430",
8202
+ contextCurrent: (tokens3) => `\u041E\u043A\u043D\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430: ${tokens3.toLocaleString()} \u0442\u043E\u043A\u0435\u043D\u043E\u0432`,
8203
+ contextSet: (tokens3) => `\u041E\u043A\u043D\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430: ${tokens3.toLocaleString()} \u0442\u043E\u043A\u0435\u043D\u043E\u0432. \u0410\u0432\u0442\u043E\u0441\u0436\u0430\u0442\u0438\u0435 \u0441\u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442 \u043D\u0430 80%.`,
8204
+ contextAuto: (tokens3) => tokens3 === null ? "\u041E\u043A\u043D\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430: \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u043E \u2014 \u0431\u0435\u0440\u0451\u043C \u0442\u043E, \u0447\u0442\u043E \u0441\u043E\u043E\u0431\u0449\u0430\u0435\u0442 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440." : `\u041E\u043A\u043D\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430: \u0430\u0432\u0442\u043E (${tokens3.toLocaleString()} \u0442\u043E\u043A\u0435\u043D\u043E\u0432 \u043E\u0442 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430).`,
8205
+ contextUnknown: "\u041E\u043A\u043D\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430 \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u043E. \u0417\u0430\u0434\u0430\u0439 \u0435\u0433\u043E: /context 256k, /context 512k \u0438\u043B\u0438 /context 1m.",
8206
+ contextInvalid: "\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u0438\u0435: /context 256k | 512k | 1m | <\u0442\u043E\u043A\u0435\u043D\u044B> | auto",
8207
+ contextCurrentLabel: "\u0442\u0435\u043A\u0443\u0449\u0435\u0435",
8172
8208
  titleAccent: "\u0426\u0432\u0435\u0442 \u0430\u043A\u0446\u0435\u043D\u0442\u0430",
8173
8209
  titleEffort: "\u0413\u043B\u0443\u0431\u0438\u043D\u0430 \u0440\u0430\u0437\u043C\u044B\u0448\u043B\u0435\u043D\u0438\u0439",
8174
8210
  titleModel: "\u041C\u043E\u0434\u0435\u043B\u044C",
@@ -8189,6 +8225,7 @@ ${url}`,
8189
8225
  refresh: "\u043E\u0431\u043D\u043E\u0432\u0438\u0442\u044C \u0441\u043F\u0438\u0441\u043E\u043A \u043C\u043E\u0434\u0435\u043B\u0435\u0439 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430",
8190
8226
  effort: "\u0433\u043B\u0443\u0431\u0438\u043D\u0430 \u0440\u0430\u0437\u043C\u044B\u0448\u043B\u0435\u043D\u0438\u0439",
8191
8227
  thinking: "\u0432\u043A\u043B\u044E\u0447\u0438\u0442\u044C/\u0432\u044B\u043A\u043B\u044E\u0447\u0438\u0442\u044C \u0440\u0430\u0437\u043C\u044B\u0448\u043B\u0435\u043D\u0438\u044F",
8228
+ context: "\u0437\u0430\u0434\u0430\u0442\u044C \u0438\u043B\u0438 \u043F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u043E\u043A\u043D\u043E \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0430 \u043C\u043E\u0434\u0435\u043B\u0438",
8192
8229
  budget: "\u043B\u0438\u043C\u0438\u0442 \u0442\u043E\u043A\u0435\u043D\u043E\u0432 \u0437\u0430 \u0445\u043E\u0434 (0 = \u0431\u0435\u0437\u043B\u0438\u043C\u0438\u0442)",
8193
8230
  theme: "\u0441\u043C\u0435\u043D\u0438\u0442\u044C \u0446\u0432\u0435\u0442 \u0430\u043A\u0446\u0435\u043D\u0442\u0430",
8194
8231
  lang: "\u0441\u043C\u0435\u043D\u0438\u0442\u044C \u044F\u0437\u044B\u043A \u0438\u043D\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430",
@@ -8665,6 +8702,18 @@ function appendInputHistory(history, value, limit = 100) {
8665
8702
  // src/ui/components/PromptInput.tsx
8666
8703
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
8667
8704
  var WINDOW2 = 6;
8705
+ var CAT_FRAME_MS = 500;
8706
+ var CAT_FRAMES = [
8707
+ "/\u1420\uFF61\uA788\uFF61\u141F\\ ~",
8708
+ "/\u1420\uFF61\uA788\uFF61\u141F\\ \u223F",
8709
+ "/\u1420\uFF61\uA788\uFF61\u141F\\ ~",
8710
+ "/\u1420\uFF61\uA788\uFF61\u141F\\ \u223E",
8711
+ "/\u1420\uFF61\uA788\uFF61\u141F\\ ~",
8712
+ "/\u1420\uFF61\uA788\uFF61\u141F\\ \u223F",
8713
+ "/\u1420-\uA788-\u141F\\ ~",
8714
+ "/\u1420\uFF61\uA788\uFF61\u141F\\ \u223E"
8715
+ ];
8716
+ var MAX_INPUT_ROWS = 6;
8668
8717
  var PromptInput = memo(function PromptInput2({
8669
8718
  value,
8670
8719
  onChange,
@@ -8680,7 +8729,18 @@ var PromptInput = memo(function PromptInput2({
8680
8729
  }) {
8681
8730
  const theme = useTheme();
8682
8731
  const { columns } = useTerminalSize();
8732
+ const panelWidth = Math.max(2, columns - 1);
8733
+ const inputRows = MAX_INPUT_ROWS;
8683
8734
  const strings = useStrings();
8735
+ const [catFrame, setCatFrame] = useState4(0);
8736
+ useEffect(() => {
8737
+ if (!disabled) return;
8738
+ const timer = setInterval(() => setCatFrame((frame) => (frame + 1) % CAT_FRAMES.length), CAT_FRAME_MS);
8739
+ return () => clearInterval(timer);
8740
+ }, [disabled]);
8741
+ const panelColor = disabled ? theme.warn : theme.accent;
8742
+ const fullTitle = disabled ? `KitCode ${CAT_FRAMES[catFrame]}` : "KitCode";
8743
+ const panelTitle = fullTitle.slice(0, Math.max(0, panelWidth - 5));
8684
8744
  const safeValue = sanitizeTerminalText(value);
8685
8745
  const [selectionCursor, setSelectionCursor] = useState4(0);
8686
8746
  const [inputCursor, setInputCursor] = useState4(() => characters2(safeValue).length);
@@ -8843,28 +8903,64 @@ var PromptInput = memo(function PromptInput2({
8843
8903
  const start = Math.max(0, Math.min(active2 - WINDOW2 + 2, suggestions.length - WINDOW2));
8844
8904
  const visible = suggestions.slice(start, start + WINDOW2);
8845
8905
  const fileStart = Math.max(0, Math.min(active2 - WINDOW2 + 2, files.length - WINDOW2));
8846
- return /* @__PURE__ */ jsxs8(Box8, { width: columns, maxWidth: "100%", flexDirection: "column", marginTop: 1, flexShrink: 0, children: [
8847
- /* @__PURE__ */ jsxs8(
8848
- Box8,
8849
- {
8850
- width: "100%",
8851
- paddingX: 1,
8852
- children: [
8853
- /* @__PURE__ */ jsx8(Text8, { color: disabled ? "gray" : theme.accent, children: "\u203A " }),
8854
- /* @__PURE__ */ jsx8(Box8, { flexGrow: 1, flexShrink: 1, minWidth: 0, children: /* @__PURE__ */ jsx8(EditableText, { value: safeValue, cursor: inputCursor, placeholder: strings.placeholder }) }),
8855
- pending && pending > 0 ? /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
8856
- " \xB7 ",
8857
- strings.queued(pending)
8858
- ] }) : null
8859
- ]
8860
- }
8861
- ),
8906
+ return /* @__PURE__ */ jsxs8(Box8, { width: panelWidth, maxWidth: "100%", flexDirection: "column", marginTop: 1, flexShrink: 0, children: [
8907
+ /* @__PURE__ */ jsxs8(Box8, { width: "100%", flexShrink: 0, children: [
8908
+ /* @__PURE__ */ jsx8(Box8, { flexShrink: 0, children: /* @__PURE__ */ jsx8(Text8, { color: panelColor, children: "\u256D" }) }),
8909
+ /* @__PURE__ */ jsx8(Box8, { flexShrink: 1, minWidth: 0, children: /* @__PURE__ */ jsxs8(Text8, { color: panelColor, wrap: "truncate-end", children: [
8910
+ "\u2500 ",
8911
+ panelTitle,
8912
+ " "
8913
+ ] }) }),
8914
+ /* @__PURE__ */ jsx8(
8915
+ Box8,
8916
+ {
8917
+ flexGrow: 1,
8918
+ flexShrink: 1,
8919
+ minWidth: 0,
8920
+ height: 1,
8921
+ borderStyle: "single",
8922
+ borderColor: panelColor,
8923
+ borderTop: false,
8924
+ borderBottom: true,
8925
+ borderLeft: false,
8926
+ borderRight: false
8927
+ }
8928
+ ),
8929
+ /* @__PURE__ */ jsx8(Box8, { flexShrink: 0, children: /* @__PURE__ */ jsx8(Text8, { color: panelColor, children: "\u256E" }) })
8930
+ ] }),
8931
+ /* @__PURE__ */ jsxs8(Box8, { width: "100%", paddingX: 1, maxHeight: inputRows, overflowY: "hidden", children: [
8932
+ /* @__PURE__ */ jsx8(Text8, { color: panelColor, children: "\u203A " }),
8933
+ /* @__PURE__ */ jsx8(Box8, { flexGrow: 1, flexShrink: 1, minWidth: 0, children: /* @__PURE__ */ jsx8(EditableText, { value: safeValue, cursor: inputCursor, placeholder: strings.placeholder }) }),
8934
+ pending && pending > 0 ? /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
8935
+ " \xB7 ",
8936
+ strings.queued(pending)
8937
+ ] }) : null
8938
+ ] }),
8862
8939
  attachments.length > 0 && /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
8863
8940
  " ",
8864
8941
  "\u{1F4CE} ",
8865
8942
  attachments.map(sanitizeTerminalText).join(" \xB7 ")
8866
8943
  ] }),
8867
- disabled && hint && /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
8944
+ /* @__PURE__ */ jsxs8(Box8, { width: "100%", flexShrink: 0, children: [
8945
+ /* @__PURE__ */ jsx8(Box8, { flexShrink: 0, children: /* @__PURE__ */ jsx8(Text8, { color: panelColor, children: "\u2570" }) }),
8946
+ /* @__PURE__ */ jsx8(
8947
+ Box8,
8948
+ {
8949
+ flexGrow: 1,
8950
+ flexShrink: 1,
8951
+ minWidth: 0,
8952
+ height: 1,
8953
+ borderStyle: "single",
8954
+ borderColor: panelColor,
8955
+ borderTop: false,
8956
+ borderBottom: true,
8957
+ borderLeft: false,
8958
+ borderRight: false
8959
+ }
8960
+ ),
8961
+ /* @__PURE__ */ jsx8(Box8, { flexShrink: 0, children: /* @__PURE__ */ jsx8(Text8, { color: panelColor, children: "\u256F" }) })
8962
+ ] }),
8963
+ disabled && hint && /* @__PURE__ */ jsxs8(Text8, { color: theme.accent, children: [
8868
8964
  " ",
8869
8965
  sanitizeTerminalText(hint)
8870
8966
  ] }),
@@ -9148,13 +9244,12 @@ function ContextMeter({
9148
9244
  // src/ui/components/TerminalViewport.tsx
9149
9245
  import { Box as Box10 } from "ink";
9150
9246
  import { jsx as jsx10 } from "react/jsx-runtime";
9151
- function interactiveViewportRows(rows) {
9152
- if (!Number.isFinite(rows)) return 22;
9153
- return Math.max(1, Math.floor(rows) - 2);
9154
- }
9155
9247
  var INTERACTIVE_CHROME_ROWS = 14;
9248
+ var MAX_LIVE_ROWS = 8;
9249
+ var MIN_LIVE_ROWS = 4;
9156
9250
  function liveTranscriptRows(rows) {
9157
- return Math.max(1, interactiveViewportRows(rows) - INTERACTIVE_CHROME_ROWS);
9251
+ if (!Number.isFinite(rows)) return MAX_LIVE_ROWS;
9252
+ return Math.max(MIN_LIVE_ROWS, Math.min(MAX_LIVE_ROWS, Math.floor(rows) - INTERACTIVE_CHROME_ROWS - 2));
9158
9253
  }
9159
9254
  function TerminalViewport({ children, rows }) {
9160
9255
  const { columns } = useTerminalSize();
@@ -9164,7 +9259,7 @@ function TerminalViewport({ children, rows }) {
9164
9259
  width: columns,
9165
9260
  maxWidth: "100%",
9166
9261
  flexDirection: "column",
9167
- maxHeight: interactiveViewportRows(rows),
9262
+ maxHeight: liveTranscriptRows(rows) + INTERACTIVE_CHROME_ROWS,
9168
9263
  overflowY: "hidden",
9169
9264
  children
9170
9265
  }
@@ -9183,9 +9278,17 @@ import { createContext as createContext3, Fragment as Fragment4, useContext as u
9183
9278
  import stringWidth from "string-width";
9184
9279
  import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
9185
9280
  var DimContext = createContext3(false);
9186
- function Text10({ dimColor, color, ...props }) {
9281
+ function Text10({ dimColor, color, bold, ...props }) {
9187
9282
  const dim = useContext3(DimContext);
9188
- return /* @__PURE__ */ jsx11(InkText, { ...props, color: dim ? void 0 : color, dimColor: dimColor ?? dim });
9283
+ return /* @__PURE__ */ jsx11(
9284
+ InkText,
9285
+ {
9286
+ ...props,
9287
+ color: dim ? void 0 : color,
9288
+ bold: dim ? void 0 : bold,
9289
+ dimColor: dimColor ?? dim
9290
+ }
9291
+ );
9189
9292
  }
9190
9293
  function Markdown({ children, dim = false }) {
9191
9294
  const blocks = useMemo3(() => extractBlocks(children), [children]);
@@ -9758,6 +9861,8 @@ var Transcript = memo2(function Transcript2({
9758
9861
  }
9759
9862
  const live = liveAt === -1 ? [] : bubbles.slice(liveAt);
9760
9863
  const staticItems = [HEADER, ...stableRef.current];
9864
+ const agents = live.filter((bubble) => bubble.kind === "subagent");
9865
+ const groupedAgents = agents.length > 1;
9761
9866
  return /* @__PURE__ */ jsxs11(
9762
9867
  Box12,
9763
9868
  {
@@ -9768,7 +9873,7 @@ var Transcript = memo2(function Transcript2({
9768
9873
  overflowY: "hidden",
9769
9874
  children: [
9770
9875
  /* @__PURE__ */ jsx12(Static, { items: staticItems, children: (item) => item.kind === "header" ? /* @__PURE__ */ jsx12(Logo, { workspace }, "kitcode-header") : /* @__PURE__ */ jsx12(BubbleView, { bubble: item }, item.id) }),
9771
- /* @__PURE__ */ jsx12(
9876
+ /* @__PURE__ */ jsxs11(
9772
9877
  Box12,
9773
9878
  {
9774
9879
  flexDirection: "column",
@@ -9777,7 +9882,10 @@ var Transcript = memo2(function Transcript2({
9777
9882
  maxHeight: maxLiveRows,
9778
9883
  overflowY: "hidden",
9779
9884
  justifyContent: "flex-end",
9780
- children: live.map((bubble) => /* @__PURE__ */ jsx12(BubbleView, { bubble, maxRows: maxLiveRows }, bubble.id))
9885
+ children: [
9886
+ live.filter((bubble) => !groupedAgents || bubble.kind !== "subagent" && !(bubble.kind === "tool" && bubble.name === "task")).map((bubble) => /* @__PURE__ */ jsx12(BubbleView, { bubble, maxRows: maxLiveRows }, bubble.id)),
9887
+ groupedAgents && /* @__PURE__ */ jsx12(SubagentSummary, { agents })
9888
+ ]
9781
9889
  }
9782
9890
  )
9783
9891
  ]
@@ -9914,6 +10022,30 @@ function previewLines(content) {
9914
10022
  if (lines.length <= 6) return lines;
9915
10023
  return [...lines.slice(0, 6), `\u2026 ${lines.length - 6} more lines`];
9916
10024
  }
10025
+ var VISIBLE_SUBAGENTS = 3;
10026
+ function SubagentSummary({ agents }) {
10027
+ const theme = useTheme();
10028
+ const running = agents.filter((agent) => agent.state === "running");
10029
+ return /* @__PURE__ */ jsxs11(Box12, { flexDirection: "column", marginTop: 1, minWidth: 0, children: [
10030
+ /* @__PURE__ */ jsxs11(Text11, { color: theme.accent, wrap: "truncate-end", children: [
10031
+ "Subagents: ",
10032
+ running.length,
10033
+ " running \xB7 ",
10034
+ agents.length - running.length,
10035
+ " done"
10036
+ ] }),
10037
+ running.slice(0, VISIBLE_SUBAGENTS).map((agent) => {
10038
+ const latest = agent.bubbles.at(-1);
10039
+ const progress = latest?.kind === "tool" ? latest.summary : latest?.kind === "assistant" ? sanitizeThinkingText(latest.text || latest.thinking, latest.streaming) : "waiting";
10040
+ return /* @__PURE__ */ jsxs11(Text11, { dimColor: true, wrap: "truncate-end", children: [
10041
+ agent.description,
10042
+ " \xB7 ",
10043
+ progress.replace(/\s+/g, " ").trim() || "waiting"
10044
+ ] }, agent.id);
10045
+ }),
10046
+ /* @__PURE__ */ jsx12(Text11, { dimColor: true, children: "/subagents \xB7 all active tasks" })
10047
+ ] });
10048
+ }
9917
10049
  function SubagentView({ bubble, maxRows }) {
9918
10050
  const theme = useTheme();
9919
10051
  const { columns } = useTerminalSize();
@@ -10172,6 +10304,18 @@ import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
10172
10304
  var EFFORTS = ["auto", "low", "medium", "high", "xhigh", "max"];
10173
10305
  var STREAM_FRAME_MS = 50;
10174
10306
  var MAX_ATTACHMENTS = 8;
10307
+ var CONTEXT_PRESETS = [256e3, 512e3, 1e6];
10308
+ function formatContextTokens(tokens3) {
10309
+ if (tokens3 >= 1e6) return `${tokens3 / 1e6}M`;
10310
+ return `${Math.round(tokens3 / 1e3)}k`;
10311
+ }
10312
+ function parseContextTokens(input) {
10313
+ const match = /^(\d+(?:\.\d+)?)\s*([km])?$/i.exec(input.trim());
10314
+ if (!match) return null;
10315
+ const scale = match[2]?.toLowerCase() === "m" ? 1e6 : match[2]?.toLowerCase() === "k" ? 1e3 : 1;
10316
+ const tokens3 = Math.round(Number(match[1]) * scale);
10317
+ return tokens3 >= 1e3 && tokens3 <= 1e7 ? tokens3 : null;
10318
+ }
10175
10319
  function App({
10176
10320
  runtime,
10177
10321
  initialHistory,
@@ -11092,6 +11236,57 @@ ${strings.mcpAddUsage}`
11092
11236
  }
11093
11237
  return;
11094
11238
  }
11239
+ case "context": {
11240
+ const override = runtime.getContextWindowOverride();
11241
+ const current = runtime.modelContext();
11242
+ const requested = rest[0]?.toLowerCase();
11243
+ if (requested !== void 0 && requested !== "") {
11244
+ if (requested === "auto") {
11245
+ await runtime.setContextWindowOverride(void 0);
11246
+ notice("info", strings.contextAuto(runtime.modelContext().window));
11247
+ forceRender((n) => n + 1);
11248
+ return;
11249
+ }
11250
+ const tokens3 = parseContextTokens(requested);
11251
+ if (tokens3 === null) {
11252
+ notice("warn", strings.contextInvalid);
11253
+ return;
11254
+ }
11255
+ await runtime.setContextWindowOverride(tokens3);
11256
+ notice("info", strings.contextSet(tokens3));
11257
+ forceRender((n) => n + 1);
11258
+ return;
11259
+ }
11260
+ const effective = override ?? current.window;
11261
+ notice("info", effective === null ? strings.contextUnknown : strings.contextCurrent(effective));
11262
+ const choice = await pick(
11263
+ strings.titleContext,
11264
+ CONTEXT_PRESETS.map((preset) => ({
11265
+ key: String(preset),
11266
+ label: formatContextTokens(preset),
11267
+ hint: current.window === preset ? strings.contextCurrentLabel : void 0
11268
+ })).concat([
11269
+ {
11270
+ key: "auto",
11271
+ label: "auto",
11272
+ hint: override === void 0 ? strings.contextCurrentLabel : void 0
11273
+ }
11274
+ ])
11275
+ );
11276
+ if (!choice) return;
11277
+ const chosen = CONTEXT_PRESETS.find((preset) => String(preset) === choice);
11278
+ if (choice === "auto") {
11279
+ await runtime.setContextWindowOverride(void 0);
11280
+ notice("info", strings.contextAuto(runtime.modelContext().window));
11281
+ } else if (chosen !== void 0) {
11282
+ await runtime.setContextWindowOverride(chosen);
11283
+ notice("info", strings.contextSet(chosen));
11284
+ } else {
11285
+ return;
11286
+ }
11287
+ forceRender((n) => n + 1);
11288
+ return;
11289
+ }
11095
11290
  case "model": {
11096
11291
  const items = await runtime.listModelItems();
11097
11292
  if (items.length === 0) {
@@ -11332,8 +11527,7 @@ Rename the file if you want a different name.`);
11332
11527
  const submitSlash = () => {
11333
11528
  if (!detachedInput) setInput("");
11334
11529
  const command = text.slice(1).trim().split(/\s+/)[0]?.toLowerCase();
11335
- const liveCommand = ["help", "usage", "subagents", "config", "exit", "quit"].includes(command ?? "");
11336
- if (busyRef.current && !liveCommand || slashPendingRef.current > 0) {
11530
+ if (busyRef.current && !commandRunsWhileBusy(command) || slashPendingRef.current > 0) {
11337
11531
  queueRef.current = [...queueRef.current, { kind: "command", line: text }];
11338
11532
  setPendingCount(queueRef.current.length);
11339
11533
  return;