@codeany/open-agent-sdk 0.1.0 → 0.2.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 (254) hide show
  1. package/README.md +144 -31
  2. package/dist/agent.d.ts +113 -0
  3. package/dist/agent.d.ts.map +1 -0
  4. package/dist/agent.js +446 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/engine.d.ts +62 -0
  7. package/dist/engine.d.ts.map +1 -0
  8. package/dist/engine.js +494 -0
  9. package/dist/engine.js.map +1 -0
  10. package/dist/hooks.d.ts +111 -0
  11. package/dist/hooks.d.ts.map +1 -0
  12. package/dist/hooks.js +179 -0
  13. package/dist/hooks.js.map +1 -0
  14. package/dist/index.d.ts +66 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +133 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/mcp/client.d.ts +19 -0
  19. package/dist/mcp/client.d.ts.map +1 -0
  20. package/dist/mcp/client.js +126 -0
  21. package/dist/mcp/client.js.map +1 -0
  22. package/dist/providers/anthropic.d.ts +17 -0
  23. package/dist/providers/anthropic.d.ts.map +1 -0
  24. package/dist/providers/anthropic.js +47 -0
  25. package/dist/providers/anthropic.js.map +1 -0
  26. package/dist/providers/index.d.ts +20 -0
  27. package/dist/providers/index.d.ts.map +1 -0
  28. package/dist/providers/index.js +26 -0
  29. package/dist/providers/index.js.map +1 -0
  30. package/dist/providers/openai.d.ts +26 -0
  31. package/dist/providers/openai.d.ts.map +1 -0
  32. package/dist/providers/openai.js +212 -0
  33. package/dist/providers/openai.js.map +1 -0
  34. package/dist/providers/types.d.ts +84 -0
  35. package/dist/providers/types.d.ts.map +1 -0
  36. package/dist/providers/types.js +11 -0
  37. package/dist/providers/types.js.map +1 -0
  38. package/dist/sdk-mcp-server.d.ts +52 -0
  39. package/dist/sdk-mcp-server.d.ts.map +1 -0
  40. package/dist/sdk-mcp-server.js +57 -0
  41. package/dist/sdk-mcp-server.js.map +1 -0
  42. package/dist/session.d.ts +73 -0
  43. package/dist/session.d.ts.map +1 -0
  44. package/dist/session.js +159 -0
  45. package/dist/session.js.map +1 -0
  46. package/dist/skills/bundled/commit.d.ts +7 -0
  47. package/dist/skills/bundled/commit.d.ts.map +1 -0
  48. package/dist/skills/bundled/commit.js +35 -0
  49. package/dist/skills/bundled/commit.js.map +1 -0
  50. package/dist/skills/bundled/debug.d.ts +7 -0
  51. package/dist/skills/bundled/debug.d.ts.map +1 -0
  52. package/dist/skills/bundled/debug.js +46 -0
  53. package/dist/skills/bundled/debug.js.map +1 -0
  54. package/dist/skills/bundled/index.d.ts +11 -0
  55. package/dist/skills/bundled/index.d.ts.map +1 -0
  56. package/dist/skills/bundled/index.js +26 -0
  57. package/dist/skills/bundled/index.js.map +1 -0
  58. package/dist/skills/bundled/review.d.ts +7 -0
  59. package/dist/skills/bundled/review.d.ts.map +1 -0
  60. package/dist/skills/bundled/review.js +38 -0
  61. package/dist/skills/bundled/review.js.map +1 -0
  62. package/dist/skills/bundled/simplify.d.ts +8 -0
  63. package/dist/skills/bundled/simplify.d.ts.map +1 -0
  64. package/dist/skills/bundled/simplify.js +48 -0
  65. package/dist/skills/bundled/simplify.js.map +1 -0
  66. package/dist/skills/bundled/test.d.ts +7 -0
  67. package/dist/skills/bundled/test.d.ts.map +1 -0
  68. package/dist/skills/bundled/test.js +40 -0
  69. package/dist/skills/bundled/test.js.map +1 -0
  70. package/dist/skills/index.d.ts +7 -0
  71. package/dist/skills/index.d.ts.map +1 -0
  72. package/dist/skills/index.js +8 -0
  73. package/dist/skills/index.js.map +1 -0
  74. package/dist/skills/registry.d.ts +43 -0
  75. package/dist/skills/registry.d.ts.map +1 -0
  76. package/dist/skills/registry.js +111 -0
  77. package/dist/skills/registry.js.map +1 -0
  78. package/dist/skills/types.d.ts +83 -0
  79. package/dist/skills/types.d.ts.map +1 -0
  80. package/dist/skills/types.js +8 -0
  81. package/dist/skills/types.js.map +1 -0
  82. package/dist/tool-helper.d.ts +73 -0
  83. package/dist/tool-helper.d.ts.map +1 -0
  84. package/dist/tool-helper.js +86 -0
  85. package/dist/tool-helper.js.map +1 -0
  86. package/dist/tools/agent-tool.d.ts +17 -0
  87. package/dist/tools/agent-tool.d.ts.map +1 -0
  88. package/dist/tools/agent-tool.js +146 -0
  89. package/dist/tools/agent-tool.js.map +1 -0
  90. package/dist/tools/ask-user.d.ts +18 -0
  91. package/dist/tools/ask-user.d.ts.map +1 -0
  92. package/dist/tools/ask-user.js +72 -0
  93. package/dist/tools/ask-user.js.map +1 -0
  94. package/dist/tools/bash.d.ts +5 -0
  95. package/dist/tools/bash.d.ts.map +1 -0
  96. package/dist/tools/bash.js +67 -0
  97. package/dist/tools/bash.js.map +1 -0
  98. package/dist/tools/config-tool.d.ts +20 -0
  99. package/dist/tools/config-tool.d.ts.map +1 -0
  100. package/dist/tools/config-tool.js +83 -0
  101. package/dist/tools/config-tool.js.map +1 -0
  102. package/dist/tools/cron-tools.d.ts +33 -0
  103. package/dist/tools/cron-tools.d.ts.map +1 -0
  104. package/dist/tools/cron-tools.js +128 -0
  105. package/dist/tools/cron-tools.js.map +1 -0
  106. package/dist/tools/edit.d.ts +5 -0
  107. package/dist/tools/edit.d.ts.map +1 -0
  108. package/dist/tools/edit.js +70 -0
  109. package/dist/tools/edit.js.map +1 -0
  110. package/dist/tools/glob.d.ts +5 -0
  111. package/dist/tools/glob.d.ts.map +1 -0
  112. package/dist/tools/glob.js +75 -0
  113. package/dist/tools/glob.js.map +1 -0
  114. package/dist/tools/grep.d.ts +5 -0
  115. package/dist/tools/grep.d.ts.map +1 -0
  116. package/dist/tools/grep.js +168 -0
  117. package/dist/tools/grep.js.map +1 -0
  118. package/dist/tools/index.d.ts +45 -0
  119. package/dist/tools/index.d.ts.map +1 -0
  120. package/dist/tools/index.js +162 -0
  121. package/dist/tools/index.js.map +1 -0
  122. package/dist/tools/lsp-tool.d.ts +9 -0
  123. package/dist/tools/lsp-tool.d.ts.map +1 -0
  124. package/dist/tools/lsp-tool.js +137 -0
  125. package/dist/tools/lsp-tool.js.map +1 -0
  126. package/dist/tools/mcp-resource-tools.d.ts +14 -0
  127. package/dist/tools/mcp-resource-tools.d.ts.map +1 -0
  128. package/dist/tools/mcp-resource-tools.js +117 -0
  129. package/dist/tools/mcp-resource-tools.js.map +1 -0
  130. package/dist/tools/notebook-edit.d.ts +5 -0
  131. package/dist/tools/notebook-edit.d.ts.map +1 -0
  132. package/dist/tools/notebook-edit.js +85 -0
  133. package/dist/tools/notebook-edit.js.map +1 -0
  134. package/dist/tools/plan-tools.d.ts +12 -0
  135. package/dist/tools/plan-tools.d.ts.map +1 -0
  136. package/dist/tools/plan-tools.js +77 -0
  137. package/dist/tools/plan-tools.js.map +1 -0
  138. package/dist/tools/read.d.ts +5 -0
  139. package/dist/tools/read.d.ts.map +1 -0
  140. package/dist/tools/read.js +66 -0
  141. package/dist/tools/read.js.map +1 -0
  142. package/dist/tools/send-message.d.ts +31 -0
  143. package/dist/tools/send-message.d.ts.map +1 -0
  144. package/dist/tools/send-message.js +77 -0
  145. package/dist/tools/send-message.js.map +1 -0
  146. package/dist/tools/skill-tool.d.ts +9 -0
  147. package/dist/tools/skill-tool.d.ts.map +1 -0
  148. package/dist/tools/skill-tool.js +115 -0
  149. package/dist/tools/skill-tool.js.map +1 -0
  150. package/dist/tools/task-tools.d.ts +48 -0
  151. package/dist/tools/task-tools.d.ts.map +1 -0
  152. package/dist/tools/task-tools.js +242 -0
  153. package/dist/tools/task-tools.js.map +1 -0
  154. package/dist/tools/team-tools.d.ts +34 -0
  155. package/dist/tools/team-tools.d.ts.map +1 -0
  156. package/dist/tools/team-tools.js +103 -0
  157. package/dist/tools/team-tools.js.map +1 -0
  158. package/dist/tools/todo-tool.d.ts +22 -0
  159. package/dist/tools/todo-tool.d.ts.map +1 -0
  160. package/dist/tools/todo-tool.js +93 -0
  161. package/dist/tools/todo-tool.js.map +1 -0
  162. package/dist/tools/tool-search.d.ts +13 -0
  163. package/dist/tools/tool-search.d.ts.map +1 -0
  164. package/dist/tools/tool-search.js +76 -0
  165. package/dist/tools/tool-search.js.map +1 -0
  166. package/dist/tools/types.d.ts +29 -0
  167. package/dist/tools/types.d.ts.map +1 -0
  168. package/dist/tools/types.js +52 -0
  169. package/dist/tools/types.js.map +1 -0
  170. package/dist/tools/web-fetch.d.ts +5 -0
  171. package/dist/tools/web-fetch.d.ts.map +1 -0
  172. package/dist/tools/web-fetch.js +60 -0
  173. package/dist/tools/web-fetch.js.map +1 -0
  174. package/dist/tools/web-search.d.ts +5 -0
  175. package/dist/tools/web-search.d.ts.map +1 -0
  176. package/dist/tools/web-search.js +77 -0
  177. package/dist/tools/web-search.js.map +1 -0
  178. package/dist/tools/worktree-tools.d.ts +10 -0
  179. package/dist/tools/worktree-tools.d.ts.map +1 -0
  180. package/dist/tools/worktree-tools.js +130 -0
  181. package/dist/tools/worktree-tools.js.map +1 -0
  182. package/dist/tools/write.d.ts +5 -0
  183. package/dist/tools/write.d.ts.map +1 -0
  184. package/dist/tools/write.js +40 -0
  185. package/dist/tools/write.js.map +1 -0
  186. package/dist/types.d.ts +423 -0
  187. package/dist/types.d.ts.map +1 -0
  188. package/dist/types.js +5 -0
  189. package/dist/types.js.map +1 -0
  190. package/dist/utils/compact.d.ts +44 -0
  191. package/dist/utils/compact.d.ts.map +1 -0
  192. package/dist/utils/compact.js +162 -0
  193. package/dist/utils/compact.js.map +1 -0
  194. package/dist/utils/context.d.ts +35 -0
  195. package/dist/utils/context.d.ts.map +1 -0
  196. package/dist/utils/context.js +174 -0
  197. package/dist/utils/context.js.map +1 -0
  198. package/dist/utils/fileCache.d.ts +64 -0
  199. package/dist/utils/fileCache.d.ts.map +1 -0
  200. package/dist/utils/fileCache.js +117 -0
  201. package/dist/utils/fileCache.js.map +1 -0
  202. package/dist/utils/messages.d.ts +57 -0
  203. package/dist/utils/messages.d.ts.map +1 -0
  204. package/dist/utils/messages.js +152 -0
  205. package/dist/utils/messages.js.map +1 -0
  206. package/dist/utils/retry.d.ts +48 -0
  207. package/dist/utils/retry.d.ts.map +1 -0
  208. package/dist/utils/retry.js +111 -0
  209. package/dist/utils/retry.js.map +1 -0
  210. package/dist/utils/tokens.d.ts +57 -0
  211. package/dist/utils/tokens.d.ts.map +1 -0
  212. package/dist/utils/tokens.js +134 -0
  213. package/dist/utils/tokens.js.map +1 -0
  214. package/package.json +7 -1
  215. package/src/agent.ts +106 -15
  216. package/src/engine.ts +169 -59
  217. package/src/index.ts +51 -1
  218. package/src/providers/anthropic.ts +60 -0
  219. package/src/providers/index.ts +34 -0
  220. package/src/providers/openai.ts +315 -0
  221. package/src/providers/types.ts +85 -0
  222. package/src/session.ts +5 -5
  223. package/src/skills/bundled/commit.ts +38 -0
  224. package/src/skills/bundled/debug.ts +48 -0
  225. package/src/skills/bundled/index.ts +28 -0
  226. package/src/skills/bundled/review.ts +41 -0
  227. package/src/skills/bundled/simplify.ts +51 -0
  228. package/src/skills/bundled/test.ts +43 -0
  229. package/src/skills/index.ts +25 -0
  230. package/src/skills/registry.ts +133 -0
  231. package/src/skills/types.ts +99 -0
  232. package/src/tools/agent-tool.ts +13 -2
  233. package/src/tools/index.ts +8 -0
  234. package/src/tools/skill-tool.ts +133 -0
  235. package/src/tools/types.ts +7 -3
  236. package/src/types.ts +35 -8
  237. package/src/utils/compact.ts +18 -17
  238. package/src/utils/messages.ts +12 -13
  239. package/src/utils/tokens.ts +29 -6
  240. package/.env.example +0 -8
  241. package/examples/01-simple-query.ts +0 -43
  242. package/examples/02-multi-tool.ts +0 -44
  243. package/examples/03-multi-turn.ts +0 -39
  244. package/examples/04-prompt-api.ts +0 -29
  245. package/examples/05-custom-system-prompt.ts +0 -26
  246. package/examples/06-mcp-server.ts +0 -49
  247. package/examples/07-custom-tools.ts +0 -87
  248. package/examples/08-official-api-compat.ts +0 -38
  249. package/examples/09-subagents.ts +0 -48
  250. package/examples/10-permissions.ts +0 -40
  251. package/examples/11-custom-mcp-tools.ts +0 -101
  252. package/examples/web/index.html +0 -365
  253. package/examples/web/server.ts +0 -157
  254. package/tsconfig.json +0 -19
