@osovv/vv-opencode 1.1.0 → 1.1.1

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +25 -11
  3. package/dist/commands/doctor.js +7 -1
  4. package/dist/commands/doctor.js.map +1 -1
  5. package/dist/commands/init.js +14 -5
  6. package/dist/commands/init.js.map +1 -1
  7. package/dist/commands/install.js +6 -3
  8. package/dist/commands/install.js.map +1 -1
  9. package/dist/commands/launch.d.ts +1 -0
  10. package/dist/commands/launch.js +20 -8
  11. package/dist/commands/launch.js.map +1 -1
  12. package/dist/commands/status.js +8 -2
  13. package/dist/commands/status.js.map +1 -1
  14. package/dist/commands/sync.js +6 -3
  15. package/dist/commands/sync.js.map +1 -1
  16. package/dist/lib/config-layers.d.ts +6 -0
  17. package/dist/lib/config-layers.js +65 -3
  18. package/dist/lib/config-layers.js.map +1 -1
  19. package/dist/lib/opencode.d.ts +16 -0
  20. package/dist/lib/opencode.js +144 -9
  21. package/dist/lib/opencode.js.map +1 -1
  22. package/dist/lib/plugin-toggle-config.d.ts +1 -1
  23. package/dist/lib/plugin-toggle-config.js +2 -0
  24. package/dist/lib/plugin-toggle-config.js.map +1 -1
  25. package/dist/tui/context/analyze.d.ts +4 -0
  26. package/dist/tui/context/analyze.js +256 -0
  27. package/dist/tui/context/analyze.js.map +1 -0
  28. package/dist/tui/context/collect.d.ts +3 -0
  29. package/dist/tui/context/collect.js +94 -0
  30. package/dist/tui/context/collect.js.map +1 -0
  31. package/dist/tui/context/estimate.d.ts +2 -0
  32. package/dist/tui/context/estimate.js +49 -0
  33. package/dist/tui/context/estimate.js.map +1 -0
  34. package/dist/tui/context/plugin.d.ts +10 -0
  35. package/dist/tui/context/plugin.js +91 -0
  36. package/dist/tui/context/plugin.js.map +1 -0
  37. package/dist/tui/context/types.d.ts +61 -0
  38. package/dist/tui/context/types.js +25 -0
  39. package/dist/tui/context/types.js.map +1 -0
  40. package/dist/tui/context/view.d.ts +3 -0
  41. package/dist/tui/context/view.js +45 -0
  42. package/dist/tui/context/view.js.map +1 -0
  43. package/dist/tui.d.ts +7 -0
  44. package/dist/tui.js +27 -0
  45. package/dist/tui.js.map +1 -0
  46. package/package.json +16 -4
  47. package/schemas/vvoc/v3.json +1 -1
