@pellux/goodvibes-tui 2.0.1 → 2.0.2
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 +11 -0
- package/README.md +1 -1
- package/docs/foundation-artifacts/operator-contract.json +1 -1
- package/package.json +8 -8
- package/src/input/payments-config.ts +4 -4
- package/src/input/settings-modal-data.ts +4 -50
- package/src/input/worktree-setup-config.ts +1 -1
- package/src/renderer/theme-mode-config.ts +21 -33
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,17 @@ All notable changes to GoodVibes TUI.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [2.0.2] - 2026-08-01
|
|
8
|
+
|
|
9
|
+
### Changes
|
|
10
|
+
|
|
11
|
+
- Fixed: importing settings that include `display.themeMode` no longer prints
|
|
12
|
+
an "unknown key" warning. The key is now declared in the platform
|
|
13
|
+
configuration schema (SDK 2.0.1) — `auto` probes the terminal background
|
|
14
|
+
once at startup, `dark` and `light` force a fixed appearance — and the
|
|
15
|
+
settings screen's row for it comes from that schema like every other
|
|
16
|
+
`display.*` key, instead of a local stand-in descriptor.
|
|
17
|
+
|
|
7
18
|
## [2.0.1] - 2026-08-01
|
|
8
19
|
|
|
9
20
|
### Changes
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/mgd34msu/goodvibes-tui/actions/workflows/ci.yml)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
[](https://github.com/mgd34msu/goodvibes-tui)
|
|
6
6
|
|
|
7
7
|
GoodVibes is a terminal console for coding and operations work with an AI model. You run `goodvibes` in a project directory and get a full-screen terminal app where you talk to a model that can read and edit your files, run shell commands, search the web, and hand work off to background agents — asking your permission before anything that writes or executes. It talks to many model providers (OpenAI, Anthropic, Gemini, Bedrock, Copilot, OpenRouter and other OpenAI-compatible gateways, plus local servers like Ollama and LM Studio that it finds on startup), keeps its settings, sessions, and secrets on your own machine, and shows you the token count and running cost of every turn. Alongside the conversation, panels turn background work — running agents, git state, diffs, tokens, cost — into live control rooms.
|
|
8
8
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pellux/goodvibes-tui",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "Terminal-native GoodVibes client for coding, operations, knowledge and channel workflows — a pure client of the GoodVibes daemon.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/main.ts",
|
|
@@ -95,11 +95,11 @@
|
|
|
95
95
|
"@anthropic-ai/bedrock-sdk": "^0.29.1",
|
|
96
96
|
"@anthropic-ai/sdk": "^0.92.0",
|
|
97
97
|
"@ast-grep/napi": "^0.42.0",
|
|
98
|
-
"@pellux/goodvibes-sdk": "2.0.
|
|
99
|
-
"@pellux/goodvibes-terminal-shell": "2.0.
|
|
98
|
+
"@pellux/goodvibes-sdk": "2.0.1",
|
|
99
|
+
"@pellux/goodvibes-terminal-shell": "2.0.1",
|
|
100
100
|
"bash-language-server": "^5.6.0",
|
|
101
101
|
"fuse.js": "^7.1.0",
|
|
102
|
-
"@pellux/goodvibes-daemon": "1.28.
|
|
102
|
+
"@pellux/goodvibes-daemon": "1.28.3",
|
|
103
103
|
"graphql": "^16.13.2",
|
|
104
104
|
"jszip": "^3.10.1",
|
|
105
105
|
"node-edge-tts": "^1.2.10",
|
|
@@ -124,10 +124,10 @@
|
|
|
124
124
|
"typescript": "^5.9.3"
|
|
125
125
|
},
|
|
126
126
|
"optionalDependencies": {
|
|
127
|
-
"@pellux/goodvibes-tui-linux-x64": "2.0.
|
|
128
|
-
"@pellux/goodvibes-tui-linux-arm64": "2.0.
|
|
129
|
-
"@pellux/goodvibes-tui-darwin-x64": "2.0.
|
|
130
|
-
"@pellux/goodvibes-tui-darwin-arm64": "2.0.
|
|
127
|
+
"@pellux/goodvibes-tui-linux-x64": "2.0.2",
|
|
128
|
+
"@pellux/goodvibes-tui-linux-arm64": "2.0.2",
|
|
129
|
+
"@pellux/goodvibes-tui-darwin-x64": "2.0.2",
|
|
130
|
+
"@pellux/goodvibes-tui-darwin-arm64": "2.0.2"
|
|
131
131
|
},
|
|
132
132
|
"trustedDependencies": [],
|
|
133
133
|
"overrides": {
|
|
@@ -26,15 +26,15 @@
|
|
|
26
26
|
* implicit card the way this app's `/payments card` flow models it, so these
|
|
27
27
|
* four fields are synthetic sub-keys under the SDK's real `payments`
|
|
28
28
|
* section — the same established pattern this codebase already uses for
|
|
29
|
-
* `behavior.notifyAfterSeconds`, `
|
|
30
|
-
* under an EXISTING section that CONFIG_SCHEMA has no scalar entry for.
|
|
29
|
+
* `behavior.notifyAfterSeconds`, `storage.codeIndexEnabled`, etc.: a key one
|
|
30
|
+
* level under an EXISTING section that CONFIG_SCHEMA has no scalar entry for.
|
|
31
31
|
*
|
|
32
32
|
* That one-level-under-an-existing-section shape matters mechanically: the
|
|
33
33
|
* real ConfigManager's dotted-path resolver only throws "Invalid config path"
|
|
34
34
|
* when an INTERMEDIATE segment is missing (e.g. `payments.card.number`, where
|
|
35
35
|
* `card` is not itself a section) — it does not throw for a final leaf that
|
|
36
36
|
* the schema has not declared (`payments.cardNumber`), the same tolerance
|
|
37
|
-
* `
|
|
37
|
+
* `storage.codeIndexEnabled` relies on under `storage`. So these four keys are named
|
|
38
38
|
* FLAT (`payments.cardNumber`, not `payments.card.number`) specifically so
|
|
39
39
|
* `ConfigManager.get/setDynamic` accepts them — verified against the real
|
|
40
40
|
* ConfigManager (not the fake store this module used before this round): a
|
|
@@ -79,7 +79,7 @@ export const PAYMENTS_CVV_HANDLING_CONFIG_KEY = 'payments.cvvHandling' as Config
|
|
|
79
79
|
function readStringField(configManager: PaymentsConfigReader, key: ConfigKey): string {
|
|
80
80
|
// Defensive try/catch, same rationale as worktree-setup-config.ts's
|
|
81
81
|
// readWorktreeSetupList and this codebase's other one-level-under-a-real-
|
|
82
|
-
// section synthetic reads (behavior.notifyAfterSeconds,
|
|
82
|
+
// section synthetic reads (behavior.notifyAfterSeconds, storage.codeIndexEnabled): the real
|
|
83
83
|
// ConfigManager never throws for these particular keys (verified — see
|
|
84
84
|
// header comment), but degrading to empty rather than crashing the whole
|
|
85
85
|
// settings modal is the same posture every synthetic setting here takes
|
|
@@ -36,13 +36,6 @@ import {
|
|
|
36
36
|
isExecEnvScrubAllowlistConfigKey,
|
|
37
37
|
readExecEnvScrubAllowlist,
|
|
38
38
|
} from './exec-env-scrub-config.ts';
|
|
39
|
-
import {
|
|
40
|
-
THEME_MODE_CONFIG_KEY,
|
|
41
|
-
THEME_MODE_VALUES,
|
|
42
|
-
THEME_MODE_DEFAULT,
|
|
43
|
-
THEME_MODE_DESCRIPTION,
|
|
44
|
-
coerceThemeModeSetting,
|
|
45
|
-
} from '../renderer/theme-mode-config.ts';
|
|
46
39
|
import {
|
|
47
40
|
SETTINGS_CATEGORIES,
|
|
48
41
|
type FlagEntry,
|
|
@@ -145,12 +138,6 @@ export function buildSettingGroups(
|
|
|
145
138
|
if (rawCat === 'cluster' && groups.has('network')) groups.get('network')!.push(entry);
|
|
146
139
|
}
|
|
147
140
|
|
|
148
|
-
// Synthetic display.themeMode enum (auto|dark|light) — TUI-local, see theme-mode-config.ts.
|
|
149
|
-
const displayEntries = groups.get('display');
|
|
150
|
-
if (displayEntries && !displayEntries.some((e) => e.setting.key === (THEME_MODE_CONFIG_KEY as ConfigKey))) {
|
|
151
|
-
displayEntries.push(buildThemeModeSyntheticEntry(configManager));
|
|
152
|
-
}
|
|
153
|
-
|
|
154
141
|
const uiEntries = groups.get('ui');
|
|
155
142
|
if (uiEntries) {
|
|
156
143
|
const uiPriority: Record<string, number> = {
|
|
@@ -283,34 +270,6 @@ export function buildSettingGroups(
|
|
|
283
270
|
return groups;
|
|
284
271
|
}
|
|
285
272
|
|
|
286
|
-
// ---------------------------------------------------------------------------
|
|
287
|
-
// display.themeMode synthetic setting (light theme)
|
|
288
|
-
// ---------------------------------------------------------------------------
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* The synthetic ConfigSetting descriptor for display.themeMode. TUI-local (not
|
|
292
|
-
* in the SDK ConfigKey union); the key is cast to ConfigKey as the other
|
|
293
|
-
* synthetic settings do. Stored under the existing `display` section so
|
|
294
|
-
* ConfigManager.setDynamic/get round-trip it with no SDK change.
|
|
295
|
-
*/
|
|
296
|
-
export const THEME_MODE_SYNTHETIC_SETTING: ConfigSetting = {
|
|
297
|
-
key: THEME_MODE_CONFIG_KEY as ConfigKey,
|
|
298
|
-
type: 'enum',
|
|
299
|
-
default: THEME_MODE_DEFAULT,
|
|
300
|
-
enumValues: [...THEME_MODE_VALUES],
|
|
301
|
-
description: THEME_MODE_DESCRIPTION,
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
/** Build the synthetic SettingEntry for display.themeMode. */
|
|
305
|
-
export function buildThemeModeSyntheticEntry(configManager: Pick<ConfigManager, 'get'>): SettingEntry {
|
|
306
|
-
const currentValue = coerceThemeModeSetting(configManager.get(THEME_MODE_CONFIG_KEY as ConfigKey));
|
|
307
|
-
return {
|
|
308
|
-
setting: THEME_MODE_SYNTHETIC_SETTING,
|
|
309
|
-
currentValue,
|
|
310
|
-
isDefault: currentValue === THEME_MODE_DEFAULT,
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
|
|
314
273
|
// ---------------------------------------------------------------------------
|
|
315
274
|
// behavior.notifyAfterSeconds synthetic setting
|
|
316
275
|
// ---------------------------------------------------------------------------
|
|
@@ -623,11 +582,9 @@ export function refreshEntryValues(
|
|
|
623
582
|
}
|
|
624
583
|
const raw = configManager.get(entry.setting.key as ConfigKey);
|
|
625
584
|
// Synthetic entries that have no SDK schema key return undefined from
|
|
626
|
-
// configManager
|
|
627
|
-
//
|
|
628
|
-
entry.currentValue =
|
|
629
|
-
? coerceThemeModeSetting(raw)
|
|
630
|
-
: raw;
|
|
585
|
+
// configManager; the schema-driven entries above already normalize on
|
|
586
|
+
// their own read paths, so the raw value is used as-is here.
|
|
587
|
+
entry.currentValue = raw;
|
|
631
588
|
entry.isDefault = deepEqual(entry.currentValue, entry.setting.default);
|
|
632
589
|
}
|
|
633
590
|
}
|
|
@@ -670,10 +627,7 @@ export function updateEntryForKey(
|
|
|
670
627
|
continue;
|
|
671
628
|
}
|
|
672
629
|
const raw = configManager.get(key);
|
|
673
|
-
|
|
674
|
-
entry.currentValue = key === (THEME_MODE_CONFIG_KEY as ConfigKey)
|
|
675
|
-
? coerceThemeModeSetting(raw)
|
|
676
|
-
: raw;
|
|
630
|
+
entry.currentValue = raw;
|
|
677
631
|
entry.isDefault = deepEqual(entry.currentValue, entry.setting.default);
|
|
678
632
|
}
|
|
679
633
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* settings modal even though they fully control what runs when a fresh
|
|
10
10
|
* isolated worktree is provisioned (setup commands, then untracked-file
|
|
11
11
|
* carry-over). Same rationale as the other synthetic settings in
|
|
12
|
-
* settings-modal-data.ts (behavior.notifyAfterSeconds,
|
|
12
|
+
* settings-modal-data.ts (behavior.notifyAfterSeconds, storage.codeIndexEnabled, etc).
|
|
13
13
|
*
|
|
14
14
|
* Both keys hold a JSON array of strings on disk. The settings modal's
|
|
15
15
|
* inline editor is a single-line text field, so — matching the SDK's own
|
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
* theme-mode-config — the appearance/theme-mode preference, TUI-side.
|
|
3
3
|
*
|
|
4
4
|
* The preference lives at the config key `display.themeMode` (auto | dark |
|
|
5
|
-
* light, default auto)
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
5
|
+
* light, default auto), stored under the existing SDK `display` section
|
|
6
|
+
* alongside `display.theme` (the color palette — a separate, independent
|
|
7
|
+
* concept: theme picks the palette, themeMode picks light/dark appearance).
|
|
8
|
+
*
|
|
9
|
+
* `display.themeMode` is a real CONFIG_SCHEMA entry (SDK 2.0.0+); this module
|
|
10
|
+
* no longer carries a TUI-local synthetic descriptor for it. The settings
|
|
11
|
+
* modal renders the schema's own row like every other key, and this module
|
|
12
|
+
* keeps the read-side helpers (coerceThemeModeSetting,
|
|
13
|
+
* resolveConfiguredThemeMode) that the renderer's runtime paths (theme.ts,
|
|
14
|
+
* terminal-bg-probe.ts, ui-openers.ts) use to turn the resolved value into a
|
|
15
|
+
* ThemeMode ('dark' | 'light') for actual rendering.
|
|
14
16
|
*
|
|
15
17
|
* This module is deliberately free of any terminal/probe state so both the
|
|
16
18
|
* settings-modal data layer (input) and the probe (renderer) can import it
|
|
@@ -21,27 +23,15 @@ import type { ThemeModeSetting } from './theme.ts';
|
|
|
21
23
|
import type { ConfigKey, ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
|
|
22
24
|
|
|
23
25
|
/** Config key backing the appearance/theme-mode preference (see module doc). */
|
|
24
|
-
export const THEME_MODE_CONFIG_KEY = 'display.themeMode';
|
|
25
|
-
|
|
26
|
-
/** The three valid preference values, in settings-cycle order. */
|
|
27
|
-
export const THEME_MODE_VALUES: readonly ThemeModeSetting[] = ['auto', 'dark', 'light'];
|
|
28
|
-
|
|
29
|
-
/** Default when unset: probe the terminal background on startup. */
|
|
30
|
-
export const THEME_MODE_DEFAULT: ThemeModeSetting = 'auto';
|
|
26
|
+
export const THEME_MODE_CONFIG_KEY: ConfigKey = 'display.themeMode';
|
|
31
27
|
|
|
32
28
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
29
|
+
* Default when unset: probe the terminal background on startup. Matches the
|
|
30
|
+
* CONFIG_SCHEMA default for display.themeMode (module-private: nothing
|
|
31
|
+
* outside this file needs the default value directly — callers read the
|
|
32
|
+
* resolved mode via coerceThemeModeSetting/resolveConfiguredThemeMode).
|
|
36
33
|
*/
|
|
37
|
-
|
|
38
|
-
'Terminal background theme. auto probes the terminal background colour once at '
|
|
39
|
-
+ 'startup (OSC 11) and picks light or dark; dark/light force a fixed theme. '
|
|
40
|
-
+ 'Forced modes take effect immediately; auto is only evaluated at startup, so '
|
|
41
|
-
+ 'selecting auto takes effect on the next launch. Unreadable/unsupported '
|
|
42
|
-
+ 'terminals fall back to dark. Scope (honest): transcript markdown, modal '
|
|
43
|
-
+ 'accents, and the header/footer/thinking chrome all flip with this setting; '
|
|
44
|
-
+ 'only the background colour itself follows your terminal.';
|
|
34
|
+
const THEME_MODE_DEFAULT: ThemeModeSetting = 'auto';
|
|
45
35
|
|
|
46
36
|
/** Narrow an unknown config value to a valid ThemeModeSetting, else the default. */
|
|
47
37
|
export function coerceThemeModeSetting(raw: unknown): ThemeModeSetting {
|
|
@@ -49,17 +39,15 @@ export function coerceThemeModeSetting(raw: unknown): ThemeModeSetting {
|
|
|
49
39
|
}
|
|
50
40
|
|
|
51
41
|
/**
|
|
52
|
-
* Read the configured theme-mode preference.
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* throwing, and we coerce undefined → 'auto'.
|
|
42
|
+
* Read the configured theme-mode preference. Safe: the `display` section
|
|
43
|
+
* exists in DEFAULT_CONFIG, so get() returns undefined for an unset field
|
|
44
|
+
* rather than throwing, and we coerce undefined → 'auto'.
|
|
56
45
|
*/
|
|
57
46
|
export function resolveConfiguredThemeMode(
|
|
58
47
|
configManager: Pick<ConfigManager, 'get'>,
|
|
59
48
|
): ThemeModeSetting {
|
|
60
49
|
try {
|
|
61
|
-
|
|
62
|
-
return coerceThemeModeSetting(configManager.get(THEME_MODE_CONFIG_KEY as ConfigKey));
|
|
50
|
+
return coerceThemeModeSetting(configManager.get(THEME_MODE_CONFIG_KEY));
|
|
63
51
|
} catch {
|
|
64
52
|
// Defensive: any unexpected resolvePath/section error → honest default.
|
|
65
53
|
return THEME_MODE_DEFAULT;
|
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 = '2.0.
|
|
9
|
+
let _version = '2.0.2';
|
|
10
10
|
try {
|
|
11
11
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
|
|
12
12
|
// Only trust a version read from OUR OWN package.json. A Bun single-file
|