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.
- package/dist/index.js +16 -16
- package/dist/template/.opencode/memory/observations/2026-01-30-decision-github-copilot-claude-routing-keep-disab.md +32 -0
- package/dist/template/.opencode/memory/observations/2026-01-30-discovery-context-management-research-critical-gap.md +14 -0
- package/dist/template/.opencode/memory/observations/2026-01-31-decision-copilot-auth-plugin-updated-with-baseurl.md +63 -0
- package/dist/template/.opencode/memory/observations/2026-01-31-learning-opencode-copilot-auth-comparison-finding.md +61 -0
- package/dist/template/.opencode/memory/observations/2026-01-31-learning-opencode-copilot-reasoning-architecture-.md +66 -0
- package/dist/template/.opencode/memory/observations/2026-01-31-warning-copilot-claude-v1-endpoint-returns-404-c.md +48 -0
- package/dist/template/.opencode/memory/research/context-management-analysis.md +685 -0
- package/dist/template/.opencode/opencode.json +52 -156
- package/dist/template/.opencode/package.json +1 -1
- package/dist/template/.opencode/plugins/copilot-auth.ts +286 -29
- package/dist/template/.opencode/plugins/sdk/copilot/chat/convert-to-openai-compatible-chat-messages.ts +181 -0
- package/dist/template/.opencode/plugins/sdk/copilot/chat/get-response-metadata.ts +15 -0
- package/dist/template/.opencode/plugins/sdk/copilot/chat/map-openai-compatible-finish-reason.ts +19 -0
- package/dist/template/.opencode/plugins/sdk/copilot/chat/openai-compatible-api-types.ts +72 -0
- package/dist/template/.opencode/plugins/sdk/copilot/chat/openai-compatible-chat-language-model.ts +823 -0
- package/dist/template/.opencode/plugins/sdk/copilot/chat/openai-compatible-chat-options.ts +30 -0
- package/dist/template/.opencode/plugins/sdk/copilot/chat/openai-compatible-metadata-extractor.ts +48 -0
- package/dist/template/.opencode/plugins/sdk/copilot/chat/openai-compatible-prepare-tools.ts +92 -0
- package/dist/template/.opencode/plugins/sdk/copilot/copilot-provider.ts +94 -0
- package/dist/template/.opencode/plugins/sdk/copilot/index.ts +5 -0
- package/dist/template/.opencode/plugins/sdk/copilot/openai-compatible-error.ts +30 -0
- package/dist/template/.opencode/skills/notebooklm/SKILL.md +272 -0
- package/dist/template/.opencode/skills/notebooklm/references/setup.md +353 -0
- package/dist/template/.opencode/tools/notebooklm.ts +488 -0
- 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
|
+
>;
|
package/dist/template/.opencode/plugins/sdk/copilot/chat/openai-compatible-metadata-extractor.ts
ADDED
|
@@ -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,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`
|