opencode-usage-coach 0.1.1 → 0.1.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.
Files changed (3) hide show
  1. package/dist/index.js +92 -16
  2. package/dist/tui.js +147 -82
  3. package/package.json +13 -4
package/dist/index.js CHANGED
@@ -1,16 +1,28 @@
1
1
  // src/index.ts
2
2
  import { mkdirSync, writeFileSync, appendFileSync, readFileSync, existsSync } from "fs";
3
3
  import { spawn } from "child_process";
4
+ import { createHash } from "crypto";
4
5
  import { homedir } from "os";
5
- import { join } from "path";
6
+ import { join, resolve } from "path";
6
7
  import { tool } from "@opencode-ai/plugin";
7
8
  var PLUGIN_NAME = "opencode-usage-coach";
8
- var STATE_DIR = process.env.UC_STATE_DIR ?? join(homedir(), ".cache", "opencode-usage-coach");
9
+ var DEBUG = process.env.UC_DEBUG === "1";
10
+ var TTL_MS = Number(process.env.UC_TTL_MS ?? 6e4);
11
+ var STATE_DIR = join(homedir(), ".cache", "opencode-usage-coach");
9
12
  var STATE_FILE = join(STATE_DIR, "state.json");
10
13
  var HARNESS_FILE = join(STATE_DIR, "harness.json");
11
14
  var LOG_FILE = join(STATE_DIR, "coach.log");
12
- var DEBUG = process.env.UC_DEBUG === "1";
13
- var TTL_MS = Number(process.env.UC_TTL_MS ?? 6e4);
15
+ function projectStateDir(dir) {
16
+ const abs = resolve(dir || ".");
17
+ const h = createHash("sha1").update(abs).digest("hex").slice(0, 12);
18
+ return join(homedir(), ".cache", "opencode-usage-coach", "projects", h);
19
+ }
20
+ function setStateDir(dir) {
21
+ STATE_DIR = process.env.UC_STATE_DIR ?? projectStateDir(dir);
22
+ STATE_FILE = join(STATE_DIR, "state.json");
23
+ HARNESS_FILE = join(STATE_DIR, "harness.json");
24
+ LOG_FILE = join(STATE_DIR, "coach.log");
25
+ }
14
26
  var NOOP_HOOKS = {};
