opencodekit 0.20.8 → 0.21.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.
Files changed (48) hide show
  1. package/dist/index.js +1 -1
  2. package/dist/template/.opencode/AGENTS.md +25 -1
  3. package/dist/template/.opencode/memory.db +0 -0
  4. package/dist/template/.opencode/memory.db-shm +0 -0
  5. package/dist/template/.opencode/memory.db-wal +0 -0
  6. package/dist/template/.opencode/opencode.json +83 -609
  7. package/dist/template/.opencode/opencodex-fast.jsonc +1 -1
  8. package/dist/template/.opencode/package.json +2 -2
  9. package/dist/template/.opencode/plugin/copilot-auth.ts +86 -12
  10. package/dist/template/.opencode/plugin/prompt-leverage.ts +191 -0
  11. package/dist/template/.opencode/plugin/sdk/copilot/copilot-provider.ts +14 -2
  12. package/dist/template/.opencode/plugin/sdk/copilot/index.ts +2 -2
  13. package/dist/template/.opencode/plugin/sdk/copilot/responses/convert-to-openai-responses-input.ts +335 -0
  14. package/dist/template/.opencode/plugin/sdk/copilot/responses/map-openai-responses-finish-reason.ts +22 -0
  15. package/dist/template/.opencode/plugin/sdk/copilot/responses/openai-config.ts +18 -0
  16. package/dist/template/.opencode/plugin/sdk/copilot/responses/openai-error.ts +22 -0
  17. package/dist/template/.opencode/plugin/sdk/copilot/responses/openai-responses-api-types.ts +214 -0
  18. package/dist/template/.opencode/plugin/sdk/copilot/responses/openai-responses-language-model.ts +1770 -0
  19. package/dist/template/.opencode/plugin/sdk/copilot/responses/openai-responses-prepare-tools.ts +173 -0
  20. package/dist/template/.opencode/plugin/sdk/copilot/responses/openai-responses-settings.ts +1 -0
  21. package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/code-interpreter.ts +87 -0
  22. package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/file-search.ts +127 -0
  23. package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/image-generation.ts +114 -0
  24. package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/local-shell.ts +64 -0
  25. package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/web-search-preview.ts +103 -0
  26. package/dist/template/.opencode/plugin/sdk/copilot/responses/tool/web-search.ts +102 -0
  27. package/dist/template/.opencode/skill/gh-address-comments/SKILL.md +29 -0
  28. package/dist/template/.opencode/skill/gh-address-comments/scripts/fetch_comments.py +237 -0
  29. package/dist/template/.opencode/skill/gh-fix-ci/SKILL.md +38 -0
  30. package/dist/template/.opencode/skill/gh-fix-ci/scripts/inspect_pr_checks.py +509 -0
  31. package/dist/template/.opencode/skill/prompt-leverage/SKILL.md +90 -0
  32. package/dist/template/.opencode/skill/prompt-leverage/references/framework.md +91 -0
  33. package/dist/template/.opencode/skill/prompt-leverage/scripts/augment_prompt.py +157 -0
  34. package/dist/template/.opencode/skill/screenshot/SKILL.md +48 -0
  35. package/dist/template/.opencode/skill/screenshot/scripts/ensure_macos_permissions.sh +54 -0
  36. package/dist/template/.opencode/skill/screenshot/scripts/macos_display_info.swift +22 -0
  37. package/dist/template/.opencode/skill/screenshot/scripts/macos_permissions.swift +40 -0
  38. package/dist/template/.opencode/skill/screenshot/scripts/macos_window_info.swift +126 -0
  39. package/dist/template/.opencode/skill/screenshot/scripts/take_screenshot.ps1 +163 -0
  40. package/dist/template/.opencode/skill/screenshot/scripts/take_screenshot.py +585 -0
  41. package/dist/template/.opencode/skill/security-threat-model/SKILL.md +36 -0
  42. package/dist/template/.opencode/skill/security-threat-model/references/prompt-template.md +255 -0
  43. package/dist/template/.opencode/skill/security-threat-model/references/security-controls-and-assets.md +32 -0
  44. package/dist/template/.opencode/skill/skill-installer/SKILL.md +58 -0
  45. package/dist/template/.opencode/skill/skill-installer/scripts/github_utils.py +21 -0
  46. package/dist/template/.opencode/skill/skill-installer/scripts/install-skill-from-github.py +313 -0
  47. package/dist/template/.opencode/skill/skill-installer/scripts/list-skills.py +106 -0
  48. package/package.json +1 -1
