@stacksjs/ai 0.70.55 → 0.70.57

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 (46) hide show
  1. package/package.json +10 -7
  2. package/src/agents/claude/index.ts +0 -310
  3. package/src/agents/index.ts +0 -7
  4. package/src/buddy.ts +0 -619
  5. package/src/drivers/anthropic/index.ts +0 -430
  6. package/src/drivers/claude-agent-sdk/index.ts +0 -370
  7. package/src/drivers/index.ts +0 -14
  8. package/src/drivers/ollama/index.ts +0 -514
  9. package/src/drivers/openai/index.ts +0 -529
  10. package/src/image.ts +0 -607
  11. package/src/index.ts +0 -53
  12. package/src/mcp.ts +0 -658
  13. package/src/personalization.ts +0 -490
  14. package/src/search.ts +0 -555
  15. package/src/text.ts +0 -79
  16. package/src/types.ts +0 -229
  17. package/src/utils/client-bedrock-runtime.ts +0 -51
  18. package/src/utils/client-bedrock.ts +0 -75
  19. package/src/utils/model-access.ts +0 -32
  20. package/src/utils/retry.ts +0 -124
  21. package/src/utils/tokens.ts +0 -159
  22. package/src/utils/usage.ts +0 -98
  23. package/src/utils/vision.ts +0 -119
  24. /package/dist/{src/agents → agents}/claude/index.d.ts +0 -0
  25. /package/dist/{src/agents → agents}/index.d.ts +0 -0
  26. /package/dist/{src/buddy.d.ts → buddy.d.ts} +0 -0
  27. /package/dist/{src/drivers → drivers}/anthropic/index.d.ts +0 -0
  28. /package/dist/{src/drivers → drivers}/claude-agent-sdk/index.d.ts +0 -0
  29. /package/dist/{src/drivers → drivers}/index.d.ts +0 -0
  30. /package/dist/{src/drivers → drivers}/ollama/index.d.ts +0 -0
  31. /package/dist/{src/drivers → drivers}/openai/index.d.ts +0 -0
  32. /package/dist/{src/image.d.ts → image.d.ts} +0 -0
  33. /package/dist/{src/index.d.ts → index.d.ts} +0 -0
  34. /package/dist/{src/index.js → index.js} +0 -0
  35. /package/dist/{src/mcp.d.ts → mcp.d.ts} +0 -0
  36. /package/dist/{src/personalization.d.ts → personalization.d.ts} +0 -0
  37. /package/dist/{src/search.d.ts → search.d.ts} +0 -0
  38. /package/dist/{src/text.d.ts → text.d.ts} +0 -0
  39. /package/dist/{src/types.d.ts → types.d.ts} +0 -0
  40. /package/dist/{src/utils → utils}/client-bedrock-runtime.d.ts +0 -0
  41. /package/dist/{src/utils → utils}/client-bedrock.d.ts +0 -0
  42. /package/dist/{src/utils → utils}/model-access.d.ts +0 -0
  43. /package/dist/{src/utils → utils}/retry.d.ts +0 -0
  44. /package/dist/{src/utils → utils}/tokens.d.ts +0 -0
  45. /package/dist/{src/utils → utils}/usage.d.ts +0 -0
  46. /package/dist/{src/utils → utils}/vision.d.ts +0 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/ai",
3
3
  "type": "module",
4
- "version": "0.70.55",
4
+ "version": "0.70.57",
5
5
  "description": "Stacks Artificial Intelligence.",
6
6
  "author": "Chris Breuer",
