@npv12/opencode-mini-session 0.0.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.
package/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # opencode-mini-session
2
+
3
+ OpenCode V2 TUI plugin that creates ephemeral mini sessions for side questions. Ask quick questions without disrupting your main conversation.
4
+
5
+ ## Features
6
+
7
+ - **Mini sessions** — ephemeral child sessions that auto-clean on close
8
+ - **Context copying** — optionally copies main session context into the mini prompt
9
+ - **Model picker** — switch between models for mini-session questions (Tab)
10
+ - **Streaming answers** — real-time streaming with markdown rendering
11
+ - **Continue in main thread** — paste mini session transcript back into main session
12
+ - **Thinking mode** — toggle extended thinking with Ctrl+T
13
+ - **Hide/show** — toggle the overlay without closing the session
14
+
15
+ ## Installation
16
+
17
+ Requires OpenCode `0.0.0-beta-17963` or compatible.
18
+
19
+ ```bash
20
+ opencode2 plugin add opencode-mini-session
21
+ ```
22
+
23
+ Or add to your `cli.json`:
24
+
25
+ ```json
26
+ {
27
+ "plugins": ["./path/to/opencode-mini-session/src/index.ts"]
28
+ }
29
+ ```
30
+
31
+ ## Configuration
32
+
33
+ Pass options in your `opencode.json(c)`:
34
+
35
+ ```json
36
+ {
37
+ "plugins": [
38
+ {
39
+ "package": "opencode-mini-session",
40
+ "options": {
41
+ "model": "anthropic/claude-sonnet-4-5",
42
+ "keybind": "alt+b",
43
+ "freshKeybind": "alt+n",
44
+ "enableThinking": false,
45
+ "tokenLimit": 50000
46
+ }
47
+ }
48
+ ]
49
+ }
50
+ ```
51
+
52
+ | Option | Default | Description |
53
+ |--------|---------|-------------|
54
+ | `model` | (session default) | Override model for mini sessions (`provider/model`) |
55
+ | `variant` | — | Model variant to use |
56
+ | `agent` | — | Custom agent name (falls back to plugin-managed mode if not found) |
57
+ | `keybind` | `alt+b` | Global keybind to toggle mini session (set `false` to disable) |
58
+ | `freshKeybind` | `alt+n` | Keybind for mini session without copied context |
59
+ | `enableThinking` | `false` | Default thinking state for mini sessions |
60
+ | `toggleThinkingKeybind` | `ctrl+t` | Keybind to toggle thinking in the mini dialog |
61
+ | `tokenLimit` | `50000` | Max tokens to copy from main session context |
62
+
63
+ ## Commands
64
+
65
+ | Command | Slash | Description |
66
+ |---------|-------|-------------|
67
+ | `mini` | `/mini` | Open mini session with main session context |
68
+ | `mini fresh` | `/mini-fresh` | Open mini session without copied context |
69
+ | `mini model` | `/mini-model` | Change model for future mini sessions |
70
+
71
+ ## Attribution
72
+
73
+ Based on [karamanliev/opencode-mini-session](https://github.com/karamanliev/opencode-mini-session).
74
+
75
+ ## License
76
+
77
+ MIT
package/dist/agent.js ADDED
@@ -0,0 +1,103 @@
1
+ import { formatResolvedModel } from "./model.js";
2
+ const MINI_SIDE_QUESTION_INSTRUCTION = "You are answering a quick side question about an ongoing coding session. Below is the conversation context from the session. Answer concisely based on what you can see.";
3
+ const MINI_FRESH_INSTRUCTION = "You are answering a quick side question about an ongoing coding session. No conversation context from the main session has been copied into this mini session. Answer concisely based only on the current mini-session messages and any tools or files you inspect.";
4
+ const MINI_TOOL_NOTE = " You may only use the following tools: glob, grep, list, read, webfetch. Do not attempt to use any other tools.";
5
+ export function resolveRuntimeMiniAgent(context, config) {
6
+ const agents = context.data.location.agent.list();
7
+ const mode = resolveMiniAgentMode(config, agents ? [...agents] : null);
8
+ return buildResolvedMiniAgent(config, mode);
9
+ }
10
+ export function resolveMiniAgent(config, agents) {
11
+ return buildResolvedMiniAgent(config, resolveMiniAgentMode(config, agents));
12
+ }
13
+ export function resolveMiniAgentMode(config, agents) {
14
+ if (!config.agent) return {
15
+ mode: "plugin-managed",
16
+ requestedAgent: null
17
+ };
18
+ if (agents === null) return {
19
+ mode: "plugin-managed",
20
+ requestedAgent: config.agent,
21
+ unavailableAgent: config.agent
22
+ };
23
+ const match = agents.find(a => a.name === config.agent);
24
+ if (match) return {
25
+ mode: "custom-agent",
26
+ requestedAgent: config.agent,
27
+ agent: match.name
28
+ };
29
+ return {
30
+ mode: "plugin-managed",
31
+ requestedAgent: config.agent,
32
+ missingAgent: config.agent
33
+ };
34
+ }
35
+ export function buildResolvedMiniAgent(config, mode) {
36
+ if (mode.mode === "custom-agent") {
37
+ return {
38
+ mode: "custom-agent",
39
+ requestedAgent: mode.requestedAgent,
40
+ agent: mode.agent,
41
+ notices: buildMiniAgentNotices(config, mode)
42
+ };
43
+ }
44
+ return {
45
+ mode: "plugin-managed",
46
+ requestedAgent: mode.requestedAgent,
47
+ missingAgent: mode.missingAgent,
48
+ unavailableAgent: mode.unavailableAgent,
49
+ notices: buildMiniAgentNotices(config, mode)
50
+ };
51
+ }
52
+ export function buildMiniPreamble(contextText, resolved, mode = "main") {
53
+ const intro = buildMiniIntro(resolved, mode);
54
+ const toolNote = resolved.mode === "plugin-managed" ? MINI_TOOL_NOTE : "";
55
+ const sessionContext = contextText.trim() ? `\n\n<session-context>\n${contextText}\n</session-context>` : "";
56
+ return `${intro}${toolNote}${sessionContext}`;
57
+ }
58
+ function buildMiniIntro(resolved, mode) {
59
+ if (resolved.mode !== "custom-agent") {
60
+ return mode === "fresh" ? MINI_FRESH_INSTRUCTION : MINI_SIDE_QUESTION_INSTRUCTION;
61
+ }
62
+ if (mode === "fresh") {
63
+ return `You are answering a quick side question about an ongoing coding session and you are running as the configured OpenCode agent "${resolved.agent}". Follow that agent's own instructions, role, tone, and constraints closely while answering this as a mini side question. No conversation context from the main session has been copied into this mini session.`;
64
+ }
65
+ return `You are answering a quick side question about an ongoing coding session and you are running as the configured OpenCode agent "${resolved.agent}". Follow that agent's own instructions, role, tone, and constraints closely while answering this as a mini side question. Below is the conversation context from the session.`;
66
+ }
67
+ export function buildSessionCreatePayload(resolved, base) {
68
+ return {
69
+ title: base.title,
70
+ location: {
71
+ directory: base.directory
72
+ },
73
+ ...(resolved.mode === "custom-agent" ? {
74
+ agent: resolved.agent
75
+ } : {}),
76
+ ...(base.model?.providerID && base.model?.id ? {
77
+ model: {
78
+ providerID: base.model.providerID,
79
+ id: base.model.id,
80
+ ...(base.model.variant ? {
81
+ variant: base.model.variant
82
+ } : {})
83
+ }
84
+ } : {})
85
+ };
86
+ }
87
+ export function formatMiniNotice(...notices) {
88
+ const filtered = notices.filter(n => typeof n === "string" && n.length > 0);
89
+ return filtered.length > 0 ? filtered.join(" ") : undefined;
90
+ }
91
+ export function buildMiniErrorDetail(options) {
92
+ return [`Diagnostics: path=${options.path}`, `session=${options.sessionID ?? "pending"}`, `mode=${options.resolvedAgent.mode}`, `agent=${options.resolvedAgent.mode === "custom-agent" ? options.resolvedAgent.agent : "(plugin-managed)"}`, `model=${formatResolvedModel(options.resolvedModel)}`].join(", ");
93
+ }
94
+ function buildMiniAgentNotices(config, mode) {
95
+ const notices = [];
96
+ if (mode.mode === "plugin-managed" && mode.missingAgent) {
97
+ notices.push(`Configured mini agent ${mode.missingAgent} was not found. Falling back to plugin-managed mini mode.`);
98
+ }
99
+ if (mode.mode === "plugin-managed" && mode.unavailableAgent) {
100
+ notices.push(`Could not verify configured mini agent ${mode.unavailableAgent} because the agent list is unavailable. Falling back to plugin-managed mini mode.`);
101
+ }
102
+ return notices;
103
+ }
@@ -0,0 +1,39 @@
1
+ import { createComponent as _$createComponent } from "@opentui/solid";
2
+ import { effect as _$effect } from "@opentui/solid";
3
+ import { createTextNode as _$createTextNode } from "@opentui/solid";
4
+ import { insertNode as _$insertNode } from "@opentui/solid";
5
+ import { insert as _$insert } from "@opentui/solid";
6
+ import { setProp as _$setProp } from "@opentui/solid";
7
+ import { createElement as _$createElement } from "@opentui/solid";
8
+ /** @jsxImportSource @opentui/solid */
9
+
10
+ import { Show } from "solid-js";
11
+ export function ActionButton(props) {
12
+ return (() => {
13
+ var _el$ = _$createElement("box"),
14
+ _el$2 = _$createElement("text"),
15
+ _el$3 = _$createElement("b");
16
+ _$insertNode(_el$, _el$2);
17
+ _$setProp(_el$, "flexDirection", "row");
18
+ _$setProp(_el$, "onMouseUp", () => {
19
+ if (!props.disabled) props.onPress();
20
+ });
21
+ _$insertNode(_el$2, _el$3);
22
+ _$insert(_el$3, () => props.label);
23
+ _$insert(_el$, _$createComponent(Show, {
24
+ get when() {
25
+ return props.keybind;
26
+ },
27
+ get children() {
28
+ var _el$4 = _$createElement("text"),
29
+ _el$5 = _$createTextNode(` `);
30
+ _$insertNode(_el$4, _el$5);
31
+ _$insert(_el$4, () => props.keybind, null);
32
+ _$effect(_$p => _$setProp(_el$4, "fg", props.context.theme.text.subdued, _$p));
33
+ return _el$4;
34
+ }
35
+ }), null);
36
+ _$effect(_$p => _$setProp(_el$2, "fg", props.disabled ? props.context.theme.text.subdued : props.context.theme.text.default, _$p));
37
+ return _el$;
38
+ })();
39
+ }