@osovv/vv-opencode 1.3.1 → 1.3.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 CHANGED
@@ -1,3 +1,11 @@
1
+ ## <small>1.3.2 (2026-08-19)</small>
2
+
3
+ ### Summary
4
+
5
+ The vvoc version label in the TUI has been relocated from the sidebar footer slot to the append-mode `app_bottom` slot, so it now appears in the bottom app bar on every screen without displacing OpenCode's own footer or prompt content, which previously owned that single-winner slot. Theme colors are now passed directly as RGBA values instead of being converted to hex strings, making both the version label and the live cache indicator render with accurate theme colors more reliably, and both slot registrations now include explicit plugin IDs as required by the OpenCode runtime.
6
+
7
+ * fix(tui): move vvoc label to append-mode app_bottom slot and apply theme fg color directly ([a01453d](https://github.com/osovv/vv-opencode/commit/a01453d))
8
+
1
9
  ## <small>1.3.1 (2026-08-19)</small>
2
10
 
3
11
  ### Summary
package/README.md CHANGED
@@ -183,7 +183,7 @@ OpenCode is a strong, flexible base for agentic coding, but it intentionally lea
183
183
  | **WebToolsPlugin** | Register the provider-neutral `web_search` and `web_fetch` tools, return direct image/PDF attachments, and hide OpenCode's built-in web tools at runtime unless the user explicitly configured their permissions. |
184
184
  | **ContextTuiPlugin** | Add a native scrollable `/context` dialog with measured usage plus detailed observable per-tool and per-MCP schema/history estimates, explicitly marking data that OpenCode does not expose. |
185
185
  | **ToolHistoryCompactionPlugin** | Shrink the replayed conversation context non-destructively by compacting old tool outputs in the model replay (old reads to `[Read <file>, lines X-Y]`, over-budget ephemeral outputs pruned), while retaining web/search/skill knowledge results. |
186
- | **AnalyticsPlugin** | Persist per-step token and cache telemetry with vvoc/OpenCode version attribution, show a live `cache NN%` indicator next to the session prompt plus a `vvoc vX.Y.Z` sidebar footer, and answer "did my cache optimizations help?" via `vvoc analytics cache-hit-rate`. |
186
+ | **AnalyticsPlugin** | Persist per-step token and cache telemetry with vvoc/OpenCode version attribution, show a live `cache NN%` indicator next to the session prompt plus a `vvoc vX.Y.Z` label in the bottom app bar, and answer "did my cache optimizations help?" via `vvoc analytics cache-hit-rate`. |
187
187
 
188
188
  Workflow work items are opened with explicit intent. For implementation loops, controllers use:
189
189
 
@@ -265,7 +265,7 @@ Set `outputMaxChars` to `0` to disable pruning, or `"enabled": false` to disable
265
265
 
266
266
  `AnalyticsPlugin` records one line per completed model step — fresh input, cache read, cache write, output, reasoning, recorded cost — to `$XDG_DATA_HOME/vvoc/analytics/usage-YYYY-MM.jsonl`, attributed with the vvoc version, the OpenCode version (from session telemetry), project, provider, model, and agent. Telemetry never leaves the machine; disable collection with `"plugins": { "analytics": false }` and delete old monthly files freely.
267
267
 
268
- In the TUI you get a live `cache NN%` indicator next to the session prompt (green at 80%+, yellow at 50%+, red below, muted `n/a` before the first cache-eligible step) and a `vvoc vX.Y.Z` label in the sidebar footer. The indicator is per-session and computed in memory.
268
+ In the TUI you get a live `cache NN%` indicator next to the session prompt (green at 80%+, yellow at 50%+, red below, muted `n/a` before the first cache-eligible step) and a `vvoc vX.Y.Z` label in the bottom app bar. Both use append-mode host slots, so OpenCode's own footer and prompt content stay intact. The indicator is per-session and computed in memory.
269
269
 
270
270
  Retrospective analysis lives in the CLI:
271
271
 
@@ -1,4 +1,5 @@
1
1
  import type { JSX } from "@opentui/solid";
2
+ import type { RGBA } from "@opentui/core";
2
3
  import type { TuiPluginApi } from "@opencode-ai/plugin/tui";
3
4
  import type { PluginOptions } from "@opencode-ai/plugin";
4
5
  import type { IndicatorTokens } from "../../lib/analytics/types.js";
@@ -19,8 +20,8 @@ export type IndicatorLabel = {
19
20
  };
20
21
  export type IndicatorDependencies = {
21
22
  enabled: (api: TuiPluginApi) => Promise<boolean>;
22
- /** Renders the label node; defaults to the themed span. */
23
- renderLabel: (label: IndicatorLabel, color: string) => JSX.Element;
23
+ /** Renders the label node; defaults to the themed text element. */
24
+ renderLabel: (label: IndicatorLabel, color: RGBA) => JSX.Element;
24
25
  };
25
26
  export declare const DEFAULT_DEPENDENCIES: IndicatorDependencies;
26
27
  /** Rolling per-session sums fed by step-finish parts. Same eligibility rule as metrics. */
@@ -1,7 +1,6 @@
1
1
  import { jsx as _jsx } from "@opentui/solid/jsx-runtime";
2
2
  import { loadVvocConfigForRead } from "../../lib/config-layers.js";
3
3
  import { isVvocPluginEnabled } from "../../lib/plugin-toggle-config.js";
4
- import { rgbaToHex } from "../color.js";
5
4
  export const DEFAULT_DEPENDENCIES = {
6
5
  enabled: async (api) => {
7
6
  const vvoc = await loadVvocConfigForRead({
@@ -77,7 +76,11 @@ export async function registerAnalyticsIndicator(api, options, dependencies = DE
77
76
  });
78
77
  api.lifecycle.onDispose(unsubscribe);
79
78
  try {
80
- api.slots.register({
79
+ // OpenCode's runtime requires a string plugin id on slot registrations, while
80
+ // the SDK's TuiSlotPlugin type still types id as never; cast bridges the two.
81
+ const plugin = {
82
+ id: "vvoc-analytics-indicator",
83
+ order: 900,
81
84
  slots: {
82
85
  session_prompt_right: (_ctx, props) => {
83
86
  if (props.session_id !== currentSessionID(api))
@@ -88,7 +91,8 @@ export async function registerAnalyticsIndicator(api, options, dependencies = DE
88
91
  return dependencies.renderLabel(label, toneColor(api, label.tone));
89
92
  },
90
93
  },
91
- });
94
+ };
95
+ api.slots.register(plugin);
92
96
  }
93
97
  catch {
94
98
  // Fail-soft: no indicator for this session.
@@ -103,7 +107,7 @@ function currentSessionID(api) {
103
107
  }
104
108
  return "";
105
109
  }
106
- /** Maps an indicator tone to a theme color expression. */
110
+ /** Maps an indicator tone to the theme RGBA color instance. */
107
111
  function toneColor(api, tone) {
108
112
  const theme = api.theme.current;
109
113
  const rgba = tone === "green"
@@ -113,6 +117,6 @@ function toneColor(api, tone) {
113
117
  : tone === "red"
114
118
  ? theme.error
115
119
  : theme.textMuted;
116
- return rgbaToHex(rgba);
120
+ return rgba;
117
121
  }
118
122
  //# sourceMappingURL=indicator.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"indicator.js","sourceRoot":"","sources":["../../../src/tui/analytics/indicator.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"indicator.js","sourceRoot":"","sources":["../../../src/tui/analytics/indicator.tsx"],"names":[],"mappings":"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"}
@@ -1,14 +1,20 @@
1
1
  import type { JSX } from "@opentui/solid";
2
+ import type { RGBA } from "@opentui/core";
2
3
  import type { TuiPluginApi } from "@opencode-ai/plugin/tui";
3
4
  export type BrandingDependencies = {
4
- /** Renders the label node; defaults to the muted span. */
5
- renderLabel: (text: string, color: string) => JSX.Element;
5
+ /** Renders the label node; defaults to the muted text element. */
6
+ renderLabel: (text: string, color: RGBA) => JSX.Element;
6
7
  };
7
8
  /** The rendered label, e.g. "vvoc v1.2.11". */
8
9
  export declare function brandingFooterLabel(): string;
9
10
  /**
10
- * Registers the branding footer in the "sidebar_footer" host slot.
11
- * Always on (independent of analytics config); returns silently when the slot
12
- * API is unavailable or registration fails.
11
+ * Registers the branding label in the "app_bottom" host slot.
12
+ *
13
+ * app_bottom renders in append mode at the bottom of the app on every screen,
14
+ * so the label coexists with any other plugin content and never displaces the
15
+ * OpenCode sidebar or home footers (those slots are single_winner and owned by
16
+ * OpenCode's internal footer plugins). Always on (independent of analytics
17
+ * config); returns silently when the slot API is unavailable or registration
18
+ * fails.
13
19
  */
14
20
  export declare function registerBrandingFooter(api: TuiPluginApi, dependencies?: BrandingDependencies): void;
@@ -1,6 +1,5 @@
1
1
  import { jsx as _jsx } from "@opentui/solid/jsx-runtime";
2
2
  import { getPackageVersionSync } from "../../lib/package.js";
3
- import { rgbaToHex } from "../color.js";
4
3
  const DEFAULT_DEPENDENCIES = {
5
4
  renderLabel: (text, color) => (_jsx("text", { children: _jsx("span", { style: { fg: color }, children: text }) })),
6
5
  };
@@ -10,26 +9,34 @@ export function brandingFooterLabel() {
10
9
  }
11
10
  // START_BLOCK_REGISTER_BRANDING_FOOTER
12
11
  /**
13
- * Registers the branding footer in the "sidebar_footer" host slot.
14
- * Always on (independent of analytics config); returns silently when the slot
15
- * API is unavailable or registration fails.
12
+ * Registers the branding label in the "app_bottom" host slot.
13
+ *
14
+ * app_bottom renders in append mode at the bottom of the app on every screen,
15
+ * so the label coexists with any other plugin content and never displaces the
16
+ * OpenCode sidebar or home footers (those slots are single_winner and owned by
17
+ * OpenCode's internal footer plugins). Always on (independent of analytics
18
+ * config); returns silently when the slot API is unavailable or registration
19
+ * fails.
16
20
  */
17
21
  export function registerBrandingFooter(api, dependencies = DEFAULT_DEPENDENCIES) {
18
22
  if (typeof api.slots?.register !== "function")
19
23
  return;
20
24
  try {
21
- api.slots.register({
25
+ // OpenCode's runtime requires a string plugin id on slot registrations, while
26
+ // the SDK's TuiSlotPlugin type still types id as never; cast bridges the two.
27
+ const plugin = {
28
+ id: "vvoc-branding",
29
+ order: 1000,
22
30
  slots: {
23
- sidebar_footer: () => {
24
- const muted = api.theme.current.textMuted;
25
- const color = rgbaToHex(api.theme.current.textMuted);
26
- return dependencies.renderLabel(brandingFooterLabel(), color);
31
+ app_bottom: (ctx) => {
32
+ return dependencies.renderLabel(brandingFooterLabel(), ctx.theme.current.textMuted);
27
33
  },
28
34
  },
29
- });
35
+ };
36
+ api.slots.register(plugin);
30
37
  }
31
38
  catch {
32
- // Fail-soft: no footer for this session.
39
+ // Fail-soft: no label for this session.
33
40
  }
34
41
  }
35
42
  //# sourceMappingURL=footer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"footer.js","sourceRoot":"","sources":["../../../src/tui/branding/footer.tsx"],"names":[],"mappings":";AAuBA,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAOxC,MAAM,oBAAoB,GAAyB;IACjD,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAC5B,yBACE,eAAM,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,YAAG,IAAI,GAAQ,GACpC,CACR;CACF,CAAC;AAEF,+CAA+C;AAC/C,MAAM,UAAU,mBAAmB;IACjC,OAAO,SAAS,qBAAqB,EAAE,EAAE,CAAC;AAC5C,CAAC;AAED,uCAAuC;AACvC;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CACpC,GAAiB,EACjB,eAAqC,oBAAoB;IAEzD,IAAI,OAAO,GAAG,CAAC,KAAK,EAAE,QAAQ,KAAK,UAAU;QAAE,OAAO;IACtD,IAAI,CAAC;QACH,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC;YACjB,KAAK,EAAE;gBACL,cAAc,EAAE,GAAG,EAAE;oBACnB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;oBAC1C,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;oBACrD,OAAO,YAAY,CAAC,WAAW,CAAC,mBAAmB,EAAE,EAAE,KAAK,CAAC,CAAC;gBAChE,CAAC;aACF;SACF,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,yCAAyC;IAC3C,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"footer.js","sourceRoot":"","sources":["../../../src/tui/branding/footer.tsx"],"names":[],"mappings":";AAwBA,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAO7D,MAAM,oBAAoB,GAAyB;IACjD,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAC5B,yBACE,eAAM,KAAK,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,YAAG,IAAI,GAAQ,GACpC,CACR;CACF,CAAC;AAEF,+CAA+C;AAC/C,MAAM,UAAU,mBAAmB;IACjC,OAAO,SAAS,qBAAqB,EAAE,EAAE,CAAC;AAC5C,CAAC;AAED,uCAAuC;AACvC;;;;;;;;;GASG;AACH,MAAM,UAAU,sBAAsB,CACpC,GAAiB,EACjB,eAAqC,oBAAoB;IAEzD,IAAI,OAAO,GAAG,CAAC,KAAK,EAAE,QAAQ,KAAK,UAAU;QAAE,OAAO;IACtD,IAAI,CAAC;QACH,8EAA8E;QAC9E,8EAA8E;QAC9E,MAAM,MAAM,GAAG;YACb,EAAE,EAAE,eAAe;YACnB,KAAK,EAAE,IAAI;YACX,KAAK,EAAE;gBACL,UAAU,EAAE,CAAC,GAAmB,EAAE,EAAE;oBAClC,OAAO,YAAY,CAAC,WAAW,CAAC,mBAAmB,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;gBACtF,CAAC;aACF;SAC6D,CAAC;QACjE,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,wCAAwC;IAC1C,CAAC;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@osovv/vv-opencode",
3
- "version": "1.3.1",
3
+ "version": "1.3.2",
4
4
  "description": "A curated, opinionated set of OpenCode plugins for spec-first, review-driven, safer agentic development.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://cdn.jsdelivr.net/npm/@osovv/vv-opencode@1.3.1/schemas/vvoc/v3.json",
3
+ "$id": "https://cdn.jsdelivr.net/npm/@osovv/vv-opencode@1.3.2/schemas/vvoc/v3.json",
4
4
  "title": "vvoc config",
5
5
  "description": "Canonical vvoc configuration document.",
6
6
  "type": "object",
@@ -1,13 +0,0 @@
1
- /** RGBA-like theme color shape produced by the OpenCode TUI theme API. */
2
- export type RgbaLike = {
3
- r: number;
4
- g: number;
5
- b: number;
6
- a: number;
7
- };
8
- /**
9
- * Formats an RGBA-like theme color as a six-digit hex string.
10
- * Alpha is dropped: rgba() strings and 8-digit hex parse unreliably in
11
- * @opentui/core, while #rrggbb parses exactly.
12
- */
13
- export declare function rgbaToHex(color: RgbaLike): string;
package/dist/tui/color.js DELETED
@@ -1,30 +0,0 @@
1
- // FILE: src/tui/color.ts
2
- // VERSION: 1.0.0
3
- // START_MODULE_CONTRACT
4
- // PURPOSE: Convert OpenTUI RGBA theme values into color strings the OpenTUI solid span style reliably parses.
5
- // SCOPE: Six-digit hex formatting from RGBA components; alpha is intentionally dropped because 8-digit forms parse unreliably in @opentui/core.
6
- // DEPENDS: [none]
7
- // LINKS: [M-TUI-BRANDING-FOOTER, M-TUI-ANALYTICS-INDICATOR]
8
- // ROLE: RUNTIME
9
- // MAP_MODE: EXPORTS
10
- // END_MODULE_CONTRACT
11
- //
12
- // START_MODULE_MAP
13
- // rgbaToHex - Formats an RGBA-like theme color as #rrggbb.
14
- // END_MODULE_MAP
15
- //
16
- // START_CHANGE_SUMMARY
17
- // LAST_CHANGE: [2026-08-20-orphan-text-fix - Added reliable theme color formatting for text span foregrounds.]
18
- // END_CHANGE_SUMMARY
19
- /**
20
- * Formats an RGBA-like theme color as a six-digit hex string.
21
- * Alpha is dropped: rgba() strings and 8-digit hex parse unreliably in
22
- * @opentui/core, while #rrggbb parses exactly.
23
- */
24
- export function rgbaToHex(color) {
25
- const channel = (value) => Math.max(0, Math.min(255, Math.round(value)))
26
- .toString(16)
27
- .padStart(2, "0");
28
- return `#${channel(color.r)}${channel(color.g)}${channel(color.b)}`;
29
- }
30
- //# sourceMappingURL=color.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"color.js","sourceRoot":"","sources":["../../src/tui/color.ts"],"names":[],"mappings":"AAAA,yBAAyB;AACzB,iBAAiB;AACjB,wBAAwB;AACxB,gHAAgH;AAChH,kJAAkJ;AAClJ,oBAAoB;AACpB,8DAA8D;AAC9D,kBAAkB;AAClB,sBAAsB;AACtB,sBAAsB;AACtB,EAAE;AACF,mBAAmB;AACnB,6DAA6D;AAC7D,iBAAiB;AACjB,EAAE;AACF,uBAAuB;AACvB,iHAAiH;AACjH,qBAAqB;AAUrB;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,KAAe;IACvC,MAAM,OAAO,GAAG,CAAC,KAAa,EAAE,EAAE,CAChC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;SAC1C,QAAQ,CAAC,EAAE,CAAC;SACZ,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACtB,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AACtE,CAAC"}