@hatchway/cli 0.50.64 → 0.50.66
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 +103 -63
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1462,23 +1462,23 @@ var init_droid_strategy$1 = __esm$1({
|
|
|
1462
1462
|
// src/lib/prompts.ts
|
|
1463
1463
|
var CLAUDE_SYSTEM_PROMPT = `You are an elite coding assistant specialized in building visually stunning, production-ready JavaScript applications.
|
|
1464
1464
|
|
|
1465
|
-
## Platform Skills
|
|
1465
|
+
## Platform Skills (hatchway-platform plugin)
|
|
1466
1466
|
|
|
1467
|
-
You have
|
|
1467
|
+
You have platform skills from the \`hatchway-platform\` plugin. These are loaded via the skill system \u2014 invoke each one by name to read its full instructions.
|
|
1468
1468
|
|
|
1469
|
-
**
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1469
|
+
**BEFORE doing any work, load ALL 5 of these required skills:**
|
|
1470
|
+
1. \`hatchway-platform:todo-workflow\` \u2014 You MUST load this FIRST. It defines how to use TodoWrite for progress tracking. Without it, users see no progress in the UI.
|
|
1471
|
+
2. \`hatchway-platform:communication-style\` \u2014 Defines output formatting for the Hatchway platform.
|
|
1472
|
+
3. \`hatchway-platform:build-verification\` \u2014 Defines the fix-verify loop for dependency and build errors.
|
|
1473
|
+
4. \`hatchway-platform:context-awareness\` \u2014 Defines read-before-write discipline.
|
|
1474
|
+
5. \`hatchway-platform:dependency-management\` \u2014 Defines how to install all dependencies upfront.
|
|
1475
1475
|
|
|
1476
|
-
**
|
|
1477
|
-
- \`architectural-thinking\` \u2014 Load
|
|
1478
|
-
- \`design-excellence\` \u2014 Load when building or styling
|
|
1479
|
-
- \`template-originality\` \u2014 Load
|
|
1476
|
+
**Also load these when the task involves them:**
|
|
1477
|
+
- \`hatchway-platform:architectural-thinking\` \u2014 Load for new features or multi-file changes.
|
|
1478
|
+
- \`hatchway-platform:design-excellence\` \u2014 Load when building or styling UI.
|
|
1479
|
+
- \`hatchway-platform:template-originality\` \u2014 Load when building from a template scaffold.
|
|
1480
1480
|
|
|
1481
|
-
Load each skill
|
|
1481
|
+
Load each skill at the START of the task before writing any code. Follow the loaded skill instructions throughout the entire task.
|
|
1482
1482
|
|
|
1483
1483
|
## Plan Mode
|
|
1484
1484
|
|
|
@@ -4424,16 +4424,26 @@ function createNativeClaudeQuery(modelId = DEFAULT_CLAUDE_MODEL_ID, abortControl
|
|
|
4424
4424
|
let messageCount = 0;
|
|
4425
4425
|
let toolCallCount = 0;
|
|
4426
4426
|
let textBlockCount = 0;
|
|
4427
|
-
//
|
|
4427
|
+
// Create the gen_ai.invoke_agent span as a child of the current active span.
|
|
4428
|
+
//
|
|
4429
|
+
// We use startInactiveSpan because this is an async generator — we can't use
|
|
4430
|
+
// startSpan/startSpanManual (both require a callback, and yields can't cross
|
|
4431
|
+
// callback boundaries). startInactiveSpan creates a span that inherits the
|
|
4432
|
+
// parent from the current active span (build.runner, restored by engine.ts
|
|
4433
|
+
// via Sentry.withActiveSpan).
|
|
4434
|
+
//
|
|
4435
|
+
// For tool spans, we use Sentry.withActiveSpan(agentSpan, ...) to temporarily
|
|
4436
|
+
// make the agent span active so tool spans become its children.
|
|
4428
4437
|
const agentSpan = Sentry.startInactiveSpan({
|
|
4429
4438
|
op: 'gen_ai.invoke_agent',
|
|
4430
|
-
name:
|
|
4439
|
+
name: 'invoke_agent hatchway-builder',
|
|
4431
4440
|
attributes: {
|
|
4441
|
+
'gen_ai.operation.name': 'invoke_agent',
|
|
4432
4442
|
'gen_ai.agent.name': 'hatchway-builder',
|
|
4433
4443
|
'gen_ai.request.model': modelId,
|
|
4434
|
-
'gen_ai.
|
|
4435
|
-
'gen_ai.
|
|
4436
|
-
|
|
4444
|
+
'gen_ai.request.messages': JSON.stringify([{ role: 'user', content: finalPrompt.substring(0, 1000) }]),
|
|
4445
|
+
'gen_ai.request.available_tools': JSON.stringify(['Bash', 'Read', 'Write', 'Edit', 'Glob', 'Grep', 'Task', 'TodoWrite', 'WebFetch']
|
|
4446
|
+
.map(name => ({ name, type: 'function' }))),
|
|
4437
4447
|
},
|
|
4438
4448
|
});
|
|
4439
4449
|
try {
|
|
@@ -4449,19 +4459,23 @@ function createNativeClaudeQuery(modelId = DEFAULT_CLAUDE_MODEL_ID, abortControl
|
|
|
4449
4459
|
if (block.type === 'tool_use') {
|
|
4450
4460
|
toolCallCount++;
|
|
4451
4461
|
debugLog$4(`[runner] [native-sdk] 🔧 Tool call: ${block.name}\n`);
|
|
4452
|
-
// Emit a gen_ai.execute_tool span
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
'gen_ai.
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4462
|
+
// Emit a gen_ai.execute_tool span as a child of gen_ai.invoke_agent.
|
|
4463
|
+
// withActiveSpan temporarily makes agentSpan the active span so
|
|
4464
|
+
// the startSpan inside creates a proper child.
|
|
4465
|
+
Sentry.withActiveSpan(agentSpan, () => {
|
|
4466
|
+
Sentry.startSpan({
|
|
4467
|
+
op: 'gen_ai.execute_tool',
|
|
4468
|
+
name: `execute_tool ${block.name}`,
|
|
4469
|
+
attributes: {
|
|
4470
|
+
'gen_ai.tool.name': block.name,
|
|
4471
|
+
'gen_ai.tool.call_id': block.id,
|
|
4472
|
+
'gen_ai.tool.input': JSON.stringify(block.input).substring(0, 1000),
|
|
4473
|
+
'gen_ai.request.model': modelId,
|
|
4474
|
+
},
|
|
4475
|
+
}, () => {
|
|
4476
|
+
// Span created and ended — marks the tool invocation point
|
|
4477
|
+
});
|
|
4461
4478
|
});
|
|
4462
|
-
// Tool spans are completed immediately since we get input and output
|
|
4463
|
-
// as separate messages — the output is captured in the tool_result handler below
|
|
4464
|
-
toolSpan?.end();
|
|
4465
4479
|
}
|
|
4466
4480
|
else if (block.type === 'text') {
|
|
4467
4481
|
textBlockCount++;
|
|
@@ -4481,6 +4495,11 @@ function createNativeClaudeQuery(modelId = DEFAULT_CLAUDE_MODEL_ID, abortControl
|
|
|
4481
4495
|
process.stderr.write(`[native-sdk] SDK init — plugins: ${JSON.stringify(loadedPlugins)}\n`);
|
|
4482
4496
|
process.stderr.write(`[native-sdk] SDK init — tools: ${toolCount} loaded\n`);
|
|
4483
4497
|
}
|
|
4498
|
+
// Set discovered skills on the agent span
|
|
4499
|
+
if (agentSpan) {
|
|
4500
|
+
agentSpan.setAttribute('gen_ai.agent.skills', discoveredSkills.join(', '));
|
|
4501
|
+
agentSpan.setAttribute('gen_ai.agent.skill_count', discoveredSkills.length);
|
|
4502
|
+
}
|
|
4484
4503
|
if (discoveredSkills.length > 0) {
|
|
4485
4504
|
Sentry.logger.info('SDK initialized with skills', {
|
|
4486
4505
|
skillCount: String(discoveredSkills.length),
|
|
@@ -4514,21 +4533,27 @@ function createNativeClaudeQuery(modelId = DEFAULT_CLAUDE_MODEL_ID, abortControl
|
|
|
4514
4533
|
if (sdkMessage.type === 'result') {
|
|
4515
4534
|
const resultMsg = sdkMessage;
|
|
4516
4535
|
if (agentSpan) {
|
|
4536
|
+
// Standard gen_ai token usage attributes (Sentry AI Agent Monitoring spec)
|
|
4517
4537
|
agentSpan.setAttribute('gen_ai.usage.input_tokens', resultMsg.usage?.input_tokens ?? 0);
|
|
4518
4538
|
agentSpan.setAttribute('gen_ai.usage.output_tokens', resultMsg.usage?.output_tokens ?? 0);
|
|
4519
4539
|
agentSpan.setAttribute('gen_ai.usage.total_tokens', (resultMsg.usage?.input_tokens ?? 0) + (resultMsg.usage?.output_tokens ?? 0));
|
|
4520
|
-
agentSpan.setAttribute('gen_ai.usage.cost_usd', resultMsg.total_cost_usd ?? 0);
|
|
4521
|
-
agentSpan.setAttribute('gen_ai.agent.num_turns', resultMsg.num_turns ?? 0);
|
|
4522
|
-
agentSpan.setAttribute('gen_ai.agent.num_tool_calls', toolCallCount);
|
|
4523
|
-
agentSpan.setAttribute('gen_ai.agent.result', resultMsg.subtype ?? 'unknown');
|
|
4524
|
-
agentSpan.setAttribute('gen_ai.agent.duration_ms', resultMsg.duration_ms ?? 0);
|
|
4525
|
-
agentSpan.setAttribute('gen_ai.agent.duration_api_ms', resultMsg.duration_api_ms ?? 0);
|
|
4526
4540
|
if (resultMsg.usage?.cache_read_input_tokens) {
|
|
4527
|
-
agentSpan.setAttribute('gen_ai.usage.
|
|
4541
|
+
agentSpan.setAttribute('gen_ai.usage.input_tokens.cached', resultMsg.usage.cache_read_input_tokens);
|
|
4528
4542
|
}
|
|
4529
4543
|
if (resultMsg.usage?.cache_creation_input_tokens) {
|
|
4530
|
-
agentSpan.setAttribute('gen_ai.usage.
|
|
4544
|
+
agentSpan.setAttribute('gen_ai.usage.input_tokens.cache_write', resultMsg.usage.cache_creation_input_tokens);
|
|
4531
4545
|
}
|
|
4546
|
+
// Response text (truncated for span safety)
|
|
4547
|
+
if (resultMsg.result) {
|
|
4548
|
+
agentSpan.setAttribute('gen_ai.response.text', JSON.stringify(resultMsg.result.substring(0, 1000)));
|
|
4549
|
+
}
|
|
4550
|
+
// Custom (non-spec) attributes for operational insight
|
|
4551
|
+
agentSpan.setAttribute('hatchway.cost_usd', resultMsg.total_cost_usd ?? 0);
|
|
4552
|
+
agentSpan.setAttribute('hatchway.num_turns', resultMsg.num_turns ?? 0);
|
|
4553
|
+
agentSpan.setAttribute('hatchway.num_tool_calls', toolCallCount);
|
|
4554
|
+
agentSpan.setAttribute('hatchway.result', resultMsg.subtype ?? 'unknown');
|
|
4555
|
+
agentSpan.setAttribute('hatchway.duration_ms', resultMsg.duration_ms ?? 0);
|
|
4556
|
+
agentSpan.setAttribute('hatchway.duration_api_ms', resultMsg.duration_api_ms ?? 0);
|
|
4532
4557
|
}
|
|
4533
4558
|
if (resultMsg.subtype === 'success') {
|
|
4534
4559
|
debugLog$4(`[runner] [native-sdk] ✅ Query complete - ${resultMsg.num_turns} turns, $${resultMsg.total_cost_usd?.toFixed(4)} USD\n`);
|
|
@@ -7177,38 +7202,53 @@ async function createBuildStream(options) {
|
|
|
7177
7202
|
debugLog$1();
|
|
7178
7203
|
const generator = query(fullPrompt, actualWorkingDir, systemPrompt, agent, options.codexThreadId, messageParts);
|
|
7179
7204
|
debugLog$1();
|
|
7205
|
+
// Capture the active Sentry span BEFORE creating the ReadableStream.
|
|
7206
|
+
// The ReadableStream.start() callback runs in a new async context where the
|
|
7207
|
+
// parent build.runner span is no longer active. We restore it with withActiveSpan()
|
|
7208
|
+
// so that gen_ai.invoke_agent spans created inside the query generator are
|
|
7209
|
+
// properly nested as children of the build.runner span.
|
|
7210
|
+
const parentSpan = Sentry.getActiveSpan();
|
|
7180
7211
|
// Create a ReadableStream from the AsyncGenerator
|
|
7181
7212
|
const stream = new ReadableStream({
|
|
7182
7213
|
async start(controller) {
|
|
7183
7214
|
debugLog$1();
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
|
|
7187
|
-
|
|
7188
|
-
|
|
7189
|
-
|
|
7190
|
-
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7215
|
+
const consume = async () => {
|
|
7216
|
+
let chunkCount = 0;
|
|
7217
|
+
try {
|
|
7218
|
+
for await (const chunk of generator) {
|
|
7219
|
+
chunkCount++;
|
|
7220
|
+
if (chunkCount % 5 === 0) {
|
|
7221
|
+
debugLog$1(`[runner] [build-engine] Processed ${chunkCount} chunks from generator\n`);
|
|
7222
|
+
}
|
|
7223
|
+
// Convert chunk to appropriate format
|
|
7224
|
+
if (typeof chunk === 'string') {
|
|
7225
|
+
controller.enqueue(new TextEncoder().encode(chunk));
|
|
7226
|
+
}
|
|
7227
|
+
else if (chunk instanceof Uint8Array) {
|
|
7228
|
+
controller.enqueue(chunk);
|
|
7229
|
+
}
|
|
7230
|
+
else if (typeof chunk === 'object') {
|
|
7231
|
+
controller.enqueue(new TextEncoder().encode(JSON.stringify(chunk)));
|
|
7232
|
+
}
|
|
7200
7233
|
}
|
|
7234
|
+
debugLog$1(`[runner] [build-engine] ✅ Generator exhausted after ${chunkCount} chunks, closing stream\n`);
|
|
7235
|
+
controller.close();
|
|
7201
7236
|
}
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7237
|
+
catch (error) {
|
|
7238
|
+
debugLog$1();
|
|
7239
|
+
controller.error(error);
|
|
7240
|
+
}
|
|
7241
|
+
finally {
|
|
7242
|
+
// Restore the original working directory
|
|
7243
|
+
process.chdir(originalCwd);
|
|
7244
|
+
}
|
|
7245
|
+
};
|
|
7246
|
+
// Restore the parent span context so child spans nest correctly
|
|
7247
|
+
if (parentSpan) {
|
|
7248
|
+
await Sentry.withActiveSpan(parentSpan, consume);
|
|
7208
7249
|
}
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
process.chdir(originalCwd);
|
|
7250
|
+
else {
|
|
7251
|
+
await consume();
|
|
7212
7252
|
}
|
|
7213
7253
|
},
|
|
7214
7254
|
});
|