opencodekit 0.15.18 → 0.15.20

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 (26) hide show
  1. package/dist/index.js +16 -16
  2. package/dist/template/.opencode/memory/observations/2026-01-30-decision-github-copilot-claude-routing-keep-disab.md +32 -0
  3. package/dist/template/.opencode/memory/observations/2026-01-30-discovery-context-management-research-critical-gap.md +14 -0
  4. package/dist/template/.opencode/memory/observations/2026-01-31-decision-copilot-auth-plugin-updated-with-baseurl.md +63 -0
  5. package/dist/template/.opencode/memory/observations/2026-01-31-learning-opencode-copilot-auth-comparison-finding.md +61 -0
  6. package/dist/template/.opencode/memory/observations/2026-01-31-learning-opencode-copilot-reasoning-architecture-.md +66 -0
  7. package/dist/template/.opencode/memory/observations/2026-01-31-warning-copilot-claude-v1-endpoint-returns-404-c.md +48 -0
  8. package/dist/template/.opencode/memory/research/context-management-analysis.md +685 -0
  9. package/dist/template/.opencode/opencode.json +52 -156
  10. package/dist/template/.opencode/package.json +1 -1
  11. package/dist/template/.opencode/plugins/copilot-auth.ts +286 -29
  12. package/dist/template/.opencode/plugins/sdk/copilot/chat/convert-to-openai-compatible-chat-messages.ts +181 -0
  13. package/dist/template/.opencode/plugins/sdk/copilot/chat/get-response-metadata.ts +15 -0
  14. package/dist/template/.opencode/plugins/sdk/copilot/chat/map-openai-compatible-finish-reason.ts +19 -0
  15. package/dist/template/.opencode/plugins/sdk/copilot/chat/openai-compatible-api-types.ts +72 -0
  16. package/dist/template/.opencode/plugins/sdk/copilot/chat/openai-compatible-chat-language-model.ts +823 -0
  17. package/dist/template/.opencode/plugins/sdk/copilot/chat/openai-compatible-chat-options.ts +30 -0
  18. package/dist/template/.opencode/plugins/sdk/copilot/chat/openai-compatible-metadata-extractor.ts +48 -0
  19. package/dist/template/.opencode/plugins/sdk/copilot/chat/openai-compatible-prepare-tools.ts +92 -0
  20. package/dist/template/.opencode/plugins/sdk/copilot/copilot-provider.ts +94 -0
  21. package/dist/template/.opencode/plugins/sdk/copilot/index.ts +5 -0
  22. package/dist/template/.opencode/plugins/sdk/copilot/openai-compatible-error.ts +30 -0
  23. package/dist/template/.opencode/skills/notebooklm/SKILL.md +272 -0
  24. package/dist/template/.opencode/skills/notebooklm/references/setup.md +353 -0
  25. package/dist/template/.opencode/tools/notebooklm.ts +488 -0
  26. package/package.json +1 -1