@@ -0,0 +1,49 @@
1
+ // FILE: src/tui/context/estimate.ts
2
+ // VERSION: 1.0.0
3
+ // START_MODULE_CONTRACT
4
+ // PURPOSE: Provide deterministic, provider-neutral token approximations for context category comparison.
5
+ // SCOPE: Plain text and JSON-compatible value estimation only; no provider-specific tokenizer claims.
6
+ // DEPENDS: [none]
7
+ // LINKS: [M-PLUGIN-CONTEXT-TUI, V-M-PLUGIN-CONTEXT-TUI]
8
+ // ROLE: RUNTIME
9
+ // MAP_MODE: EXPORTS
10
+ // END_MODULE_CONTRACT
11
+ //
12
+ // START_MODULE_MAP
13
+ // estimateTextTokens - Approximate text tokens with separate ASCII and non-ASCII weighting.
14
+ // estimateValueTokens - Serialize a JSON-compatible value and estimate its token count.
15
+ // END_MODULE_MAP
16
+ //
17
+ // START_CHANGE_SUMMARY
18
+ // LAST_CHANGE: [C-CONTEXT-TUI-PLUGIN - Added deterministic provider-neutral context estimates.]
19
+ // END_CHANGE_SUMMARY
20
+ // START_BLOCK_TOKEN_ESTIMATION
21
+ export function estimateTextTokens(value) {
22
+ if (!value?.trim())
23
+ return 0;
24
+ let ascii = 0;
25
+ let nonAscii = 0;
26
+ for (const character of value) {
27
+ if (character.codePointAt(0) <= 0x7f) {
28
+ ascii += 1;
29
+ }
30
+ else {
31
+ nonAscii += 1;
32
+ }
33
+ }
34
+ return Math.max(1, Math.ceil(ascii / 4 + nonAscii));
35
+ }
36
+ export function estimateValueTokens(value) {
37
+ if (value === undefined || value === null)
38
+ return 0;
39
+ if (typeof value === "string")
40
+ return estimateTextTokens(value);
41
+ try {
42
+ return estimateTextTokens(JSON.stringify(value));
43
+ }
44
+ catch {
45
+ return estimateTextTokens(String(value));
46
+ }
47
+ }
48
+ // END_BLOCK_TOKEN_ESTIMATION
49
+ //# sourceMappingURL=estimate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"estimate.js","sourceRoot":"","sources":["../../../src/tui/context/estimate.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,iBAAiB;AACjB,wBAAwB;AACxB,2GAA2G;AAC3G,wGAAwG;AACxG,oBAAoB;AACpB,0DAA0D;AAC1D,kBAAkB;AAClB,sBAAsB;AACtB,sBAAsB;AACtB,EAAE;AACF,mBAAmB;AACnB,8FAA8F;AAC9F,0FAA0F;AAC1F,iBAAiB;AACjB,EAAE;AACF,uBAAuB;AACvB,kGAAkG;AAClG,qBAAqB;AAErB,+BAA+B;AAC/B,MAAM,UAAU,kBAAkB,CAAC,KAAyB;IAC1D,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE;QAAE,OAAO,CAAC,CAAC;IAE7B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,MAAM,SAAS,IAAI,KAAK,EAAE,CAAC;QAC9B,IAAI,SAAS,CAAC,WAAW,CAAC,CAAC,CAAE,IAAI,IAAI,EAAE,CAAC;YACtC,KAAK,IAAI,CAAC,CAAC;QACb,CAAC;aAAM,CAAC;YACN,QAAQ,IAAI,CAAC,CAAC;QAChB,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,CAAC,CAAC;IACpD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAEhE,IAAI,CAAC;QACH,OAAO,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC;AACD,6BAA6B"}
@@ -0,0 +1,10 @@
1
+ import type { PluginOptions } from "@opencode-ai/plugin";
2
+ import type { TuiPlugin, TuiPluginApi } from "@opencode-ai/plugin/tui";
3
+ import type { ContextAnalysis } from "./types.js";
4
+ export type ContextTuiDependencies = {
5
+ enabled: (api: TuiPluginApi) => Promise<boolean>;
6
+ collect: (api: TuiPluginApi, sessionID: string) => Promise<ContextAnalysis>;
7
+ open: (api: TuiPluginApi, analysis: ContextAnalysis) => void;
8
+ };
9
+ export declare function registerContextTuiPlugin(api: TuiPluginApi, options: PluginOptions | undefined, dependencies?: ContextTuiDependencies): Promise<void>;
10
+ export declare const ContextTuiPlugin: TuiPlugin;
@@ -0,0 +1,91 @@
1
+ // FILE: src/tui/context/plugin.ts
2
+ // VERSION: 1.0.0
3
+ // START_MODULE_CONTRACT
4
+ // PURPOSE: Register the modern OpenCode /context TUI command and connect config toggles, collection, and rendering.
5
+ // SCOPE: Plugin enablement, active-session gating, keymap command registration, lifecycle cleanup, and bounded error toasts.
6
+ // DEPENDS: [@opencode-ai/plugin/tui, src/lib/config-layers.ts, src/lib/plugin-toggle-config.ts, src/tui/context/collect.ts, src/tui/context/view.tsx]
7
+ // LINKS: [M-PLUGIN-CONTEXT-TUI, M-CONFIG-LAYERS, M-PLUGIN-TOGGLE-CONFIG, DF-CONTEXT-INSPECTION, V-M-PLUGIN-CONTEXT-TUI]
8
+ // ROLE: INTEGRATION
9
+ // MAP_MODE: EXPORTS
10
+ // END_MODULE_CONTRACT
11
+ //
12
+ // START_MODULE_MAP
13
+ // registerContextTuiPlugin - Register /context with injectable dependencies for focused tests.
14
+ // ContextTuiPlugin - Default production TUI plugin factory.
15
+ // END_MODULE_MAP
16
+ //
17
+ // START_CHANGE_SUMMARY
18
+ // LAST_CHANGE: [C-CONTEXT-TUI-PLUGIN - Added modern slash command registration and fail-soft dialog execution.]
19
+ // END_CHANGE_SUMMARY
20
+ import { loadVvocConfigForRead } from "../../lib/config-layers.js";
21
+ import { isVvocPluginEnabled } from "../../lib/plugin-toggle-config.js";
22
+ import { collectContextAnalysis } from "./collect.js";
23
+ import { openContextDialog } from "./view.js";
24
+ const CONTEXT_COMMAND = "vvoc.context.show";
25
+ const DEFAULT_DEPENDENCIES = {
26
+ enabled: async (api) => {
27
+ const vvoc = await loadVvocConfigForRead({
28
+ scope: "effective",
29
+ allowDefault: true,
30
+ cwd: api.state.path.directory,
31
+ });
32
+ return isVvocPluginEnabled(vvoc.config, "context");
33
+ },
34
+ collect: collectContextAnalysis,
35
+ open: openContextDialog,
36
+ };
37
+ // START_BLOCK_CONTEXT_COMMAND_REGISTRATION
38
+ export async function registerContextTuiPlugin(api, options, dependencies = DEFAULT_DEPENDENCIES) {
39
+ if (options?.enabled === false || !(await dependencies.enabled(api)))
40
+ return;
41
+ const unregister = api.keymap.registerLayer({
42
+ commands: [
43
+ {
44
+ name: CONTEXT_COMMAND,
45
+ title: "Context usage",
46
+ description: "Show measured context usage and an approximate source breakdown",
47
+ category: "VVOC",
48
+ namespace: "palette",
49
+ slashName: "context",
50
+ enabled: () => api.route.current.name === "session",
51
+ async run() {
52
+ const route = api.route.current;
53
+ const sessionID = route.name === "session" &&
54
+ "params" in route &&
55
+ typeof route.params?.sessionID === "string"
56
+ ? route.params.sessionID
57
+ : undefined;
58
+ if (!sessionID) {
59
+ api.ui.toast({
60
+ variant: "warning",
61
+ title: "Context usage",
62
+ message: "Open a session before running /context.",
63
+ });
64
+ return;
65
+ }
66
+ try {
67
+ const analysis = await dependencies.collect(api, sessionID);
68
+ dependencies.open(api, analysis);
69
+ }
70
+ catch (error) {
71
+ api.ui.toast({
72
+ variant: "error",
73
+ title: "Context usage unavailable",
74
+ message: boundedError(error),
75
+ });
76
+ }
77
+ },
78
+ },
79
+ ],
80
+ });
81
+ api.lifecycle.onDispose(unregister);
82
+ }
83
+ // END_BLOCK_CONTEXT_COMMAND_REGISTRATION
84
+ export const ContextTuiPlugin = async (api, options) => {
85
+ await registerContextTuiPlugin(api, options);
86
+ };
87
+ function boundedError(error) {
88
+ const message = error instanceof Error ? error.message : String(error);
89
+ return message.length > 180 ? `${message.slice(0, 177)}...` : message;
90
+ }
91
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../../src/tui/context/plugin.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,iBAAiB;AACjB,wBAAwB;AACxB,sHAAsH;AACtH,+HAA+H;AAC/H,wJAAwJ;AACxJ,0HAA0H;AAC1H,sBAAsB;AACtB,sBAAsB;AACtB,sBAAsB;AACtB,EAAE;AACF,mBAAmB;AACnB,iGAAiG;AACjG,8DAA8D;AAC9D,iBAAiB;AACjB,EAAE;AACF,uBAAuB;AACvB,kHAAkH;AAClH,qBAAqB;AAIrB,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAEtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C,MAAM,eAAe,GAAG,mBAAmB,CAAC;AAQ5C,MAAM,oBAAoB,GAA2B;IACnD,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACrB,MAAM,IAAI,GAAG,MAAM,qBAAqB,CAAC;YACvC,KAAK,EAAE,WAAW;YAClB,YAAY,EAAE,IAAI;YAClB,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS;SAC9B,CAAC,CAAC;QACH,OAAO,mBAAmB,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,EAAE,sBAAsB;IAC/B,IAAI,EAAE,iBAAiB;CACxB,CAAC;AAEF,2CAA2C;AAC3C,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,GAAiB,EACjB,OAAkC,EAClC,eAAuC,oBAAoB;IAE3D,IAAI,OAAO,EAAE,OAAO,KAAK,KAAK,IAAI,CAAC,CAAC,MAAM,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAAE,OAAO;IAE7E,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC;QAC1C,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,eAAe;gBACrB,KAAK,EAAE,eAAe;gBACtB,WAAW,EAAE,iEAAiE;gBAC9E,QAAQ,EAAE,MAAM;gBAChB,SAAS,EAAE,SAAS;gBACpB,SAAS,EAAE,SAAS;gBACpB,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS;gBACnD,KAAK,CAAC,GAAG;oBACP,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC;oBAChC,MAAM,SAAS,GACb,KAAK,CAAC,IAAI,KAAK,SAAS;wBACxB,QAAQ,IAAI,KAAK;wBACjB,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,KAAK,QAAQ;wBACzC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS;wBACxB,CAAC,CAAC,SAAS,CAAC;oBAChB,IAAI,CAAC,SAAS,EAAE,CAAC;wBACf,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC;4BACX,OAAO,EAAE,SAAS;4BAClB,KAAK,EAAE,eAAe;4BACtB,OAAO,EAAE,yCAAyC;yBACnD,CAAC,CAAC;wBACH,OAAO;oBACT,CAAC;oBAED,IAAI,CAAC;wBACH,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;wBAC5D,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;oBACnC,CAAC;oBAAC,OAAO,KAAK,EAAE,CAAC;wBACf,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC;4BACX,OAAO,EAAE,OAAO;4BAChB,KAAK,EAAE,2BAA2B;4BAClC,OAAO,EAAE,YAAY,CAAC,KAAK,CAAC;yBAC7B,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;aACF;SACF;KACF,CAAC,CAAC;IACH,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;AACtC,CAAC;AACD,yCAAyC;AAEzC,MAAM,CAAC,MAAM,gBAAgB,GAAc,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;IAChE,MAAM,wBAAwB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC,CAAC;AAEF,SAAS,YAAY,CAAC,KAAc;IAClC,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,OAAO,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC;AACxE,CAAC"}
@@ -0,0 +1,61 @@
1
+ import type { Agent, Message, Part, ToolListItem } from "@opencode-ai/sdk/v2";
2
+ export type ContextSkill = {
3
+ name: string;
4
+ description?: string;
5
+ location: string;
6
+ content: string;
7
+ };
8
+ export type ContextMcpServer = {
9
+ name: string;
10
+ status: string;
11
+ error?: string;
12
+ };
13
+ export type ContextModel = {
14
+ providerID: string;
15
+ modelID: string;
16
+ name?: string;
17
+ contextLimit?: number;
18
+ outputLimit?: number;
19
+ };
20
+ export type ContextAnalysisInput = {
21
+ sessionID: string;
22
+ messages: readonly Message[];
23
+ parts: readonly Part[];
24
+ agents: readonly Agent[];
25
+ skills: readonly ContextSkill[];
26
+ tools: readonly ToolListItem[];
27
+ mcpServers: readonly ContextMcpServer[];
28
+ model?: ContextModel;
29
+ warnings?: readonly string[];
30
+ };
31
+ export type ContextCategoryId = "system" | "skill-catalog" | "loaded-skills" | "builtin-tool-schemas" | "vvoc-tool-schemas" | "external-tool-schemas" | "user-messages" | "assistant-messages" | "tool-results" | "files" | "compacted-summary" | "provider-only";
32
+ export type ContextCategory = {
33
+ id: ContextCategoryId;
34
+ label: string;
35
+ estimatedTokens: number;
36
+ detail?: string;
37
+ source: "estimated" | "provider-residual";
38
+ };
39
+ export type ContextMeasuredUsage = {
40
+ usedTokens: number;
41
+ contextLimit?: number;
42
+ remainingTokens?: number;
43
+ percentUsed?: number;
44
+ inputTokens: number;
45
+ cacheReadTokens: number;
46
+ outputTokens: number;
47
+ };
48
+ export type ContextAnalysis = {
49
+ sessionID: string;
50
+ agent?: string;
51
+ model?: ContextModel;
52
+ measured?: ContextMeasuredUsage;
53
+ categories: ContextCategory[];
54
+ estimatedKnownTokens: number;
55
+ estimatedTotalTokens: number;
56
+ estimationDriftTokens: number;
57
+ compacted: boolean;
58
+ activeMessageCount: number;
59
+ mcpServers: ContextMcpServer[];
60
+ warnings: string[];
61
+ };
@@ -0,0 +1,25 @@
1
+ // FILE: src/tui/context/types.ts
2
+ // VERSION: 1.0.0
3
+ // START_MODULE_CONTRACT
4
+ // PURPOSE: Define bounded input and output shapes for the context TUI collector, analyzer, and view.
5
+ // SCOPE: Context categories, model metadata, MCP status summaries, warnings, and analysis input/output types.
6
+ // DEPENDS: [@opencode-ai/sdk/v2]
7
+ // LINKS: [M-PLUGIN-CONTEXT-TUI, V-M-PLUGIN-CONTEXT-TUI]
8
+ // ROLE: TYPES
9
+ // MAP_MODE: EXPORTS
10
+ // END_MODULE_CONTRACT
11
+ //
12
+ // START_MODULE_MAP
13
+ // ContextSkill - Observable skill catalog entry.
14
+ // ContextMcpServer - MCP name/status snapshot exposed by the TUI state.
15
+ // ContextModel - Current provider/model metadata and limits.
16
+ // ContextAnalysisInput - Raw bounded data accepted by the pure analyzer.
17
+ // ContextCategory - One estimated context category row.
18
+ // ContextAnalysis - Measured usage, estimated categories, warnings, and metadata rendered by the dialog.
19
+ // END_MODULE_MAP
20
+ //
21
+ // START_CHANGE_SUMMARY
22
+ // LAST_CHANGE: [C-CONTEXT-TUI-PLUGIN - Added shared context-inspection domain types.]
23
+ // END_CHANGE_SUMMARY
24
+ export {};
25
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/tui/context/types.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,iBAAiB;AACjB,wBAAwB;AACxB,uGAAuG;AACvG,gHAAgH;AAChH,mCAAmC;AACnC,0DAA0D;AAC1D,gBAAgB;AAChB,sBAAsB;AACtB,sBAAsB;AACtB,EAAE;AACF,mBAAmB;AACnB,mDAAmD;AACnD,0EAA0E;AAC1E,+DAA+D;AAC/D,2EAA2E;AAC3E,0DAA0D;AAC1D,2GAA2G;AAC3G,iBAAiB;AACjB,EAAE;AACF,uBAAuB;AACvB,wFAAwF;AACxF,qBAAqB"}
@@ -0,0 +1,3 @@
1
+ import type { TuiPluginApi } from "@opencode-ai/plugin/tui";
2
+ import type { ContextAnalysis } from "./types.js";
3
+ export declare function openContextDialog(api: TuiPluginApi, analysis: ContextAnalysis): void;
@@ -0,0 +1,45 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@opentui/solid/jsx-runtime";
2
+ // START_BLOCK_CONTEXT_DIALOG
3
+ export function openContextDialog(api, analysis) {
4
+ const Dialog = api.ui.Dialog;
5
+ const theme = api.theme.current;
6
+ api.ui.dialog.setSize("xlarge");
7
+ api.ui.dialog.replace(() => (_jsx(Dialog, { size: "xlarge", onClose: () => api.ui.dialog.clear(), children: _jsxs("box", { flexDirection: "column", gap: 1, paddingLeft: 1, paddingRight: 1, children: [_jsxs("box", { flexDirection: "row", justifyContent: "space-between", children: [_jsx("text", { fg: theme.text, children: "Context usage" }), _jsx("text", { fg: theme.textMuted, children: formatModel(analysis) })] }), _jsx(UsageSummary, { analysis: analysis, primary: theme.primary, muted: theme.textMuted }), _jsxs("box", { flexDirection: "column", children: [_jsx("text", { fg: theme.text, children: "Approximate breakdown" }), analysis.categories.map((category) => (_jsxs("box", { flexDirection: "row", justifyContent: "space-between", children: [_jsx("text", { fg: category.source === "provider-residual" ? theme.warning : theme.textMuted, children: category.source === "estimated" ? `~ ${category.label}` : category.label }), _jsx("text", { fg: theme.text, children: formatTokens(category.estimatedTokens) })] })))] }), _jsxs("box", { flexDirection: "row", justifyContent: "space-between", children: [_jsx("text", { fg: theme.textMuted, children: "Active messages" }), _jsxs("text", { fg: theme.text, children: [analysis.activeMessageCount, analysis.compacted ? " (after compaction)" : ""] })] }), _jsx(McpSummary, { analysis: analysis, text: theme.text, muted: theme.textMuted }), analysis.estimationDriftTokens > 0 ? (_jsxs("text", { fg: theme.warning, children: ["Estimate drift: +", formatTokens(analysis.estimationDriftTokens), " above provider usage"] })) : null, analysis.warnings.slice(0, 3).map((warning) => (_jsxs("text", { fg: theme.warning, children: ["Warning: ", warning] }))), _jsx("text", { fg: theme.textMuted, children: "Measured = latest provider usage. ~ = local estimate. MCP tools without source metadata are grouped." })] }) })));
8
+ }
9
+ // END_BLOCK_CONTEXT_DIALOG
10
+ function UsageSummary(props) {
11
+ const measured = props.analysis.measured;
12
+ if (!measured) {
13
+ return (_jsx("text", { fg: props.muted, children: "Provider usage is not available until the session has an assistant turn." }));
14
+ }
15
+ const percent = measured.percentUsed;
16
+ return (_jsxs("box", { flexDirection: "column", children: [_jsxs("text", { fg: props.primary, children: [renderUsageBar(percent), " ", formatTokens(measured.usedTokens), measured.contextLimit ? ` / ${formatTokens(measured.contextLimit)}` : "", percent === undefined ? "" : ` (${percent.toFixed(1)}%)`] }), _jsxs("text", { fg: props.muted, children: ["input ", formatTokens(measured.inputTokens), " \u00B7 cache read", " ", formatTokens(measured.cacheReadTokens), " \u00B7 output ", formatTokens(measured.outputTokens), measured.remainingTokens === undefined
17
+ ? ""
18
+ : ` · remaining ${formatTokens(measured.remainingTokens)}`] })] }));
19
+ }
20
+ function McpSummary(props) {
21
+ if (props.analysis.mcpServers.length === 0) {
22
+ return _jsx("text", { fg: props.muted, children: "MCP servers: none reported" });
23
+ }
24
+ return (_jsxs("box", { flexDirection: "column", children: [_jsx("text", { fg: props.text, children: "MCP servers" }), props.analysis.mcpServers.map((server) => (_jsxs("box", { flexDirection: "row", justifyContent: "space-between", children: [_jsx("text", { fg: props.muted, children: server.name }), _jsx("text", { fg: props.text, children: server.status })] })))] }));
25
+ }
26
+ function formatModel(analysis) {
27
+ if (!analysis.model)
28
+ return analysis.agent ?? "unknown model";
29
+ const model = `${analysis.model.providerID}/${analysis.model.modelID}`;
30
+ return analysis.agent ? `${analysis.agent} · ${model}` : model;
31
+ }
32
+ function formatTokens(tokens) {
33
+ if (tokens >= 1_000_000)
34
+ return `${(tokens / 1_000_000).toFixed(2)}m`;
35
+ if (tokens >= 1_000)
36
+ return `${(tokens / 1_000).toFixed(tokens >= 100_000 ? 0 : 1)}k`;
37
+ return String(tokens);
38
+ }
39
+ function renderUsageBar(percent) {
40
+ const width = 28;
41
+ const normalized = percent === undefined ? 0 : Math.min(100, Math.max(0, percent));
42
+ const filled = Math.round((normalized / 100) * width);
43
+ return `[${"█".repeat(filled)}${"░".repeat(width - filled)}]`;
44
+ }
45
+ //# sourceMappingURL=view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"view.js","sourceRoot":"","sources":["../../../src/tui/context/view.tsx"],"names":[],"mappings":"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"}
package/dist/tui.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import type { TuiPluginModule } from "@opencode-ai/plugin/tui";
2
+ import { ContextTuiPlugin } from "./tui/context/plugin.js";
3
+ export { ContextTuiPlugin };
4
+ declare const plugin: TuiPluginModule & {
5
+ id: string;
6
+ };
7
+ export default plugin;
package/dist/tui.js ADDED
@@ -0,0 +1,27 @@
1
+ // FILE: src/tui.tsx
2
+ // VERSION: 1.0.0
3
+ // START_MODULE_CONTRACT
4
+ // PURPOSE: Publish the default @osovv/vv-opencode/tui module containing the managed context inspector.
5
+ // SCOPE: Stable TUI package entrypoint and module identity only.
6
+ // DEPENDS: [@opencode-ai/plugin/tui, src/tui/context/plugin.ts]
7
+ // LINKS: [M-PLUGIN-CONTEXT-TUI, V-M-PLUGIN-CONTEXT-TUI]
8
+ // ROLE: BARREL
9
+ // MAP_MODE: EXPORTS
10
+ // END_MODULE_CONTRACT
11
+ //
12
+ // START_MODULE_MAP
13
+ // default - OpenCode TUI plugin module registering the /context inspector.
14
+ // ContextTuiPlugin - Named TUI plugin factory for direct consumers and tests.
15
+ // END_MODULE_MAP
16
+ //
17
+ // START_CHANGE_SUMMARY
18
+ // LAST_CHANGE: [C-CONTEXT-TUI-PLUGIN - Added the public TUI package subpath.]
19
+ // END_CHANGE_SUMMARY
20
+ import { ContextTuiPlugin } from "./tui/context/plugin.js";
21
+ export { ContextTuiPlugin };
22
+ const plugin = {
23
+ id: "vvoc-context",
24
+ tui: ContextTuiPlugin,
25
+ };
26
+ export default plugin;
27
+ //# sourceMappingURL=tui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tui.js","sourceRoot":"","sources":["../src/tui.tsx"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,iBAAiB;AACjB,wBAAwB;AACxB,yGAAyG;AACzG,mEAAmE;AACnE,kEAAkE;AAClE,0DAA0D;AAC1D,iBAAiB;AACjB,sBAAsB;AACtB,sBAAsB;AACtB,EAAE;AACF,mBAAmB;AACnB,6EAA6E;AAC7E,gFAAgF;AAChF,iBAAiB;AACjB,EAAE;AACF,uBAAuB;AACvB,gFAAgF;AAChF,qBAAqB;AAGrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAE5B,MAAM,MAAM,GAAqC;IAC/C,EAAE,EAAE,cAAc;IAClB,GAAG,EAAE,gBAAgB;CACtB,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@osovv/vv-opencode",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
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",
@@ -42,6 +42,10 @@
42
42
  "types": "./dist/index.d.ts",