@@ -1,3 +1,3 @@
1
1
  {
2
- "enabled": true
2
+ "enabled": false
3
3
  }
@@ -12,11 +12,11 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@google/stitch-sdk": "^0.0.3",
15
- "@opencode-ai/plugin": "1.4.3"
15
+ "@opencode-ai/plugin": "1.4.9"
16
16
  },
17
17
  "devDependencies": {
18
18
  "@types/node": "^25.3.0",
19
19
  "bun-types": "^1.3.9",
20
20
  "typescript": "^5.9.3"
21
21
  }
22
- }
22
+ }
@@ -11,6 +11,8 @@
11
11
  * these fields to AI SDK's reasoning content parts.
12
12
  */
13
13
 
14
+ import path from "node:path";
15
+ import { pathToFileURL } from "node:url";
14
16
  import type { Plugin } from "@opencode-ai/plugin";
15
17
 
16
18
  const CLIENT_ID = "Ov23li8tweQw6odWQebz";
@@ -43,10 +45,7 @@ function setLogger(client: any) {
43
45
  const OAUTH_POLLING_SAFETY_MARGIN_MS = 3000; // 3 seconds
44
46
 
45
47
  const HEADERS = {
46
- "User-Agent": "GitHubCopilotChat/0.35.0",
47
- "Editor-Version": "vscode/1.107.0",
48
- "Editor-Plugin-Version": "copilot-chat/0.35.0",
49
- "Copilot-Integration-Id": "vscode-chat",
48
+ "User-Agent": "opencode/1.3.17",
50
49
  };
51
50
 
52
51
  const RESPONSES_API_ALTERNATE_INPUT_TYPES = [
@@ -544,10 +543,14 @@ function sanitizeResponseInputIds(input: any[]): any[] {
544
543
  });
545
544
  }
546
545
 
547
- export const CopilotAuthPlugin: Plugin = async ({ client: sdk }) => {
546
+ export const CopilotAuthPlugin: Plugin = async ({ client: sdk, directory }) => {
548
547
  // Initialize logger with the SDK client
549
548
  setLogger(sdk);
550
549
 
550
+ const localCopilotSdk = pathToFileURL(
551
+ path.join(directory, ".opencode", "plugin", "sdk", "copilot", "index.ts"),
552
+ ).toString();
553
+
551
554
  return {
552
555
  auth: {
553
556
  provider: "github-copilot",
@@ -572,11 +575,10 @@ export const CopilotAuthPlugin: Plugin = async ({ client: sdk }) => {
572
575
  },
573
576
  };
574
577
 
575
- // All models use the standard github-copilot SDK
576
- // Reasoning support for Claude models is handled via:
577
- // 1. The fetch wrapper adds thinking_budget to request body
578
- // 2. The fetch wrapper strips invalid thinking blocks from messages
579
- model.api.npm = "@ai-sdk/github-copilot";
578
+ // Route all Copilot models through the local file-based SDK.
579
+ // OpenCode's built-in github-copilot model loader will automatically
580
+ // choose sdk.responses(model) for GPT-5+ and sdk.chat(model) otherwise.
581
+ model.api.npm = localCopilotSdk;
580
582
  }
581
583
  }
582
584
 
@@ -862,6 +864,65 @@ export const CopilotAuthPlugin: Plugin = async ({ client: sdk }) => {
862
864
  ),
863
865
  );
864
866
  }
