@cortexkit/opencode-magic-context 0.21.5 → 0.21.7

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 (63) hide show
  1. package/README.md +1 -1
  2. package/dist/agents/permissions.d.ts +142 -0
  3. package/dist/agents/permissions.d.ts.map +1 -0
  4. package/dist/config/agent-disable.d.ts +26 -0
  5. package/dist/config/agent-disable.d.ts.map +1 -0
  6. package/dist/config/index.d.ts.map +1 -1
  7. package/dist/config/schema/agent-overrides.d.ts +12 -12
  8. package/dist/config/schema/magic-context.d.ts +87 -93
  9. package/dist/config/schema/magic-context.d.ts.map +1 -1
  10. package/dist/features/magic-context/compartment-lease.d.ts +14 -0
  11. package/dist/features/magic-context/compartment-lease.d.ts.map +1 -0
  12. package/dist/features/magic-context/compartment-storage.d.ts +5 -1
  13. package/dist/features/magic-context/compartment-storage.d.ts.map +1 -1
  14. package/dist/features/magic-context/compression-depth-storage.d.ts +2 -1
  15. package/dist/features/magic-context/compression-depth-storage.d.ts.map +1 -1
  16. package/dist/features/magic-context/migrations.d.ts.map +1 -1
  17. package/dist/features/magic-context/storage-db.d.ts.map +1 -1
  18. package/dist/features/magic-context/storage-meta-persisted.d.ts.map +1 -1
  19. package/dist/features/magic-context/storage-meta-session.d.ts.map +1 -1
  20. package/dist/features/magic-context/storage.d.ts +1 -1
  21. package/dist/features/magic-context/storage.d.ts.map +1 -1
  22. package/dist/hooks/magic-context/auto-search-runner.d.ts.map +1 -1
  23. package/dist/hooks/magic-context/compartment-runner-compressor.d.ts +4 -0
  24. package/dist/hooks/magic-context/compartment-runner-compressor.d.ts.map +1 -1
  25. package/dist/hooks/magic-context/compartment-runner-incremental.d.ts.map +1 -1
  26. package/dist/hooks/magic-context/compartment-runner-partial-recomp.d.ts.map +1 -1
  27. package/dist/hooks/magic-context/compartment-runner-recomp.d.ts.map +1 -1
  28. package/dist/hooks/magic-context/compartment-runner-types.d.ts +2 -0
  29. package/dist/hooks/magic-context/compartment-runner-types.d.ts.map +1 -1
  30. package/dist/hooks/magic-context/compartment-runner.d.ts +5 -0
  31. package/dist/hooks/magic-context/compartment-runner.d.ts.map +1 -1
  32. package/dist/hooks/magic-context/hook.d.ts.map +1 -1
  33. package/dist/hooks/magic-context/transform-compartment-phase.d.ts +2 -2
  34. package/dist/hooks/magic-context/transform-compartment-phase.d.ts.map +1 -1
  35. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts +1 -0
  36. package/dist/hooks/magic-context/transform-postprocess-phase.d.ts.map +1 -1
  37. package/dist/hooks/magic-context/transform.d.ts +2 -0
  38. package/dist/hooks/magic-context/transform.d.ts.map +1 -1
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +680 -188
  41. package/dist/plugin/conflict-warning-hook.d.ts +10 -0
  42. package/dist/plugin/conflict-warning-hook.d.ts.map +1 -1
  43. package/dist/plugin/dream-timer.d.ts.map +1 -1
  44. package/dist/plugin/hooks/create-session-hooks.d.ts.map +1 -1
  45. package/dist/plugin/rpc-handlers.d.ts +1 -1
  46. package/dist/plugin/rpc-handlers.d.ts.map +1 -1
  47. package/dist/plugin/tool-registry.d.ts.map +1 -1
  48. package/dist/shared/announcement.d.ts +55 -0
  49. package/dist/shared/announcement.d.ts.map +1 -0
  50. package/dist/shared/format-threshold.d.ts +24 -0
  51. package/dist/shared/format-threshold.d.ts.map +1 -0
  52. package/dist/shared/rpc-types.d.ts +10 -0
  53. package/dist/shared/rpc-types.d.ts.map +1 -1
  54. package/dist/tui/data/context-db.d.ts +14 -0
  55. package/dist/tui/data/context-db.d.ts.map +1 -1
  56. package/package.json +1 -1
  57. package/src/shared/announcement.test.ts +143 -0
  58. package/src/shared/announcement.ts +97 -0
  59. package/src/shared/format-threshold.ts +28 -0
  60. package/src/shared/rpc-types.ts +10 -0
  61. package/src/tui/data/context-db.ts +44 -0
  62. package/src/tui/index.tsx +75 -6
  63. package/src/tui/slots/sidebar-content.tsx +24 -3