43
43
  "import": "./dist/index.js"
44
44
  },
45
+ "./tui": {
46
+ "types": "./dist/tui.d.ts",
47
+ "import": "./dist/tui.js"
48
+ },
45
49
  "./plugins/guardian": {
46
50
  "types": "./dist/plugins/guardian/index.d.ts",
47
51
  "import": "./dist/plugins/guardian/index.js"
@@ -76,21 +80,24 @@
76
80
  "fmt:check": "oxfmt --check src",
77
81
  "test": "bun test",
78
82
  "check": "bun run typecheck && bun run lint && bun run fmt:check && bun test",
79
- "pack:check": "bun run build && bun -e \"const root = await import('./dist/index.js'); if (!('GuardianPlugin' in root)) throw new Error('dist root export missing GuardianPlugin'); if (!('HashlineEditPlugin' in root)) throw new Error('dist root export missing HashlineEditPlugin'); if (!('ModelRolesPlugin' in root)) throw new Error('dist root export missing ModelRolesPlugin'); if (!('SystemContextInjectionPlugin' in root)) throw new Error('dist root export missing SystemContextInjectionPlugin'); if (!('WorkflowPlugin' in root)) throw new Error('dist root export missing WorkflowPlugin'); if (!('SecretsRedactionPlugin' in root)) throw new Error('dist root export missing SecretsRedactionPlugin'); await import('./dist/plugins/guardian/index.js'); await import('./dist/plugins/hashline-edit/index.js'); await import('./dist/plugins/model-roles/index.js'); await import('./dist/plugins/system-context-injection/index.js'); await import('./dist/plugins/workflow/index.js'); await import('./dist/plugins/secrets-redaction/index.js')\" && npm pack --dry-run",
83
+ "pack:check": "bun run build && bun -e \"const root = await import('./dist/index.js'); if (!('GuardianPlugin' in root)) throw new Error('dist root export missing GuardianPlugin'); if (!('HashlineEditPlugin' in root)) throw new Error('dist root export missing HashlineEditPlugin'); if (!('ModelRolesPlugin' in root)) throw new Error('dist root export missing ModelRolesPlugin'); if (!('SystemContextInjectionPlugin' in root)) throw new Error('dist root export missing SystemContextInjectionPlugin'); if (!('WorkflowPlugin' in root)) throw new Error('dist root export missing WorkflowPlugin'); if (!('SecretsRedactionPlugin' in root)) throw new Error('dist root export missing SecretsRedactionPlugin'); const tui = await import('./dist/tui.js'); if (!tui.default || typeof tui.default.tui !== 'function') throw new Error('dist TUI export missing default tui module'); await import('./dist/plugins/guardian/index.js'); await import('./dist/plugins/hashline-edit/index.js'); await import('./dist/plugins/model-roles/index.js'); await import('./dist/plugins/system-context-injection/index.js'); await import('./dist/plugins/workflow/index.js'); await import('./dist/plugins/secrets-redaction/index.js')\" && npm pack --dry-run",
80
84
  "release:check": "bun scripts/release-check.ts",
81
85
  "release:bump": "bun scripts/release-bump.ts",
82
86
  "prepare": "lefthook install --force"
83
87
  },
