@pellux/goodvibes-tui 1.11.0 → 1.12.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/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@ All notable changes to GoodVibes TUI.
4
4
 
5
5
  ---
6
6
 
7
+ ## [1.12.0] — 2026-07-08
8
+
9
+ ### Changes
10
+ - 894099e9 chore: pin @pellux/goodvibes-sdk 1.5.0 — model compaction warnings compact immediately; persistent per-model context windows
11
+ - b89d0494 feat: /context window — view, set, or clear a custom context window for the current model
12
+
13
+ ## [1.11.1] — 2026-07-08
14
+
15
+ ### Changes
16
+ - f3abc72b chore: pin @pellux/goodvibes-sdk 1.4.1 — permission settings own command-class risk in exec
17
+
7
18
  ## [1.11.0] — 2026-07-07
8
19
 
9
20
  ### Changes
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![CI](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml/badge.svg)](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
- [![Version](https://img.shields.io/badge/version-1.11.0-blue.svg)](https://github.com/mgd34msu/goodvibes-tui)
5
+ [![Version](https://img.shields.io/badge/version-1.12.0-blue.svg)](https://github.com/mgd34msu/goodvibes-tui)
6
6
 
7
7
  A terminal-native AI coding, operations, automation, knowledge, and integration console with a typed runtime, omnichannel surfaces, structured memory/knowledge, and a raw ANSI renderer.
8
8
 
@@ -3,7 +3,7 @@
3
3
  "product": {
4
4
  "id": "goodvibes",
5
5
  "surface": "operator",
6
- "version": "1.4.0"
6
+ "version": "1.5.0"
7
7
  },
8
8
  "auth": {
9
9
  "modes": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-tui",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
4
4
  "description": "Terminal-native GoodVibes product for coding, operations, automation, knowledge, channels, and daemon-backed control-plane workflows.",
5
5
  "type": "module",
6
6
  "main": "src/main.ts",
@@ -99,7 +99,7 @@
99
99
  "@anthropic-ai/vertex-sdk": "^0.16.0",
100
100
  "@ast-grep/napi": "^0.42.0",
101
101
  "@aws/bedrock-token-generator": "^1.1.0",
102
- "@pellux/goodvibes-sdk": "1.4.0",
102
+ "@pellux/goodvibes-sdk": "1.5.0",
103
103
  "bash-language-server": "^5.6.0",
104
104
  "fuse.js": "^7.1.0",
105
105
  "graphql": "^16.13.2",
@@ -0,0 +1,89 @@
1
+ /**
2
+ * /context window — view, set, or clear a custom context window for the
3
+ * current model.
4
+ *
5
+ * The override is stored by the SDK's ProviderRegistry (persisted under the
6
+ * control-plane config dir with provenance 'configured_cap'), so it survives
7
+ * restarts, applies to any model (cloud or local), and is honored by every
8
+ * consumer of the same home. Clearing returns the model to its automatic
9
+ * window (catalog / provider API / family fallback).
10
+ */
11
+ import type { CommandContext } from '../command-registry.ts';
12
+ import type { ModelDefinition } from '@pellux/goodvibes-sdk/platform/providers';
13
+ import { MAX_CONTEXT_WINDOW_OVERRIDE } from '@pellux/goodvibes-sdk/platform/providers';
14
+
15
+ /**
16
+ * Parse a user-supplied context window size. Accepts plain token counts
17
+ * ("200000"), thousands ("200k", "12.5k"), and millions ("1m", "2M").
18
+ * Returns null for anything unparseable or outside 1..MAX_CONTEXT_WINDOW_OVERRIDE.
19
+ */
20
+ export function parseContextWindowSize(raw: string): number | null {
21
+ const match = /^(\d+(?:\.\d+)?)\s*([km])?$/i.exec(raw.trim());
22
+ if (!match) return null;
23
+ const base = Number(match[1]);
24
+ const suffix = match[2]?.toLowerCase();
25
+ const multiplier = suffix === 'm' ? 1_000_000 : suffix === 'k' ? 1_000 : 1;
26
+ const value = Math.round(base * multiplier);
27
+ if (!Number.isInteger(value) || value <= 0 || value > MAX_CONTEXT_WINDOW_OVERRIDE) return null;
28
+ return value;
29
+ }
30
+
31
+ function describeProvenance(model: ModelDefinition): string {
32
+ switch (model.contextWindowProvenance) {
33
+ case 'configured_cap': return 'custom override';
34
+ case 'provider_api': return 'reported by the provider';
35
+ case 'fallback': return 'family default (no catalog entry)';
36
+ default: return 'model catalog';
37
+ }
38
+ }
39
+
40
+ /** Status text for the current model's window + override state. */
41
+ export function buildContextWindowStatusText(
42
+ model: ModelDefinition,
43
+ resolvedWindow: number,
44
+ override: number | null,
45
+ ): string {
46
+ const lines = [
47
+ `Context window for ${model.displayName} (${model.registryKey}):`,
48
+ ` resolved: ${resolvedWindow.toLocaleString()} tokens (${describeProvenance(model)})`,
49
+ ` override: ${override === null ? 'none (automatic)' : `${override.toLocaleString()} tokens`}`,
50
+ '',
51
+ 'Set: /context window <size> (e.g. 120000, 200k, 1m)',
52
+ 'Clear: /context window clear',
53
+ ];
54
+ return lines.join('\n');
55
+ }
56
+
57
+ /** Handle `/context window [<size>|clear]`. Returns the text it printed (for tests). */
58
+ export function handleContextWindowSubcommand(args: readonly string[], ctx: CommandContext): string {
59
+ const registry = ctx.provider.providerRegistry;
60
+ const model = registry.getCurrentModel();
61
+ const arg = args[0]?.trim().toLowerCase() ?? '';
62
+
63
+ let output: string;
64
+ if (arg === '') {
65
+ output = buildContextWindowStatusText(
66
+ model,
67
+ registry.getContextWindowForModel(model),
68
+ registry.getModelContextCap(model.registryKey),
69
+ );
70
+ } else if (arg === 'clear' || arg === 'auto' || arg === 'reset') {
71
+ const existed = registry.clearModelContextCap(model.registryKey);
72
+ const resolved = registry.getContextWindowForModel(registry.getCurrentModel());
73
+ output = existed
74
+ ? `Custom context window cleared for ${model.displayName}. Back to automatic: ${resolved.toLocaleString()} tokens.`
75
+ : `${model.displayName} has no custom context window set (automatic: ${resolved.toLocaleString()} tokens).`;
76
+ } else {
77
+ const size = parseContextWindowSize(arg);
78
+ if (size === null) {
79
+ output = `Invalid size '${args[0]}'. Use a token count between 1 and ${MAX_CONTEXT_WINDOW_OVERRIDE.toLocaleString()} — e.g. 120000, 200k, 1m — or 'clear'.`;
80
+ } else {
81
+ registry.setModelContextCap(model.registryKey, size);
82
+ output = `Context window for ${model.displayName} set to ${size.toLocaleString()} tokens (was ${registry.getContextWindowForModel(model).toLocaleString()}). Clear with /context window clear.`;
83
+ }
84
+ }
85
+
86
+ ctx.print(output);
87
+ ctx.renderRequest();
88
+ return output;
89
+ }
@@ -6,6 +6,7 @@ import { registerOperatorPanelCommand } from './operator-panel-runtime.ts';
6
6
  import { requireOpsApi, requireProfileManager, requireReplayEngine } from './runtime-services.ts';
7
7
  import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
8
8
  import { estimateConversationTokens } from '@pellux/goodvibes-sdk/platform/core';
9
+ import { handleContextWindowSubcommand } from './context-window.ts';
9
10
 
10
11
  export function registerOperatorRuntimeCommands(registry: CommandRegistry): void {
11
12
  registerOperatorPanelCommand(registry);
@@ -23,8 +24,14 @@ export function registerOperatorRuntimeCommands(registry: CommandRegistry): void
23
24
  registry.register({
24
25
  name: 'context',
25
26
  aliases: ['ctx'],
26
- description: 'Inspect context window usage (token breakdown per message)',
27
- handler: (_args, ctx) => {
27
+ description: 'Inspect context usage, or set/clear a custom context window for the current model',
28
+ usage: '[window [<size>|clear]]',
29
+ argsHint: '[window <size|clear>]',
30
+ handler: (args, ctx) => {
31
+ if (args[0]?.toLowerCase() === 'window') {
32
+ handleContextWindowSubcommand(args.slice(1), ctx);
33
+ return;
34
+ }
28
35
  if (ctx.openContextInspector) {
29
36
  ctx.openContextInspector();
30
37
  } else {
@@ -120,6 +120,7 @@ export function registerShellCoreCommands(registry: CommandRegistry): void {
120
120
  { id: '/model', label: '/model [id]', detail: 'Select LLM model', category: 'Model & Provider' },
121
121
  { id: '/provider', label: '/provider [name]', detail: 'Switch provider', category: 'Model & Provider' },
122
122
  { id: '/effort', label: '/effort [level]', detail: 'Reasoning effort (instant/low/medium/high)', category: 'Model & Provider' },
123
+ { id: '/context window', label: '/context window [<size>|clear]', detail: 'Show, set, or clear a custom context window for the current model', category: 'Model & Provider' },
123
124
  { id: '/config', label: '/config [category|key]', detail: 'Open fullscreen configuration workspace', category: 'Config & Display' },
124
125
  { id: '/debug', label: '/debug', detail: 'Toggle debug mode', category: 'Config & Display' },
125
126
  { id: '/expand', label: '/expand [type]', detail: 'Expand blocks (all|thinking|tool|code)', category: 'Config & Display' },
package/src/version.ts CHANGED
@@ -6,7 +6,7 @@ import { join } from 'node:path';
6
6
  // The prebuild script updates the fallback value before compilation.
7
7
  // Uses import.meta.dir (Bun) to locate package.json relative to this file,
8
8
  // which is correct regardless of the process working directory.
9
- let _version = '1.11.0';
9
+ let _version = '1.12.0';
10
10
  try {
11
11
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
12
12
  _version = pkg.version ?? _version;