package/src/tui/index.tsx CHANGED
@@ -6,7 +6,8 @@ import { createMemo } from "solid-js"
6
6
  import type { TuiPlugin, TuiPluginApi, TuiThemeCurrent } from "@opencode-ai/plugin/tui"
7
7
  import { createSidebarContentSlot } from "./slots/sidebar-content"
8
8
  import packageJson from "../../package.json"
9
- import { closeRpc, consumeTuiMessages, getCompartmentCount, initRpcClient, loadStatusDetail, requestRecomp, type StatusDetail } from "./data/context-db"
9
+ import { closeRpc, consumeTuiMessages, getAnnouncement, getCompartmentCount, initRpcClient, loadStatusDetail, markAnnounced, requestRecomp, type StatusDetail } from "./data/context-db"
10
+ import { formatThresholdPercent } from "../shared/format-threshold"
10
11
  import { detectConflicts } from "../shared/conflict-detector"
11
12
  import { fixConflicts } from "../shared/conflict-fixer"
12
13
  import { readJsoncFile } from "../shared/jsonc-parser"
@@ -290,11 +291,16 @@ const StatusDialog = (props: { api: TuiPluginApi; s: StatusDetail }) => {
290
291
  <text fg={t().textMuted}>v{packageJson.version}</text>
291
292
  </box>
292
293
 
293
- {/* Context summary line */}
294
+ {/* Context summary line. Mirrors the sidebar header layout
295
+ ("47.5% / 65% 475K / 1.0M") so users can recognize the
296
+ same shape in the status dialog. The execute threshold tells
297
+ them how close they are to compaction triggering. */}
294
298
  <box flexDirection="row" justifyContent="space-between" width="100%">
295
- <text fg={t().text}>Context</text>
296
299
  <text fg={s().usagePercentage >= 80 ? t().error : s().usagePercentage >= 65 ? t().warning : t().accent}>
297
- <b>{s().usagePercentage.toFixed(1)}%</b> · {fmt(s().inputTokens)} / {contextLimit() > 0 ? fmt(contextLimit()) : "?"} tokens
300
+ <b>{s().usagePercentage.toFixed(1)}%</b> / {formatThresholdPercent(s().executeThreshold)}%
301
+ </text>
302
+ <text fg={s().usagePercentage >= 80 ? t().error : s().usagePercentage >= 65 ? t().warning : t().accent}>
303
+ {fmt(s().inputTokens)} / {contextLimit() > 0 ? fmt(contextLimit()) : "?"} tokens
298
304
  </text>
299
305
  </box>
300
306
 
@@ -336,7 +342,7 @@ const StatusDialog = (props: { api: TuiPluginApi; s: StatusDetail }) => {
336
342
  <R t={t()} l="Configured" v={s().cacheTtl} />
337
343
  <R t={t()} l="Last response" v={s().lastResponseTime > 0 ? `${Math.round(elapsed() / 1000)}s ago` : "never"} />
338
344
  <R t={t()} l="Remaining" v={s().cacheExpired ? "expired" : `${Math.round(s().cacheRemainingMs / 1000)}s`} fg={s().cacheExpired ? t().warning : t().textMuted} />
339
- <R t={t()} l="Auto-execute" v={s().cacheExpired ? "yes (expired)" : `at TTL or ≥${s().executeThreshold}%`} fg={t().textMuted} />
345
+ <R t={t()} l="Auto-execute" v={s().cacheExpired ? "yes (expired)" : `at TTL or ≥${formatThresholdPercent(s().executeThreshold)}%`} fg={t().textMuted} />
340
346
  <box marginTop={1}>
341
347
  <text fg={t().text}><b>Memory</b></text>
342
348
  </box>
@@ -346,7 +352,7 @@ const StatusDialog = (props: { api: TuiPluginApi; s: StatusDetail }) => {
346
352
  {/* Right column */}
347
353
  <box flexDirection="column" flexGrow={1} flexBasis={0}>
348
354
  <text fg={t().text}><b>Rolling Nudges</b></text>
349
- <R t={t()} l="Execute threshold" v={`${s().executeThreshold}%`} />
355
+ <R t={t()} l="Execute threshold" v={`${formatThresholdPercent(s().executeThreshold)}%`} />
350
356
  <R t={t()} l="Nudge anchor" v={`${fmt(s().lastNudgeTokens)} tok`} />
351
357
  <R t={t()} l="Interval" v={`${fmt(s().nudgeInterval)} tok`} fg={t().textMuted} />
352
358
  <R t={t()} l="Next nudge after" v={`${fmt(s().nextNudgeAfter)} tok`} />
@@ -558,6 +564,63 @@ function registerCommandPaletteEntries(api: TuiPluginApi): void {
558
564
  // via RPC.
559
565
  }
560
566
 
567
+ /**
568
+ * Show the one-shot "What's new" dialog on TUI startup if the server tells us
569
+ * to. The server is the source of truth: it has the version + features
570
+ * constants AND owns the persistence file. We just render and report back.
571
+ *
572
+ * Failure-tolerant by design — if the server isn't ready or the RPC fails,
573
+ * we silently skip (the next TUI launch will retry).
574
+ */
575
+ /**
576
+ * URLs render as plain text. Modern terminals (iTerm2, kitty, WezTerm, Ghostty,
577
+ * recent macOS Terminal) auto-detect URLs and let users Cmd-click; older
578
+ * terminals require manual copy. We tried opentui's `<a href>` JSX intrinsic
579
+ * for application-level OSC 8 clickability, but it's a span-like element that
580
+ * forced text out of opentui's word-wrap mode, causing bullets to bleed past
581
+ * the dialog border. Pure-string children of `<text>` wrap correctly, so the
582
+ * AFT-style DialogAlert + plain string is the right surface here.
583
+ */
584
+ async function showStartupAnnouncement(api: TuiPluginApi): Promise<void> {
585
+ try {
586
+ const ann = await getAnnouncement()
587
+ if (!ann.show || !ann.version || !ann.features || ann.features.length === 0) return
588
+
589
+ const title = `Magic Context v${ann.version}`
590
+ const lines: string[] = [
591
+ "What's new:",
592
+ "",
593
+ ...ann.features.map((line) => ` • ${line}`),
594
+ ]
595
+ if (ann.footer && ann.footer.trim().length > 0) {
596
+ // Blank-line separator keeps the persistent footer (Discord invite,
597
+ // etc.) visually distinct from the version-specific bullets.
598
+ lines.push("", ann.footer)
599
+ }
600
+ const message = lines.join("\n")
601
+
602
+ api.ui.dialog.replace(
603
+ () => (
604
+ <api.ui.DialogAlert
605
+ title={title}
606
+ message={message}
607
+ onConfirm={() => {
608
+ void markAnnounced()
609
+ }}
610
+ />
611
+ ),
612
+ () => {
613
+ // User dismissed via Escape rather than confirming. Mark
614
+ // dismissed anyway — they saw the dialog, that's the contract.
615
+ void markAnnounced()
616
+ },
617
+ )
618
+ } catch {
619
+ // RPC not ready yet (port file missing or transient HTTP failure) —
620
+ // silently skip. The next TUI start re-checks.
621
+ }
622
+ }
623
+
561
624
  const tui: TuiPlugin = async (api, _options, meta) => {
562
625
  // Initialize RPC client for server communication
563
626
  const directory = api.state.path.directory ?? ""
@@ -618,6 +681,12 @@ const tui: TuiPlugin = async (api, _options, meta) => {
618
681
  return
619
682
  }
620
683
 
684
+ // Show one-shot release announcement after conflict gate.
685
+ // Fire-and-forget: if the server isn't ready or RPC fails, the next TUI
686
+ // launch will retry. Dialog only appears once per ANNOUNCEMENT_VERSION
687
+ // (persisted via mark-announced RPC writing last_announced_version).
688
+ void showStartupAnnouncement(api)
689
+
621
690
  // Note: if TUI plugin is loaded, tui.json already has our entry.
622
691
  // But if the user added it manually and later removes it, or if they
623
692
  // use setup/doctor which handles tui.json, this code is already running.
@@ -3,6 +3,7 @@ import { createEffect, createMemo, createSignal, on, onCleanup } from "solid-js"
3
3
  import type { TuiSlotPlugin, TuiPluginApi, TuiThemeCurrent } from "@opencode-ai/plugin/tui"
4
4
  import packageJson from "../../../package.json"
5
5
  import { loadSidebarSnapshot, type SidebarSnapshot } from "../data/context-db"
6
+ import { formatThresholdPercent } from "../../shared/format-threshold"
6
7
 
7
8
  const SINGLE_BORDER = { type: "single" } as any
8
9
  const REFRESH_DEBOUNCE_MS = 150
@@ -47,7 +48,15 @@ const TokenBreakdown = (props: {
47
48
  theme: TuiThemeCurrent
48
49
  snapshot: SidebarSnapshot
49
50
  }) => {
50
- const barWidth = 36
51
+ // Bar width is hardcoded because the @opencode-ai/plugin/tui slot API does
52
+ // not expose the rendered sidebar width to plugins. 24 chars is the safe
53
+ // floor that fits every realistic sidebar configuration we've observed —
54
+ // OpenCode TUI's sidebar narrows with the terminal, and 36 (the previous
55
+ // value) overflowed users' actual layouts (issue #90), wrapping the bar
56
+ // onto a second line. If/when the slot API surfaces a real width, this
57
+ // should become Math.max(20, providedWidth) like the Pi status dialog
58
+ // already does (`Math.max(20, innerWidth)`).
59
+ const barWidth = 24
51
60
 
52
61
  const segments = createMemo<TokenSegment[]>(() => {
53
62
  const s = props.snapshot
@@ -363,9 +372,21 @@ const SidebarContent = (props: {
363
372
  {s() && s()!.inputTokens > 0 && (
364
373
  <box marginTop={1} flexDirection="column">
365
374
  {(s()?.contextLimit ?? 0) > 0 && (
366
- <box width="100%" flexDirection="row" justifyContent="flex-end">
375
+ <box width="100%" flexDirection="row" justifyContent="space-between">
376
+ {/* Left: current usage vs the per-model execute
377
+ threshold (the value Magic Context compares
378
+ against when scheduling historian / drops).
379
+ "47.5% / 65%" tells the user how close they
380
+ are to the next compaction trigger. */}
367
381
  <text fg={contextSummaryColor()}>
368
- <b>{s()!.usagePercentage.toFixed(1)}%</b> · {compactTokens(s()!.inputTokens)} / {compactTokens(s()!.contextLimit)}
382
+ <b>{s()!.usagePercentage.toFixed(1)}%</b> / {formatThresholdPercent(s()!.executeThreshold)}%
383
+ </text>
384
+ {/* Right: absolute token usage vs the model's
385
+ full context window (separate from the
386
+ execute threshold so users still know how
387
+ much headroom remains beyond compaction). */}
388
+ <text fg={contextSummaryColor()}>
389
+ {compactTokens(s()!.inputTokens)} / {compactTokens(s()!.contextLimit)}
369
390
  </text>
370
391
  </box>
371
392
  )}