@osovv/vv-opencode 1.2.11 → 1.3.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 +14 -0
- package/README.md +27 -2
- package/dist/cli.js +4 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/analytics.d.ts +58 -0
- package/dist/commands/analytics.js +220 -0
- package/dist/commands/analytics.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/lib/analytics/metrics.d.ts +33 -0
- package/dist/lib/analytics/metrics.js +162 -0
- package/dist/lib/analytics/metrics.js.map +1 -0
- package/dist/lib/analytics/store.d.ts +20 -0
- package/dist/lib/analytics/store.js +81 -0
- package/dist/lib/analytics/store.js.map +1 -0
- package/dist/lib/analytics/types.d.ts +67 -0
- package/dist/lib/analytics/types.js +26 -0
- package/dist/lib/analytics/types.js.map +1 -0
- package/dist/lib/managed-skills.d.ts +1 -1
- package/dist/lib/managed-skills.js +3 -2
- package/dist/lib/managed-skills.js.map +1 -1
- package/dist/lib/opencode.d.ts +2 -0
- package/dist/lib/opencode.js +4 -2
- package/dist/lib/opencode.js.map +1 -1
- package/dist/lib/plugin-toggle-config.d.ts +1 -1
- package/dist/lib/plugin-toggle-config.js +2 -1
- package/dist/lib/plugin-toggle-config.js.map +1 -1
- package/dist/plugins/analytics/index.d.ts +18 -0
- package/dist/plugins/analytics/index.js +151 -0
- package/dist/plugins/analytics/index.js.map +1 -0
- package/dist/tui/analytics/indicator.d.ts +40 -0
- package/dist/tui/analytics/indicator.js +117 -0
- package/dist/tui/analytics/indicator.js.map +1 -0
- package/dist/tui/branding/footer.d.ts +14 -0
- package/dist/tui/branding/footer.js +34 -0
- package/dist/tui/branding/footer.js.map +1 -0
- package/dist/tui.js +25 -7
- package/dist/tui.js.map +1 -1
- package/package.json +6 -2
- package/schemas/vvoc/v3.json +1 -1
- package/templates/skills/vvoc-usage-analytics/SKILL.md +58 -0
- package/templates/skills/vvoc-usage-analytics/references/opencode-db-queries.md +113 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indicator.js","sourceRoot":"","sources":["../../../src/tui/analytics/indicator.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { JSX } from "@opentui/solid";
|
|
2
|
+
import type { TuiPluginApi } from "@opencode-ai/plugin/tui";
|
|
3
|
+
export type BrandingDependencies = {
|
|
4
|
+
/** Renders the label node; defaults to the muted span. */
|
|
5
|
+
renderLabel: (text: string, color: string) => JSX.Element;
|
|
6
|
+
};
|
|
7
|
+
/** The rendered label, e.g. "vvoc v1.2.11". */
|
|
8
|
+
export declare function brandingFooterLabel(): string;
|
|
9
|
+
/**
|
|
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.
|
|
13
|
+
*/
|
|
14
|
+
export declare function registerBrandingFooter(api: TuiPluginApi, dependencies?: BrandingDependencies): void;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx } from "@opentui/solid/jsx-runtime";
|
|
2
|
+
import { getPackageVersionSync } from "../../lib/package.js";
|
|
3
|
+
const DEFAULT_DEPENDENCIES = {
|
|
4
|
+
renderLabel: (text, color) => _jsx("span", { style: { color }, children: text }),
|
|
5
|
+
};
|
|
6
|
+
/** The rendered label, e.g. "vvoc v1.2.11". */
|
|
7
|
+
export function brandingFooterLabel() {
|
|
8
|
+
return `vvoc v${getPackageVersionSync()}`;
|
|
9
|
+
}
|
|
10
|
+
// START_BLOCK_REGISTER_BRANDING_FOOTER
|
|
11
|
+
/**
|
|
12
|
+
* Registers the branding footer in the "sidebar_footer" host slot.
|
|
13
|
+
* Always on (independent of analytics config); returns silently when the slot
|
|
14
|
+
* API is unavailable or registration fails.
|
|
15
|
+
*/
|
|
16
|
+
export function registerBrandingFooter(api, dependencies = DEFAULT_DEPENDENCIES) {
|
|
17
|
+
if (typeof api.slots?.register !== "function")
|
|
18
|
+
return;
|
|
19
|
+
try {
|
|
20
|
+
api.slots.register({
|
|
21
|
+
slots: {
|
|
22
|
+
sidebar_footer: () => {
|
|
23
|
+
const muted = api.theme.current.textMuted;
|
|
24
|
+
const color = `rgba(${muted.r}, ${muted.g}, ${muted.b}, ${muted.a})`;
|
|
25
|
+
return dependencies.renderLabel(brandingFooterLabel(), color);
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Fail-soft: no footer for this session.
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=footer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"footer.js","sourceRoot":"","sources":["../../../src/tui/branding/footer.tsx"],"names":[],"mappings":";AAuBA,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAO7D,MAAM,oBAAoB,GAAyB;IACjD,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,eAAM,KAAK,EAAE,EAAE,KAAK,EAAE,YAAG,IAAI,GAAQ;CACpE,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,QAAQ,KAAK,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,GAAG,CAAC;oBACrE,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"}
|
package/dist/tui.js
CHANGED
|
@@ -1,27 +1,45 @@
|
|
|
1
1
|
// FILE: src/tui.tsx
|
|
2
|
-
// VERSION: 1.
|
|
2
|
+
// VERSION: 1.1.0
|
|
3
3
|
// START_MODULE_CONTRACT
|
|
4
|
-
// PURPOSE: Publish the default @osovv/vv-opencode/tui module containing the managed context inspector.
|
|
4
|
+
// PURPOSE: Publish the default @osovv/vv-opencode/tui module containing the managed context inspector, analytics indicator, and branding footer.
|
|
5
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]
|
|
6
|
+
// DEPENDS: [@opencode-ai/plugin/tui, @opencode-ai/plugin, src/tui/context/plugin.ts, src/tui/analytics/indicator.tsx, src/tui/branding/footer.tsx]
|
|
7
|
+
// LINKS: [M-PLUGIN-CONTEXT-TUI, M-TUI-ANALYTICS-INDICATOR, M-TUI-BRANDING-FOOTER, V-M-PLUGIN-CONTEXT-TUI]
|
|
8
8
|
// ROLE: BARREL
|
|
9
9
|
// MAP_MODE: SUMMARY
|
|
10
10
|
// END_MODULE_CONTRACT
|
|
11
11
|
//
|
|
12
12
|
// START_MODULE_MAP
|
|
13
|
-
// default - OpenCode TUI plugin module registering
|
|
13
|
+
// default - OpenCode TUI plugin module registering /context, the analytics indicator, and the branding footer.
|
|
14
14
|
// ContextTuiPlugin - Named TUI plugin factory for direct consumers and tests.
|
|
15
15
|
// END_MODULE_MAP
|
|
16
16
|
//
|
|
17
17
|
// START_CHANGE_SUMMARY
|
|
18
|
-
// LAST_CHANGE: [
|
|
18
|
+
// LAST_CHANGE: [2026-08-19-cache-hit-rate-analytics - Registered the live cache indicator and vvoc version footer alongside /context.]
|
|
19
19
|
// END_CHANGE_SUMMARY
|
|
20
20
|
import { ContextTuiPlugin } from "./tui/context/plugin.js";
|
|
21
|
+
import { registerAnalyticsIndicator } from "./tui/analytics/indicator.js";
|
|
22
|
+
import { registerBrandingFooter } from "./tui/branding/footer.js";
|
|
21
23
|
export { ContextTuiPlugin };
|
|
24
|
+
// START_BLOCK_TUI_MODULE
|
|
22
25
|
const plugin = {
|
|
23
26
|
id: "vvoc-context",
|
|
24
|
-
tui:
|
|
27
|
+
tui: async (api, options, meta) => {
|
|
28
|
+
await ContextTuiPlugin(api, options, meta);
|
|
29
|
+
try {
|
|
30
|
+
await registerAnalyticsIndicator(api, options);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// Fail-soft: indicator unavailable for this session.
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
registerBrandingFooter(api);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Fail-soft: footer unavailable for this session.
|
|
40
|
+
}
|
|
41
|
+
},
|
|
25
42
|
};
|
|
43
|
+
// END_BLOCK_TUI_MODULE
|
|
26
44
|
export default plugin;
|
|
27
45
|
//# sourceMappingURL=tui.js.map
|
package/dist/tui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui.js","sourceRoot":"","sources":["../src/tui.tsx"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,iBAAiB;AACjB,wBAAwB;AACxB,
|
|
1
|
+
{"version":3,"file":"tui.js","sourceRoot":"","sources":["../src/tui.tsx"],"names":[],"mappings":"AAAA,oBAAoB;AACpB,iBAAiB;AACjB,wBAAwB;AACxB,mJAAmJ;AACnJ,mEAAmE;AACnE,qJAAqJ;AACrJ,4GAA4G;AAC5G,iBAAiB;AACjB,sBAAsB;AACtB,sBAAsB;AACtB,EAAE;AACF,mBAAmB;AACnB,iHAAiH;AACjH,gFAAgF;AAChF,iBAAiB;AACjB,EAAE;AACF,uBAAuB;AACvB,yIAAyI;AACzI,qBAAqB;AAGrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAE5B,yBAAyB;AACzB,MAAM,MAAM,GAAqC;IAC/C,EAAE,EAAE,cAAc;IAClB,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;QAChC,MAAM,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC;YACH,MAAM,0BAA0B,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACjD,CAAC;QAAC,MAAM,CAAC;YACP,qDAAqD;QACvD,CAAC;QACD,IAAI,CAAC;YACH,sBAAsB,CAAC,GAAG,CAAC,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,kDAAkD;QACpD,CAAC;IACH,CAAC;CACF,CAAC;AACF,uBAAuB;AAEvB,eAAe,MAAM,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@osovv/vv-opencode",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
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",
|
|
@@ -77,6 +77,10 @@
|
|
|
77
77
|
"./plugins/tool-history-compaction": {
|
|
78
78
|
"types": "./dist/plugins/tool-history-compaction/index.d.ts",
|
|
79
79
|
"import": "./dist/plugins/tool-history-compaction/index.js"
|
|
80
|
+
},
|
|
81
|
+
"./plugins/analytics": {
|
|
82
|
+
"types": "./dist/plugins/analytics/index.d.ts",
|
|
83
|
+
"import": "./dist/plugins/analytics/index.js"
|
|
80
84
|
}
|
|
81
85
|
},
|
|
82
86
|
"scripts": {
|
|
@@ -89,7 +93,7 @@
|
|
|
89
93
|
"grace:check": "bun scripts/check-grace-markup.ts",
|
|
90
94
|
"test": "bun test",
|
|
91
95
|
"check": "bun run typecheck && bun run lint && bun run fmt:check && bun run grace:check && bun test",
|
|
92
|
-
"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'); if (!('WebToolsPlugin' in root)) throw new Error('dist root export missing WebToolsPlugin'); 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'); await import('./dist/plugins/web-tools/index.js')\" && npm pack --dry-run",
|
|
96
|
+
"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'); if (!('WebToolsPlugin' in root)) throw new Error('dist root export missing WebToolsPlugin'); if (!('AnalyticsPlugin' in root)) throw new Error('dist root export missing AnalyticsPlugin'); 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'); await import('./dist/plugins/web-tools/index.js'); await import('./dist/plugins/analytics/index.js')\" && npm pack --dry-run",
|
|
93
97
|
"tui:local": "bun scripts/tui-local.ts",
|
|
94
98
|
"release:check": "bun scripts/release-check.ts",
|
|
95
99
|
"release:bump": "bun scripts/release-bump.ts",
|
package/schemas/vvoc/v3.json
CHANGED
|
@@ -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
|
+
"$id": "https://cdn.jsdelivr.net/npm/@osovv/vv-opencode@1.3.0/schemas/vvoc/v3.json",
|
|
4
4
|
"title": "vvoc config",
|
|
5
5
|
"description": "Canonical vvoc configuration document.",
|
|
6
6
|
"type": "object",
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vvoc-usage-analytics
|
|
3
|
+
description: Use when the user asks about token usage, cache hit rate, cache efficiency, model or provider consumption, session costs, or whether a vvoc release or OpenCode upgrade changed caching behavior. Runs read-only analysis over vvoc analytics (CLI and JSONL) and historical opencode.db data, then reports findings without modifying anything.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<skill>
|
|
7
|
+
<identity>
|
|
8
|
+
You are the vvoc-usage-analytics skill. You turn usage questions into evidence-based answers. You know the three data sources, their coverage boundaries, and the exact metric definitions, and you never guess a number you did not read from a source. You are an analyst, not a fixer: you report findings and leave changes to the user.
|
|
9
|
+
</identity>
|
|
10
|
+
|
|
11
|
+
<scope>
|
|
12
|
+
<rule>OpenCode's database is strictly read-only: always connect with a `?mode=ro` URI (see references/opencode-db-queries.md). Never run UPDATE/INSERT/DELETE/DDL against it, even diagnostically.</rule>
|
|
13
|
+
<rule>Never modify, delete, truncate, or "repair" analytics JSONL files, and never suggest doing so as a fix.</rule>
|
|
14
|
+
<rule>Read-only also means no config changes: do not toggle plugins, rewrite vvoc.json, or restart anything unless the user explicitly asks for that as a separate task.</rule>
|
|
15
|
+
<rule>Report findings only. Recommendations are fine; silent "fixes" to telemetry or config are out of scope.</rule>
|
|
16
|
+
<rule>If a source is unavailable (no db file, empty JSONL, sqlite3 missing), say so explicitly and continue with the sources that exist instead of implying there was no usage.</rule>
|
|
17
|
+
</scope>
|
|
18
|
+
|
|
19
|
+
<data_sources>
|
|
20
|
+
<source name="vvoc CLI" precedence="1">
|
|
21
|
+
Prefer `vvoc analytics cache-hit-rate` for anything it can answer. It already dedupes, filters, groups, and formats:
|
|
22
|
+
- Trend: `vvoc analytics cache-hit-rate --group-by day|week|month --since 30d`
|
|
23
|
+
- Compare vvoc releases: `--group-by vvoc --since 30d`
|
|
24
|
+
- Compare OpenCode versions: `--group-by opencode --since 30d`
|
|
25
|
+
- Per session / model / provider / project: `--group-by session|model|provider|project`
|
|
26
|
+
- Filters and output: `--since`/`--until` accept `Nd`/`Nw`/`Nm` or `YYYY-MM-DD`; `--project` is a case-insensitive substring; `--order steps|hit-rate|date`; `--limit N`; `--json` for machine-readable rows.
|
|
27
|
+
Note: CLI coverage starts when the analytics plugin first ran (JSONL start date), and only steps after an OpenCode restart are recorded.
|
|
28
|
+
</source>
|
|
29
|
+
<source name="analytics JSONL" precedence="2">
|
|
30
|
+
Use the raw monthly files `$XDG_DATA_HOME/vvoc/analytics/usage-YYYY-MM.jsonl` when the question needs something the CLI does not compute (unusual groupings, per-agent or per-message drill-downs, exact record inspection). Lines are JSON objects: `kind` is `usage` (one per step-finish part, dedupe by `partID` keeping the last) or `session` (dedupe by `sessionID`). jq, python, or bun one-liners are all fine — reading only.
|
|
31
|
+
</source>
|
|
32
|
+
<source name="opencode.db history" precedence="3">
|
|
33
|
+
Use OpenCode's own SQLite database for history that predates the plugin and for per-session cost aggregates: `~/.local/share/opencode/opencode.db` (override via $OPENCODE_DATA_HOME when the user has one). It holds months of step-finish parts with tokens, session cost/token totals, and `session.version` = the OpenCode version. Always read it with the validated snippets in references/opencode-db-queries.md (`?mode=ro` URI). This is the only source that can answer "how did caching look before vvoc analytics existed" and "what did sessions cost".
|
|
34
|
+
</source>
|
|
35
|
+
</data_sources>
|
|
36
|
+
|
|
37
|
+
<workflow>
|
|
38
|
+
<step>Clarify the question into: metric(s), grouping, and time range. If the user's ask is vague ("is my cache ok?"), default to hit rate grouped by day for the last 14 days plus coverage, then offer deeper cuts.</step>
|
|
39
|
+
<step>Pick the narrowest source that can answer it: CLI first; JSONL only for cuts the CLI lacks; opencode.db only for pre-plugin history or cost-per-session questions. State which source you are using and why.</step>
|
|
40
|
+
<step>Run the query read-only. For db queries, use the reference snippets; adapt only filters (WHERE/LIMIT), not the hit-rate formula.</step>
|
|
41
|
+
<step>Sanity-check coverage before answering: how many steps does the range contain, what share is cache-eligible (coverage), and where does each source's data begin. A hit rate over 12 steps means more than one over 12,000.</step>
|
|
42
|
+
<step>Present findings as a compact table plus 2-4 sentences of interpretation. Every answer names its data source and time range. When mixing sources, state the JSONL start boundary explicitly so the user knows which numbers come from where.</step>
|
|
43
|
+
<step>Offer the natural follow-up cut (by model, by session, before/after a date) instead of running all of them unasked.</step>
|
|
44
|
+
</workflow>
|
|
45
|
+
|
|
46
|
+
<metric_definitions>
|
|
47
|
+
<metric name="cache hit rate">Token-weighted: `cacheRead / (cacheRead + cacheWrite + input)` summed over cache-eligible steps. A step is cache-eligible when `cacheRead + cacheWrite > 0`. Identical to the CLI and the SQL reference — do not invent per-step averaging.</metric>
|
|
48
|
+
<metric name="coverage">`eligibleSteps / steps`. Low coverage means the provider rarely reports cache tokens; say that, do not hide it behind a hit rate.</metric>
|
|
49
|
+
<metric name="n/a">Groups with zero eligible steps have no hit rate. Report `n/a` — never `0%` — and show coverage so "provider does not report cache" stays distinguishable from "cache misses".</metric>
|
|
50
|
+
</metric_definitions>
|
|
51
|
+
|
|
52
|
+
<output_discipline>
|
|
53
|
+
<rule>Tables over prose for any comparison; columns in the CLI's spirit (GROUP, STEPS, COVERAGE, HIT-RATE, CACHE-READ, CACHE-WRITE, FRESH-IN or their subset).</rule>
|
|
54
|
+
<rule>Every number carries its source (CLI / JSONL / opencode.db) and time range.</rule>
|
|
55
|
+
<rule>State validation context for db queries: snippets were validated against OpenCode 1.18.x; if columns error out, report the schema mismatch instead of improvising SQL guesses.</rule>
|
|
56
|
+
<rule>Interpretation stays factual: rising/falling hit rate, coverage share, outliers. Avoid unfounded causal claims; a vvoc version bump and a project switch can co-occur.</rule>
|
|
57
|
+
</output_discipline>
|
|
58
|
+
</skill>
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# opencode.db read-only queries
|
|
2
|
+
|
|
3
|
+
Validated against OpenCode **1.18.x** on a real 5.7 GB database (2026-08). If a column
|
|
4
|
+
errors out after an OpenCode upgrade, report the schema mismatch — do not improvise
|
|
5
|
+
guesses about the new schema.
|
|
6
|
+
|
|
7
|
+
## Connection — always read-only
|
|
8
|
+
|
|
9
|
+
OpenCode keeps this database open (WAL). Concurrent readers are fine; writers are not.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
sqlite3 "file:$HOME/.local/share/opencode/opencode.db?mode=ro" "SELECT ..."
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
No `sqlite3` CLI? Use bun:sqlite read-only:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { Database } from "bun:sqlite";
|
|
19
|
+
const db = new Database(`${process.env.HOME}/.local/share/opencode/opencode.db`, { readonly: true });
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Respect `$XDG_DATA_HOME`/`$OPENCODE_DATA_HOME` if the user has a custom data dir
|
|
23
|
+
(check the effective OpenCode config before assuming the default path).
|
|
24
|
+
|
|
25
|
+
## Schema orientation
|
|
26
|
+
|
|
27
|
+
- `session` — one row per session. `version` is the **OpenCode version** that created
|
|
28
|
+
it. Cost/token aggregate columns: `cost`, `tokens_input`, `tokens_output`,
|
|
29
|
+
`tokens_reasoning`, `tokens_cache_read`, `tokens_cache_write`. `time_created` is
|
|
30
|
+
epoch **milliseconds**.
|
|
31
|
+
- `message` — one row per message; `data` JSON carries `role`, `providerID`,
|
|
32
|
+
`modelID`, `agent`, `cost`, `tokens` for assistant messages.
|
|
33
|
+
- `part` — one row per message part; `data` JSON with `type: "step-finish"` carries
|
|
34
|
+
final per-step tokens: `tokens.input`, `tokens.output`, `tokens.reasoning`,
|
|
35
|
+
`tokens.cache.read`, `tokens.cache.write`.
|
|
36
|
+
|
|
37
|
+
## Hit-rate formula (must match the vvoc CLI)
|
|
38
|
+
|
|
39
|
+
`cacheRead / (cacheRead + cacheWrite + input)` **summed over cache-eligible steps**
|
|
40
|
+
( eligible := cache.read + cache.write > 0 ), token-weighted. `NULLIF` guards the
|
|
41
|
+
division; NULL hit rate means "no eligible steps" → report `n/a`, not 0%.
|
|
42
|
+
|
|
43
|
+
## (a) Cache hit rate by OpenCode version
|
|
44
|
+
|
|
45
|
+
```sql
|
|
46
|
+
SELECT s.version AS opencode,
|
|
47
|
+
COUNT(*) AS steps,
|
|
48
|
+
SUM(CASE WHEN json_extract(p.data,'$.tokens.cache.read')
|
|
49
|
+
+ json_extract(p.data,'$.tokens.cache.write') > 0
|
|
50
|
+
THEN 1 ELSE 0 END) AS eligible,
|
|
51
|
+
ROUND(100.0 * SUM(json_extract(p.data,'$.tokens.cache.read')) /
|
|
52
|
+
NULLIF(SUM(json_extract(p.data,'$.tokens.cache.read')
|
|
53
|
+
+ json_extract(p.data,'$.tokens.cache.write')
|
|
54
|
+
+ json_extract(p.data,'$.tokens.input')), 0), 1) AS hit_pct
|
|
55
|
+
FROM part p JOIN session s ON s.id = p.session_id
|
|
56
|
+
WHERE json_extract(p.data,'$.type') = 'step-finish'
|
|
57
|
+
GROUP BY s.version
|
|
58
|
+
ORDER BY MIN(s.time_created) DESC;
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## (b) Cache hit rate by month
|
|
62
|
+
|
|
63
|
+
```sql
|
|
64
|
+
SELECT strftime('%Y-%m', p.time_created/1000, 'unixepoch') AS month,
|
|
65
|
+
COUNT(*) AS steps,
|
|
66
|
+
ROUND(100.0 * SUM(json_extract(p.data,'$.tokens.cache.read')) /
|
|
67
|
+
NULLIF(SUM(json_extract(p.data,'$.tokens.cache.read')
|
|
68
|
+
+ json_extract(p.data,'$.tokens.cache.write')
|
|
69
|
+
+ json_extract(p.data,'$.tokens.input')), 0), 1) AS hit_pct
|
|
70
|
+
FROM part p
|
|
71
|
+
WHERE json_extract(p.data,'$.type') = 'step-finish'
|
|
72
|
+
GROUP BY month
|
|
73
|
+
ORDER BY month DESC;
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## (c) Recent sessions: cost and token totals
|
|
77
|
+
|
|
78
|
+
Straight from the session table — no JSON extraction needed:
|
|
79
|
+
|
|
80
|
+
```sql
|
|
81
|
+
SELECT substr(s.id, 1, 8) AS session,
|
|
82
|
+
s.title,
|
|
83
|
+
s.version AS opencode,
|
|
84
|
+
ROUND(s.cost, 3) AS cost,
|
|
85
|
+
s.tokens_cache_read AS cache_read,
|
|
86
|
+
s.tokens_cache_write AS cache_write,
|
|
87
|
+
s.tokens_input AS fresh_in,
|
|
88
|
+
datetime(s.time_created/1000, 'unixepoch') AS started
|
|
89
|
+
FROM session s
|
|
90
|
+
ORDER BY s.time_created DESC
|
|
91
|
+
LIMIT 20;
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Caveat: `cost` is what OpenCode recorded (often `0` for subscription/zai providers);
|
|
95
|
+
say "recorded cost" in findings, do not treat 0 as free.
|
|
96
|
+
|
|
97
|
+
## (d) Step-finish counts by provider/model
|
|
98
|
+
|
|
99
|
+
Model attribution lives on the message, so join through it:
|
|
100
|
+
|
|
101
|
+
```sql
|
|
102
|
+
SELECT json_extract(m.data,'$.providerID') AS provider,
|
|
103
|
+
json_extract(m.data,'$.modelID') AS model,
|
|
104
|
+
COUNT(*) AS steps
|
|
105
|
+
FROM part p JOIN message m ON m.id = p.message_id
|
|
106
|
+
WHERE json_extract(p.data,'$.type') = 'step-finish'
|
|
107
|
+
GROUP BY provider, model
|
|
108
|
+
ORDER BY steps DESC;
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
To get hit rate per model instead of counts, wrap snippet (a)'s `SUM/NULLIF`
|
|
112
|
+
expression in the same query but group by `json_extract(m.data,'$.providerID') ||
|
|
113
|
+
'/' || json_extract(m.data,'$.modelID')`.
|