867
+
868
+ // Sanitize tool definitions: strip non-standard fields like
869
+ // "custom" (wrapping eager_input_streaming) injected by
870
+ // runtime layers. These cause "Extra inputs are not permitted"
871
+ // on the Copilot API.
872
+ const toolSource = modifiedBody || body;
873
+ if (Array.isArray(toolSource?.tools)) {
874
+ let toolsChanged = false;
875
+ const sanitizedTools = toolSource.tools.map(
876
+ (tool: any) => {
877
+ if (!tool || typeof tool !== "object") return tool;
878
+
879
+ let result = tool;
880
+
881
+ // Strip top-level non-standard fields from tool object
882
+ if (
883
+ "custom" in result ||
884
+ "eager_input_streaming" in result
885
+ ) {
886
+ toolsChanged = true;
887
+ const {
888
+ custom: _custom,
889
+ eager_input_streaming: _eis,
890
+ ...clean
891
+ } = result;
892
+ result = clean;
893
+ }
894
+
895
+ // Also check nested function object (Chat Completions format)
896
+ if (
897
+ result.function &&
898
+ typeof result.function === "object" &&
899
+ ("custom" in result.function ||
900
+ "eager_input_streaming" in result.function)
901
+ ) {
902
+ toolsChanged = true;
903
+ const {
904
+ custom: _c,
905
+ eager_input_streaming: _e,
906
+ ...cleanFn
907
+ } = result.function;
908
+ result = { ...result, function: cleanFn };
909
+ }
910
+
911
+ return result;
912
+ },
913
+ );
914
+
915
+ if (toolsChanged) {
916
+ modifiedBody = {
917
+ ...(modifiedBody || body),
918
+ tools: sanitizedTools,
919
+ };
920
+ log(
921
+ "debug",
922
+ "Stripped non-standard fields (custom/eager_input_streaming) from tool definitions",
923
+ );
924
+ }
925
+ }
865
926
  } catch {}
866
927
 