84
88
  "dependencies": {
85
89
  "@clack/prompts": "^1.2.0",
86
- "@opencode-ai/plugin": "1.3.17",
87
- "@opencode-ai/sdk": "1.3.17",
90
+ "@opencode-ai/plugin": "1.18.2",
91
+ "@opencode-ai/sdk": "1.18.2",
88
92
  "ajv": "^8.17.1",
89
93
  "citty": "0.2.2",
90
94
  "jsonc-parser": "3.3.1"
91
95
  },
92
96
  "devDependencies": {
93
97
  "@types/node": "22.13.9",
98
+ "@opentui/core": "0.4.3",
99
+ "@opentui/keymap": "0.4.3",
100
+ "@opentui/solid": "0.4.3",
94
101
  "bun-types": "1.3.11",
95
102
  "lefthook": "2.1.5",
96
103
  "oxfmt": "0.43.0",
@@ -100,6 +107,11 @@
100
107
  "@commitlint/cli": "^19.0.0",
101
108
  "@commitlint/config-conventional": "^19.0.0"
102
109
  },
110
+ "peerDependencies": {
111
+ "@opentui/core": ">=0.4.3",
112
+ "@opentui/keymap": ">=0.4.3",
113
+ "@opentui/solid": ">=0.4.3"
114
+ },
103
115
  "engines": {
104
116
  "bun": ">=1.3.8"
105
117
  },
@@ -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.1.0/schemas/vvoc/v3.json",
3
+ "$id": "https://cdn.jsdelivr.net/npm/@osovv/vv-opencode@1.1.1/schemas/vvoc/v3.json",
4
4
  "title": "vvoc config",
5
5
  "description": "Canonical vvoc configuration document.",
6
6
  "type": "object",