7
7
  "contributors": [
@@ -34,20 +34,23 @@
34
34
  "exports": {
35
35
  ".": {
36
36
  "types": "./dist/index.d.ts",
37
- "bun": "./src/index.ts",
38
- "import": "./dist/index.js"
37
+ "development": "./src/index.ts",
38
+ "bun": "./dist/index.js",
39
+ "import": "./dist/index.js",
40
+ "default": "./dist/index.js"
39
41
  },
40
42
  "./*": {
41
- "bun": "./src/*",
42
- "import": "./dist/*"
43
+ "development": "./src/*",
44
+ "bun": "./dist/*",
45
+ "import": "./dist/*",
46
+ "default": "./dist/*"
43
47
  }
44
48
  },
45
49
  "module": "dist/index.js",
46
50
  "types": "dist/index.d.ts",
47
51
  "files": [
48
52
  "README.md",
49
- "dist",
50
- "src"
53
+ "dist"
51
54
  ],
52
55
  "scripts": {
53
56
  "build": "bun build.ts",
@@ -1,310 +0,0 @@
1
- /**
2
- * Claude CLI Agent
3
- *
4
- * Provides integration with Claude CLI for local and remote (EC2) execution.
5
- * Supports both synchronous and streaming responses.
6
- */
7
-
8
- import type { AIDriver, AIMessage, ClaudeStreamEvent, StreamingResult } from '../../types'
9
-
10
- // Track prompt count for session feedback
11
- let promptCount = 0
12
-
13
- export interface ClaudeAgentConfig {
14
- /** Working directory for command execution */
15
- cwd?: string
16
- /** EC2 host for remote execution */
17
- ec2Host?: string
18
- /** EC2 user (default: ubuntu) */
19
- ec2User?: string
20
- /** Path to EC2 SSH key */
21
- ec2Key?: string
22
- }
23
-
24
- /**
25
- * Format tool usage for display
26
- */
27
- function formatToolUsage(toolName: string, input: Record<string, unknown>): string {
28
- switch (toolName) {
29
- case 'Read': {
30
- const filePath = (input.file_path as string) || ''
31
- const fileName = filePath.split('/').pop() || filePath
32
- return `\n📖 Reading: ${fileName}\n`
33
- }
34
- case 'Glob':
35
- return `\n🔍 Searching files: ${input.pattern || ''}\n`
36
- case 'Grep':
37
- return `\n🔎 Searching for: "${input.pattern || ''}"\n`
38
- case 'Edit': {
39
- const filePath = (input.file_path as string) || ''
40
- const fileName = filePath.split('/').pop() || filePath
41
- return `\n✏️ Editing: ${fileName}\n`
42
- }
43
- case 'Write': {
44
- const filePath = (input.file_path as string) || ''
45
- const fileName = filePath.split('/').pop() || filePath
46
- return `\n📝 Writing: ${fileName}\n`
47
- }
48
- case 'Bash': {
49
- const cmd = ((input.command as string) || '').substring(0, 50)
50
- return `\n💻 Running: ${cmd}${cmd.length >= 50 ? '...' : ''}\n`
51
- }
52
- case 'Task':
53
- return `\n🚀 Launching agent: ${input.description || 'task'}\n`
54
- default:
55
- return `\n🔧 Using: ${toolName}\n`
56
- }
57
- }
58
-
59
- /**
60
- * Create a local Claude CLI agent
61
- */
62
- export function createClaudeLocalAgent(config: ClaudeAgentConfig = {}): AIDriver {
63
- return {
64
- name: 'Claude CLI (Local)',
65
-
66
- async process(command: string, context: string, _history: AIMessage[]): Promise<string> {
67
- const { $: _$ } = await import('bun')
68
-
69
- // Check if claude CLI is available
70
- const whichResult = await _$`which claude`.quiet().nothrow()
71
- if (whichResult.exitCode !== 0) {
72
- throw new Error('Claude CLI not found. Install it with: npm install -g @anthropic-ai/claude-code')
73
- }
74
-
75
- // Build the full prompt with context
76
- const fullPrompt = context
77
- ? `Context:\n${context}\n\nUser request: ${command}`
78
- : command
79
-
80
- const cwd = config.cwd || process.cwd()
81
-
82
- try {
83
- const result = await _$`cd ${cwd} && claude --print --dangerously-skip-permissions ${fullPrompt}`.quiet()
84
- return result.text().trim()
85
- }
86
- catch {
87
- // Try with allowedTools flag as alternative
88
- try {
89
- const result = await _$`cd ${cwd} && claude --print --allowedTools "Write,Edit,Bash" ${fullPrompt}`.quiet()
90
- return result.text().trim()
91
- }
92
- catch (innerError) {
93
- throw new Error(`Claude CLI error: ${(innerError as Error).message}`)
94
- }
95
- }
96
- },
97
- }
98
- }
99
-
100
- /**
101
- * Create a remote Claude CLI agent (EC2)
102
- */
103
- export function createClaudeEC2Agent(config: ClaudeAgentConfig): AIDriver {
104
- const {
105
- ec2Host = process.env.BUDDY_EC2_HOST,
106
- ec2User = process.env.BUDDY_EC2_USER || 'ubuntu',
107
- ec2Key = process.env.BUDDY_EC2_KEY,
108
- } = config
109
-
110
- return {
111
- name: 'Claude CLI (EC2)',
112
-
113
- async process(command: string, context: string, _history: AIMessage[]): Promise<string> {
114
- const { $: _$ } = await import('bun')
115
-
116
- if (!ec2Host) {
117
- throw new Error('BUDDY_EC2_HOST environment variable not set. Set it to your EC2 instance IP/hostname.')
118
- }
119
-
120
- // Build the full prompt with context
121
- const fullPrompt = context
122
- ? `Context:\n${context}\n\nUser request: ${command}`
123
- : command
124
-
125
- // Escape the prompt for SSH
126
- const escapedPrompt = fullPrompt.replace(/'/g, `'\\''`)
127
-
128
- // Build SSH command
129
- const sshArgs = ec2Key ? `-i ${ec2Key}` : ''
130
- const sshTarget = `${ec2User}@${ec2Host}`
131
-
132
- try {
133
- const result = await _$`ssh ${sshArgs} ${sshTarget} "claude --print '${escapedPrompt}'"`.quiet()
134
- return result.text().trim()
135
- }
136
- catch (error) {
137
- throw new Error(`EC2 Claude CLI error: ${(error as Error).message}. Make sure SSH is configured and claude CLI is installed on EC2.`)
138
- }
139
- },
140
- }
141
- }
142
-
143
- /**
144
- * Process command with streaming output using Claude CLI
145
- *
146
- * Returns a ReadableStream that emits chunks of the response in real-time.
147
- * Uses --output-format stream-json for detailed streaming with tool usage.
148
- */
149
- export async function processCommandStreaming(
150
- command: string,
151
- cwd: string,
152
- ): Promise<StreamingResult> {
153
- const { spawn } = await import('bun')
154
-
155
- // Spawn Claude CLI process with stream-json output format
156
- const proc = spawn([
157
- 'claude',
158
- '--print',
159
- '--verbose',
160
- '--output-format', 'stream-json',
161
- '--dangerously-skip-permissions',
162
- command,
163
- ], {
164
- cwd,
165
- stdout: 'pipe',
166
- stderr: 'pipe',
167
- })
168
-
169
- let fullResponse = ''
170
- const decoder = new TextDecoder()
171
- const encoder = new TextEncoder()
172
-
173
- const stream = new ReadableStream<Uint8Array>({
174
- async start(controller) {
175
- try {
176
- const reader = proc.stdout.getReader()
177
- let buffer = ''
178
- let lastSentText = ''
179
-
180
- let result = await reader.read()
181
- while (!result.done) {
182
- buffer += decoder.decode(result.value, { stream: true })
183
-
184
- // Parse JSON lines from buffer
185
- const lines = buffer.split('\n')
186
- buffer = lines.pop() || ''
187
-
188
- for (const line of lines) {
189
- if (!line.trim()) continue
190
-
191
- try {
192
- const event = JSON.parse(line) as ClaudeStreamEvent
193
- let textContent = ''
194
-
195
- if (event.type === 'system' && event.subtype === 'init') {
196
- promptCount++
197
- // UI shows thinking indicator, no need to output text
198
- }
199
- // Handle content_block_start - initial text from a block
200
- else if (event.type === 'content_block_start' && event.content_block?.text) {
201
- textContent = event.content_block.text
202
- }
203
- // Handle content_block_delta - streaming text deltas (most important for real-time feel)
204
- else if (event.type === 'content_block_delta' && event.delta?.text) {
205
- textContent = event.delta.text
206
- }
207
- // Handle assistant messages with full content
208
- else if (event.type === 'assistant' && event.message?.content) {
209
- for (const block of event.message.content) {
210
- if (block.type === 'text' && block.text) {
211
- textContent += block.text
212
- }
213
- else if (block.type === 'tool_use' && block.name) {
214
- textContent += formatToolUsage(block.name, block.input || {})
215
- }
216
- }
217
- }
218
- // Handle tool results
219
- else if (event.type === 'user' && event.message?.content) {
220
- for (const block of event.message.content) {
221
- if (block.type === 'tool_result') {
222
- textContent += ` ✓ done\n`
223
- }
224
- }
225
- }
226
- // Handle final result
227
- else if (event.type === 'result' && event.result) {
228
- fullResponse = event.result
229
- // Only send result if we haven't already sent equivalent content
230
- const resultPrefix = event.result.substring(0, Math.min(50, event.result.length))
231
- if (!lastSentText.includes(resultPrefix)) {
232
- textContent = event.result
233
- }
234
- }
235
-
236
- // Send new content immediately for responsive streaming
237
- if (textContent) {
238
- // For deltas, always send (they're incremental)
239
- if (event.type === 'content_block_delta') {
240
- lastSentText += textContent
241
- controller.enqueue(encoder.encode(textContent))
242
- }
243
- // For other events, check we haven't sent it already
244
- else if (!lastSentText.endsWith(textContent)) {
245
- lastSentText += textContent
246
- controller.enqueue(encoder.encode(textContent))
247
- }
248
- }
249
- }
250
- catch {
251
- // Not valid JSON, might be plain text output
252
- if (!lastSentText.includes(line)) {
253
- lastSentText += line
254
- fullResponse += line
255
- controller.enqueue(encoder.encode(line))
256
- }
257
- }
258
- }
259
-
260
- result = await reader.read()
261
- }
262
-
263
- // Process remaining buffer
264
- if (buffer.trim()) {
265
- try {
266
- const event = JSON.parse(buffer) as ClaudeStreamEvent
267
- if (event.result && !fullResponse) {
268
- fullResponse = event.result
269
- }
270
- }
271
- catch {
272
- if (!lastSentText.includes(buffer)) {
273
- fullResponse += buffer
274
- controller.enqueue(encoder.encode(buffer))
275
- }
276
- }
277
- }
278
-
279
- await proc.exited
280
- controller.close()
281
- }
282
- catch (error) {
283
- controller.error(error)
284
- }
285
- },
286
- })
287
-
288
- const fullResponsePromise = (async () => {
289
- await proc.exited
290
- return fullResponse.trim()
291
- })()
292
-
293
- return { stream, fullResponse: fullResponsePromise }
294
- }
295
-
296
- export function resetPromptCount(): void {
297
- promptCount = 0
298
- }
299
-
300
- export const claudeAgent: {
301
- createLocal: typeof createClaudeLocalAgent
302
- createEC2: typeof createClaudeEC2Agent
303
- processStreaming: typeof processCommandStreaming
304
- resetPromptCount: typeof resetPromptCount
305
- } = {
306
- createLocal: createClaudeLocalAgent,
307
- createEC2: createClaudeEC2Agent,
308
- processStreaming: processCommandStreaming,
309
- resetPromptCount,
310
- }
@@ -1,7 +0,0 @@
1
- /**
2
- * AI Agents
3
- *
4
- * Export all available AI agents.
5
- */
6
-
7
- export * from './claude'