@zibby/core 0.1.0
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/LICENSE +21 -0
- package/README.md +147 -0
- package/package.json +94 -0
- package/src/agents/base.js +361 -0
- package/src/constants.js +47 -0
- package/src/enrichment/base.js +49 -0
- package/src/enrichment/enrichers/accessibility-enricher.js +197 -0
- package/src/enrichment/enrichers/dom-enricher.js +171 -0
- package/src/enrichment/enrichers/page-state-enricher.js +129 -0
- package/src/enrichment/enrichers/position-enricher.js +67 -0
- package/src/enrichment/index.js +96 -0
- package/src/enrichment/mcp-integration.js +149 -0
- package/src/enrichment/mcp-ref-enricher.js +78 -0
- package/src/enrichment/pipeline.js +192 -0
- package/src/enrichment/trace-text-enricher.js +115 -0
- package/src/framework/AGENTS.md +98 -0
- package/src/framework/agents/base.js +72 -0
- package/src/framework/agents/claude-strategy.js +278 -0
- package/src/framework/agents/cursor-strategy.js +459 -0
- package/src/framework/agents/index.js +105 -0
- package/src/framework/agents/utils/cursor-output-formatter.js +67 -0
- package/src/framework/agents/utils/openai-proxy-formatter.js +249 -0
- package/src/framework/code-generator.js +301 -0
- package/src/framework/constants.js +33 -0
- package/src/framework/context-loader.js +101 -0
- package/src/framework/function-bridge.js +78 -0
- package/src/framework/function-skill-registry.js +20 -0
- package/src/framework/graph-compiler.js +342 -0
- package/src/framework/graph.js +610 -0
- package/src/framework/index.js +28 -0
- package/src/framework/node-registry.js +163 -0
- package/src/framework/node.js +259 -0
- package/src/framework/output-parser.js +71 -0
- package/src/framework/skill-registry.js +55 -0
- package/src/framework/state-utils.js +52 -0
- package/src/framework/state.js +67 -0
- package/src/framework/tool-resolver.js +65 -0
- package/src/index.js +342 -0
- package/src/runtime/generation/base.js +46 -0
- package/src/runtime/generation/index.js +70 -0
- package/src/runtime/generation/mcp-ref-strategy.js +197 -0
- package/src/runtime/generation/stable-id-strategy.js +170 -0
- package/src/runtime/stable-id-runtime.js +248 -0
- package/src/runtime/verification/base.js +44 -0
- package/src/runtime/verification/index.js +67 -0
- package/src/runtime/verification/playwright-json-strategy.js +119 -0
- package/src/runtime/zibby-runtime.js +299 -0
- package/src/sync/index.js +2 -0
- package/src/sync/uploader.js +29 -0
- package/src/tools/run-playwright-test.js +158 -0
- package/src/utils/adf-converter.js +68 -0
- package/src/utils/ast-utils.js +37 -0
- package/src/utils/ci-setup.js +124 -0
- package/src/utils/cursor-utils.js +71 -0
- package/src/utils/logger.js +144 -0
- package/src/utils/mcp-config-writer.js +115 -0
- package/src/utils/node-schema-parser.js +522 -0
- package/src/utils/post-process-events.js +55 -0
- package/src/utils/result-handler.js +102 -0
- package/src/utils/ripple-effect.js +84 -0
- package/src/utils/selector-generator.js +239 -0
- package/src/utils/streaming-parser.js +387 -0
- package/src/utils/test-post-processor.js +211 -0
- package/src/utils/timeline.js +217 -0
- package/src/utils/trace-parser.js +325 -0
- package/src/utils/video-organizer.js +91 -0
- package/templates/browser-test-automation/README.md +114 -0
- package/templates/browser-test-automation/graph.js +54 -0
- package/templates/browser-test-automation/nodes/execute-live.js +250 -0
- package/templates/browser-test-automation/nodes/generate-script.js +77 -0
- package/templates/browser-test-automation/nodes/index.js +3 -0
- package/templates/browser-test-automation/nodes/preflight.js +59 -0
- package/templates/browser-test-automation/nodes/utils.js +154 -0
- package/templates/browser-test-automation/result-handler.js +286 -0
- package/templates/code-analysis/graph.js +72 -0
- package/templates/code-analysis/index.js +18 -0
- package/templates/code-analysis/nodes/analyze-ticket-node.js +204 -0
- package/templates/code-analysis/nodes/create-pr-node.js +175 -0
- package/templates/code-analysis/nodes/finalize-node.js +118 -0
- package/templates/code-analysis/nodes/generate-code-node.js +425 -0
- package/templates/code-analysis/nodes/generate-test-cases-node.js +376 -0
- package/templates/code-analysis/nodes/services/prMetaService.js +86 -0
- package/templates/code-analysis/nodes/setup-node.js +142 -0
- package/templates/code-analysis/prompts/analyze-ticket.md +181 -0
- package/templates/code-analysis/prompts/generate-code.md +33 -0
- package/templates/code-analysis/prompts/generate-test-cases.md +110 -0
- package/templates/code-analysis/state.js +40 -0
- package/templates/code-implementation/graph.js +35 -0
- package/templates/code-implementation/index.js +7 -0
- package/templates/code-implementation/state.js +14 -0
- package/templates/global-setup.js +56 -0
- package/templates/index.js +94 -0
- package/templates/register-nodes.js +24 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Agent Strategy - Uses @anthropic-ai/claude-agent-sdk
|
|
3
|
+
* Provides full agent with Read, Write, Bash, Grep, Glob tools + MCP support
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { AgentStrategy } from './base.js';
|
|
7
|
+
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
8
|
+
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
9
|
+
import { logger } from '../../utils/logger.js';
|
|
10
|
+
import { timeline } from '../../utils/timeline.js';
|
|
11
|
+
import { DEFAULT_MODELS, CLAUDE_MODEL_MAP } from '../../constants.js';
|
|
12
|
+
import { getSkill } from '../skill-registry.js';
|
|
13
|
+
|
|
14
|
+
export class ClaudeAgentStrategy extends AgentStrategy {
|
|
15
|
+
constructor() {
|
|
16
|
+
super(
|
|
17
|
+
'claude',
|
|
18
|
+
'Claude Agent SDK',
|
|
19
|
+
50 // Lower priority - fallback option
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
canHandle(_context) {
|
|
24
|
+
const hasKey = !!process.env.ANTHROPIC_API_KEY;
|
|
25
|
+
if (!hasKey) {
|
|
26
|
+
logger.debug('ClaudeAgentStrategy: ANTHROPIC_API_KEY not set');
|
|
27
|
+
}
|
|
28
|
+
return hasKey;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async invoke(prompt, options = {}) {
|
|
32
|
+
const {
|
|
33
|
+
model: requestedModel,
|
|
34
|
+
workspace = process.cwd(),
|
|
35
|
+
schema = null,
|
|
36
|
+
images: _images = [],
|
|
37
|
+
skills = null,
|
|
38
|
+
sessionPath = null,
|
|
39
|
+
timeout: _timeout,
|
|
40
|
+
config: _config = {}
|
|
41
|
+
} = options;
|
|
42
|
+
|
|
43
|
+
// Model resolution
|
|
44
|
+
let userModel = requestedModel;
|
|
45
|
+
if (!userModel || userModel === 'auto') {
|
|
46
|
+
logger.debug(`Model is '${userModel || 'undefined'}', using default: ${DEFAULT_MODELS.CLAUDE}`);
|
|
47
|
+
userModel = DEFAULT_MODELS.CLAUDE;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const model = CLAUDE_MODEL_MAP[userModel] || userModel;
|
|
51
|
+
if (CLAUDE_MODEL_MAP[userModel] && userModel !== model) {
|
|
52
|
+
logger.debug(`Mapped model: ${userModel} → ${model}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
logger.debug(`Invoking Claude Agent SDK with model: ${model}, skills: ${JSON.stringify(skills)}`);
|
|
56
|
+
|
|
57
|
+
const { allowedTools, mcpServers } = this._resolveSkills(skills, { sessionPath, workspace });
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const queryOptions = {
|
|
61
|
+
cwd: workspace,
|
|
62
|
+
allowedTools,
|
|
63
|
+
permissionMode: 'bypassPermissions',
|
|
64
|
+
model,
|
|
65
|
+
...(Object.keys(mcpServers).length > 0 && { mcpServers }),
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
if (schema) {
|
|
69
|
+
const isZod = typeof schema.parse === 'function';
|
|
70
|
+
const jsonSchema = isZod ? zodToJsonSchema(schema, { target: 'openApi3' }) : schema;
|
|
71
|
+
queryOptions.outputFormat = { type: 'json_schema', schema: jsonSchema };
|
|
72
|
+
logger.debug('Structured output enforced via SDK outputFormat');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
logger.debug(`Agent SDK options: ${JSON.stringify({
|
|
76
|
+
cwd: queryOptions.cwd,
|
|
77
|
+
toolCount: allowedTools.length,
|
|
78
|
+
permissionMode: queryOptions.permissionMode,
|
|
79
|
+
model: queryOptions.model,
|
|
80
|
+
hasOutputFormat: !!queryOptions.outputFormat
|
|
81
|
+
})}`);
|
|
82
|
+
|
|
83
|
+
// Collect all messages from agent
|
|
84
|
+
let finalText = '';
|
|
85
|
+
let toolCallCount = 0;
|
|
86
|
+
const allMessages = [];
|
|
87
|
+
|
|
88
|
+
logger.debug('Starting Claude Agent SDK query stream');
|
|
89
|
+
|
|
90
|
+
let queryIterator;
|
|
91
|
+
try {
|
|
92
|
+
queryIterator = query({ prompt, options: queryOptions });
|
|
93
|
+
} catch (initError) {
|
|
94
|
+
logger.error(`Failed to initialize Claude Agent SDK: ${initError.message}`);
|
|
95
|
+
throw initError;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let lastMsgSig = null;
|
|
99
|
+
let repeatCount = 0;
|
|
100
|
+
const MAX_REPEATS = 3;
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
for await (const message of queryIterator) {
|
|
104
|
+
allMessages.push(message);
|
|
105
|
+
|
|
106
|
+
// Bail on explicit error events
|
|
107
|
+
if (message.type === 'error' || message.error) {
|
|
108
|
+
const errMsg = message.error?.message || message.error || message.message || 'Unknown API error';
|
|
109
|
+
throw new Error(typeof errMsg === 'string' ? errMsg : JSON.stringify(errMsg));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Detect infinite loops: if the same message repeats N times, the API is stuck
|
|
113
|
+
const sig = JSON.stringify(message.message?.content || message.text || '').slice(0, 200);
|
|
114
|
+
if (sig === lastMsgSig) {
|
|
115
|
+
repeatCount++;
|
|
116
|
+
if (repeatCount >= MAX_REPEATS) {
|
|
117
|
+
const preview = (message.message?.content?.[0]?.text || message.text || 'unknown').slice(0, 100);
|
|
118
|
+
throw new Error(`API stuck in loop (${repeatCount}x repeated): ${preview}`);
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
lastMsgSig = sig;
|
|
122
|
+
repeatCount = 1;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// AssistantMessage: Claude's thinking and tool calls
|
|
126
|
+
if (message.type === 'assistant' || message.constructor?.name === 'AssistantMessage') {
|
|
127
|
+
// Agent SDK wraps content in message.message.content (not message.content!)
|
|
128
|
+
const content = message.message?.content || message.content || [];
|
|
129
|
+
for (const block of content) {
|
|
130
|
+
if (block.type === 'thinking' && block.thinking) {
|
|
131
|
+
console.log(`${block.thinking.substring(0, 200)}${block.thinking.length > 200 ? '...' : ''}`);
|
|
132
|
+
} else if (block.type === 'text' && block.text) {
|
|
133
|
+
finalText += block.text;
|
|
134
|
+
if (block.text.length < 500) {
|
|
135
|
+
console.log(`${block.text}`);
|
|
136
|
+
} else {
|
|
137
|
+
console.log(`${block.text.substring(0, 200)}... (${block.text.length} chars)`);
|
|
138
|
+
}
|
|
139
|
+
} else if (block.type === 'tool_use') {
|
|
140
|
+
toolCallCount++;
|
|
141
|
+
const isMemTool = block.name.includes('memory');
|
|
142
|
+
if (isMemTool) {
|
|
143
|
+
timeline.stepMemory(`Tool: ${block.name}`);
|
|
144
|
+
} else {
|
|
145
|
+
timeline.stepTool(`Tool: ${block.name}`);
|
|
146
|
+
}
|
|
147
|
+
const inputPreview = JSON.stringify(block.input).substring(0, 100);
|
|
148
|
+
console.log(` Input: ${inputPreview}${JSON.stringify(block.input).length > 100 ? '...' : ''}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// ToolResultMessage: Show tool execution results (silent, no prefix)
|
|
153
|
+
else if (message.type === 'user' && message.tool_use_result) {
|
|
154
|
+
// Tool results are silent - no output
|
|
155
|
+
}
|
|
156
|
+
// ResultMessage: final outcome
|
|
157
|
+
else if (message.type === 'result' || message.constructor?.name === 'ResultMessage') {
|
|
158
|
+
timeline.step(`Agent completed (${toolCallCount} tool calls)`);
|
|
159
|
+
logger.debug(`Agent completed (${toolCallCount} tool calls)`);
|
|
160
|
+
|
|
161
|
+
const resultText = message.result || message.text || message.content || finalText;
|
|
162
|
+
|
|
163
|
+
if (schema) {
|
|
164
|
+
// Priority 1: SDK native structured_output
|
|
165
|
+
if (message.structured_output) {
|
|
166
|
+
logger.debug('Using SDK native structured_output');
|
|
167
|
+
const isZod = typeof schema.parse === 'function';
|
|
168
|
+
const validated = isZod ? schema.parse(message.structured_output) : message.structured_output;
|
|
169
|
+
return { raw: resultText, structured: validated };
|
|
170
|
+
}
|
|
171
|
+
// Priority 2: Extract JSON from result text
|
|
172
|
+
if (resultText) {
|
|
173
|
+
const extracted = this._extractJson(resultText, schema);
|
|
174
|
+
if (extracted) {
|
|
175
|
+
return { raw: resultText, structured: extracted };
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
logger.warn(`Could not extract structured output — returning raw text (${(resultText || '').length} chars)`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return resultText || '';
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
logger.warn(`Agent SDK ended without result. Collected ${allMessages.length} messages`);
|
|
186
|
+
if (finalText.length > 0) {
|
|
187
|
+
logger.debug('Returning accumulated text from messages');
|
|
188
|
+
return finalText;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
throw new Error('Claude Agent SDK query ended without result');
|
|
192
|
+
} catch (streamError) {
|
|
193
|
+
logger.error(`Error during query stream: ${streamError.message}`);
|
|
194
|
+
throw streamError;
|
|
195
|
+
}
|
|
196
|
+
} catch (error) {
|
|
197
|
+
logger.error('Claude Agent SDK call failed', { error: error.message });
|
|
198
|
+
throw error;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
_resolveSkills(skills, resolveOpts) {
|
|
203
|
+
if (skills === null) {
|
|
204
|
+
logger.debug('No skills — pure LLM mode');
|
|
205
|
+
return { allowedTools: [], mcpServers: {} };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!Array.isArray(skills) || skills.length === 0) {
|
|
209
|
+
logger.debug('Default IDE skills for code generation');
|
|
210
|
+
return { allowedTools: ['Read', 'Write', 'Bash', 'Grep', 'Glob'], mcpServers: {} };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const allowedTools = [];
|
|
214
|
+
const mcpServers = {};
|
|
215
|
+
|
|
216
|
+
for (const skillId of skills) {
|
|
217
|
+
const skill = getSkill(skillId);
|
|
218
|
+
if (!skill) { logger.warn(`Unknown skill "${skillId}" — skipping`); continue; }
|
|
219
|
+
if (skill.allowedTools) allowedTools.push(...skill.allowedTools);
|
|
220
|
+
if (typeof skill.resolve === 'function') {
|
|
221
|
+
const resolved = skill.resolve(resolveOpts);
|
|
222
|
+
if (resolved) {
|
|
223
|
+
mcpServers[skill.serverName] = resolved;
|
|
224
|
+
logger.debug(`MCP: ${skill.serverName} → ${resolved.command} ${resolved.args[0]}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return { allowedTools, mcpServers };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
_extractJson(text, schema) {
|
|
233
|
+
const strategies = [
|
|
234
|
+
() => {
|
|
235
|
+
if (text.includes('===JSON_START===')) {
|
|
236
|
+
const start = text.indexOf('===JSON_START===') + '===JSON_START==='.length;
|
|
237
|
+
const end = text.indexOf('===JSON_END===');
|
|
238
|
+
return text.substring(start, end).trim();
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
() => {
|
|
242
|
+
const match = text.match(/```json\s*\n([\s\S]*?)\n```/);
|
|
243
|
+
return match?.[1]?.trim();
|
|
244
|
+
},
|
|
245
|
+
() => {
|
|
246
|
+
if (!text.startsWith('{')) {
|
|
247
|
+
const match = text.match(/```\s*\n([\s\S]*?)\n```/);
|
|
248
|
+
return match?.[1]?.trim();
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
() => text.trim(),
|
|
252
|
+
() => {
|
|
253
|
+
const firstBrace = text.indexOf('{');
|
|
254
|
+
const lastBrace = text.lastIndexOf('}');
|
|
255
|
+
if (firstBrace !== -1 && lastBrace > firstBrace) {
|
|
256
|
+
return text.substring(firstBrace, lastBrace + 1);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
];
|
|
260
|
+
|
|
261
|
+
for (const extract of strategies) {
|
|
262
|
+
try {
|
|
263
|
+
const candidate = extract();
|
|
264
|
+
if (!candidate) continue;
|
|
265
|
+
const parsed = JSON.parse(candidate);
|
|
266
|
+
if (typeof parsed !== 'object' || parsed === null) continue;
|
|
267
|
+
const isZod = typeof schema.parse === 'function';
|
|
268
|
+
if (isZod) {
|
|
269
|
+
return schema.parse(parsed);
|
|
270
|
+
}
|
|
271
|
+
return parsed;
|
|
272
|
+
} catch {
|
|
273
|
+
// try next strategy
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
}
|