@serkanalgur/opencodev2-slim 2.1.0 → 2.2.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/package.json +1 -1
- package/src/tui.tsx +235 -13
package/package.json
CHANGED
package/src/tui.tsx
CHANGED
|
@@ -157,17 +157,38 @@ async function measureSession(context: any, sessionID: string): Promise<Measured
|
|
|
157
157
|
(typeof tokens.reasoning === "number" ? tokens.reasoning : 0) +
|
|
158
158
|
(typeof tokens.cache?.read === "number" ? tokens.cache.read : 0) +
|
|
159
159
|
(typeof tokens.cache?.write === "number" ? tokens.cache.write : 0)
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
160
|
+
|
|
161
|
+
// Resolve context limit: try model.list() first, then session info, then default.
|
|
162
|
+
let contextLimit = 200000
|
|
163
|
+
const modelID: string = info.model?.id || ""
|
|
164
|
+
const providerID: string = info.model?.providerID || ""
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const modelList: any = await context.client.model.list()
|
|
168
|
+
const models: any[] = modelList?.data ?? modelList ?? []
|
|
169
|
+
// Find by exact match (providerID/modelID), then by modelID alone
|
|
170
|
+
const found = models.find(
|
|
171
|
+
(m: any) => m.providerID === providerID && m.modelID === modelID,
|
|
172
|
+
) || models.find((m: any) => m.modelID === modelID)
|
|
173
|
+
if (found?.limit?.context && found.limit.context > 0) {
|
|
174
|
+
contextLimit = found.limit.context
|
|
175
|
+
}
|
|
176
|
+
} catch {
|
|
177
|
+
// Fall through to session info or default
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Fallback: session info might have model.limit.context
|
|
181
|
+
if (contextLimit === 200000 && info.model?.limit?.context && info.model.limit.context > 0) {
|
|
182
|
+
contextLimit = info.model.limit.context
|
|
183
|
+
}
|
|
184
|
+
|
|
164
185
|
const usagePercent =
|
|
165
186
|
contextLimit > 0 ? Math.min(100, Math.round((tokenCount / contextLimit) * 100)) : 0
|
|
166
187
|
return {
|
|
167
188
|
tokens: tokenCount,
|
|
168
189
|
cost: typeof info.cost === "number" ? info.cost : 0,
|
|
169
190
|
contextLimit,
|
|
170
|
-
model:
|
|
191
|
+
model: modelID || "unknown",
|
|
171
192
|
usagePercent,
|
|
172
193
|
}
|
|
173
194
|
} catch {
|
|
@@ -178,7 +199,7 @@ async function measureSession(context: any, sessionID: string): Promise<Measured
|
|
|
178
199
|
export default Plugin.define({
|
|
179
200
|
id: "opencodev2-slim.cli",
|
|
180
201
|
setup(context) {
|
|
181
|
-
// Register
|
|
202
|
+
// Register commands inside the "app" slot render, where the keymap
|
|
182
203
|
// provider is available (consistent with OpenCode V2 CLI plugins).
|
|
183
204
|
context.ui.slot({
|
|
184
205
|
append: "app",
|
|
@@ -187,6 +208,7 @@ export default Plugin.define({
|
|
|
187
208
|
mode: "global",
|
|
188
209
|
priority: 10,
|
|
189
210
|
commands: [
|
|
211
|
+
// ─── /panel ────────────────────────────────────
|
|
190
212
|
{
|
|
191
213
|
id: "opencodev2-slim.panel",
|
|
192
214
|
title: "Show Slim Context Panel",
|
|
@@ -212,16 +234,12 @@ export default Plugin.define({
|
|
|
212
234
|
}
|
|
213
235
|
|
|
214
236
|
try {
|
|
215
|
-
// Make sure the cached transcript is loaded before reading it.
|
|
216
237
|
await context.data.session.message.sync(sessionID)
|
|
217
238
|
const messages =
|
|
218
239
|
context.data.session.message.list(sessionID) || []
|
|
219
|
-
// Prefer live server-measured context numbers when available.
|
|
220
240
|
const real = await measureSession(context, sessionID)
|
|
221
241
|
const stats = deriveStats(messages)
|
|
222
242
|
const text = renderPanelText(sessionID, stats, real)
|
|
223
|
-
// Inject the panel as plain text into the session stream,
|
|
224
|
-
// so it doesn't take over OpenCode's own panel UI.
|
|
225
243
|
await context.client.session.synthetic({
|
|
226
244
|
sessionID,
|
|
227
245
|
text,
|
|
@@ -236,6 +254,210 @@ export default Plugin.define({
|
|
|
236
254
|
}
|
|
237
255
|
},
|
|
238
256
|
},
|
|
257
|
+
|
|
258
|
+
// ─── /compress ─────────────────────────────────
|
|
259
|
+
{
|
|
260
|
+
id: "opencodev2-slim.compress",
|
|
261
|
+
title: "Compress Context",
|
|
262
|
+
group: "Slim",
|
|
263
|
+
palette: true,
|
|
264
|
+
slash: {
|
|
265
|
+
name: "compress",
|
|
266
|
+
aliases: ["slim-compress"],
|
|
267
|
+
args: [
|
|
268
|
+
{
|
|
269
|
+
name: "focus",
|
|
270
|
+
description: "What to compress (e.g., 'old exploration')",
|
|
271
|
+
required: false,
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
},
|
|
275
|
+
enabled: true,
|
|
276
|
+
suggested: true,
|
|
277
|
+
run: async (input: unknown, event: unknown) => {
|
|
278
|
+
const sessionID =
|
|
279
|
+
resolveCurrentSession(context) ||
|
|
280
|
+
(event && typeof event === "object" && "sessionID" in event
|
|
281
|
+
? (event as any).sessionID
|
|
282
|
+
: null)
|
|
283
|
+
|
|
284
|
+
if (!sessionID) {
|
|
285
|
+
context.ui.toast.show({
|
|
286
|
+
title: "Slim Compress",
|
|
287
|
+
message: "No active session found.",
|
|
288
|
+
variant: "warning",
|
|
289
|
+
})
|
|
290
|
+
return
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
try {
|
|
294
|
+
const args = (input as any) || {}
|
|
295
|
+
const focus = args.focus || "user-requested compression"
|
|
296
|
+
|
|
297
|
+
// Inject a synthetic message asking the assistant to compress.
|
|
298
|
+
const prompt = `Please call the compress tool now with: compress({ focus: "${focus}", mode: "auto" })`
|
|
299
|
+
await context.client.session.synthetic({
|
|
300
|
+
sessionID,
|
|
301
|
+
text: prompt,
|
|
302
|
+
description: "slim-compress",
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
context.ui.toast.show({
|
|
306
|
+
title: "Slim Compress",
|
|
307
|
+
message: `Compression requested: "${focus}". The assistant will process it on the next turn.`,
|
|
308
|
+
variant: "success",
|
|
309
|
+
duration: 3000,
|
|
310
|
+
})
|
|
311
|
+
} catch (e) {
|
|
312
|
+
context.ui.toast.show({
|
|
313
|
+
title: "Slim Compress",
|
|
314
|
+
message: `Error: ${e instanceof Error ? e.message : e}`,
|
|
315
|
+
variant: "error",
|
|
316
|
+
})
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
|
|
321
|
+
// ─── /status ──────────────────────────────────
|
|
322
|
+
{
|
|
323
|
+
id: "opencodev2-slim.status",
|
|
324
|
+
title: "Show Compact Status",
|
|
325
|
+
group: "Slim",
|
|
326
|
+
palette: true,
|
|
327
|
+
slash: { name: "status", aliases: ["slim-status"] },
|
|
328
|
+
enabled: true,
|
|
329
|
+
suggested: false,
|
|
330
|
+
run: async (input: unknown, event: unknown) => {
|
|
331
|
+
const sessionID =
|
|
332
|
+
resolveCurrentSession(context) ||
|
|
333
|
+
(event && typeof event === "object" && "sessionID" in event
|
|
334
|
+
? (event as any).sessionID
|
|
335
|
+
: null)
|
|
336
|
+
|
|
337
|
+
if (!sessionID) {
|
|
338
|
+
context.ui.toast.show({
|
|
339
|
+
title: "Slim Status",
|
|
340
|
+
message: "No active session found.",
|
|
341
|
+
variant: "warning",
|
|
342
|
+
})
|
|
343
|
+
return
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
try {
|
|
347
|
+
const real = await measureSession(context, sessionID)
|
|
348
|
+
if (!real) {
|
|
349
|
+
context.ui.toast.show({
|
|
350
|
+
title: "Slim Status",
|
|
351
|
+
message: "Could not measure session.",
|
|
352
|
+
variant: "warning",
|
|
353
|
+
})
|
|
354
|
+
return
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const status =
|
|
358
|
+
real.usagePercent >= 90
|
|
359
|
+
? "🔴 CRITICAL"
|
|
360
|
+
: real.usagePercent >= 70
|
|
361
|
+
? "🟡 WARNING"
|
|
362
|
+
: "🟢 HEALTHY"
|
|
363
|
+
|
|
364
|
+
const text = [
|
|
365
|
+
`**Context Status:** ${status}`,
|
|
366
|
+
`**Usage:** ${real.tokens.toLocaleString()} / ${real.contextLimit.toLocaleString()} tokens (${real.usagePercent}%)`,
|
|
367
|
+
`**Model:** ${real.model}`,
|
|
368
|
+
real.cost > 0 ? `**Cost:** $${real.cost.toFixed(4)}` : "",
|
|
369
|
+
]
|
|
370
|
+
.filter(Boolean)
|
|
371
|
+
.join("\n")
|
|
372
|
+
|
|
373
|
+
await context.client.session.synthetic({
|
|
374
|
+
sessionID,
|
|
375
|
+
text,
|
|
376
|
+
description: "slim-status",
|
|
377
|
+
})
|
|
378
|
+
} catch (e) {
|
|
379
|
+
context.ui.toast.show({
|
|
380
|
+
title: "Slim Status",
|
|
381
|
+
message: `Error: ${e instanceof Error ? e.message : e}`,
|
|
382
|
+
variant: "error",
|
|
383
|
+
})
|
|
384
|
+
}
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
|
|
388
|
+
// ─── /slim-debug ──────────────────────────────
|
|
389
|
+
{
|
|
390
|
+
id: "opencodev2-slim.debug",
|
|
391
|
+
title: "Toggle Slim Debug Mode",
|
|
392
|
+
group: "Slim",
|
|
393
|
+
palette: true,
|
|
394
|
+
slash: { name: "slim-debug", aliases: ["debug-slim"] },
|
|
395
|
+
enabled: true,
|
|
396
|
+
suggested: false,
|
|
397
|
+
run: async (input: unknown, event: unknown) => {
|
|
398
|
+
const sessionID =
|
|
399
|
+
resolveCurrentSession(context) ||
|
|
400
|
+
(event && typeof event === "object" && "sessionID" in event
|
|
401
|
+
? (event as any).sessionID
|
|
402
|
+
: null)
|
|
403
|
+
|
|
404
|
+
if (!sessionID) {
|
|
405
|
+
context.ui.toast.show({
|
|
406
|
+
title: "Slim Debug",
|
|
407
|
+
message: "No active session found.",
|
|
408
|
+
variant: "warning",
|
|
409
|
+
})
|
|
410
|
+
return
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
try {
|
|
414
|
+
// Read current config
|
|
415
|
+
const configPath = `${process.env.HOME || "~"}/.config/opencode/slim.jsonc`
|
|
416
|
+
const fs = await import("fs")
|
|
417
|
+
let debug = false
|
|
418
|
+
if (fs.existsSync(configPath)) {
|
|
419
|
+
const content = fs.readFileSync(configPath, "utf-8")
|
|
420
|
+
const match = content.match(/"debug"\s*:\s*(true|false)/)
|
|
421
|
+
if (match) debug = match[1] === "true"
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Toggle
|
|
425
|
+
debug = !debug
|
|
426
|
+
|
|
427
|
+
// Write back
|
|
428
|
+
const { parse } = await import("jsonc-parser")
|
|
429
|
+
let config: any = {}
|
|
430
|
+
if (fs.existsSync(configPath)) {
|
|
431
|
+
config = parse(fs.readFileSync(configPath, "utf-8")) || {}
|
|
432
|
+
}
|
|
433
|
+
config.debug = debug
|
|
434
|
+
|
|
435
|
+
const dir = `${process.env.HOME || "~"}/.config/opencode`
|
|
436
|
+
if (!fs.existsSync(dir)) {
|
|
437
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
438
|
+
}
|
|
439
|
+
fs.writeFileSync(
|
|
440
|
+
configPath,
|
|
441
|
+
JSON.stringify(config, null, 2),
|
|
442
|
+
"utf-8",
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
const text = `**Slim Debug Mode:** ${debug ? "ON 🔴" : "OFF ⚪"}\n\nDebug logs will ${debug ? "now" : "no longer"} appear in the console.`
|
|
446
|
+
|
|
447
|
+
await context.client.session.synthetic({
|
|
448
|
+
sessionID,
|
|
449
|
+
text,
|
|
450
|
+
description: "slim-debug",
|
|
451
|
+
})
|
|
452
|
+
} catch (e) {
|
|
453
|
+
context.ui.toast.show({
|
|
454
|
+
title: "Slim Debug",
|
|
455
|
+
message: `Error: ${e instanceof Error ? e.message : e}`,
|
|
456
|
+
variant: "error",
|
|
457
|
+
})
|
|
458
|
+
}
|
|
459
|
+
},
|
|
460
|
+
},
|
|
239
461
|
],
|
|
240
462
|
}))
|
|
241
463
|
return null
|
|
@@ -243,10 +465,10 @@ export default Plugin.define({
|
|
|
243
465
|
})
|
|
244
466
|
|
|
245
467
|
context.ui.toast.show({
|
|
246
|
-
title: "Slim Plugin",
|
|
247
|
-
message: "
|
|
468
|
+
title: "Slim Plugin v2.1.0",
|
|
469
|
+
message: "Commands: /panel, /compress, /status, /slim-debug",
|
|
248
470
|
variant: "success",
|
|
249
|
-
duration:
|
|
471
|
+
duration: 4000,
|
|
250
472
|
})
|
|
251
473
|
|
|
252
474
|
return () => {}
|