15
27
  function log(msg) {
16
28
  if (DEBUG) try {
@@ -63,14 +75,72 @@ function humanRemaining(iso) {
63
75
  const mins = Math.floor((new Date(iso).getTime() - Date.now()) / 6e4);
64
76
  if (mins < 0) return "resets soon";
65
77
  if (mins < 60) return `resets in ${mins}m`;
66
- if (mins < 1440) return `resets in ${Math.floor(mins / 60)}h`;
67
- return `resets in ${Math.floor(mins / 1440)}d`;
78
+ if (mins < 1440) return `resets in ${Math.floor(mins / 60)}h ${mins % 60}m`;
79
+ return `${Math.floor(mins / 1440)}d left`;
68
80
  } catch {
69
81
  return "";
70
82
  }
71
83
  }
84
+ function captureStdout(args) {
85
+ return new Promise((resolve2) => {
86
+ let out = "";
87
+ let p;
88
+ try {
89
+ p = spawn("codexbar", args, { stdio: ["ignore", "pipe", "ignore"] });
90
+ } catch {
91
+ return resolve2("");
92
+ }
93
+ p.stdout?.on("data", (d) => {
94
+ out += d.toString();
95
+ });
96
+ p.on("error", () => resolve2(""));
97
+ p.on("close", () => resolve2(out));
98
+ });
99
+ }
100
+ async function fetchEnabledProviders() {
101
+ const out = await captureStdout(["config", "providers"]);
102
+ const ids = [];
103
+ for (const line of out.split("\n")) {
104
+ const m = line.match(/^\s*([a-zA-Z0-9_-]+):\s*enabled/);
105
+ if (m) ids.push(m[1]);
106
+ }
107
+ return ids;
108
+ }
109
+ function providerAdvice(h5, wk) {
110
+ const S5H = STOP_5H, SWK = STOP_WK, T5H = THR_5H, TWK = THR_WK;
111
+ if (h5 >= S5H || wk >= SWK) return "STOP \u2014 finish current only";
112
+ if (h5 >= T5H && wk >= TWK) return "small tasks only \u2014 big ones will hit both limits";
113
+ if (h5 >= T5H) return "small tasks only \u2014 5h window nearly full, big tasks after reset";
114
+ if (wk >= TWK) return "small tasks only \u2014 big ones will strain late-week";
115
+ if (h5 >= 50 || wk >= 50) return "moderate tasks OK \u2014 save big ones for headroom";
116
+ return "big tasks OK \u2014 short & long limits comfortable";
117
+ }
118
+ async function fetchProvidersCoach() {
119
+ const ids = await fetchEnabledProviders();
120
+ const results = await Promise.all(ids.map(async (id) => {
121
+ try {
122
+ const out = await captureStdout(["usage", "--provider", id, "--json"]);
123
+ const u = JSON.parse(out)[0]?.usage;
124
+ if (!u) return null;
125
+ const h5 = Math.round(u.tertiary?.usedPercent ?? 0);
126
+ const wk = Math.round(u.primary?.usedPercent ?? 0);
127
+ return {
128
+ id,
129
+ name: id,
130
+ fiveHour: h5,
131
+ weekly: wk,
132
+ fiveHourReset: humanRemaining(u.tertiary?.resetsAt),
133
+ weeklyReset: humanRemaining(u.primary?.resetsAt),
134
+ advice: providerAdvice(h5, wk)
135
+ };
136
+ } catch {
137
+ return null;
138
+ }
139
+ }));
140
+ return results.filter(Boolean);
141
+ }
72
142
  function fetchQuota() {
73
- return new Promise((resolve) => {
143
+ return new Promise((resolve2) => {
74
144
  let out = "";
75
145
  let p;
76
146
  try {
@@ -78,21 +148,21 @@ function fetchQuota() {
78
148
  stdio: ["ignore", "pipe", "ignore"]
79
149
  });
80
150
  } catch {
81
- return resolve(null);
151
+ return resolve2(null);
82
152
  }
83
153
  p.stdout?.on("data", (d) => {
84
154
  out += d.toString();
85
155
  });
86
- p.on("error", () => resolve(null));
156
+ p.on("error", () => resolve2(null));
87
157
  p.on("close", () => {
88
158
  try {
89
159
  const text = out.trim();
90
- if (!text || text === "[]") return resolve(null);
160
+ if (!text || text === "[]") return resolve2(null);
91
161
  const u = JSON.parse(text)[0]?.usage;
92
- if (!u) return resolve(null);
93
- resolve({ weekly: u.primary ?? { usedPercent: 0 }, monthly: u.secondary ?? { usedPercent: 0 }, fiveHour: u.tertiary ?? { usedPercent: 0 } });
162
+ if (!u) return resolve2(null);
163
+ resolve2({ weekly: u.primary ?? { usedPercent: 0 }, monthly: u.secondary ?? { usedPercent: 0 }, fiveHour: u.tertiary ?? { usedPercent: 0 } });
94
164
  } catch {
95
- resolve(null);
165
+ resolve2(null);
96
166
  }
97
167
  });
98
168
  });
@@ -113,6 +183,7 @@ function coach(q) {
113
183
  var LOADING = { decision: "GO", advice: "quota loading\u2026", weekly: -1, monthly: -1, fiveHour: -1 };
114
184
  async function UsageCoachPlugin(input) {
115
185
  try {
186
+ setStateDir(input.directory);
116
187
  let last = null;
117
188
  let lastFetchedAt = 0;
118
189
  let refreshing = false;
@@ -121,12 +192,17 @@ async function UsageCoachPlugin(input) {
121
192
  if (refreshing) return;
122
193
  if (last && Date.now() - lastFetchedAt < TTL_MS) return;
123
194
  refreshing = true;
124
- fetchQuota().then((q) => {
195
+ fetchQuota().then(async (q) => {
125
196
  try {
126
197
  last = coach(q);
127
198
  lastFetchedAt = Date.now();
128
- writeState(last);
129
- log(`${last.decision} | weekly=${last.weekly}% 5h=${last.fiveHour}%`);
199
+ let providers = [];
200
+ try {
201
+ providers = await fetchProvidersCoach();
202
+ } catch {
203
+ }
204
+ writeState({ ...last, providers, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
205
+ log(`${last.decision} | weekly=${last.weekly}% 5h=${last.fiveHour}% | providers=${providers.length}`);
130
206
  } catch (e) {
131
207
  log(`refresh-in-then err: ${String(e)}`);
132
208
  }
package/dist/tui.js CHANGED
@@ -1,14 +1,21 @@
1
1
  // src/tui.tsx
2
2
  import { setProp as _$setProp } from "@opentui/solid";
3
+ import { effect as _$effect } from "@opentui/solid";
3
4
  import { insert as _$insert } from "@opentui/solid";
4
5
  import { createTextNode as _$createTextNode } from "@opentui/solid";
5
6
  import { insertNode as _$insertNode } from "@opentui/solid";
6
7
  import { createElement as _$createElement } from "@opentui/solid";
7
8
  import { readFileSync, existsSync, writeFileSync, mkdirSync } from "fs";
9
+ import { createHash } from "crypto";
8
10
  import { homedir } from "os";
9
- import { join } from "path";
11
+ import { join, resolve } from "path";
10
12
  import { createRoot, createSignal, onCleanup } from "solid-js";
11
- var STATE_DIR = process.env.UC_STATE_DIR ?? join(homedir(), ".cache", "opencode-usage-coach");
13
+ function projectStateDir(dir) {
14
+ const abs = resolve(dir || ".");
15
+ const h = createHash("sha1").update(abs).digest("hex").slice(0, 12);
16
+ return join(homedir(), ".cache", "opencode-usage-coach", "projects", h);
17
+ }
18
+ var STATE_DIR = join(homedir(), ".cache", "opencode-usage-coach");
12
19
  var STATE_FILE = join(STATE_DIR, "state.json");
13
20
  var HARNESS_FILE = join(STATE_DIR, "harness.json");
14
21
  var MARKER = join(STATE_DIR, "tui-loaded.txt");
@@ -50,11 +57,15 @@ function short(s, n) {
50
57
  return s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
51
58
  }
52
59
  function initializeTui(api, disposeRoot) {
60
+ STATE_DIR = process.env.UC_STATE_DIR ?? projectStateDir(api.state.path.directory);
61
+ STATE_FILE = join(STATE_DIR, "state.json");
62
+ HARNESS_FILE = join(STATE_DIR, "harness.json");
63
+ MARKER = join(STATE_DIR, "tui-loaded.txt");
53
64
  try {
54
65
  mkdirSync(STATE_DIR, {
55
66
  recursive: true
56
67
  });
57
- writeFileSync(MARKER, `loaded ${(/* @__PURE__ */ new Date()).toISOString()}`);
68
+ writeFileSync(MARKER, `loaded ${(/* @__PURE__ */ new Date()).toISOString()} @ ${api.state.path.directory}`);
58
69
  } catch {
59
70
  }
60
71
  const [getState, setState] = createSignal(readState());
@@ -68,16 +79,20 @@ function initializeTui(api, disposeRoot) {
68
79
  }, 3e3);
69
80
  onCleanup(() => clearInterval(timer));
70
81
  onCleanup(() => disposeRoot());
71
- const statusDot = {
72
- generating: "\u25CF",
73
- grading: "\u25CF",
74
- revising: "\u25CF",
75
- completed: "\u25CF",
76
- failed: "\u25CF",
77
- timed_out: "\u25CF",
78
- halted_quota: "\u25CF"
82
+ const statusKey = {
83
+ generating: "info",
84
+ grading: "accent",
85
+ revising: "warning",
86
+ completed: "success",
87
+ failed: "error",
88
+ timed_out: "error",
89
+ halted_quota: "error"
79
90
  };
80
91
  const panel = (ctx) => {
92
+ const th = ctx.theme?.current ?? {};
93
+ const st = (k) => ({
94
+ fg: th[k]
95
+ });
81
96
  let s = null;
82
97
  try {
83
98
  s = getState();
@@ -92,98 +107,148 @@ function initializeTui(api, disposeRoot) {
92
107
  }
93
108
  const nodes = [];
94
109
  if (s) {
110
+ const dKey = s.decision === "GO" ? "success" : s.decision === "THROTTLE" ? "warning" : "error";
95
111
  nodes.push((() => {
96
112
  var _el$ = _$createElement("text"), _el$2 = _$createTextNode(`usage-coach [`), _el$3 = _$createTextNode(`]`);
97
113
  _$insertNode(_el$, _el$2);
98
114
  _$insertNode(_el$, _el$3);
99
115
  _$insert(_el$, () => TAG[s.decision], _el$3);
116
+ _$effect((_$p) => _$setProp(_el$, "style", st(dKey), _$p));
100
117
  return _el$;
101
118
  })());
119
+ if (s.providers && s.providers.length > 0) {
120
+ for (const p of s.providers) {
121
+ nodes.push((() => {
122
+ var _el$4 = _$createElement("text"), _el$5 = _$createTextNode(` `);
123
+ _$insertNode(_el$4, _el$5);
124
+ _$insert(_el$4, () => p.name, null);
125
+ _$effect((_$p) => _$setProp(_el$4, "style", st("textMuted"), _$p));
126
+ return _el$4;
127
+ })());
128
+ const k5 = p.fiveHour >= 70 ? "error" : p.fiveHour >= 40 ? "warning" : "success";
129
+ const kw = p.weekly >= 85 ? "error" : p.weekly >= 60 ? "warning" : "success";
130
+ nodes.push((() => {
131
+ var _el$6 = _$createElement("text"), _el$7 = _$createTextNode(` 5h `), _el$8 = _$createTextNode(` `), _el$9 = _$createTextNode(`% `);
132
+ _$insertNode(_el$6, _el$7);
133
+ _$insertNode(_el$6, _el$8);
134
+ _$insertNode(_el$6, _el$9);
135
+ _$insert(_el$6, () => bar(p.fiveHour), _el$8);
136
+ _$insert(_el$6, () => p.fiveHour, _el$9);
137
+ _$insert(_el$6, () => p.fiveHourReset, null);
138
+ _$effect((_$p) => _$setProp(_el$6, "style", st(k5), _$p));
139
+ return _el$6;
140
+ })());
141
+ nodes.push((() => {
142
+ var _el$0 = _$createElement("text"), _el$1 = _$createTextNode(` wk `), _el$10 = _$createTextNode(` `), _el$11 = _$createTextNode(`% `);
143
+ _$insertNode(_el$0, _el$1);
144
+ _$insertNode(_el$0, _el$10);
145
+ _$insertNode(_el$0, _el$11);
146
+ _$insert(_el$0, () => bar(p.weekly), _el$10);
147
+ _$insert(_el$0, () => p.weekly, _el$11);
148
+ _$insert(_el$0, () => p.weeklyReset, null);
149
+ _$effect((_$p) => _$setProp(_el$0, "style", st(kw), _$p));
150
+ return _el$0;
151
+ })());
152
+ nodes.push((() => {
153
+ var _el$12 = _$createElement("text"), _el$13 = _$createTextNode(` -> `);
154
+ _$insertNode(_el$12, _el$13);
155
+ _$insert(_el$12, () => p.advice, null);
156
+ _$effect((_$p) => _$setProp(_el$12, "style", st(dKey), _$p));
157
+ return _el$12;
158
+ })());
159
+ }
160
+ } else {
161
+ nodes.push((() => {
162
+ var _el$16 = _$createElement("text"), _el$17 = _$createTextNode(` 5h `), _el$18 = _$createTextNode(` `), _el$19 = _$createTextNode(`%`);
163
+ _$insertNode(_el$16, _el$17);
164
+ _$insertNode(_el$16, _el$18);
165
+ _$insertNode(_el$16, _el$19);
166
+ _$insert(_el$16, () => bar(s.fiveHour), _el$18);
167
+ _$insert(_el$16, () => s.fiveHour, _el$19);
168
+ return _el$16;
169
+ })());
170
+ nodes.push((() => {
171
+ var _el$20 = _$createElement("text"), _el$21 = _$createTextNode(` wk `), _el$22 = _$createTextNode(` `), _el$23 = _$createTextNode(`%`);
172
+ _$insertNode(_el$20, _el$21);
173
+ _$insertNode(_el$20, _el$22);
174
+ _$insertNode(_el$20, _el$23);
175
+ _$insert(_el$20, () => bar(s.weekly), _el$22);
176
+ _$insert(_el$20, () => s.weekly, _el$23);
177
+ return _el$20;
178
+ })());
179
+ nodes.push((() => {
180
+ var _el$24 = _$createElement("text"), _el$25 = _$createTextNode(` mo `), _el$26 = _$createTextNode(` `), _el$27 = _$createTextNode(`%`);
181
+ _$insertNode(_el$24, _el$25);
182
+ _$insertNode(_el$24, _el$26);
183
+ _$insertNode(_el$24, _el$27);
184
+ _$insert(_el$24, () => bar(s.monthly), _el$26);
185
+ _$insert(_el$24, () => s.monthly, _el$27);
186
+ return _el$24;
187
+ })());
188
+ }
189
+ } else {
102
190
  nodes.push((() => {
103
- var _el$4 = _$createElement("text"), _el$5 = _$createTextNode(` 5h `), _el$6 = _$createTextNode(` `), _el$7 = _$createTextNode(`%`);
104
- _$insertNode(_el$4, _el$5);
105
- _$insertNode(_el$4, _el$6);
106
- _$insertNode(_el$4, _el$7);
107
- _$insert(_el$4, () => bar(s.fiveHour), _el$6);
108
- _$insert(_el$4, () => s.fiveHour, _el$7);
109
- return _el$4;
110
- })());
111
- nodes.push((() => {
112
- var _el$8 = _$createElement("text"), _el$9 = _$createTextNode(` wk `), _el$0 = _$createTextNode(` `), _el$1 = _$createTextNode(`%`);
113
- _$insertNode(_el$8, _el$9);
114
- _$insertNode(_el$8, _el$0);
115
- _$insertNode(_el$8, _el$1);
116
- _$insert(_el$8, () => bar(s.weekly), _el$0);
117
- _$insert(_el$8, () => s.weekly, _el$1);
118
- return _el$8;
191
+ var _el$28 = _$createElement("text");
192
+ _$insertNode(_el$28, _$createTextNode(`usage-coach: ...`));
193
+ return _el$28;
119
194
  })());
195
+ }
196
+ if (h && h.active !== false && h.tasks.length > 0) {
120
197
  nodes.push((() => {
121
- var _el$10 = _$createElement("text"), _el$11 = _$createTextNode(` mo `), _el$12 = _$createTextNode(` `), _el$13 = _$createTextNode(`%`);
122
- _$insertNode(_el$10, _el$11);
123
- _$insertNode(_el$10, _el$12);
124
- _$insertNode(_el$10, _el$13);
125
- _$insert(_el$10, () => bar(s.monthly), _el$12);
126
- _$insert(_el$10, () => s.monthly, _el$13);
127
- return _el$10;
198
+ var _el$30 = _$createElement("text");
199
+ _$insertNode(_el$30, _$createTextNode(` `));
200
+ return _el$30;
128
201
  })());
129
- } else {
130
202
  nodes.push((() => {
131
- var _el$14 = _$createElement("text");
132
- _$insertNode(_el$14, _$createTextNode(`usage-coach: ...`));
133
- return _el$14;
203
+ var _el$32 = _$createElement("text"), _el$33 = _$createTextNode(`harness: `), _el$34 = _$createTextNode(` `), _el$35 = _$createTextNode(`/`);
204
+ _$insertNode(_el$32, _el$33);
205
+ _$insertNode(_el$32, _el$34);
206
+ _$insertNode(_el$32, _el$35);
207
+ _$insert(_el$32, () => h.name, _el$34);
208
+ _$insert(_el$32, () => h.current, _el$35);
209
+ _$insert(_el$32, () => h.total, null);
210
+ _$effect((_$p) => _$setProp(_el$32, "style", st("textMuted"), _$p));
211
+ return _el$32;
134
212
  })());
135
- }
136
- const hStatus = h && h.tasks.length > 0 ? `${h.name} ${h.current}/${h.total}` : "idle";
137
- nodes.push((() => {
138
- var _el$16 = _$createElement("text");
139
- _$insertNode(_el$16, _$createTextNode(` `));
140
- return _el$16;
141
- })());
142
- nodes.push((() => {
143
- var _el$18 = _$createElement("text"), _el$19 = _$createTextNode(`harness: `);
144
- _$insertNode(_el$18, _el$19);
145
- _$insert(_el$18, hStatus, null);
146
- return _el$18;
147
- })());
148
- if (h && h.tasks.length > 0) {
149
213
  for (const t of h.tasks) {
150
- const dot = statusDot[t.status] ?? "\xB7";
214
+ const sKey = statusKey[t.status] ?? "text";
151
215
  const lbl = TLABEL[t.status] ?? t.status;
152
216
  const rev = t.revisions > 0 && t.status === "revising" ? `(${t.revisions})` : "";
153
217
  const mdl = t.model ? ` ${t.model.split("/").pop() ?? t.model}` : "";
154
218
  nodes.push((() => {
155
- var _el$20 = _$createElement("text"), _el$21 = _$createTextNode(` `), _el$22 = _$createTextNode(` `), _el$23 = _$createTextNode(` `), _el$24 = _$createTextNode(` `);
156
- _$insertNode(_el$20, _el$21);
157
- _$insertNode(_el$20, _el$22);
158
- _$insertNode(_el$20, _el$23);
159
- _$insertNode(_el$20, _el$24);
160
- _$insert(_el$20, dot, _el$22);
161
- _$insert(_el$20, () => t.id, _el$23);
162
- _$insert(_el$20, mdl, _el$23);
163
- _$insert(_el$20, lbl, _el$24);
164
- _$insert(_el$20, rev, _el$24);
165
- _$insert(_el$20, () => short(t.title, 12), null);
166
- return _el$20;
219
+ var _el$36 = _$createElement("text"), _el$37 = _$createTextNode(` \u25CF `), _el$38 = _$createTextNode(` `), _el$39 = _$createTextNode(` `);
220
+ _$insertNode(_el$36, _el$37);
221
+ _$insertNode(_el$36, _el$38);
222
+ _$insertNode(_el$36, _el$39);
223
+ _$insert(_el$36, () => t.id, _el$38);
224
+ _$insert(_el$36, mdl, _el$38);
225
+ _$insert(_el$36, lbl, _el$39);
226
+ _$insert(_el$36, rev, _el$39);
227
+ _$insert(_el$36, () => short(t.title, 12), null);
228
+ _$effect((_$p) => _$setProp(_el$36, "style", st(sKey), _$p));
229
+ return _el$36;
167
230
  })());
168
231
  const pv = t.model ? t.model.startsWith("zai") ? "zai" : (t.model.split("/")[0] ?? "").split("-")[0] : "";
169
232
  const q = pv && h.quotas?.[pv] ? h.quotas[pv] : null;
170
233
  const pct = q ? q.fiveHour : 0;
234
+ const qKey = pct >= 70 ? "error" : pct >= 40 ? "warning" : "success";
171
235
  nodes.push((() => {
172
- var _el$25 = _$createElement("text"), _el$26 = _$createTextNode(` 5h `), _el$27 = _$createTextNode(` `), _el$28 = _$createTextNode(`%`);
173
- _$insertNode(_el$25, _el$26);
174
- _$insertNode(_el$25, _el$27);
175
- _$insertNode(_el$25, _el$28);
176
- _$insert(_el$25, () => bar(pct), _el$27);
177
- _$insert(_el$25, pct, _el$28);
178
- return _el$25;
236
+ var _el$40 = _$createElement("text"), _el$41 = _$createTextNode(` 5h `), _el$42 = _$createTextNode(` `), _el$43 = _$createTextNode(`%`);
237
+ _$insertNode(_el$40, _el$41);
238
+ _$insertNode(_el$40, _el$42);
239
+ _$insertNode(_el$40, _el$43);
240
+ _$insert(_el$40, () => bar(pct), _el$42);
241
+ _$insert(_el$40, pct, _el$43);
242
+ _$effect((_$p) => _$setProp(_el$40, "style", st(qKey), _$p));
243
+ return _el$40;
179
244
  })());
180
245
  }
181
246
  }
182
247
  return (() => {
183
- var _el$29 = _$createElement("box");
184
- _$setProp(_el$29, "flexDirection", "column");
185
- _$insert(_el$29, nodes);
186
- return _el$29;
248
+ var _el$44 = _$createElement("box");
249
+ _$setProp(_el$44, "flexDirection", "column");
250
+ _$insert(_el$44, nodes);
251
+ return _el$44;
187
252
  })();
188
253
  };
189
254
  api.slots.register({
@@ -194,9 +259,9 @@ function initializeTui(api, disposeRoot) {
194
259
  return panel(ctx);
195
260
  } catch {
196
261
  return (() => {
197
- var _el$30 = _$createElement("text");
198
- _$insertNode(_el$30, _$createTextNode(`usage-coach`));
199
- return _el$30;
262
+ var _el$45 = _$createElement("text");
263
+ _$insertNode(_el$45, _$createTextNode(`usage-coach`));
264
+ return _el$45;
200
265
  })();
201
266
  }
202
267
  },
@@ -205,9 +270,9 @@ function initializeTui(api, disposeRoot) {
205
270
  return panel(ctx);
206
271
  } catch {
207
272
  return (() => {
208
- var _el$32 = _$createElement("text");
209
- _$insertNode(_el$32, _$createTextNode(`usage-coach`));
210
- return _el$32;
273
+ var _el$47 = _$createElement("text");
274
+ _$insertNode(_el$47, _$createTextNode(`usage-coach`));
275
+ return _el$47;
211
276
  })();
212
277
  }
213
278
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "opencode-usage-coach",
3
- "version": "0.1.1",
4
- "description": "opencode closed-loop usage coach quota SENSE -> coaching DECIDE -> loop ACT + TUI integration",
3
+ "version": "0.1.2",
4
+ "description": "opencode closed-loop usage coach \u2014 quota SENSE -> coaching DECIDE -> loop ACT + TUI integration",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",
@@ -26,7 +26,16 @@
26
26
  "README.md",
27
27
  "LICENSE"
28
28
  ],
29
- "keywords": ["opencode", "opencode-plugin", "quota", "usage", "coach", "loop", "zai", "glm"],
29
+ "keywords": [
30
+ "opencode",
31
+ "opencode-plugin",
32
+ "quota",
33
+ "usage",
34
+ "coach",
35
+ "loop",
36
+ "zai",
37
+ "glm"
38
+ ],
30
39
  "license": "MIT",
31
40
  "peerDependencies": {
32
41
  "@opencode-ai/plugin": ">=1.14",
@@ -43,4 +52,4 @@
43
52
  "tsup": "^8.5",
44
53
  "typescript": "^5"
45
54
  }
46
- }
55
+ }