867
928
  const headers: Record<string, string> = {
@@ -1053,7 +1114,7 @@ export const CopilotAuthPlugin: Plugin = async ({ client: sdk }) => {
1053
1114
  headers: {
1054
1115
  Accept: "application/json",
1055
1116
  "Content-Type": "application/json",
1056
- "User-Agent": "GitHubCopilotChat/0.35.0",
1117
+ "User-Agent": "opencode/1.3.17",
1057
1118
  },
1058
1119
  body: JSON.stringify({
1059
1120
  client_id: CLIENT_ID,
@@ -1078,7 +1139,7 @@ export const CopilotAuthPlugin: Plugin = async ({ client: sdk }) => {
1078
1139
  headers: {
1079
1140
  Accept: "application/json",
1080
1141
  "Content-Type": "application/json",
1081
- "User-Agent": "GitHubCopilotChat/0.35.0",
1142
+ "User-Agent": "opencode/1.3.17",
1082
1143
  },
1083
1144
  body: JSON.stringify({
1084
1145
  client_id: CLIENT_ID,
@@ -1162,6 +1223,10 @@ export const CopilotAuthPlugin: Plugin = async ({ client: sdk }) => {
1162
1223
  output.headers["anthropic-beta"] = "interleaved-thinking-2025-05-14";
1163
1224
  }
1164
1225
 
1226
+ // When using the local Copilot SDK, let the SDK/message pipeline decide
1227
+ // initiator semantics. Overriding x-initiator here can break Responses routing.
1228
+ if (input.model?.api?.npm === localCopilotSdk) return;
1229
+
1165
1230
  // Mark subagent sessions as agent-initiated (matching standard Copilot tools)
1166
1231
  try {
1167
1232
  const session = await sdk.session
@@ -1179,5 +1244,14 @@ export const CopilotAuthPlugin: Plugin = async ({ client: sdk }) => {
1179
1244
  // Ignore errors from session lookup
1180
1245
  }
1181
1246
  },
1247
+ // Hook to strip maxOutputTokens for GPT models (matches upstream Copilot CLI behavior)
1248
+ "chat.params": async (input: any, output: any) => {
1249
+ if (!input.model?.providerID?.includes("github-copilot")) return;
1250
+
1251
+ // GPT models don't support maxOutputTokens through the Copilot proxy
1252
+ if (input.model?.api?.id?.includes("gpt")) {
1253
+ output.maxOutputTokens = undefined;
1254
+ }
1255
+ },
1182
1256
  };
1183
1257
  };
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Prompt Leverage Plugin
3
+ *
4
+ * Automatically upgrades every user prompt with the seven-block framework before the AI processes it.
5
+ * Uses chat.message hook — safely inspects input structure before accessing properties.
6
+ */
7
+
8
+ import type { Plugin } from "@opencode-ai/plugin";
9
+
10
+ const TASK_KEYWORDS: Record<string, string[]> = {
11
+ coding: [
12
+ "code",
13
+ "bug",
14
+ "repo",
15
+ "refactor",
16
+ "test",
17
+ "implement",
18
+ "fix",
19
+ "function",
20
+ "api",
21
+ "add",
22
+ "update",
23
+ "remove",
24
+ ],
25
+ research: [
26
+ "research",
27
+ "compare",
28
+ "find",
29
+ "latest",
30
+ "sources",
31
+ "analyze",
32
+ "look up",
33
+ ],
34
+ writing: [
35
+ "write",
36
+ "rewrite",
37
+ "draft",
38
+ "email",
39
+ "memo",
40
+ "blog",
41
+ "copy",
42
+ "tone",
43
+ ],
44
+ review: ["review", "audit", "critique", "inspect", "evaluate", "assess"],
45
+ planning: ["plan", "roadmap", "strategy", "framework", "outline"],
46
+ analysis: ["analyze", "explain", "break down", "diagnose", "root cause"],
47
+ };
48
+
49
+ function detectTask(prompt: string): string {
50
+ const lowered = prompt.toLowerCase();
51
+ const scores: Record<string, number> = {};
52
+ for (const [task, keywords] of Object.entries(TASK_KEYWORDS)) {
53
+ scores[task] = keywords.filter((k) => lowered.includes(k)).length;
54
+ }
55
+ const best = Object.entries(scores).sort((a, b) => b[1] - a[1]);
56
+ return best[0]?.[1] ? best[0][0] : "analysis";
57
+ }
58
+
59
+ function inferIntensity(prompt: string, task: string): string {
60
+ const lowered = prompt.toLowerCase();
61
+ if (
62
+ ["careful", "deep", "thorough", "critical", "production"].some((t) =>
63
+ lowered.includes(t),
64
+ )
65
+ ) {
66
+ return "Deep";
67
+ }
68
+ if (task === "coding" || task === "research" || task === "review") {
69
+ return "Standard";
70
+ }
71
+ return "Light";
72
+ }
73
+
74
+ function buildToolRules(task: string): string {
75
+ const rules: Record<string, string> = {
76
+ coding:
77
+ "Inspect the relevant files and dependencies first. Validate the final change with the narrowest useful checks before broadening scope.",
78
+ research:
79
+ "Retrieve evidence from reliable sources before concluding. Do not guess facts that can be checked.",
80
+ review:
81
+ "Read enough surrounding context to understand intent before critiquing. Distinguish confirmed issues from plausible risks.",
82
+ };
83
+ return (
84
+ rules[task] ||
85
+ "Use tools or extra context only when they materially improve correctness or completeness."
86
+ );
87
+ }
88
+
89
+ function buildOutputContract(task: string): string {
90
+ const contracts: Record<string, string> = {
91
+ coding:
92
+ "Return the result in a practical execution format: concise summary, concrete changes or code, validation notes, and any remaining risks.",
93
+ research:
94
+ "Return a structured synthesis with key findings, supporting evidence, uncertainty where relevant, and a concise bottom line.",
95
+ writing:
96
+ "Return polished final copy in the requested tone and format. If useful, include a short rationale for major editorial choices.",
97
+ review:
98
+ "Return findings grouped by severity or importance, explain why each matters, and suggest the smallest credible next step.",
99
+ };
100
+ return (
101
+ contracts[task] ||
102
+ "Return a clear, well-structured response matched to the task, with no unnecessary verbosity."
103
+ );
104
+ }
105
+
106
+ function upgradePrompt(userPrompt: string): string {
107
+ const normalized = userPrompt.trim().replace(/\s+/g, " ");
108
+ const task = detectTask(normalized);
109
+ const intensity = inferIntensity(normalized, task);
110
+ const toolRules = buildToolRules(task);
111
+ const outputContract = buildOutputContract(task);
112
+
113
+ if (normalized.includes("Objective:") && normalized.includes("Context:")) {
114
+ return userPrompt;
115
+ }
116
+ if (normalized.length < 15 || normalized.split(" ").length < 3) {
117
+ return userPrompt;
118
+ }
119
+
120
+ return `Objective:
121
+ - Complete this task: ${normalized}
122
+ - Optimize for a correct, useful result rather than a merely plausible one.
123
+
124
+ Context:
125
+ - Preserve the user's original intent and constraints.
126
+ - Surface any key assumptions if required information is missing.
127
+
128
+ Work Style:
129
+ - Task type: ${task}
130
+ - Effort level: ${intensity}
131
+ - Understand the problem broadly enough to avoid narrow mistakes, then go deep where the risk or complexity is highest.
132
+ - Use first-principles reasoning before proposing changes.
133
+ - For non-trivial work, review the result once with fresh eyes before finalizing.
134
+
135
+ Tool Rules:
136
+ - ${toolRules}
137
+
138
+ Output Contract:
139
+ - ${outputContract}
140
+
141
+ Verification:
142
+ - Check correctness, completeness, and edge cases.
143
+ - Improve obvious weaknesses if a better approach is available within scope.
144
+
145
+ Done Criteria:
146
+ - Stop only when the response satisfies the task, matches the requested format, and passes the verification step.`;
147
+ }
148
+
149
+ export const PromptLeverage: Plugin = async ({ client }) => {
150
+ const showToast = async (message: string) => {
151
+ try {
152
+ await client.tui.showToast({
153
+ body: {
154
+ title: "PromptLeverage",
155
+ message,
156
+ variant: "info",
157
+ duration: 3000,
158
+ },
159
+ });
160
+ } catch {
161
+ /* Toast API unavailable */
162
+ }
163
+ };
164
+
165
+ return {
166
+ "experimental.chat.messages.transform": async (input: any, output: any) => {
167
+ try {
168
+ const msgs = output.messages || input.messages || [];
169
+
170
+ // Find the last message with parts (user message)
171
+ for (let i = msgs.length - 1; i >= 0; i--) {
172
+ const msg = msgs[i];
173
+ if (msg?.parts) {
174
+ for (const part of msg.parts) {
175
+ if (part.type === "text" && part.text) {
176
+ const upgraded = upgradePrompt(part.text);
177
+ if (upgraded !== part.text) {
178
+ part.text = upgraded;
179
+ showToast("Upgraded prompt!");
180
+ }
181
+ }
182
+ }
183
+ break; // Only upgrade the most recent message
184
+ }
185
+ }
186
+ } catch (e: any) {
187
+ showToast(`Error: ${e.message}`);
188
+ }
189
+ },
190
+ };
191
+ };
@@ -1,10 +1,11 @@
1
1
  import type { LanguageModelV2 } from "@ai-sdk/provider";
2
2
  import {
3
3
  type FetchFunction,
4
- withUserAgentSuffix,
5
4
  withoutTrailingSlash,
5
+ withUserAgentSuffix,
6
6
  } from "@ai-sdk/provider-utils";
7
- import { OpenAICompatibleChatLanguageModel } from "./chat/openai-compatible-chat-language-model";
7
+ import { OpenAICompatibleChatLanguageModel } from "./chat/openai-compatible-chat-language-model.js";
8
+ import { OpenAIResponsesLanguageModel } from "./responses/openai-responses-language-model.js";
8
9
 
9
10
  // Import the version or define it
10
11
  const VERSION = "0.1.0";
@@ -41,6 +42,7 @@ export interface OpenaiCompatibleProviderSettings {
41
42
  export interface OpenaiCompatibleProvider {
42
43
  (modelId: OpenaiCompatibleModelId): LanguageModelV2;
43
44
  chat(modelId: OpenaiCompatibleModelId): LanguageModelV2;
45
+ responses(modelId: OpenaiCompatibleModelId): LanguageModelV2;
44
46
  languageModel(modelId: OpenaiCompatibleModelId): LanguageModelV2;
45
47
  }
46
48
 
@@ -78,6 +80,15 @@ export function createOpenaiCompatible(
78
80
  });
79
81
  };
80
82
 
83
+ const createResponsesModel = (modelId: OpenaiCompatibleModelId) => {
84
+ return new OpenAIResponsesLanguageModel(modelId, {
85
+ provider: `${options.name ?? "openai-compatible"}.responses`,
86
+ headers: getHeaders,
87
+ url: ({ path, modelId: _modelId }) => `${baseURL}${path}`,
88
+ fetch: options.fetch,
89
+ });
90
+ };
91
+
81
92
  const createLanguageModel = (modelId: OpenaiCompatibleModelId) =>
82
93
  createChatModel(modelId);
83
94
 
@@ -86,6 +97,7 @@ export function createOpenaiCompatible(
86
97
 
87
98
  provider.languageModel = createLanguageModel;
88
99
  provider.chat = createChatModel;
100
+ provider.responses = createResponsesModel;
89
101
 
90
102
  return provider as OpenaiCompatibleProvider;
91
103
  }
@@ -1,5 +1,5 @@
1
- export { createOpenaiCompatible, openaiCompatible } from "./copilot-provider";
1
+ export { createOpenaiCompatible, openaiCompatible } from "./copilot-provider.js";
2
2
  export type {
3
3
  OpenaiCompatibleProvider,
4
4
  OpenaiCompatibleProviderSettings,
5
- } from "./copilot-provider";
5
+ } from "./copilot-provider.js";