@@ -0,0 +1,315 @@
1
+ /**
2
+ * OpenAI Chat Completions API Provider
3
+ *
4
+ * Converts between the SDK's internal Anthropic-like message format
5
+ * and OpenAI's Chat Completions API format.
6
+ *
7
+ * Uses native fetch (no openai SDK dependency required).
8
+ */
9
+
10
+ import type {
11
+ LLMProvider,
12
+ CreateMessageParams,
13
+ CreateMessageResponse,
14
+ NormalizedMessageParam,
15
+ NormalizedContentBlock,
16
+ NormalizedTool,
17
+ NormalizedResponseBlock,
18
+ } from './types.js'
19
+
20
+ // --------------------------------------------------------------------------
21
+ // OpenAI-specific types (minimal, just what we need)
22
+ // --------------------------------------------------------------------------
23
+
24
+ interface OpenAIChatMessage {
25
+ role: 'system' | 'user' | 'assistant' | 'tool'
26
+ content?: string | null
27
+ tool_calls?: OpenAIToolCall[]
28
+ tool_call_id?: string
29
+ }
30
+
31
+ interface OpenAIToolCall {
32
+ id: string
33
+ type: 'function'
34
+ function: {
35
+ name: string
36
+ arguments: string
37
+ }
38
+ }
39
+
40
+ interface OpenAITool {
41
+ type: 'function'
42
+ function: {
43
+ name: string
44
+ description: string
45
+ parameters: Record<string, any>
46
+ }
47
+ }
48
+
49
+ interface OpenAIChatResponse {
50
+ id: string
51
+ choices: Array<{
52
+ index: number
53
+ message: {
54
+ role: 'assistant'
55
+ content: string | null
56
+ tool_calls?: OpenAIToolCall[]
57
+ }
58
+ finish_reason: 'stop' | 'length' | 'tool_calls' | 'content_filter' | string
59
+ }>
60
+ usage?: {
61
+ prompt_tokens: number
62
+ completion_tokens: number
63
+ total_tokens: number
64
+ }
65
+ }
66
+
67
+ // --------------------------------------------------------------------------
68
+ // Provider
69
+ // --------------------------------------------------------------------------
70
+
71
+ export class OpenAIProvider implements LLMProvider {
72
+ readonly apiType = 'openai-completions' as const
73
+ private apiKey: string
74
+ private baseURL: string
75
+
76
+ constructor(opts: { apiKey?: string; baseURL?: string }) {
77
+ this.apiKey = opts.apiKey || ''
78
+ this.baseURL = (opts.baseURL || 'https://api.openai.com/v1').replace(/\/$/, '')
79
+ }
80
+
81
+ async createMessage(params: CreateMessageParams): Promise<CreateMessageResponse> {
82
+ // Convert to OpenAI format
83
+ const messages = this.convertMessages(params.system, params.messages)
84
+ const tools = params.tools ? this.convertTools(params.tools) : undefined
85
+
86
+ const body: Record<string, any> = {
87
+ model: params.model,
88
+ max_tokens: params.maxTokens,
89
+ messages,
90
+ }
91
+
92
+ if (tools && tools.length > 0) {
93
+ body.tools = tools
94
+ }
95
+
96
+ // Make API call
97
+ const response = await fetch(`${this.baseURL}/chat/completions`, {
98
+ method: 'POST',
99
+ headers: {
100
+ 'Content-Type': 'application/json',
101
+ Authorization: `Bearer ${this.apiKey}`,
102
+ },
103
+ body: JSON.stringify(body),
104
+ })
105
+
106
+ if (!response.ok) {
107
+ const errBody = await response.text().catch(() => '')
108
+ const err: any = new Error(
109
+ `OpenAI API error: ${response.status} ${response.statusText}: ${errBody}`,
110
+ )
111
+ err.status = response.status
112
+ throw err
113
+ }
114
+
115
+ const data = (await response.json()) as OpenAIChatResponse
116
+
117
+ // Convert response back to normalized format
118
+ return this.convertResponse(data)
119
+ }
120
+
121
+ // --------------------------------------------------------------------------
122
+ // Message Conversion: Internal → OpenAI
123
+ // --------------------------------------------------------------------------
124
+
125
+ private convertMessages(
126
+ system: string,
127
+ messages: NormalizedMessageParam[],
128
+ ): OpenAIChatMessage[] {
129
+ const result: OpenAIChatMessage[] = []
130
+
131
+ // System prompt as first message
132
+ if (system) {
133
+ result.push({ role: 'system', content: system })
134
+ }
135
+
136
+ for (const msg of messages) {
137
+ if (msg.role === 'user') {
138
+ this.convertUserMessage(msg, result)
139
+ } else if (msg.role === 'assistant') {
140
+ this.convertAssistantMessage(msg, result)
141
+ }
142
+ }
143
+
144
+ return result
145
+ }
146
+
147
+ private convertUserMessage(
148
+ msg: NormalizedMessageParam,
149
+ result: OpenAIChatMessage[],
150
+ ): void {
151
+ if (typeof msg.content === 'string') {
152
+ result.push({ role: 'user', content: msg.content })
153
+ return
154
+ }
155
+
156
+ // Content blocks may contain text and/or tool_result blocks
157
+ const textParts: string[] = []
158
+ const toolResults: Array<{ tool_use_id: string; content: string }> = []
159
+
160
+ for (const block of msg.content) {
161
+ if (block.type === 'text') {
162
+ textParts.push(block.text)
163
+ } else if (block.type === 'tool_result') {
164
+ toolResults.push({
165
+ tool_use_id: block.tool_use_id,
166
+ content: block.content,
167
+ })
168
+ }
169
+ }
170
+
171
+ // Tool results become separate tool messages
172
+ for (const tr of toolResults) {
173
+ result.push({
174
+ role: 'tool',
175
+ tool_call_id: tr.tool_use_id,
176
+ content: tr.content,
177
+ })
178
+ }
179
+
180
+ // Text parts become a user message
181
+ if (textParts.length > 0) {
182
+ result.push({ role: 'user', content: textParts.join('\n') })
183
+ }
184
+ }
185
+
186
+ private convertAssistantMessage(
187
+ msg: NormalizedMessageParam,
188
+ result: OpenAIChatMessage[],
189
+ ): void {
190
+ if (typeof msg.content === 'string') {
191
+ result.push({ role: 'assistant', content: msg.content })
192
+ return
193
+ }
194
+
195
+ // Extract text and tool_use blocks
196
+ const textParts: string[] = []
197
+ const toolCalls: OpenAIToolCall[] = []
198
+
199
+ for (const block of msg.content) {
200
+ if (block.type === 'text') {
201
+ textParts.push(block.text)
202
+ } else if (block.type === 'tool_use') {
203
+ toolCalls.push({
204
+ id: block.id,
205
+ type: 'function',
206
+ function: {
207
+ name: block.name,
208
+ arguments: typeof block.input === 'string'
209
+ ? block.input
210
+ : JSON.stringify(block.input),
211
+ },
212
+ })
213
+ }
214
+ }
215
+
216
+ const assistantMsg: OpenAIChatMessage = {
217
+ role: 'assistant',
218
+ content: textParts.length > 0 ? textParts.join('\n') : null,
219
+ }
220
+
221
+ if (toolCalls.length > 0) {
222
+ assistantMsg.tool_calls = toolCalls
223
+ }
224
+
225
+ result.push(assistantMsg)
226
+ }
227
+
228
+ // --------------------------------------------------------------------------
229
+ // Tool Conversion: Internal → OpenAI
230
+ // --------------------------------------------------------------------------
231
+
232
+ private convertTools(tools: NormalizedTool[]): OpenAITool[] {
233
+ return tools.map((t) => ({
234
+ type: 'function' as const,
235
+ function: {
236
+ name: t.name,
237
+ description: t.description,
238
+ parameters: t.input_schema,
239
+ },
240
+ }))
241
+ }
242
+
243
+ // --------------------------------------------------------------------------
244
+ // Response Conversion: OpenAI → Internal
245
+ // --------------------------------------------------------------------------
246
+
247
+ private convertResponse(data: OpenAIChatResponse): CreateMessageResponse {
248
+ const choice = data.choices[0]
249
+ if (!choice) {
250
+ return {
251
+ content: [{ type: 'text', text: '' }],
252
+ stopReason: 'end_turn',
253
+ usage: { input_tokens: 0, output_tokens: 0 },
254
+ }
255
+ }
256
+
257
+ const content: NormalizedResponseBlock[] = []
258
+
259
+ // Add text content
260
+ if (choice.message.content) {
261
+ content.push({ type: 'text', text: choice.message.content })
262
+ }
263
+
264
+ // Add tool calls
265
+ if (choice.message.tool_calls) {
266
+ for (const tc of choice.message.tool_calls) {
267
+ let input: any
268
+ try {
269
+ input = JSON.parse(tc.function.arguments)
270
+ } catch {
271
+ input = tc.function.arguments
272
+ }
273
+
274
+ content.push({
275
+ type: 'tool_use',
276
+ id: tc.id,
277
+ name: tc.function.name,
278
+ input,
279
+ })
280
+ }
281
+ }
282
+
283
+ // If no content at all, add empty text
284
+ if (content.length === 0) {
285
+ content.push({ type: 'text', text: '' })
286
+ }
287
+
288
+ // Map finish_reason to our normalized stop reasons
289
+ const stopReason = this.mapFinishReason(choice.finish_reason)
290
+
291
+ return {
292
+ content,
293
+ stopReason,
294
+ usage: {
295
+ input_tokens: data.usage?.prompt_tokens || 0,
296
+ output_tokens: data.usage?.completion_tokens || 0,
297
+ },
298
+ }
299
+ }
300
+
301
+ private mapFinishReason(
302
+ reason: string,
303
+ ): 'end_turn' | 'max_tokens' | 'tool_use' | string {
304
+ switch (reason) {
305
+ case 'stop':
306
+ return 'end_turn'
307
+ case 'length':
308
+ return 'max_tokens'
309
+ case 'tool_calls':
310
+ return 'tool_use'
311
+ default:
312
+ return reason
313
+ }
314
+ }
315
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * LLM Provider Abstraction Types
3
+ *
4
+ * Defines a provider interface that normalizes API differences between
5
+ * Anthropic Messages API and OpenAI Chat Completions API.
6
+ *
7
+ * Internally the SDK uses Anthropic-like message format as the canonical
8
+ * representation. Providers convert to/from their native API format.
9
+ */
10
+
11
+ // --------------------------------------------------------------------------
12
+ // API Type
13
+ // --------------------------------------------------------------------------
14
+
15
+ export type ApiType = 'anthropic-messages' | 'openai-completions'
16
+
17
+ // --------------------------------------------------------------------------
18
+ // Normalized Request
19
+ // --------------------------------------------------------------------------
20
+
21
+ export interface CreateMessageParams {
22
+ model: string
23
+ maxTokens: number
24
+ system: string
25
+ messages: NormalizedMessageParam[]
26
+ tools?: NormalizedTool[]
27
+ thinking?: { type: string; budget_tokens?: number }
28
+ }
29
+
30
+ /**
31
+ * Normalized message format (Anthropic-like).
32
+ * This is the internal representation used throughout the SDK.
33
+ */
34
+ export interface NormalizedMessageParam {
35
+ role: 'user' | 'assistant'
36
+ content: string | NormalizedContentBlock[]
37
+ }
38
+
39
+ export type NormalizedContentBlock =
40
+ | { type: 'text'; text: string }
41
+ | { type: 'tool_use'; id: string; name: string; input: any }
42
+ | { type: 'tool_result'; tool_use_id: string; content: string; is_error?: boolean }
43
+ | { type: 'image'; source: any }
44
+ | { type: 'thinking'; thinking: string }
45
+
46
+ export interface NormalizedTool {
47
+ name: string
48
+ description: string
49
+ input_schema: {
50
+ type: 'object'
51
+ properties: Record<string, any>
52
+ required?: string[]
53
+ }
54
+ }
55
+
56
+ // --------------------------------------------------------------------------
57
+ // Normalized Response
58
+ // --------------------------------------------------------------------------
59
+
60
+ export interface CreateMessageResponse {
61
+ content: NormalizedResponseBlock[]
62
+ stopReason: 'end_turn' | 'max_tokens' | 'tool_use' | string
63
+ usage: {
64
+ input_tokens: number
65
+ output_tokens: number
66
+ cache_creation_input_tokens?: number
67
+ cache_read_input_tokens?: number
68
+ }
69
+ }
70
+
71
+ export type NormalizedResponseBlock =
72
+ | { type: 'text'; text: string }
73
+ | { type: 'tool_use'; id: string; name: string; input: any }
74
+
75
+ // --------------------------------------------------------------------------
76
+ // Provider Interface
77
+ // --------------------------------------------------------------------------
78
+
79
+ export interface LLMProvider {
80
+ /** The API type this provider implements. */
81
+ readonly apiType: ApiType
82
+
83
+ /** Send a message and get a response. */
84
+ createMessage(params: CreateMessageParams): Promise<CreateMessageResponse>
85
+ }
package/src/session.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  import { readFile, writeFile, mkdir, readdir, stat } from 'fs/promises'
9
9
  import { join } from 'path'
10
10
  import type { Message } from './types.js'
11
- import type Anthropic from '@anthropic-ai/sdk'
11
+ import type { NormalizedMessageParam } from './providers/types.js'
12
12
 
13
13
  /**
14
14
  * Session metadata.
@@ -28,7 +28,7 @@ export interface SessionMetadata {
28
28
  */
29
29
  export interface SessionData {
30
30
  metadata: SessionMetadata
31
- messages: Anthropic.MessageParam[]
31
+ messages: NormalizedMessageParam[]
32
32
  }
33
33
 
34
34
  /**
@@ -51,7 +51,7 @@ function getSessionPath(sessionId: string): string {
51
51
  */
52
52
  export async function saveSession(
53
53
  sessionId: string,
54
- messages: Anthropic.MessageParam[],
54
+ messages: NormalizedMessageParam[],
55
55
  metadata: Partial<SessionMetadata>,
56
56
  ): Promise<void> {
57
57
  const dir = getSessionPath(sessionId)
@@ -146,7 +146,7 @@ export async function forkSession(
146
146
  */
147
147
  export async function getSessionMessages(
148
148
  sessionId: string,
149
- ): Promise<Anthropic.MessageParam[]> {
149
+ ): Promise<NormalizedMessageParam[]> {
150
150
  const data = await loadSession(sessionId)
151
151
  return data?.messages || []
152
152
  }
@@ -156,7 +156,7 @@ export async function getSessionMessages(
156
156
  */
157
157
  export async function appendToSession(
158
158
  sessionId: string,
159
- message: Anthropic.MessageParam,
159
+ message: NormalizedMessageParam,
160
160
  ): Promise<void> {
161
161
  const data = await loadSession(sessionId)
162
162
  if (!data) return
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Bundled Skill: commit
3
+ *
4
+ * Create a git commit with a well-crafted message based on staged changes.
5
+ */
6
+
7
+ import { registerSkill } from '../registry.js'
8
+ import type { SkillContentBlock } from '../types.js'
9
+
10
+ const COMMIT_PROMPT = `Create a git commit for the current changes. Follow these steps:
11
+
12
+ 1. Run \`git status\` and \`git diff --cached\` to understand what's staged
13
+ 2. If nothing is staged, run \`git diff\` to see unstaged changes and suggest what to stage
14
+ 3. Analyze the changes and draft a concise commit message that:
15
+ - Uses imperative mood ("Add feature" not "Added feature")
16
+ - Summarizes the "why" not just the "what"
17
+ - Keeps the first line under 72 characters
18
+ - Adds a body with details if the change is complex
19
+ 4. Create the commit
20
+
21
+ Do NOT push to remote unless explicitly asked.`
22
+
23
+ export function registerCommitSkill(): void {
24
+ registerSkill({
25
+ name: 'commit',
26
+ description: 'Create a git commit with a well-crafted message based on staged changes.',
27
+ aliases: ['ci'],
28
+ allowedTools: ['Bash', 'Read', 'Glob', 'Grep'],
29
+ userInvocable: true,
30
+ async getPrompt(args): Promise<SkillContentBlock[]> {
31
+ let prompt = COMMIT_PROMPT
32
+ if (args.trim()) {
33
+ prompt += `\n\nAdditional instructions: ${args}`
34
+ }
35
+ return [{ type: 'text', text: prompt }]
36
+ },
37
+ })
38
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Bundled Skill: debug
3
+ *
4
+ * Systematic debugging of an issue using structured investigation.
5
+ */
6
+
7
+ import { registerSkill } from '../registry.js'
8
+ import type { SkillContentBlock } from '../types.js'
9
+
10
+ const DEBUG_PROMPT = `Debug the described issue using a systematic approach:
11
+
12
+ 1. **Reproduce**: Understand and reproduce the issue
13
+ - Read relevant error messages or logs
14
+ - Identify the failing component
15
+
16
+ 2. **Investigate**: Trace the root cause
17
+ - Read the relevant source code
18
+ - Add logging or use debugging tools if needed
19
+ - Check recent changes that might have introduced the issue (\`git log --oneline -20\`)
20
+
21
+ 3. **Hypothesize**: Form a theory about the cause
22
+ - State your hypothesis clearly before attempting a fix
23
+
24
+ 4. **Fix**: Implement the minimal fix
25
+ - Make the smallest change that resolves the issue
26
+ - Don't refactor unrelated code
27
+
28
+ 5. **Verify**: Confirm the fix works
29
+ - Run relevant tests
30
+ - Check for regressions`
31
+
32
+ export function registerDebugSkill(): void {
33
+ registerSkill({
34
+ name: 'debug',
35
+ description: 'Systematic debugging of an issue using structured investigation.',
36
+ aliases: ['investigate', 'diagnose'],
37
+ userInvocable: true,
38
+ async getPrompt(args): Promise<SkillContentBlock[]> {
39
+ let prompt = DEBUG_PROMPT
40
+ if (args.trim()) {
41
+ prompt += `\n\n## Issue Description\n${args}`
42
+ } else {
43
+ prompt += `\n\nAsk the user to describe the issue they're experiencing.`
44
+ }
45
+ return [{ type: 'text', text: prompt }]
46
+ },
47
+ })
48
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Bundled Skills Initialization
3
+ *
4
+ * Registers all built-in skills at SDK startup.
5
+ */
6
+
7
+ import { registerSimplifySkill } from './simplify.js'
8
+ import { registerCommitSkill } from './commit.js'
9
+ import { registerReviewSkill } from './review.js'
10
+ import { registerDebugSkill } from './debug.js'
11
+ import { registerTestSkill } from './test.js'
12
+
13
+ let initialized = false
14
+
15
+ /**
16
+ * Initialize all bundled skills.
17
+ * Safe to call multiple times (idempotent).
18
+ */
19
+ export function initBundledSkills(): void {
20
+ if (initialized) return
21
+ initialized = true
22
+
23
+ registerSimplifySkill()
24
+ registerCommitSkill()
25
+ registerReviewSkill()
26
+ registerDebugSkill()
27
+ registerTestSkill()
28
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Bundled Skill: review
3
+ *
4
+ * Review code changes (PR or local diff) for issues and improvements.
5
+ */
6
+
7
+ import { registerSkill } from '../registry.js'
8
+ import type { SkillContentBlock } from '../types.js'
9
+
10
+ const REVIEW_PROMPT = `Review the current code changes for potential issues. Follow these steps:
11
+
12
+ 1. Run \`git diff\` to see uncommitted changes, or \`git diff main...HEAD\` for branch changes
13
+ 2. For each changed file, analyze:
14
+ - **Correctness**: Logic errors, edge cases, off-by-one errors
15
+ - **Security**: Injection vulnerabilities, auth issues, data exposure
16
+ - **Performance**: N+1 queries, unnecessary allocations, blocking I/O
17
+ - **Style**: Naming, consistency with surrounding code, readability
18
+ - **Testing**: Are the changes adequately tested?
19
+ 3. Provide a summary with:
20
+ - Critical issues (must fix)
21
+ - Suggestions (nice to have)
22
+ - Questions (need clarification)
23
+
24
+ Be specific: reference file names, line numbers, and suggest fixes.`
25
+
26
+ export function registerReviewSkill(): void {
27
+ registerSkill({
28
+ name: 'review',
29
+ description: 'Review code changes for correctness, security, performance, and style issues.',
30
+ aliases: ['review-pr', 'cr'],
31
+ allowedTools: ['Bash', 'Read', 'Glob', 'Grep'],
32
+ userInvocable: true,
33
+ async getPrompt(args): Promise<SkillContentBlock[]> {
34
+ let prompt = REVIEW_PROMPT
35
+ if (args.trim()) {
36
+ prompt += `\n\nFocus area: ${args}`
37
+ }
38
+ return [{ type: 'text', text: prompt }]
39
+ },
40
+ })
41
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Bundled Skill: simplify
3
+ *
4
+ * Reviews changed code for reuse opportunities, code quality issues,
5
+ * and efficiency improvements, then fixes any issues found.
6
+ */
7
+
8
+ import { registerSkill } from '../registry.js'
9
+ import type { SkillContentBlock } from '../types.js'
10
+
11
+ const SIMPLIFY_PROMPT = `Review the recently changed code for three categories of improvements. Launch 3 parallel Agent sub-tasks:
12
+
13
+ ## Task 1: Reuse Analysis
14
+ Look for:
15
+ - Duplicated code that could be consolidated
16
+ - Existing utilities or helpers that could replace new code
17
+ - Patterns that should be extracted into shared functions
18
+ - Re-implementations of functionality that already exists elsewhere
19
+
20
+ ## Task 2: Code Quality
21
+ Look for:
22
+ - Overly complex logic that could be simplified
23
+ - Poor naming or unclear intent
24
+ - Missing edge case handling
25
+ - Unnecessary abstractions or over-engineering
26
+ - Dead code or unused imports
27
+
28
+ ## Task 3: Efficiency
29
+ Look for:
30
+ - Unnecessary allocations or copies
31
+ - N+1 query patterns or redundant I/O
32
+ - Blocking operations that could be async
33
+ - Inefficient data structures for the access pattern
34
+ - Unnecessary re-computation
35
+
36
+ After all three analyses complete, fix any issues found. Prioritize by impact.`
37
+
38
+ export function registerSimplifySkill(): void {
39
+ registerSkill({
40
+ name: 'simplify',
41
+ description: 'Review changed code for reuse, quality, and efficiency, then fix any issues found.',
42
+ userInvocable: true,
43
+ async getPrompt(args): Promise<SkillContentBlock[]> {
44
+ let prompt = SIMPLIFY_PROMPT
45
+ if (args.trim()) {
46
+ prompt += `\n\n## Additional Focus\n${args}`
47
+ }
48
+ return [{ type: 'text', text: prompt }]
49
+ },
50
+ })
51
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Bundled Skill: test
3
+ *
4
+ * Run tests and analyze failures.
5
+ */
6
+
7
+ import { registerSkill } from '../registry.js'
8
+ import type { SkillContentBlock } from '../types.js'
9
+
10
+ const TEST_PROMPT = `Run the project's test suite and analyze the results:
11
+
12
+ 1. **Discover**: Find the test runner configuration
13
+ - Look for package.json scripts, jest.config, vitest.config, pytest.ini, etc.
14
+ - Identify the appropriate test command
15
+
16
+ 2. **Execute**: Run the tests
17
+ - Run the full test suite or specific tests if specified
18
+ - Capture output including failures and errors
19
+
20
+ 3. **Analyze**: If tests fail:
21
+ - Read the failing test to understand what it expects
22
+ - Read the source code being tested
23
+ - Identify why the test is failing
24
+ - Fix the issue (in tests or source as appropriate)
25
+
26
+ 4. **Re-verify**: Run the failing tests again to confirm the fix`
27
+
28
+ export function registerTestSkill(): void {
29
+ registerSkill({
30
+ name: 'test',
31
+ description: 'Run tests and analyze failures, fixing any issues found.',
32
+ aliases: ['run-tests'],
33
+ allowedTools: ['Bash', 'Read', 'Write', 'Edit', 'Glob', 'Grep'],
34
+ userInvocable: true,
35
+ async getPrompt(args): Promise<SkillContentBlock[]> {
36
+ let prompt = TEST_PROMPT
37
+ if (args.trim()) {
38
+ prompt += `\n\nSpecific test target: ${args}`
39
+ }
40
+ return [{ type: 'text', text: prompt }]
41
+ },
42
+ })
43
+ }