@jc01rho/opencode-smart-title 0.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-selector.d.ts","sourceRoot":"","sources":["../../lib/model-selector.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AACxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,MAAM,WAAW,SAAS;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CASlD,CAAC;AAaF,MAAM,WAAW,oBAAoB;IACjC,KAAK,EAAE,aAAa,CAAC;IACrB,SAAS,EAAE,SAAS,CAAC;IACrB,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,SAAS,CAAC;CAC3B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAC7B,MAAM,CAAC,EAAE,MAAM,EACf,WAAW,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,oBAAoB,CAAC,CAmG/B"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Model Selection and Fallback Logic for Smart Title
3
+ *
4
+ * This module handles intelligent model selection for title generation.
5
+ * It tries models in order from a predefined fallback list.
6
+ *
7
+ * NOTE: OpencodeAI is lazily imported to avoid loading the 812KB package during
8
+ * plugin initialization. The package is only loaded when model selection is needed.
9
+ */
10
+ export const FALLBACK_MODELS = {
11
+ openai: 'gpt-5-mini',
12
+ anthropic: 'claude-haiku-4-5',
13
+ google: 'gemini-2.5-flash',
14
+ deepseek: 'deepseek-chat',
15
+ xai: 'grok-4-fast',
16
+ alibaba: 'qwen3-coder-flash',
17
+ zai: 'glm-4.5-flash',
18
+ opencode: 'big-pickle'
19
+ };
20
+ const PROVIDER_PRIORITY = [
21
+ 'openai',
22
+ 'anthropic',
23
+ 'google',
24
+ 'deepseek',
25
+ 'xai',
26
+ 'alibaba',
27
+ 'zai',
28
+ 'opencode'
29
+ ];
30
+ /**
31
+ * Main model selection function with intelligent fallback logic
32
+ *
33
+ * Selection hierarchy:
34
+ * 1. Try the config-specified model (if provided)
35
+ * 2. Try fallback models from authenticated providers (in priority order)
36
+ *
37
+ * @param logger - Logger instance for debug output
38
+ * @param configModel - Model string in "provider/model" format (e.g., "anthropic/claude-haiku-4-5")
39
+ * @returns Selected model with metadata about the selection
40
+ */
41
+ export async function selectModel(logger, configModel) {
42
+ logger?.info('model-selector', 'Model selection started', { configModel });
43
+ // Lazy import - only load the 812KB auth provider package when actually needed
44
+ const { OpencodeAI } = await import('@tarquinen/opencode-auth-provider');
45
+ const opencodeAI = new OpencodeAI();
46
+ let failedModelInfo;
47
+ if (configModel) {
48
+ const parts = configModel.split('/');
49
+ if (parts.length !== 2) {
50
+ logger?.warn('model-selector', '✗ Invalid config model format, expected "provider/model"', {
51
+ configModel
52
+ });
53
+ }
54
+ else {
55
+ const [providerID, modelID] = parts;
56
+ logger?.debug('model-selector', 'Attempting to use config-specified model', {
57
+ providerID,
58
+ modelID
59
+ });
60
+ try {
61
+ const model = await opencodeAI.getLanguageModel(providerID, modelID);
62
+ logger?.info('model-selector', '✓ Successfully using config-specified model', {
63
+ providerID,
64
+ modelID
65
+ });
66
+ return {
67
+ model,
68
+ modelInfo: { providerID, modelID },
69
+ source: 'config',
70
+ reason: 'Using model specified in smart-title.jsonc config'
71
+ };
72
+ }
73
+ catch (error) {
74
+ logger?.warn('model-selector', '✗ Failed to use config-specified model, falling back', {
75
+ providerID,
76
+ modelID,
77
+ error: error.message
78
+ });
79
+ // Track the failed model
80
+ failedModelInfo = { providerID, modelID };
81
+ }
82
+ }
83
+ }
84
+ logger?.debug('model-selector', 'Fetching available authenticated providers');
85
+ const providers = await opencodeAI.listProviders();
86
+ const availableProviderIDs = Object.keys(providers);
87
+ logger?.info('model-selector', 'Available authenticated providers', {
88
+ providerCount: availableProviderIDs.length,
89
+ providerIDs: availableProviderIDs,
90
+ providers: Object.entries(providers).map(([id, info]) => ({
91
+ id,
92
+ source: info.source,
93
+ name: info.info.name
94
+ }))
95
+ });
96
+ logger?.debug('model-selector', 'Attempting fallback models from providers', {
97
+ priorityOrder: PROVIDER_PRIORITY
98
+ });
99
+ for (const providerID of PROVIDER_PRIORITY) {
100
+ if (!providers[providerID]) {
101
+ logger?.debug('model-selector', `Skipping ${providerID} (not authenticated)`);
102
+ continue;
103
+ }
104
+ const fallbackModelID = FALLBACK_MODELS[providerID];
105
+ if (!fallbackModelID) {
106
+ logger?.debug('model-selector', `Skipping ${providerID} (no fallback model configured)`);
107
+ continue;
108
+ }
109
+ logger?.debug('model-selector', `Attempting ${providerID}/${fallbackModelID}`);
110
+ try {
111
+ const model = await opencodeAI.getLanguageModel(providerID, fallbackModelID);
112
+ logger?.info('model-selector', `✓ Successfully using fallback model`, {
113
+ providerID,
114
+ modelID: fallbackModelID
115
+ });
116
+ return {
117
+ model,
118
+ modelInfo: { providerID, modelID: fallbackModelID },
119
+ source: 'fallback',
120
+ reason: `Using ${providerID}/${fallbackModelID}`,
121
+ failedModel: failedModelInfo
122
+ };
123
+ }
124
+ catch (error) {
125
+ logger?.warn('model-selector', `✗ Failed to use ${providerID}/${fallbackModelID}`, {
126
+ error: error.message
127
+ });
128
+ continue;
129
+ }
130
+ }
131
+ throw new Error('No available models for title generation. Please authenticate with at least one provider.');
132
+ }
133
+ //# sourceMappingURL=model-selector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-selector.js","sourceRoot":"","sources":["../../lib/model-selector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { OpenCodeClient } from "./types.js";
2
+ import type { Logger } from "./logger.js";
3
+ export declare function isSubagentSession(client: OpenCodeClient, sessionID: string, logger: Logger): Promise<boolean>;
4
+ export declare const sessionIdleCount: Map<string, number>;
5
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../lib/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAKzC,wBAAsB,iBAAiB,CACnC,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CAgDlB;AAED,eAAO,MAAM,gBAAgB,qBAA4B,CAAA"}
@@ -0,0 +1,43 @@
1
+ const subagentSessionCache = new Map();
2
+ const subagentSessionChecksInFlight = new Map();
3
+ export async function isSubagentSession(client, sessionID, logger) {
4
+ try {
5
+ const cached = subagentSessionCache.get(sessionID);
6
+ if (typeof cached === "boolean") {
7
+ return cached;
8
+ }
9
+ const existingCheck = subagentSessionChecksInFlight.get(sessionID);
10
+ if (existingCheck) {
11
+ return await existingCheck;
12
+ }
13
+ const checkPromise = (async () => {
14
+ const result = await client.session.get({ path: { id: sessionID } });
15
+ const isSubagent = Boolean(result.data?.parentID);
16
+ subagentSessionCache.set(sessionID, isSubagent);
17
+ if (isSubagent) {
18
+ logger.debug("subagent-check", "Detected subagent session, skipping title generation", {
19
+ sessionID,
20
+ parentID: result.data.parentID
21
+ });
22
+ }
23
+ return isSubagent;
24
+ })();
25
+ subagentSessionChecksInFlight.set(sessionID, checkPromise);
26
+ const isSubagent = await checkPromise;
27
+ subagentSessionChecksInFlight.delete(sessionID);
28
+ if (isSubagent) {
29
+ return true;
30
+ }
31
+ return false;
32
+ }
33
+ catch (error) {
34
+ subagentSessionChecksInFlight.delete(sessionID);
35
+ logger.error("subagent-check", "Failed to check if session is subagent", {
36
+ sessionID,
37
+ error: error.message
38
+ });
39
+ return false;
40
+ }
41
+ }
42
+ export const sessionIdleCount = new Map();
43
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../lib/session.ts"],"names":[],"mappings":"AAGA,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAmB,CAAA;AACvD,MAAM,6BAA6B,GAAG,IAAI,GAAG,EAA4B,CAAA;AAEzE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACnC,MAAsB,EACtB,SAAiB,EACjB,MAAc;IAEd,IAAI,CAAC;QACD,MAAM,MAAM,GAAG,oBAAoB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QAElD,IAAI,OAAO,MAAM,KAAK,SAAS,EAAE,CAAC;YAC9B,OAAO,MAAM,CAAA;QACjB,CAAC;QAED,MAAM,aAAa,GAAG,6BAA6B,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QAElE,IAAI,aAAa,EAAE,CAAC;YAChB,OAAO,MAAM,aAAa,CAAA;QAC9B,CAAC;QAED,MAAM,YAAY,GAAG,CAAC,KAAK,IAAI,EAAE;YAC7B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC,CAAA;YACpE,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;YAEjD,oBAAoB,CAAC,GAAG,CAAC,SAAS,EAAE,UAAU,CAAC,CAAA;YAE/C,IAAI,UAAU,EAAE,CAAC;gBACb,MAAM,CAAC,KAAK,CAAC,gBAAgB,EAAE,sDAAsD,EAAE;oBACnF,SAAS;oBACT,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ;iBACjC,CAAC,CAAA;YACN,CAAC;YAED,OAAO,UAAU,CAAA;QACrB,CAAC,CAAC,EAAE,CAAA;QAEJ,6BAA6B,CAAC,GAAG,CAAC,SAAS,EAAE,YAAY,CAAC,CAAA;QAE1D,MAAM,UAAU,GAAG,MAAM,YAAY,CAAA;QACrC,6BAA6B,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;QAE/C,IAAI,UAAU,EAAE,CAAC;YACb,OAAO,IAAI,CAAA;QACf,CAAC;QAED,OAAO,KAAK,CAAA;IAChB,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QAClB,6BAA6B,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;QAC/C,MAAM,CAAC,KAAK,CAAC,gBAAgB,EAAE,wCAAwC,EAAE;YACrE,SAAS;YACT,KAAK,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC,CAAA;QACF,OAAO,KAAK,CAAA;IAChB,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAkB,CAAA"}
@@ -0,0 +1,9 @@
1
+ import type { OpenCodeClient } from "./types.js";
2
+ import type { Logger } from "./logger.js";
3
+ import type { PluginConfig } from "./config.js";
4
+ export type TerminalStatus = "idle" | "running";
5
+ export declare function updateTerminalTitle(directory: string | undefined, status: TerminalStatus, logger: Logger): void;
6
+ export declare function cleanTitle(raw: string): string;
7
+ export declare function generateTitleFromContext(context: string, configModel: string | undefined, logger: Logger, client: OpenCodeClient): Promise<string | null>;
8
+ export declare function updateSessionTitle(client: OpenCodeClient, sessionId: string, logger: Logger, config: PluginConfig): Promise<void>;
9
+ //# sourceMappingURL=title.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"title.d.ts","sourceRoot":"","sources":["../../lib/title.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAM/C,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,SAAS,CAAA;AA+C/C,wBAAgB,mBAAmB,CAC/B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,MAAM,GACf,IAAI,CAoCN;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAW9C;AAED,wBAAsB,wBAAwB,CAC1C,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,cAAc,GACvB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAsExB;AAED,wBAAsB,kBAAkB,CACpC,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,GACrB,OAAO,CAAC,IAAI,CAAC,CA0Ef"}
@@ -0,0 +1,201 @@
1
+ import { extractSmartContext, formatContextForTitle, truncate } from "./context.js";
2
+ import { selectModel } from "./model-selector.js";
3
+ import { TITLE_PROMPT } from "../prompt.js";
4
+ import { basename } from "path";
5
+ let lastTerminalTitle = null;
6
+ const inFlightSessionTitleUpdates = new Set();
7
+ const queuedSessionTitleUpdates = new Set();
8
+ function sanitizeTerminalTitle(value) {
9
+ return value
10
+ .replace(/[\u0007\u001b]/g, "")
11
+ .replace(/[\r\n]+/g, " ")
12
+ .trim();
13
+ }
14
+ function formatTerminalTitle(directory, status) {
15
+ const projectName = directory ? basename(directory) || "opencode" : "opencode";
16
+ const decoratedStatus = status === "running" ? "🟢 running" : "💤 idle";
17
+ return sanitizeTerminalTitle(`${projectName} : ${decoratedStatus}`);
18
+ }
19
+ function getTerminalWriter() {
20
+ if (process.stdout.isTTY) {
21
+ return process.stdout;
22
+ }
23
+ if (process.stderr.isTTY) {
24
+ return process.stderr;
25
+ }
26
+ return null;
27
+ }
28
+ function wrapOscSequenceForTerminal(sequence) {
29
+ const term = process.env.TERM ?? "";
30
+ const tmux = process.env.TMUX;
31
+ const isScreenLike = term.startsWith("screen") || term.startsWith("tmux");
32
+ if (!tmux && !isScreenLike) {
33
+ return sequence;
34
+ }
35
+ return `\u001bPtmux;${sequence.replace(/\u001b/g, "\u001b\u001b")}\u001b\\`;
36
+ }
37
+ export function updateTerminalTitle(directory, status, logger) {
38
+ try {
39
+ const writer = getTerminalWriter();
40
+ if (!writer) {
41
+ logger.debug("terminal-title", "Skipping terminal title update because no TTY writer is available", {
42
+ status
43
+ });
44
+ return;
45
+ }
46
+ const title = formatTerminalTitle(directory, status);
47
+ if (!title || title === lastTerminalTitle) {
48
+ return;
49
+ }
50
+ const osc0 = wrapOscSequenceForTerminal(`\u001b]0;${title}\u0007`);
51
+ const osc2 = wrapOscSequenceForTerminal(`\u001b]2;${title}\u0007`);
52
+ writer.write(osc0);
53
+ writer.write(osc2);
54
+ lastTerminalTitle = title;
55
+ logger.debug("terminal-title", "Terminal title updated", {
56
+ title,
57
+ status,
58
+ writer: writer === process.stdout ? "stdout" : "stderr",
59
+ tmux: Boolean(process.env.TMUX)
60
+ });
61
+ }
62
+ catch (error) {
63
+ logger.warn("terminal-title", "Failed to update terminal title", {
64
+ status,
65
+ error: error?.message ?? String(error)
66
+ });
67
+ }
68
+ }
69
+ export function cleanTitle(raw) {
70
+ let cleaned = raw.replace(/<think>[\s\S]*?<\/think>\s*/g, "");
71
+ const lines = cleaned.split("\n").map(line => line.trim());
72
+ cleaned = lines.find(line => line.length > 0) || "Untitled";
73
+ if (cleaned.length > 100) {
74
+ cleaned = cleaned.substring(0, 97) + "...";
75
+ }
76
+ return cleaned;
77
+ }
78
+ export async function generateTitleFromContext(context, configModel, logger, client) {
79
+ try {
80
+ logger.debug('title-generation', 'Selecting model', { configModel });
81
+ const { model, modelInfo, source, reason, failedModel } = await selectModel(logger, configModel);
82
+ logger.info('title-generation', 'Model selected', {
83
+ providerID: modelInfo.providerID,
84
+ modelID: modelInfo.modelID,
85
+ source,
86
+ reason
87
+ });
88
+ if (failedModel) {
89
+ try {
90
+ await client.tui.showToast({
91
+ body: {
92
+ title: "Smart Title: Model fallback",
93
+ message: `${failedModel.providerID}/${failedModel.modelID} failed\nUsing ${modelInfo.providerID}/${modelInfo.modelID}`,
94
+ variant: "info",
95
+ duration: 5000
96
+ }
97
+ });
98
+ logger.info('title-generation', 'Toast notification shown for model fallback', {
99
+ failedModel,
100
+ selectedModel: modelInfo
101
+ });
102
+ }
103
+ catch (toastError) {
104
+ logger.error('title-generation', 'Failed to show toast notification', {
105
+ error: toastError.message
106
+ });
107
+ }
108
+ }
109
+ logger.debug('title-generation', 'Generating title', {
110
+ contextLength: context.length
111
+ });
112
+ const { generateText } = await import('ai');
113
+ const result = await generateText({
114
+ model,
115
+ messages: [
116
+ {
117
+ role: 'user',
118
+ content: `${TITLE_PROMPT}\n\n<conversation>\n${context}\n</conversation>\n\nOutput the title now:`
119
+ }
120
+ ]
121
+ });
122
+ const title = cleanTitle(result.text);
123
+ logger.info('title-generation', 'Title generated successfully', {
124
+ title,
125
+ titleLength: title.length,
126
+ rawLength: result.text.length
127
+ });
128
+ return title;
129
+ }
130
+ catch (error) {
131
+ logger.error('title-generation', 'Failed to generate title', {
132
+ error: error.message,
133
+ stack: error.stack
134
+ });
135
+ return null;
136
+ }
137
+ }
138
+ export async function updateSessionTitle(client, sessionId, logger, config) {
139
+ if (inFlightSessionTitleUpdates.has(sessionId)) {
140
+ queuedSessionTitleUpdates.add(sessionId);
141
+ logger.debug('update-title', 'Skipping duplicate session title update while one is already in flight', {
142
+ sessionId
143
+ });
144
+ return;
145
+ }
146
+ inFlightSessionTitleUpdates.add(sessionId);
147
+ try {
148
+ logger.info('update-title', 'Title update triggered', { sessionId });
149
+ const turns = await extractSmartContext(client, sessionId, logger);
150
+ if (turns.length === 0) {
151
+ logger.warn('update-title', 'No conversation turns found', { sessionId });
152
+ return;
153
+ }
154
+ logger.info('update-title', 'Context extracted', {
155
+ sessionId,
156
+ turnCount: turns.length
157
+ });
158
+ for (const turn of turns) {
159
+ logger.debug('update-title', 'Turn context', {
160
+ user: truncate(turn.user.text, 100),
161
+ hasAssistant: !!turn.assistant
162
+ });
163
+ }
164
+ const context = formatContextForTitle(turns);
165
+ logger.debug('update-title', 'Formatted context prepared but fixed title override is enabled', {
166
+ sessionId,
167
+ contextLength: context.length,
168
+ configuredModel: config.model
169
+ });
170
+ const newTitle = "hi";
171
+ logger.info('update-title', 'Updating session with new title', {
172
+ sessionId,
173
+ title: newTitle
174
+ });
175
+ await client.session.update({
176
+ path: { id: sessionId },
177
+ body: { title: newTitle }
178
+ });
179
+ logger.info('update-title', 'Session title updated successfully', {
180
+ sessionId,
181
+ title: newTitle
182
+ });
183
+ }
184
+ catch (error) {
185
+ logger.error('update-title', 'Failed to update session title', {
186
+ sessionId,
187
+ error: error.message,
188
+ stack: error.stack
189
+ });
190
+ }
191
+ finally {
192
+ inFlightSessionTitleUpdates.delete(sessionId);
193
+ if (queuedSessionTitleUpdates.delete(sessionId)) {
194
+ logger.debug('update-title', 'Re-running queued session title update after in-flight completion', {
195
+ sessionId
196
+ });
197
+ await updateSessionTitle(client, sessionId, logger, config);
198
+ }
199
+ }
200
+ }
201
+ //# sourceMappingURL=title.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"title.js","sourceRoot":"","sources":["../../lib/title.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Type definitions for Smart Title Plugin
3
+ */
4
+ export interface OpenCodeClient {
5
+ session: {
6
+ messages: (params: {
7
+ path: {
8
+ id: string;
9
+ };
10
+ }) => Promise<any>;
11
+ update: (params: {
12
+ path: {
13
+ id: string;
14
+ };
15
+ body: {
16
+ title: string;
17
+ };
18
+ }) => Promise<any>;
19
+ get: (params: {
20
+ path: {
21
+ id: string;
22
+ };
23
+ }) => Promise<any>;
24
+ };
25
+ tui: {
26
+ showToast: (params: {
27
+ body: {
28
+ title: string;
29
+ message: string;
30
+ variant: "info" | "success" | "warning" | "error";
31
+ duration: number;
32
+ };
33
+ }) => Promise<any>;
34
+ };
35
+ }
36
+ export interface ConversationTurn {
37
+ user: {
38
+ text: string;
39
+ time: number;
40
+ };
41
+ assistant?: {
42
+ first: string;
43
+ last: string;
44
+ time: number;
45
+ };
46
+ }
47
+ export interface MessagePart {
48
+ type: string;
49
+ text?: string;
50
+ synthetic?: boolean;
51
+ }
52
+ export interface Message {
53
+ info: {
54
+ id: string;
55
+ role: "user" | "assistant" | "system";
56
+ sessionID: string;
57
+ time: {
58
+ created: number;
59
+ completed?: number;
60
+ };
61
+ parentID?: string;
62
+ };
63
+ parts: MessagePart[];
64
+ }
65
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../lib/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE;QACL,QAAQ,EAAE,CAAC,MAAM,EAAE;YAAE,IAAI,EAAE;gBAAE,EAAE,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;QAC5D,MAAM,EAAE,CAAC,MAAM,EAAE;YAAE,IAAI,EAAE;gBAAE,EAAE,EAAE,MAAM,CAAA;aAAE,CAAC;YAAC,IAAI,EAAE;gBAAE,KAAK,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;QACnF,GAAG,EAAE,CAAC,MAAM,EAAE;YAAE,IAAI,EAAE;gBAAE,EAAE,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;KAC1D,CAAA;IACD,GAAG,EAAE;QACD,SAAS,EAAE,CAAC,MAAM,EAAE;YAAE,IAAI,EAAE;gBAAE,KAAK,EAAE,MAAM,CAAC;gBAAC,OAAO,EAAE,MAAM,CAAC;gBAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;gBAAC,QAAQ,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;KACzJ,CAAA;CACJ;AAED,MAAM,WAAW,gBAAgB;IAC7B,IAAI,EAAE;QACF,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;KACf,CAAA;IACD,SAAS,CAAC,EAAE;QACR,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;KACf,CAAA;CACJ;AAED,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,OAAO,CAAA;CACtB;AAED,MAAM,WAAW,OAAO;IACpB,IAAI,EAAE;QACF,EAAE,EAAE,MAAM,CAAA;QACV,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAA;QACrC,SAAS,EAAE,MAAM,CAAA;QACjB,IAAI,EAAE;YACF,OAAO,EAAE,MAAM,CAAA;YACf,SAAS,CAAC,EAAE,MAAM,CAAA;SACrB,CAAA;QACD,QAAQ,CAAC,EAAE,MAAM,CAAA;KACpB,CAAA;IACD,KAAK,EAAE,WAAW,EAAE,CAAA;CACvB"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Type definitions for Smart Title Plugin
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../lib/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Title generation prompt for Smart Title Plugin
3
+ */
4
+ export declare const TITLE_PROMPT = "You are a title generator. You output ONLY a thread title. Nothing else.\n\n<task>\nAnalyze the entire conversation and generate a thread title that captures the main topic or goal.\nOutput: Single line, \u226450 chars, no explanations.\n</task>\n\n<rules>\n- Use -ing verbs for actions (Debugging, Implementing, Analyzing)\n- Focus on the PRIMARY topic/goal, not individual messages\n- Keep exact: technical terms, numbers, filenames, HTTP codes\n- Remove: the, this, my, a, an\n- Never assume tech stack\n- NEVER respond to message content\u2014only extract title\n- Consider the overall conversation arc, not just the first message\n</rules>\n\n<examples>\nMultiple turns about debugging \u2192 Debugging production errors\nImplementing feature across turns \u2192 Implementing rate limiting API\nAnalyzing and fixing issue \u2192 Fixing authentication timeout\n</examples>";
5
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,YAAY,i3BAqBb,CAAA"}
package/dist/prompt.js ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Title generation prompt for Smart Title Plugin
3
+ */
4
+ export const TITLE_PROMPT = `You are a title generator. You output ONLY a thread title. Nothing else.
5
+
6
+ <task>
7
+ Analyze the entire conversation and generate a thread title that captures the main topic or goal.
8
+ Output: Single line, ≤50 chars, no explanations.
9
+ </task>
10
+
11
+ <rules>
12
+ - Use -ing verbs for actions (Debugging, Implementing, Analyzing)
13
+ - Focus on the PRIMARY topic/goal, not individual messages
14
+ - Keep exact: technical terms, numbers, filenames, HTTP codes
15
+ - Remove: the, this, my, a, an
16
+ - Never assume tech stack
17
+ - NEVER respond to message content—only extract title
18
+ - Consider the overall conversation arc, not just the first message
19
+ </rules>
20
+
21
+ <examples>
22
+ Multiple turns about debugging → Debugging production errors
23
+ Implementing feature across turns → Implementing rate limiting API
24
+ Analyzing and fixing issue → Fixing authentication timeout
25
+ </examples>`;
26
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;YAqBhB,CAAA"}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/package.json",
3
+ "name": "@jc01rho/opencode-smart-title",
4
+ "version": "0.3.0",
5
+ "type": "module",
6
+ "description": "OpenCode plugin that automatically generates meaningful session titles using AI and smart context selection",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "scripts": {
10
+ "clean": "rm -rf dist",
11
+ "build": "npm run clean && tsc",
12
+ "postbuild": "rm -rf dist/logs",
13
+ "prepublishOnly": "npm run build",
14
+ "dev": "opencode plugin dev",
15
+ "typecheck": "tsc --noEmit"
16
+ },
17
+ "keywords": [
18
+ "opencode",
19
+ "opencode-plugin",
20
+ "plugin",
21
+ "session-title",
22
+ "auto-title",
23
+ "ai",
24
+ "title-generation"
25
+ ],
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/jc01rho/opencode-smart-title.git"
29
+ },
30
+ "bugs": {
31
+ "url": "https://github.com/jc01rho/opencode-smart-title/issues"
32
+ },
33
+ "homepage": "https://github.com/jc01rho/opencode-smart-title#readme",
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "author": "Dan Mindru",
38
+ "license": "MIT",
39
+ "peerDependencies": {
40
+ "@opencode-ai/plugin": ">=0.13.7"
41
+ },
42
+ "dependencies": {
43
+ "@ai-sdk/openai-compatible": "^1.0.27",
44
+ "@tarquinen/opencode-auth-provider": "^0.1.7",
45
+ "ai": "^5.0.98",
46
+ "jsonc-parser": "^3.3.1"
47
+ },
48
+ "devDependencies": {
49
+ "@opencode-ai/plugin": ">=0.13.7",
50
+ "@types/node": "^24.10.1",
51
+ "typescript": "^5.9.3"
52
+ },
53
+ "files": [
54
+ "dist/",
55
+ "README.md",
56
+ "LICENSE"
57
+ ]
58
+ }