@@ -0,0 +1,30 @@
1
+ import { z } from "zod";
2
+
3
+ export type OpenAICompatibleChatModelId = string;
4
+
5
+ export const openaiCompatibleProviderOptions = z.object({
6
+ /**
7
+ * A unique identifier representing your end-user, which can help the provider to
8
+ * monitor and detect abuse.
9
+ */
10
+ user: z.string().optional(),
11
+
12
+ /**
13
+ * Reasoning effort for reasoning models. Defaults to `medium`.
14
+ */
15
+ reasoningEffort: z.string().optional(),
16
+
17
+ /**
18
+ * Controls the verbosity of the generated text. Defaults to `medium`.
19
+ */
20
+ textVerbosity: z.string().optional(),
21
+
22
+ /**
23
+ * Copilot thinking_budget used for Anthropic models.
24
+ */
25
+ thinking_budget: z.number().optional(),
26
+ });
27
+
28
+ export type OpenAICompatibleProviderOptions = z.infer<
29
+ typeof openaiCompatibleProviderOptions
30
+ >;
@@ -0,0 +1,48 @@
1
+ import type { SharedV2ProviderMetadata } from "@ai-sdk/provider";
2
+
3
+ /**
4
+ Extracts provider-specific metadata from API responses.
5
+ Used to standardize metadata handling across different LLM providers while allowing
6
+ provider-specific metadata to be captured.
7
+ */
8
+ export type MetadataExtractor = {
9
+ /**
10
+ * Extracts provider metadata from a complete, non-streaming response.
11
+ *
12
+ * @param parsedBody - The parsed response JSON body from the provider's API.
13
+ *
14
+ * @returns Provider-specific metadata or undefined if no metadata is available.
15
+ * The metadata should be under a key indicating the provider id.
16
+ */
17
+ extractMetadata: ({
18
+ parsedBody,
19
+ }: {
20
+ parsedBody: unknown;
21
+ }) => Promise<SharedV2ProviderMetadata | undefined>;
22
+
23
+ /**
24
+ * Creates an extractor for handling streaming responses. The returned object provides
25
+ * methods to process individual chunks and build the final metadata from the accumulated
26
+ * stream data.
27
+ *
28
+ * @returns An object with methods to process chunks and build metadata from a stream
29
+ */
30
+ createStreamExtractor: () => {
31
+ /**
32
+ * Process an individual chunk from the stream. Called for each chunk in the response stream
33
+ * to accumulate metadata throughout the streaming process.
34
+ *
35
+ * @param parsedChunk - The parsed JSON response chunk from the provider's API
36
+ */
37
+ processChunk(parsedChunk: unknown): void;
38
+
39
+ /**
40
+ * Builds the metadata object after all chunks have been processed.
41
+ * Called at the end of the stream to generate the complete provider metadata.
42
+ *
43
+ * @returns Provider-specific metadata or undefined if no metadata is available.
44
+ * The metadata should be under a key indicating the provider id.
45
+ */
46
+ buildMetadata(): SharedV2ProviderMetadata | undefined;
47
+ };
48
+ };
@@ -0,0 +1,92 @@
1
+ import {
2
+ type LanguageModelV2CallOptions,
3
+ type LanguageModelV2CallWarning,
4
+ UnsupportedFunctionalityError,
5
+ } from "@ai-sdk/provider";
6
+
7
+ export function prepareTools({
8
+ tools,
9
+ toolChoice,
10
+ }: {
11
+ tools: LanguageModelV2CallOptions["tools"];
12
+ toolChoice?: LanguageModelV2CallOptions["toolChoice"];
13
+ }): {
14
+ tools:
15
+ | undefined
16
+ | Array<{
17
+ type: "function";
18
+ function: {
19
+ name: string;
20
+ description: string | undefined;
21
+ parameters: unknown;
22
+ };
23
+ }>;
24
+ toolChoice:
25
+ | { type: "function"; function: { name: string } }
26
+ | "auto"
27
+ | "none"
28
+ | "required"
29
+ | undefined;
30
+ toolWarnings: LanguageModelV2CallWarning[];
31
+ } {
32
+ // when the tools array is empty, change it to undefined to prevent errors:
33
+ tools = tools?.length ? tools : undefined;
34
+
35
+ const toolWarnings: LanguageModelV2CallWarning[] = [];
36
+
37
+ if (tools == null) {
38
+ return { tools: undefined, toolChoice: undefined, toolWarnings };
39
+ }
40
+
41
+ const openaiCompatTools: Array<{
42
+ type: "function";
43
+ function: {
44
+ name: string;
45
+ description: string | undefined;
46
+ parameters: unknown;
47
+ };
48
+ }> = [];
49
+
50
+ for (const tool of tools) {
51
+ if (tool.type === "provider-defined") {
52
+ toolWarnings.push({ type: "unsupported-tool", tool });
53
+ } else {
54
+ openaiCompatTools.push({
55
+ type: "function",
56
+ function: {
57
+ name: tool.name,
58
+ description: tool.description,
59
+ parameters: tool.inputSchema,
60
+ },
61
+ });
62
+ }
63
+ }
64
+
65
+ if (toolChoice == null) {
66
+ return { tools: openaiCompatTools, toolChoice: undefined, toolWarnings };
67
+ }
68
+
69
+ const type = toolChoice.type;
70
+
71
+ switch (type) {
72
+ case "auto":
73
+ case "none":
74
+ case "required":
75
+ return { tools: openaiCompatTools, toolChoice: type, toolWarnings };
76
+ case "tool":
77
+ return {
78
+ tools: openaiCompatTools,
79
+ toolChoice: {
80
+ type: "function",
81
+ function: { name: toolChoice.toolName },
82
+ },
83
+ toolWarnings,
84
+ };
85
+ default: {
86
+ const _exhaustiveCheck: never = type;
87
+ throw new UnsupportedFunctionalityError({
88
+ functionality: `tool choice type: ${_exhaustiveCheck}`,
89
+ });
90
+ }
91
+ }
92
+ }
@@ -0,0 +1,94 @@
1
+ import type { LanguageModelV2 } from "@ai-sdk/provider";
2
+ import {
3
+ type FetchFunction,
4
+ withUserAgentSuffix,
5
+ withoutTrailingSlash,
6
+ } from "@ai-sdk/provider-utils";
7
+ import { OpenAICompatibleChatLanguageModel } from "./chat/openai-compatible-chat-language-model";
8
+
9
+ // Import the version or define it
10
+ const VERSION = "0.1.0";
11
+
12
+ export type OpenaiCompatibleModelId = string;
13
+
14
+ export interface OpenaiCompatibleProviderSettings {
15
+ /**
16
+ * API key for authenticating requests.
17
+ */
18
+ apiKey?: string;
19
+
20
+ /**
21
+ * Base URL for the OpenAI Compatible API calls.
22
+ */
23
+ baseURL?: string;
24
+
25
+ /**
26
+ * Name of the provider.
27
+ */
28
+ name?: string;
29
+
30
+ /**
31
+ * Custom headers to include in the requests.
32
+ */
33
+ headers?: Record<string, string>;
34
+
35
+ /**
36
+ * Custom fetch implementation.
37
+ */
38
+ fetch?: FetchFunction;
39
+ }
40
+
41
+ export interface OpenaiCompatibleProvider {
42
+ (modelId: OpenaiCompatibleModelId): LanguageModelV2;
43
+ chat(modelId: OpenaiCompatibleModelId): LanguageModelV2;
44
+ languageModel(modelId: OpenaiCompatibleModelId): LanguageModelV2;
45
+ }
46
+
47
+ /**
48
+ * Create an OpenAI Compatible provider instance for GitHub Copilot.
49
+ * This custom provider handles Claude reasoning tokens (thinking_budget).
50
+ */
51
+ export function createOpenaiCompatible(
52
+ options: OpenaiCompatibleProviderSettings = {},
53
+ ): OpenaiCompatibleProvider {
54
+ const baseURL = withoutTrailingSlash(
55
+ options.baseURL ?? "https://api.openai.com/v1",
56
+ );
57
+
58
+ if (!baseURL) {
59
+ throw new Error("baseURL is required");
60
+ }
61
+
62
+ // Merge headers: defaults first, then user overrides
63
+ const headers = {
64
+ // Default OpenAI Compatible headers (can be overridden by user)
65
+ ...(options.apiKey && { Authorization: `Bearer ${options.apiKey}` }),
66
+ ...options.headers,
67
+ };
68
+
69
+ const getHeaders = () =>
70
+ withUserAgentSuffix(headers, `ai-sdk/openai-compatible/${VERSION}`);
71
+
72
+ const createChatModel = (modelId: OpenaiCompatibleModelId) => {
73
+ return new OpenAICompatibleChatLanguageModel(modelId, {
74
+ provider: `${options.name ?? "openai-compatible"}.chat`,
75
+ headers: getHeaders,
76
+ url: ({ path }) => `${baseURL}${path}`,
77
+ fetch: options.fetch,
78
+ });
79
+ };
80
+
81
+ const createLanguageModel = (modelId: OpenaiCompatibleModelId) =>
82
+ createChatModel(modelId);
83
+
84
+ const provider = (modelId: OpenaiCompatibleModelId) =>
85
+ createChatModel(modelId);
86
+
87
+ provider.languageModel = createLanguageModel;
88
+ provider.chat = createChatModel;
89
+
90
+ return provider as OpenaiCompatibleProvider;
91
+ }
92
+
93
+ // Default OpenAI Compatible provider instance
94
+ export const openaiCompatible = createOpenaiCompatible();
@@ -0,0 +1,5 @@
1
+ export { createOpenaiCompatible, openaiCompatible } from "./copilot-provider";
2
+ export type {
3
+ OpenaiCompatibleProvider,
4
+ OpenaiCompatibleProviderSettings,
5
+ } from "./copilot-provider";
@@ -0,0 +1,30 @@
1
+ import { type ZodType, z } from "zod";
2
+
3
+ export const openaiCompatibleErrorDataSchema = z.object({
4
+ error: z.object({
5
+ message: z.string(),
6
+
7
+ // The additional information below is handled loosely to support
8
+ // OpenAI-compatible providers that have slightly different error
9
+ // responses:
10
+ type: z.string().nullish(),
11
+ param: z.any().nullish(),
12
+ code: z.union([z.string(), z.number()]).nullish(),
13
+ }),
14
+ });
15
+
16
+ export type OpenAICompatibleErrorData = z.infer<
17
+ typeof openaiCompatibleErrorDataSchema
18
+ >;
19
+
20
+ export type ProviderErrorStructure<T> = {
21
+ errorSchema: ZodType<T>;
22
+ errorToMessage: (error: T) => string;
23
+ isRetryable?: (response: Response, error?: T) => boolean;
24
+ };
25
+
26
+ export const defaultOpenAICompatibleErrorStructure: ProviderErrorStructure<OpenAICompatibleErrorData> =
27
+ {
28
+ errorSchema: openaiCompatibleErrorDataSchema,
29
+ errorToMessage: (data) => data.error.message,
30
+ };
@@ -0,0 +1,272 @@
1
+ ---
2
+ name: notebooklm
3
+ description: >
4
+ Query Google NotebookLM notebooks directly from OpenCode for source-grounded, citation-backed answers.
5
+ Use when user mentions NotebookLM, shares notebook URLs, or wants to query their uploaded documents.
6
+ Provides document-only responses with drastically reduced hallucinations.
7
+ version: "1.0.0"
8
+ license: MIT
9
+ ---
10
+
11
+ # NotebookLM Skill
12
+
13
+ Interact with Google NotebookLM to query documentation with Gemini's source-grounded answers. Each question opens a fresh browser session, retrieves the answer exclusively from uploaded documents, and closes.
14
+
15
+ ## When to Use This Skill
16
+
17
+ Trigger when user:
18
+
19
+ - Mentions NotebookLM explicitly
20
+ - Shares NotebookLM URL (`https://notebooklm.google.com/notebook/...`)
21
+ - Asks to query their notebooks/documentation
22
+ - Wants to add documentation to NotebookLM library
23
+ - Uses phrases like "ask my NotebookLM", "check my docs", "query my notebook"
24
+
25
+ ## Prerequisites
26
+
27
+ Before using this skill, ensure:
28
+
29
+ 1. **Python 3.10+** is installed
30
+ 2. **Google Chrome** browser is installed
31
+ 3. **NotebookLM account** with uploaded documents
32
+ 4. **Google authentication** (one-time setup)
33
+
34
+ ## Quick Start
35
+
36
+ ```typescript
37
+ // Check authentication status
38
+ await notebooklm({ operation: "auth", subOperation: "status" });
39
+
40
+ // Query a notebook
41
+ await notebooklm({
42
+ operation: "query",
43
+ question: "What are the key findings in this document?",
44
+ notebookUrl: "https://notebooklm.google.com/notebook/...",
45
+ });
46
+ ```
47
+
48
+ ## Core Workflow
49
+
50
+ ### Step 1: Check Authentication
51
+
52
+ ```typescript
53
+ const authStatus = await notebooklm({
54
+ operation: "auth",
55
+ subOperation: "status",
56
+ });
57
+ ```
58
+
59
+ If not authenticated, proceed to setup.
60
+
61
+ ### Step 2: Authenticate (One-Time Setup)
62
+
63
+ ```typescript
64
+ await notebooklm({
65
+ operation: "auth",
66
+ subOperation: "setup",
67
+ showBrowser: true, // Browser must be visible for manual login
68
+ });
69
+ ```
70
+
71
+ **Important:**
72
+
73
+ - Browser is VISIBLE for authentication
74
+ - Browser window opens automatically
75
+ - User must manually log in to Google
76
+ - Tell user: "A browser window will open for Google login"
77
+
78
+ ### Step 3: Manage Notebook Library
79
+
80
+ ```typescript
81
+ // List all notebooks
82
+ await notebooklm({ operation: "library", subOperation: "list" });
83
+
84
+ // Add notebook to library
85
+ await notebooklm({
86
+ operation: "library",
87
+ subOperation: "add",
88
+ url: "https://notebooklm.google.com/notebook/...",
89
+ name: "API Documentation",
90
+ description: "REST API reference and examples",
91
+ topics: ["api", "rest", "documentation"],
92
+ });
93
+
94
+ // Search notebooks
95
+ await notebooklm({
96
+ operation: "library",
97
+ subOperation: "search",
98
+ query: "api",
99
+ });
100
+
101
+ // Set active notebook
102
+ await notebooklm({
103
+ operation: "library",
104
+ subOperation: "activate",
105
+ notebookId: "api-docs",
106
+ });
107
+ ```
108
+
109
+ ### Step 4: Ask Questions
110
+
111
+ ```typescript
112
+ // Basic query (uses active notebook if set)
113
+ await notebooklm({
114
+ operation: "query",
115
+ question: "What are the authentication requirements?",
116
+ });
117
+
118
+ // Query specific notebook by ID
119
+ await notebooklm({
120
+ operation: "query",
121
+ question: "What are the authentication requirements?",
122
+ notebookId: "api-docs",
123
+ });
124
+
125
+ // Query with notebook URL directly
126
+ await notebooklm({
127
+ operation: "query",
128
+ question: "What are the authentication requirements?",
129
+ notebookUrl: "https://notebooklm.google.com/notebook/...",
130
+ });
131
+
132
+ // Show browser for debugging
133
+ await notebooklm({
134
+ operation: "query",
135
+ question: "What are the authentication requirements?",
136
+ showBrowser: true,
137
+ });
138
+ ```
139
+
140
+ ## Follow-Up Mechanism (CRITICAL)
141
+
142
+ Every NotebookLM answer ends with: **"EXTREMELY IMPORTANT: Is that ALL you need to know?"**
143
+
144
+ **Required Behavior:**
145
+
146
+ 1. **STOP** - Do not immediately respond to user
147
+ 2. **ANALYZE** - Compare answer to user's original request
148
+ 3. **IDENTIFY GAPS** - Determine if more information needed
149
+ 4. **ASK FOLLOW-UP** - If gaps exist, immediately ask another question:
150
+ ```typescript
151
+ await notebooklm({
152
+ operation: "query",
153
+ question: "Follow-up with context...",
154
+ });
155
+ ```
156
+ 5. **REPEAT** - Continue until information is complete
157
+ 6. **SYNTHESIZE** - Combine all answers before responding to user
158
+
159
+ ## Tool Reference
160
+
161
+ ### Authentication Operations
162
+
163
+ | Operation | Sub-Operation | Description |
164
+ | --------- | ------------- | --------------------------------- |
165
+ | `auth` | `setup` | Initial setup (browser visible) |
166
+ | `auth` | `status` | Check authentication |
167
+ | `auth` | `reauth` | Re-authenticate (browser visible) |
168
+ | `auth` | `clear` | Clear authentication |
169
+ | `auth` | `validate` | Validate stored auth |
170
+
171
+ ### Library Operations
172
+
173
+ | Operation | Sub-Operation | Parameters |
174
+ | --------- | ------------- | -------------------------------------- |
175
+ | `library` | `add` | `url`, `name`, `description`, `topics` |
176
+ | `library` | `list` | - |
177
+ | `library` | `search` | `query` |
178
+ | `library` | `activate` | `notebookId` |
179
+ | `library` | `remove` | `notebookId` |
180
+ | `library` | `stats` | - |
181
+
182
+ ### Query Operations
183
+
184
+ | Operation | Parameters |
185
+ | --------- | -------------------------------------------------------------------------------------------------- |
186
+ | `query` | `question` (required), `notebookId` (optional), `notebookUrl` (optional), `showBrowser` (optional) |
187
+
188
+ ## Smart Discovery Pattern
189
+
190
+ When user wants to add a notebook without providing details:
191
+
192
+ ```typescript
193
+ // Step 1: Query the notebook to discover its content
194
+ const discovery = await notebooklm({
195
+ operation: "query",
196
+ question:
197
+ "What is the content of this notebook? What topics are covered? Provide a complete overview briefly and concisely",
198
+ notebookUrl: "https://notebooklm.google.com/notebook/...",
199
+ });
200
+
201
+ // Step 2: Use discovered information to add it
202
+ await notebooklm({
203
+ operation: "library",
204
+ subOperation: "add",
205
+ url: "https://notebooklm.google.com/notebook/...",
206
+ name: "[Based on content]",
207
+ description: "[Based on content]",
208
+ topics: ["[Based on content]"],
209
+ });
210
+ ```
211
+
212
+ ## Decision Flow
213
+
214
+ ```
215
+ User mentions NotebookLM
216
+
217
+ Check auth → notebooklm({ operation: "auth", subOperation: "status" })
218
+
219
+ If not authenticated → notebooklm({ operation: "auth", subOperation: "setup", showBrowser: true })
220
+
221
+ Check/Add notebook → notebooklm({ operation: "library", subOperation: "list/add" })
222
+
223
+ Activate notebook → notebooklm({ operation: "library", subOperation: "activate", notebookId: "..." })
224
+
225
+ Ask question → notebooklm({ operation: "query", question: "..." })
226
+
227
+ See "Is that ALL you need?" → Ask follow-ups until complete
228
+
229
+ Synthesize and respond to user
230
+ ```
231
+
232
+ ## Troubleshooting
233
+
234
+ | Problem | Solution |
235
+ | -------------------- | --------------------------------------- |
236
+ | Not authenticated | Run auth setup with `showBrowser: true` |
237
+ | Authentication fails | Browser must be visible for setup |
238
+ | Rate limit (50/day) | Wait or switch Google account |
239
+ | Notebook not found | Check with `library` → `list` operation |
240
+ | ModuleNotFoundError | Tool auto-installs dependencies |
241
+
242
+ ## Best Practices
243
+
244
+ 1. **Always check auth first** - Before any operations
245
+ 2. **Follow-up questions** - Don't stop at first answer
246
+ 3. **Browser visible for auth** - Required for manual login
247
+ 4. **Include context** - Each question is independent
248
+ 5. **Synthesize answers** - Combine multiple responses
249
+
250
+ ## Limitations
251
+
252
+ - No session persistence (each question = new browser)
253
+ - Rate limits on free Google accounts (50 queries/day)
254
+ - Manual upload required (user must add docs to NotebookLM)
255
+ - Browser overhead (few seconds per question)
256
+ - Requires Python and Chrome installed
257
+
258
+ ## Data Storage
259
+
260
+ All data stored in `~/.opencode/skills/notebooklm/data/`:
261
+
262
+ - `library.json` - Notebook metadata
263
+ - `auth_info.json` - Authentication status
264
+ - `browser_state/` - Browser cookies and session
265
+
266
+ **Security:** Protected by `.gitignore`, never commit to git.
267
+
268
+ ## Resources
269
+
270
+ - **Original Skill**: https://github.com/PleasePrompto/notebooklm-skill
271
+ - **NotebookLM**: https://notebooklm.google.com
272
+ - **Setup Guide**: See `references/setup.md`