@rallycry/conveyor-agent 7.2.16 → 7.2.18
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/{chunk-4XJPZGXU.js → chunk-5OMUMWQR.js} +677 -764
- package/dist/chunk-5OMUMWQR.js.map +1 -0
- package/dist/chunk-FDWECEDJ.js +33 -0
- package/dist/chunk-FDWECEDJ.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +1 -17
- package/dist/index.js +2 -2
- package/dist/{task-audit-handler-KGTVMVAP.js → task-audit-handler-TJOM5OJS.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-4XJPZGXU.js.map +0 -1
- package/dist/chunk-C5YAMQJ2.js +0 -17
- package/dist/chunk-C5YAMQJ2.js.map +0 -1
- /package/dist/{task-audit-handler-KGTVMVAP.js.map → task-audit-handler-TJOM5OJS.js.map} +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
+
formatCliEvent,
|
|
2
3
|
imageBlock,
|
|
3
4
|
isImageMimeType,
|
|
4
5
|
textResult
|
|
5
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-FDWECEDJ.js";
|
|
6
7
|
import {
|
|
7
8
|
createHarness,
|
|
8
9
|
createServiceLogger,
|
|
@@ -2230,23 +2231,8 @@ async function buildInitialPrompt(mode, context, isAuto, agentMode) {
|
|
|
2230
2231
|
return [...body, ...instructions].join("\n");
|
|
2231
2232
|
}
|
|
2232
2233
|
|
|
2233
|
-
// src/tools/
|
|
2234
|
+
// src/tools/task-context-tools.ts
|
|
2234
2235
|
import { z } from "zod";
|
|
2235
|
-
var cliEventFormatters = {
|
|
2236
|
-
thinking: (e) => e.message ?? "",
|
|
2237
|
-
tool_use: (e) => `${e.tool}: ${e.input?.slice(0, 1e3) ?? ""}`,
|
|
2238
|
-
tool_result: (e) => `${e.tool} \u2192 ${e.output?.slice(0, 500) ?? ""}${e.isError ? " [ERROR]" : ""}`,
|
|
2239
|
-
message: (e) => e.content ?? "",
|
|
2240
|
-
error: (e) => `ERROR: ${e.message ?? ""}`,
|
|
2241
|
-
completed: (e) => `Completed: ${e.summary ?? ""} (cost: $${e.costUsd ?? "?"}, duration: ${e.durationMs ?? "?"}ms)`,
|
|
2242
|
-
setup_output: (e) => `[${e.stream ?? "stdout"}] ${e.data ?? ""}`,
|
|
2243
|
-
start_command_output: (e) => `[${e.stream ?? "stdout"}] ${e.data ?? ""}`,
|
|
2244
|
-
turn_end: (e) => `Turn complete (${e.toolCalls?.length ?? 0} tool calls)`
|
|
2245
|
-
};
|
|
2246
|
-
function formatCliEvent(e) {
|
|
2247
|
-
const formatter = cliEventFormatters[e.type];
|
|
2248
|
-
return formatter ? formatter(e) : JSON.stringify(e);
|
|
2249
|
-
}
|
|
2250
2236
|
function buildReadTaskChatTool(connection) {
|
|
2251
2237
|
return defineTool(
|
|
2252
2238
|
"read_task_chat",
|
|
@@ -2408,149 +2394,19 @@ function buildGetTaskFileTool(connection) {
|
|
|
2408
2394
|
{ annotations: { readOnlyHint: true } }
|
|
2409
2395
|
);
|
|
2410
2396
|
}
|
|
2411
|
-
function
|
|
2412
|
-
return
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
try {
|
|
2421
|
-
const incidents = await connection.call("searchIncidents", {
|
|
2422
|
-
sessionId: connection.sessionId,
|
|
2423
|
-
status,
|
|
2424
|
-
source
|
|
2425
|
-
});
|
|
2426
|
-
return textResult(JSON.stringify(incidents, null, 2));
|
|
2427
|
-
} catch (error) {
|
|
2428
|
-
return textResult(
|
|
2429
|
-
`Failed to search incidents: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2430
|
-
);
|
|
2431
|
-
}
|
|
2432
|
-
},
|
|
2433
|
-
{ annotations: { readOnlyHint: true } }
|
|
2434
|
-
);
|
|
2435
|
-
}
|
|
2436
|
-
function buildGetTaskIncidentsTool(connection) {
|
|
2437
|
-
return defineTool(
|
|
2438
|
-
"get_task_incidents",
|
|
2439
|
-
"Get incidents linked to the current task. Returns a summary of each incident with available sections. Use get_incident_detail to drill into specific sections (messages, logs, task details).",
|
|
2440
|
-
{
|
|
2441
|
-
task_id: z.string().optional().describe("Task ID (defaults to current task)")
|
|
2442
|
-
},
|
|
2443
|
-
async ({ task_id }) => {
|
|
2444
|
-
try {
|
|
2445
|
-
const incidents = await connection.call("getTaskIncidents", {
|
|
2446
|
-
sessionId: connection.sessionId,
|
|
2447
|
-
taskId: task_id
|
|
2448
|
-
});
|
|
2449
|
-
return textResult(JSON.stringify(incidents, null, 2));
|
|
2450
|
-
} catch (error) {
|
|
2451
|
-
return textResult(
|
|
2452
|
-
`Failed to get task incidents: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2453
|
-
);
|
|
2454
|
-
}
|
|
2455
|
-
},
|
|
2456
|
-
{ annotations: { readOnlyHint: true } }
|
|
2457
|
-
);
|
|
2458
|
-
}
|
|
2459
|
-
function buildGetIncidentDetailTool(connection) {
|
|
2460
|
-
return defineTool(
|
|
2461
|
-
"get_incident_detail",
|
|
2462
|
-
"Get detailed incident data by section. Use after get_task_incidents to drill into specific sections of an incident. Supports pagination and text search within sections.",
|
|
2463
|
-
{
|
|
2464
|
-
incident_id: z.string().describe("The incident ID to get details for"),
|
|
2465
|
-
section: z.enum(["all", "user_report", "task_details", "subtasks", "messages", "logs"]).optional().default("all").describe("Which section to retrieve (default: all)"),
|
|
2466
|
-
search: z.string().optional().describe("Search for specific text within the section (case-insensitive)"),
|
|
2467
|
-
offset: z.number().optional().describe("Line offset for pagination (default 0)"),
|
|
2468
|
-
limit: z.number().optional().describe("Max lines to return (default 100, max 500)")
|
|
2469
|
-
},
|
|
2470
|
-
async ({ incident_id, section, search, offset, limit }) => {
|
|
2471
|
-
try {
|
|
2472
|
-
const result = await connection.call("getIncidentDetail", {
|
|
2473
|
-
sessionId: connection.sessionId,
|
|
2474
|
-
incidentId: incident_id,
|
|
2475
|
-
section,
|
|
2476
|
-
search,
|
|
2477
|
-
offset,
|
|
2478
|
-
limit
|
|
2479
|
-
});
|
|
2480
|
-
if (result.matchCount !== void 0) {
|
|
2481
|
-
return textResult(
|
|
2482
|
-
`${result.content}
|
|
2483
|
-
|
|
2484
|
-
--- ${result.matchCount} matches found (${result.totalLines} total lines in section) ---`
|
|
2485
|
-
);
|
|
2486
|
-
}
|
|
2487
|
-
if (result.hasMore) {
|
|
2488
|
-
return textResult(
|
|
2489
|
-
`${result.content}
|
|
2490
|
-
|
|
2491
|
-
--- Showing lines ${result.offset ?? 0}-${(result.offset ?? 0) + (limit ?? 100)} of ${result.totalLines} (more available \u2014 increase offset) ---`
|
|
2492
|
-
);
|
|
2493
|
-
}
|
|
2494
|
-
return textResult(result.content);
|
|
2495
|
-
} catch (error) {
|
|
2496
|
-
return textResult(
|
|
2497
|
-
`Failed to get incident detail: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2498
|
-
);
|
|
2499
|
-
}
|
|
2500
|
-
},
|
|
2501
|
-
{ annotations: { readOnlyHint: true } }
|
|
2502
|
-
);
|
|
2397
|
+
function buildTaskContextTools(connection) {
|
|
2398
|
+
return [
|
|
2399
|
+
buildReadTaskChatTool(connection),
|
|
2400
|
+
buildGetTaskPlanTool(connection),
|
|
2401
|
+
buildGetTaskTool(connection),
|
|
2402
|
+
buildGetTaskCliTool(connection),
|
|
2403
|
+
buildListTaskFilesTool(connection),
|
|
2404
|
+
buildGetTaskFileTool(connection)
|
|
2405
|
+
];
|
|
2503
2406
|
}
|
|
2504
|
-
function buildQueryGcpLogsTool(connection) {
|
|
2505
|
-
return defineTool(
|
|
2506
|
-
"query_gcp_logs",
|
|
2507
|
-
"Query GCP Cloud Logging for the current project. Returns log entries matching the given filters. Use severity to filter by minimum log level. The project's GCP credentials are used automatically.",
|
|
2508
|
-
{
|
|
2509
|
-
filter: z.string().optional().describe(
|
|
2510
|
-
`Cloud Logging filter expression (e.g., 'resource.type="gce_instance"'). Max 1000 chars.`
|
|
2511
|
-
),
|
|
2512
|
-
start_time: z.string().optional().describe("Start time in ISO 8601 format (e.g., '2024-01-01T00:00:00Z')"),
|
|
2513
|
-
end_time: z.string().optional().describe("End time in ISO 8601 format (e.g., '2024-01-02T00:00:00Z')"),
|
|
2514
|
-
severity: z.enum([
|
|
2515
|
-
"DEFAULT",
|
|
2516
|
-
"DEBUG",
|
|
2517
|
-
"INFO",
|
|
2518
|
-
"NOTICE",
|
|
2519
|
-
"WARNING",
|
|
2520
|
-
"ERROR",
|
|
2521
|
-
"CRITICAL",
|
|
2522
|
-
"ALERT",
|
|
2523
|
-
"EMERGENCY"
|
|
2524
|
-
]).optional().describe("Minimum severity level to filter by (default: all levels)"),
|
|
2525
|
-
page_size: z.number().optional().describe("Number of log entries to return (default 100, max 500)")
|
|
2526
|
-
},
|
|
2527
|
-
async ({ filter, start_time, end_time, severity, page_size }) => {
|
|
2528
|
-
try {
|
|
2529
|
-
const result = await connection.call("queryGcpLogs", {
|
|
2530
|
-
sessionId: connection.sessionId,
|
|
2531
|
-
filter,
|
|
2532
|
-
startTime: start_time,
|
|
2533
|
-
endTime: end_time,
|
|
2534
|
-
severity,
|
|
2535
|
-
pageSize: page_size
|
|
2536
|
-
});
|
|
2537
|
-
if (result.entries.length === 0) {
|
|
2538
|
-
return textResult("No log entries found matching the given filters.");
|
|
2539
|
-
}
|
|
2540
|
-
const summary = `Found ${result.entries.length} log entries${result.hasMore ? " (more available \u2014 refine filters or increase page_size)" : ""}.`;
|
|
2541
|
-
const formatted = result.entries.map((e) => `[${e.timestamp}] [${e.severity}] ${e.message}`).join("\n");
|
|
2542
|
-
return textResult(`${summary}
|
|
2543
2407
|
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
return textResult(
|
|
2547
|
-
`Failed to query GCP logs: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2548
|
-
);
|
|
2549
|
-
}
|
|
2550
|
-
},
|
|
2551
|
-
{ annotations: { readOnlyHint: true } }
|
|
2552
|
-
);
|
|
2553
|
-
}
|
|
2408
|
+
// src/tools/dependency-suggestion-tools.ts
|
|
2409
|
+
import { z as z2 } from "zod";
|
|
2554
2410
|
function buildGetDependenciesTool(connection) {
|
|
2555
2411
|
return defineTool(
|
|
2556
2412
|
"get_dependencies",
|
|
@@ -2576,8 +2432,8 @@ function buildGetSuggestionsTool(connection) {
|
|
|
2576
2432
|
"get_suggestions",
|
|
2577
2433
|
"List project suggestions sorted by vote score. Use this to see what the team thinks is important.",
|
|
2578
2434
|
{
|
|
2579
|
-
status:
|
|
2580
|
-
limit:
|
|
2435
|
+
status: z2.string().optional().describe("Filter by status: Open, Accepted, Rejected, Implemented"),
|
|
2436
|
+
limit: z2.number().optional().describe("Max results (default 20)")
|
|
2581
2437
|
},
|
|
2582
2438
|
async ({ status: _status, limit: _limit }) => {
|
|
2583
2439
|
try {
|
|
@@ -2597,13 +2453,16 @@ function buildGetSuggestionsTool(connection) {
|
|
|
2597
2453
|
{ annotations: { readOnlyHint: true } }
|
|
2598
2454
|
);
|
|
2599
2455
|
}
|
|
2456
|
+
|
|
2457
|
+
// src/tools/mutation-tools.ts
|
|
2458
|
+
import { z as z3 } from "zod";
|
|
2600
2459
|
function buildPostToChatTool(connection) {
|
|
2601
2460
|
return defineTool(
|
|
2602
2461
|
"post_to_chat",
|
|
2603
2462
|
"Post a message to a task chat. Your normal replies already appear in chat \u2014 only use this for explicit out-of-band updates or posting to a child task's chat.",
|
|
2604
2463
|
{
|
|
2605
|
-
message:
|
|
2606
|
-
task_id:
|
|
2464
|
+
message: z3.string().describe("The message to post to the team"),
|
|
2465
|
+
task_id: z3.string().optional().describe("Child task ID to post to. Omit to post to the current task's chat.")
|
|
2607
2466
|
},
|
|
2608
2467
|
async ({ message, task_id }) => {
|
|
2609
2468
|
try {
|
|
@@ -2630,8 +2489,8 @@ function buildForceUpdateTaskStatusTool(connection) {
|
|
|
2630
2489
|
"force_update_task_status",
|
|
2631
2490
|
"EMERGENCY ONLY: Force-override a task's Kanban status. Status transitions happen automatically (building sets InProgress, PR creation sets ReviewPR, merge sets ReviewDev). Only use this if an automatic transition failed or a task is stuck in the wrong status. Omit task_id to update the current task, or provide a child task ID.",
|
|
2632
2491
|
{
|
|
2633
|
-
status:
|
|
2634
|
-
task_id:
|
|
2492
|
+
status: z3.enum(["InProgress", "ReviewPR", "ReviewDev", "Complete"]).describe("The new status for the task"),
|
|
2493
|
+
task_id: z3.string().optional().describe("Child task ID to update. Omit to update the current task.")
|
|
2635
2494
|
},
|
|
2636
2495
|
async ({ status, task_id }) => {
|
|
2637
2496
|
try {
|
|
@@ -2662,15 +2521,15 @@ function buildCreatePullRequestTool(connection, config) {
|
|
|
2662
2521
|
"create_pull_request",
|
|
2663
2522
|
"Create a GitHub pull request for this task. Automatically stages uncommitted changes, commits them, and pushes before creating the PR. Use this instead of gh CLI or git commands to create PRs.",
|
|
2664
2523
|
{
|
|
2665
|
-
title:
|
|
2666
|
-
body:
|
|
2667
|
-
branch:
|
|
2524
|
+
title: z3.string().describe("The PR title"),
|
|
2525
|
+
body: z3.string().describe("The PR description/body in markdown"),
|
|
2526
|
+
branch: z3.string().optional().describe(
|
|
2668
2527
|
"The head branch name for the PR. If the task doesn't have a branch set, this will be used. Defaults to the task's existing branch."
|
|
2669
2528
|
),
|
|
2670
|
-
baseBranch:
|
|
2529
|
+
baseBranch: z3.string().optional().describe(
|
|
2671
2530
|
"The base branch to target for the PR (e.g. 'main', 'develop'). Defaults to the project's configured dev branch."
|
|
2672
2531
|
),
|
|
2673
|
-
commitMessage:
|
|
2532
|
+
commitMessage: z3.string().optional().describe(
|
|
2674
2533
|
"Commit message for staging uncommitted changes. If not provided, a default message based on the PR title will be used."
|
|
2675
2534
|
)
|
|
2676
2535
|
},
|
|
@@ -2748,7 +2607,7 @@ function buildAddDependencyTool(connection) {
|
|
|
2748
2607
|
"add_dependency",
|
|
2749
2608
|
"Add a dependency \u2014 this task cannot start until the specified task is merged to dev",
|
|
2750
2609
|
{
|
|
2751
|
-
depends_on_slug_or_id:
|
|
2610
|
+
depends_on_slug_or_id: z3.string().describe("Slug or ID of the task this task depends on")
|
|
2752
2611
|
},
|
|
2753
2612
|
async ({ depends_on_slug_or_id }) => {
|
|
2754
2613
|
try {
|
|
@@ -2770,7 +2629,7 @@ function buildRemoveDependencyTool(connection) {
|
|
|
2770
2629
|
"remove_dependency",
|
|
2771
2630
|
"Remove a dependency from this task",
|
|
2772
2631
|
{
|
|
2773
|
-
depends_on_slug_or_id:
|
|
2632
|
+
depends_on_slug_or_id: z3.string().describe("Slug or ID of the task to remove as dependency")
|
|
2774
2633
|
},
|
|
2775
2634
|
async ({ depends_on_slug_or_id }) => {
|
|
2776
2635
|
try {
|
|
@@ -2792,10 +2651,10 @@ function buildCreateFollowUpTaskTool(connection) {
|
|
|
2792
2651
|
"create_follow_up_task",
|
|
2793
2652
|
"Create a follow-up task in this project that depends on the current task. Use for out-of-scope work, v1.1 features, or cleanup that should happen after this task merges.",
|
|
2794
2653
|
{
|
|
2795
|
-
title:
|
|
2796
|
-
description:
|
|
2797
|
-
plan:
|
|
2798
|
-
story_point_value:
|
|
2654
|
+
title: z3.string().describe("Follow-up task title"),
|
|
2655
|
+
description: z3.string().optional().describe("Brief description of the follow-up work"),
|
|
2656
|
+
plan: z3.string().optional().describe("Implementation plan if known"),
|
|
2657
|
+
story_point_value: z3.number().optional().describe("Story point estimate (1=Common, 2=Magic, 3=Rare, 5=Unique)")
|
|
2799
2658
|
},
|
|
2800
2659
|
async ({ title, description, plan, story_point_value }) => {
|
|
2801
2660
|
try {
|
|
@@ -2822,11 +2681,11 @@ function buildCreateSuggestionTool(connection) {
|
|
|
2822
2681
|
"create_suggestion",
|
|
2823
2682
|
"Suggest a feature, improvement, or idea for the project. If you want to recommend something \u2014 a document, a rule, a feature, a task, an optimization \u2014 use this tool. If a similar suggestion already exists, your vote will be added to it instead of creating a duplicate.",
|
|
2824
2683
|
{
|
|
2825
|
-
title:
|
|
2826
|
-
description:
|
|
2684
|
+
title: z3.string().describe("Short title for the suggestion"),
|
|
2685
|
+
description: z3.string().optional().describe(
|
|
2827
2686
|
"1-2 sentence description of what should change and why. Keep concise and project-focused."
|
|
2828
2687
|
),
|
|
2829
|
-
tag_names:
|
|
2688
|
+
tag_names: z3.array(z3.string()).optional().describe("Tag names to categorize the suggestion")
|
|
2830
2689
|
},
|
|
2831
2690
|
async ({ title, description, tag_names }) => {
|
|
2832
2691
|
try {
|
|
@@ -2855,8 +2714,8 @@ function buildVoteSuggestionTool(connection) {
|
|
|
2855
2714
|
"vote_suggestion",
|
|
2856
2715
|
"Vote on a project suggestion. Use +1 to upvote or -1 to downvote.",
|
|
2857
2716
|
{
|
|
2858
|
-
suggestion_id:
|
|
2859
|
-
value:
|
|
2717
|
+
suggestion_id: z3.string().describe("The suggestion ID to vote on"),
|
|
2718
|
+
value: z3.number().refine((v) => v === 1 || v === -1, { message: "Value must be 1 or -1" }).describe("+1 to upvote, -1 to downvote")
|
|
2860
2719
|
},
|
|
2861
2720
|
async ({ suggestion_id, value }) => {
|
|
2862
2721
|
try {
|
|
@@ -2874,41 +2733,38 @@ function buildVoteSuggestionTool(connection) {
|
|
|
2874
2733
|
}
|
|
2875
2734
|
);
|
|
2876
2735
|
}
|
|
2877
|
-
function
|
|
2878
|
-
|
|
2879
|
-
buildReadTaskChatTool(connection),
|
|
2736
|
+
function buildMutationTools(connection, config) {
|
|
2737
|
+
return [
|
|
2880
2738
|
buildPostToChatTool(connection),
|
|
2881
|
-
buildGetTaskPlanTool(connection),
|
|
2882
|
-
buildGetTaskTool(connection),
|
|
2883
|
-
buildGetTaskCliTool(connection),
|
|
2884
|
-
buildListTaskFilesTool(connection),
|
|
2885
|
-
buildGetTaskFileTool(connection),
|
|
2886
|
-
buildSearchIncidentsTool(connection),
|
|
2887
|
-
buildGetTaskIncidentsTool(connection),
|
|
2888
|
-
buildGetIncidentDetailTool(connection),
|
|
2889
|
-
buildQueryGcpLogsTool(connection),
|
|
2890
2739
|
buildCreatePullRequestTool(connection, config),
|
|
2891
2740
|
buildAddDependencyTool(connection),
|
|
2892
2741
|
buildRemoveDependencyTool(connection),
|
|
2893
|
-
buildGetDependenciesTool(connection),
|
|
2894
2742
|
buildCreateFollowUpTaskTool(connection),
|
|
2895
2743
|
buildCreateSuggestionTool(connection),
|
|
2896
|
-
buildVoteSuggestionTool(connection)
|
|
2897
|
-
|
|
2744
|
+
buildVoteSuggestionTool(connection)
|
|
2745
|
+
];
|
|
2746
|
+
}
|
|
2747
|
+
|
|
2748
|
+
// src/tools/common-tools.ts
|
|
2749
|
+
function buildCommonTools(connection, config) {
|
|
2750
|
+
return [
|
|
2751
|
+
...buildTaskContextTools(connection),
|
|
2752
|
+
buildGetDependenciesTool(connection),
|
|
2753
|
+
buildGetSuggestionsTool(connection),
|
|
2754
|
+
...buildMutationTools(connection, config)
|
|
2898
2755
|
];
|
|
2899
|
-
return tools;
|
|
2900
2756
|
}
|
|
2901
2757
|
|
|
2902
2758
|
// src/tools/pm-tools.ts
|
|
2903
|
-
import { z as
|
|
2759
|
+
import { z as z4 } from "zod";
|
|
2904
2760
|
var SP_DESCRIPTION = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique)";
|
|
2905
2761
|
function buildUpdateTaskTool(connection) {
|
|
2906
2762
|
return defineTool(
|
|
2907
2763
|
"update_task",
|
|
2908
2764
|
"Save the finalized task plan and/or description",
|
|
2909
2765
|
{
|
|
2910
|
-
plan:
|
|
2911
|
-
description:
|
|
2766
|
+
plan: z4.string().optional().describe("The task plan in markdown"),
|
|
2767
|
+
description: z4.string().optional().describe("Updated task description")
|
|
2912
2768
|
},
|
|
2913
2769
|
async ({ plan, description }) => {
|
|
2914
2770
|
try {
|
|
@@ -2929,11 +2785,11 @@ function buildCreateSubtaskTool(connection) {
|
|
|
2929
2785
|
"create_subtask",
|
|
2930
2786
|
"Create a subtask under the current parent task. Use for breaking complex tasks into smaller pieces.",
|
|
2931
2787
|
{
|
|
2932
|
-
title:
|
|
2933
|
-
description:
|
|
2934
|
-
plan:
|
|
2935
|
-
ordinal:
|
|
2936
|
-
storyPointValue:
|
|
2788
|
+
title: z4.string().describe("Subtask title"),
|
|
2789
|
+
description: z4.string().optional().describe("Brief description"),
|
|
2790
|
+
plan: z4.string().optional().describe("Implementation plan in markdown"),
|
|
2791
|
+
ordinal: z4.number().optional().describe("Step/order number (0-based)"),
|
|
2792
|
+
storyPointValue: z4.number().optional().describe(SP_DESCRIPTION)
|
|
2937
2793
|
},
|
|
2938
2794
|
async ({ title, description, plan, ordinal, storyPointValue }) => {
|
|
2939
2795
|
try {
|
|
@@ -2959,12 +2815,12 @@ function buildUpdateSubtaskTool(connection) {
|
|
|
2959
2815
|
"update_subtask",
|
|
2960
2816
|
"Update an existing subtask's fields",
|
|
2961
2817
|
{
|
|
2962
|
-
subtaskId:
|
|
2963
|
-
title:
|
|
2964
|
-
description:
|
|
2965
|
-
plan:
|
|
2966
|
-
ordinal:
|
|
2967
|
-
storyPointValue:
|
|
2818
|
+
subtaskId: z4.string().describe("The subtask ID to update"),
|
|
2819
|
+
title: z4.string().optional(),
|
|
2820
|
+
description: z4.string().optional(),
|
|
2821
|
+
plan: z4.string().optional(),
|
|
2822
|
+
ordinal: z4.number().optional(),
|
|
2823
|
+
storyPointValue: z4.number().optional().describe(SP_DESCRIPTION)
|
|
2968
2824
|
},
|
|
2969
2825
|
async ({ subtaskId, title, description, plan, storyPointValue }) => {
|
|
2970
2826
|
try {
|
|
@@ -2987,7 +2843,7 @@ function buildDeleteSubtaskTool(connection) {
|
|
|
2987
2843
|
return defineTool(
|
|
2988
2844
|
"delete_subtask",
|
|
2989
2845
|
"Delete a subtask",
|
|
2990
|
-
{ subtaskId:
|
|
2846
|
+
{ subtaskId: z4.string().describe("The subtask ID to delete") },
|
|
2991
2847
|
async ({ subtaskId }) => {
|
|
2992
2848
|
try {
|
|
2993
2849
|
await connection.call("deleteSubtask", {
|
|
@@ -3025,7 +2881,7 @@ function buildPackTools(connection) {
|
|
|
3025
2881
|
"start_child_cloud_build",
|
|
3026
2882
|
"Start a cloud build for a child task. The child must be in Open status with story points and an agent assigned.",
|
|
3027
2883
|
{
|
|
3028
|
-
childTaskId:
|
|
2884
|
+
childTaskId: z4.string().describe("The child task ID to start a cloud build for")
|
|
3029
2885
|
},
|
|
3030
2886
|
async ({ childTaskId }) => {
|
|
3031
2887
|
try {
|
|
@@ -3045,7 +2901,7 @@ function buildPackTools(connection) {
|
|
|
3045
2901
|
"stop_child_build",
|
|
3046
2902
|
"Stop a running cloud build for a child task. Sends a stop signal to the child agent.",
|
|
3047
2903
|
{
|
|
3048
|
-
childTaskId:
|
|
2904
|
+
childTaskId: z4.string().describe("The child task ID whose build should be stopped")
|
|
3049
2905
|
},
|
|
3050
2906
|
async ({ childTaskId }) => {
|
|
3051
2907
|
try {
|
|
@@ -3065,7 +2921,7 @@ function buildPackTools(connection) {
|
|
|
3065
2921
|
"approve_and_merge_pr",
|
|
3066
2922
|
"Approve and merge a child task's pull request. Only succeeds if all CI/CD checks are passing. Returns an error if checks are pending (retry after waiting) or failed (investigate). The child task must be in ReviewPR status.",
|
|
3067
2923
|
{
|
|
3068
|
-
childTaskId:
|
|
2924
|
+
childTaskId: z4.string().describe("The child task ID whose PR should be approved and merged")
|
|
3069
2925
|
},
|
|
3070
2926
|
async ({ childTaskId }) => {
|
|
3071
2927
|
try {
|
|
@@ -3103,7 +2959,7 @@ function buildPmTools(connection, options) {
|
|
|
3103
2959
|
}
|
|
3104
2960
|
|
|
3105
2961
|
// src/tools/discovery-tools.ts
|
|
3106
|
-
import { z as
|
|
2962
|
+
import { z as z5 } from "zod";
|
|
3107
2963
|
var SP_DESCRIPTION2 = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique)";
|
|
3108
2964
|
function buildDiscoveryTools(connection) {
|
|
3109
2965
|
return [
|
|
@@ -3111,11 +2967,11 @@ function buildDiscoveryTools(connection) {
|
|
|
3111
2967
|
"update_task_properties",
|
|
3112
2968
|
"Set one or more task properties in a single call. All fields are optional \u2014 only include the fields you want to update.",
|
|
3113
2969
|
{
|
|
3114
|
-
title:
|
|
3115
|
-
storyPointValue:
|
|
3116
|
-
tagNames:
|
|
3117
|
-
githubPRUrl:
|
|
3118
|
-
githubBranch:
|
|
2970
|
+
title: z5.string().optional().describe("The new task title"),
|
|
2971
|
+
storyPointValue: z5.number().optional().describe(SP_DESCRIPTION2),
|
|
2972
|
+
tagNames: z5.array(z5.string()).optional().describe("Array of tag names to assign"),
|
|
2973
|
+
githubPRUrl: z5.string().url().optional().describe("GitHub pull request URL to link to this task"),
|
|
2974
|
+
githubBranch: z5.string().optional().describe("Set the GitHub branch name for this task (e.g. 'conveyor/my-feature-abc123')")
|
|
3119
2975
|
},
|
|
3120
2976
|
async ({ title, storyPointValue, tagNames, githubPRUrl, githubBranch }) => {
|
|
3121
2977
|
try {
|
|
@@ -3146,14 +3002,14 @@ function buildDiscoveryTools(connection) {
|
|
|
3146
3002
|
}
|
|
3147
3003
|
|
|
3148
3004
|
// src/tools/code-review-tools.ts
|
|
3149
|
-
import { z as
|
|
3005
|
+
import { z as z6 } from "zod";
|
|
3150
3006
|
function buildCodeReviewTools(connection) {
|
|
3151
3007
|
return [
|
|
3152
3008
|
defineTool(
|
|
3153
3009
|
"approve_code_review",
|
|
3154
3010
|
"Approve the code review. Use this when the code passes all review criteria and is ready to merge.",
|
|
3155
3011
|
{
|
|
3156
|
-
summary:
|
|
3012
|
+
summary: z6.string().describe("Brief summary of what was reviewed and why it looks good")
|
|
3157
3013
|
},
|
|
3158
3014
|
async ({ summary }) => {
|
|
3159
3015
|
const content = `**Code Review: Approved** :white_check_mark:
|
|
@@ -3176,15 +3032,15 @@ ${summary}`;
|
|
|
3176
3032
|
"request_code_changes",
|
|
3177
3033
|
"Request changes during code review. Use this when substantive issues are found that need to be fixed before merge.",
|
|
3178
3034
|
{
|
|
3179
|
-
issues:
|
|
3180
|
-
|
|
3181
|
-
file:
|
|
3182
|
-
line:
|
|
3183
|
-
severity:
|
|
3184
|
-
description:
|
|
3035
|
+
issues: z6.array(
|
|
3036
|
+
z6.object({
|
|
3037
|
+
file: z6.string().describe("File path where the issue was found"),
|
|
3038
|
+
line: z6.number().optional().describe("Line number (if applicable)"),
|
|
3039
|
+
severity: z6.enum(["critical", "major", "minor"]).describe("Issue severity"),
|
|
3040
|
+
description: z6.string().describe("What is wrong and how to fix it")
|
|
3185
3041
|
})
|
|
3186
3042
|
).describe("List of issues found during review"),
|
|
3187
|
-
summary:
|
|
3043
|
+
summary: z6.string().describe("Brief overall summary of the review findings")
|
|
3188
3044
|
},
|
|
3189
3045
|
async ({ issues, summary }) => {
|
|
3190
3046
|
const issueLines = issues.map((issue) => {
|
|
@@ -3214,10 +3070,10 @@ ${issueLines}`;
|
|
|
3214
3070
|
}
|
|
3215
3071
|
|
|
3216
3072
|
// src/tools/debug-tools.ts
|
|
3217
|
-
import { z as
|
|
3073
|
+
import { z as z9 } from "zod";
|
|
3218
3074
|
|
|
3219
3075
|
// src/tools/telemetry-tools.ts
|
|
3220
|
-
import { z as
|
|
3076
|
+
import { z as z7 } from "zod";
|
|
3221
3077
|
|
|
3222
3078
|
// src/debug/telemetry-injector.ts
|
|
3223
3079
|
var BUFFER_SIZE = 200;
|
|
@@ -3612,12 +3468,12 @@ function buildGetTelemetryTool(manager) {
|
|
|
3612
3468
|
"debug_get_telemetry",
|
|
3613
3469
|
"Query structured telemetry events (HTTP requests, database queries, Socket.IO events, errors) captured from the running dev server. Returns filtered, structured data instead of raw logs.",
|
|
3614
3470
|
{
|
|
3615
|
-
type:
|
|
3616
|
-
urlPattern:
|
|
3617
|
-
minDuration:
|
|
3618
|
-
errorOnly:
|
|
3619
|
-
since:
|
|
3620
|
-
limit:
|
|
3471
|
+
type: z7.enum(["http", "db", "socket", "error"]).optional().describe("Filter by event type"),
|
|
3472
|
+
urlPattern: z7.string().optional().describe("Regex pattern to filter HTTP events by URL"),
|
|
3473
|
+
minDuration: z7.number().optional().describe("Minimum duration in ms \u2014 only return events slower than this"),
|
|
3474
|
+
errorOnly: z7.boolean().optional().describe("Only return error events and HTTP 4xx/5xx responses"),
|
|
3475
|
+
since: z7.number().optional().describe("Only return events after this timestamp (ms since epoch)"),
|
|
3476
|
+
limit: z7.number().optional().describe("Max events to return (default: 20, from most recent)")
|
|
3621
3477
|
},
|
|
3622
3478
|
async ({ type, urlPattern, minDuration, errorOnly, since, limit }) => {
|
|
3623
3479
|
const clientOrErr = requireDebugClient(manager);
|
|
@@ -3687,7 +3543,7 @@ function buildTelemetryTools(manager) {
|
|
|
3687
3543
|
}
|
|
3688
3544
|
|
|
3689
3545
|
// src/tools/client-debug-tools.ts
|
|
3690
|
-
import { z as
|
|
3546
|
+
import { z as z8 } from "zod";
|
|
3691
3547
|
function requirePlaywrightClient(manager) {
|
|
3692
3548
|
if (!manager.isClientDebugMode()) {
|
|
3693
3549
|
return "Client debug mode is not active. Use debug_enter_mode with clientSide: true first.";
|
|
@@ -3707,11 +3563,11 @@ function buildClientBreakpointTools(manager) {
|
|
|
3707
3563
|
"debug_set_client_breakpoint",
|
|
3708
3564
|
"Set a breakpoint in client-side code running in the headless Chromium browser. V8 resolves source maps automatically, so original .tsx/.ts file paths work. Use this for React components, client utilities, and browser-side code.",
|
|
3709
3565
|
{
|
|
3710
|
-
file:
|
|
3566
|
+
file: z8.string().describe(
|
|
3711
3567
|
"Original source file path (e.g., src/components/App.tsx) \u2014 source maps resolve automatically"
|
|
3712
3568
|
),
|
|
3713
|
-
line:
|
|
3714
|
-
condition:
|
|
3569
|
+
line: z8.number().describe("Line number (1-based) in the original source file"),
|
|
3570
|
+
condition: z8.string().optional().describe("JavaScript condition expression \u2014 breakpoint only triggers when truthy")
|
|
3715
3571
|
},
|
|
3716
3572
|
async ({ file, line, condition }) => {
|
|
3717
3573
|
const clientOrErr = requirePlaywrightClient(manager);
|
|
@@ -3733,7 +3589,7 @@ Breakpoint ID: ${breakpointId}${sourceMapNote}`
|
|
|
3733
3589
|
"debug_remove_client_breakpoint",
|
|
3734
3590
|
"Remove a previously set client-side breakpoint by its ID.",
|
|
3735
3591
|
{
|
|
3736
|
-
breakpointId:
|
|
3592
|
+
breakpointId: z8.string().describe("The breakpoint ID returned by debug_set_client_breakpoint")
|
|
3737
3593
|
},
|
|
3738
3594
|
async ({ breakpointId }) => {
|
|
3739
3595
|
const clientOrErr = requirePlaywrightClient(manager);
|
|
@@ -3813,8 +3669,8 @@ ${JSON.stringify(queuedHits, null, 2)}`
|
|
|
3813
3669
|
"debug_evaluate_client",
|
|
3814
3670
|
"Evaluate a JavaScript expression in the client-side browser context. When paused at a client breakpoint, evaluates in the paused scope. Can access DOM, window, React internals, etc.",
|
|
3815
3671
|
{
|
|
3816
|
-
expression:
|
|
3817
|
-
frameIndex:
|
|
3672
|
+
expression: z8.string().describe("JavaScript expression to evaluate in the browser context"),
|
|
3673
|
+
frameIndex: z8.number().optional().describe("Call stack frame index (0 = top frame). Defaults to the top frame.")
|
|
3818
3674
|
},
|
|
3819
3675
|
async ({ expression, frameIndex }) => {
|
|
3820
3676
|
const clientOrErr = requirePlaywrightClient(manager);
|
|
@@ -3887,7 +3743,7 @@ function buildClientInteractionTools(manager) {
|
|
|
3887
3743
|
"debug_navigate_client",
|
|
3888
3744
|
"Navigate the headless browser to a specific URL. Use this to reproduce specific flows or visit different pages.",
|
|
3889
3745
|
{
|
|
3890
|
-
url:
|
|
3746
|
+
url: z8.string().describe("URL to navigate to (e.g., http://localhost:3000/dashboard)")
|
|
3891
3747
|
},
|
|
3892
3748
|
async ({ url }) => {
|
|
3893
3749
|
const clientOrErr = requirePlaywrightClient(manager);
|
|
@@ -3904,7 +3760,7 @@ function buildClientInteractionTools(manager) {
|
|
|
3904
3760
|
"debug_click_client",
|
|
3905
3761
|
"Click an element on the page in the headless browser. Use CSS selectors to target elements. Useful for reproducing bugs by interacting with the UI programmatically.",
|
|
3906
3762
|
{
|
|
3907
|
-
selector:
|
|
3763
|
+
selector: z8.string().describe(
|
|
3908
3764
|
"CSS selector of the element to click (e.g., 'button.submit', '#login-form input[type=submit]')"
|
|
3909
3765
|
)
|
|
3910
3766
|
},
|
|
@@ -3926,8 +3782,8 @@ function buildClientConsoleTool(manager) {
|
|
|
3926
3782
|
"debug_get_client_console",
|
|
3927
3783
|
"Get console messages captured from the headless browser. Includes console.log, warn, error, etc.",
|
|
3928
3784
|
{
|
|
3929
|
-
level:
|
|
3930
|
-
limit:
|
|
3785
|
+
level: z8.string().optional().describe("Filter by console level: log, warn, error, info, debug"),
|
|
3786
|
+
limit: z8.number().optional().describe("Maximum number of recent messages to return (default: all)")
|
|
3931
3787
|
},
|
|
3932
3788
|
// oxlint-disable-next-line require-await
|
|
3933
3789
|
async ({ level, limit }) => {
|
|
@@ -3954,8 +3810,8 @@ function buildClientNetworkTool(manager) {
|
|
|
3954
3810
|
"debug_get_client_network",
|
|
3955
3811
|
"Get network requests captured from the headless browser. Shows URLs, methods, status codes, and timing.",
|
|
3956
3812
|
{
|
|
3957
|
-
filter:
|
|
3958
|
-
limit:
|
|
3813
|
+
filter: z8.string().optional().describe("Regex pattern to filter requests by URL"),
|
|
3814
|
+
limit: z8.number().optional().describe("Maximum number of recent requests to return (default: all)")
|
|
3959
3815
|
},
|
|
3960
3816
|
// oxlint-disable-next-line require-await
|
|
3961
3817
|
async ({ filter, limit }) => {
|
|
@@ -3983,7 +3839,7 @@ function buildClientErrorsTool(manager) {
|
|
|
3983
3839
|
"debug_get_client_errors",
|
|
3984
3840
|
"Get uncaught errors captured from the headless browser. Includes error messages and source-mapped stack traces.",
|
|
3985
3841
|
{
|
|
3986
|
-
limit:
|
|
3842
|
+
limit: z8.number().optional().describe("Maximum number of recent errors to return (default: all)")
|
|
3987
3843
|
},
|
|
3988
3844
|
// oxlint-disable-next-line require-await
|
|
3989
3845
|
async ({ limit }) => {
|
|
@@ -4077,12 +3933,12 @@ function buildDebugLifecycleTools(manager) {
|
|
|
4077
3933
|
"debug_enter_mode",
|
|
4078
3934
|
"Activate debug mode: restarts the dev server with Node.js --inspect flag and connects the CDP debugger. Optionally launch a headless Chromium browser for client-side debugging. Use serverSide for backend breakpoints, clientSide for frontend breakpoints, or both for full-stack.",
|
|
4079
3935
|
{
|
|
4080
|
-
hypothesis:
|
|
4081
|
-
serverSide:
|
|
3936
|
+
hypothesis: z9.string().optional().describe("Your hypothesis about the bug \u2014 helps track debugging intent"),
|
|
3937
|
+
serverSide: z9.boolean().optional().describe(
|
|
4082
3938
|
"Enable server-side Node.js debugging (default: true if clientSide is not set)"
|
|
4083
3939
|
),
|
|
4084
|
-
clientSide:
|
|
4085
|
-
previewUrl:
|
|
3940
|
+
clientSide: z9.boolean().optional().describe("Enable client-side browser debugging via headless Chromium + Playwright"),
|
|
3941
|
+
previewUrl: z9.string().optional().describe(
|
|
4086
3942
|
"Preview URL for client-side debugging (e.g., http://localhost:3000). Required when clientSide is true."
|
|
4087
3943
|
)
|
|
4088
3944
|
},
|
|
@@ -4118,9 +3974,9 @@ function buildBreakpointTools(manager) {
|
|
|
4118
3974
|
"debug_set_breakpoint",
|
|
4119
3975
|
"Set a breakpoint at the specified file and line number. Optionally provide a condition expression that must evaluate to true for the breakpoint to pause execution.",
|
|
4120
3976
|
{
|
|
4121
|
-
file:
|
|
4122
|
-
line:
|
|
4123
|
-
condition:
|
|
3977
|
+
file: z9.string().describe("Absolute or relative file path to set the breakpoint in"),
|
|
3978
|
+
line: z9.number().describe("Line number (1-based) to set the breakpoint on"),
|
|
3979
|
+
condition: z9.string().optional().describe("JavaScript condition expression \u2014 breakpoint only triggers when truthy")
|
|
4124
3980
|
},
|
|
4125
3981
|
async ({ file, line, condition }) => {
|
|
4126
3982
|
const clientOrErr = requireDebugClient2(manager);
|
|
@@ -4142,7 +3998,7 @@ Breakpoint ID: ${breakpointId}`
|
|
|
4142
3998
|
"debug_remove_breakpoint",
|
|
4143
3999
|
"Remove a previously set breakpoint by its ID.",
|
|
4144
4000
|
{
|
|
4145
|
-
breakpointId:
|
|
4001
|
+
breakpointId: z9.string().describe("The breakpoint ID returned by debug_set_breakpoint")
|
|
4146
4002
|
},
|
|
4147
4003
|
async ({ breakpointId }) => {
|
|
4148
4004
|
const clientOrErr = requireDebugClient2(manager);
|
|
@@ -4223,8 +4079,8 @@ ${JSON.stringify(queuedHits, null, 2)}`
|
|
|
4223
4079
|
"debug_evaluate",
|
|
4224
4080
|
"Evaluate a JavaScript expression in the current paused scope (or globally if not paused). When paused, use frameIndex to evaluate in a specific call frame.",
|
|
4225
4081
|
{
|
|
4226
|
-
expression:
|
|
4227
|
-
frameIndex:
|
|
4082
|
+
expression: z9.string().describe("The JavaScript expression to evaluate"),
|
|
4083
|
+
frameIndex: z9.number().optional().describe("Call stack frame index (0 = top frame). Defaults to the top frame.")
|
|
4228
4084
|
},
|
|
4229
4085
|
async ({ expression, frameIndex }) => {
|
|
4230
4086
|
const clientOrErr = requireDebugClient2(manager);
|
|
@@ -4252,12 +4108,12 @@ function buildProbeManagementTools(manager) {
|
|
|
4252
4108
|
"debug_add_probe",
|
|
4253
4109
|
"Add a debug probe at a specific code location. Captures expression values each time the line executes \u2014 without pausing or modifying source files. Like console.log but better: structured, no diff pollution, auto-cleaned on debug exit.",
|
|
4254
4110
|
{
|
|
4255
|
-
file:
|
|
4256
|
-
line:
|
|
4257
|
-
expressions:
|
|
4111
|
+
file: z9.string().describe("File path to probe"),
|
|
4112
|
+
line: z9.number().describe("Line number (1-based) to probe"),
|
|
4113
|
+
expressions: z9.array(z9.string()).describe(
|
|
4258
4114
|
'JavaScript expressions to capture when the line executes (e.g., ["req.params.id", "user.role"])'
|
|
4259
4115
|
),
|
|
4260
|
-
label:
|
|
4116
|
+
label: z9.string().optional().describe("Optional label for this probe (defaults to file:line)")
|
|
4261
4117
|
},
|
|
4262
4118
|
async ({ file, line, expressions, label }) => {
|
|
4263
4119
|
const clientOrErr = requireDebugClient2(manager);
|
|
@@ -4282,7 +4138,7 @@ Trigger the code path, then use debug_get_probe_results to see captured values.`
|
|
|
4282
4138
|
"debug_remove_probe",
|
|
4283
4139
|
"Remove a previously set debug probe by its ID.",
|
|
4284
4140
|
{
|
|
4285
|
-
probeId:
|
|
4141
|
+
probeId: z9.string().describe("The probe ID returned by debug_add_probe")
|
|
4286
4142
|
},
|
|
4287
4143
|
async ({ probeId }) => {
|
|
4288
4144
|
const clientOrErr = requireDebugClient2(manager);
|
|
@@ -4322,9 +4178,9 @@ function buildProbeResultTools(manager) {
|
|
|
4322
4178
|
"debug_get_probe_results",
|
|
4323
4179
|
"Fetch captured probe hit data. Returns expression values from each time a probed line executed.",
|
|
4324
4180
|
{
|
|
4325
|
-
probeId:
|
|
4326
|
-
label:
|
|
4327
|
-
limit:
|
|
4181
|
+
probeId: z9.string().optional().describe("Filter results by probe ID (resolves to its label)"),
|
|
4182
|
+
label: z9.string().optional().describe("Filter results by probe label"),
|
|
4183
|
+
limit: z9.number().optional().describe("Maximum number of recent hits to return (default: all)")
|
|
4328
4184
|
},
|
|
4329
4185
|
async ({ probeId, label, limit }) => {
|
|
4330
4186
|
const clientOrErr = requireDebugClient2(manager);
|
|
@@ -4795,10 +4651,20 @@ function stopTypingIfNeeded(host, isTyping) {
|
|
|
4795
4651
|
if (isTyping) host.connection.sendTypingStop();
|
|
4796
4652
|
}
|
|
4797
4653
|
function flushPendingToolCalls(host, turnToolCalls) {
|
|
4798
|
-
if (turnToolCalls.length === 0)
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4654
|
+
if (turnToolCalls.length === 0) {
|
|
4655
|
+
host.pendingToolOutputs.length = 0;
|
|
4656
|
+
return;
|
|
4657
|
+
}
|
|
4658
|
+
const outputsByTool = /* @__PURE__ */ new Map();
|
|
4659
|
+
for (const entry of host.pendingToolOutputs) {
|
|
4660
|
+
const list = outputsByTool.get(entry.tool) ?? [];
|
|
4661
|
+
list.push(entry.output);
|
|
4662
|
+
outputsByTool.set(entry.tool, list);
|
|
4663
|
+
}
|
|
4664
|
+
for (const call of turnToolCalls) {
|
|
4665
|
+
const list = outputsByTool.get(call.tool);
|
|
4666
|
+
if (list && list.length > 0) {
|
|
4667
|
+
call.output = list.shift();
|
|
4802
4668
|
}
|
|
4803
4669
|
}
|
|
4804
4670
|
host.connection.sendEvent({ type: "turn_end", toolCalls: [...turnToolCalls] });
|
|
@@ -5120,7 +4986,7 @@ function buildHooks(host) {
|
|
|
5120
4986
|
output,
|
|
5121
4987
|
isError: false
|
|
5122
4988
|
});
|
|
5123
|
-
host.pendingToolOutputs.push(output);
|
|
4989
|
+
host.pendingToolOutputs.push({ tool: input.tool_name, output });
|
|
5124
4990
|
if (input.tool_name === "mcp__conveyor__create_pull_request") {
|
|
5125
4991
|
try {
|
|
5126
4992
|
const props = await host.connection.getTaskProperties();
|
|
@@ -6441,67 +6307,8 @@ var ProjectConnection = class {
|
|
|
6441
6307
|
}
|
|
6442
6308
|
};
|
|
6443
6309
|
|
|
6444
|
-
// src/setup/commands.ts
|
|
6445
|
-
import { spawn, execSync as execSync2 } from "child_process";
|
|
6446
|
-
function runSetupCommand(cmd, cwd, onOutput) {
|
|
6447
|
-
return new Promise((resolve2, reject) => {
|
|
6448
|
-
const child = spawn("sh", ["-c", cmd], {
|
|
6449
|
-
cwd,
|
|
6450
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
6451
|
-
env: { ...process.env }
|
|
6452
|
-
});
|
|
6453
|
-
child.stdout.on("data", (chunk) => {
|
|
6454
|
-
onOutput("stdout", chunk.toString());
|
|
6455
|
-
});
|
|
6456
|
-
child.stderr.on("data", (chunk) => {
|
|
6457
|
-
onOutput("stderr", chunk.toString());
|
|
6458
|
-
});
|
|
6459
|
-
child.on("close", (code) => {
|
|
6460
|
-
if (code === 0) {
|
|
6461
|
-
resolve2();
|
|
6462
|
-
} else {
|
|
6463
|
-
reject(new Error(`Setup command exited with code ${code}`));
|
|
6464
|
-
}
|
|
6465
|
-
});
|
|
6466
|
-
child.on("error", (err) => {
|
|
6467
|
-
reject(err);
|
|
6468
|
-
});
|
|
6469
|
-
});
|
|
6470
|
-
}
|
|
6471
|
-
var AUTH_TOKEN_TIMEOUT_MS = 3e4;
|
|
6472
|
-
function runAuthTokenCommand(cmd, userEmail, cwd) {
|
|
6473
|
-
try {
|
|
6474
|
-
const output = execSync2(`${cmd} ${JSON.stringify(userEmail)}`, {
|
|
6475
|
-
cwd,
|
|
6476
|
-
timeout: AUTH_TOKEN_TIMEOUT_MS,
|
|
6477
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
6478
|
-
env: { ...process.env }
|
|
6479
|
-
});
|
|
6480
|
-
const token = output.toString().trim();
|
|
6481
|
-
return token || null;
|
|
6482
|
-
} catch {
|
|
6483
|
-
return null;
|
|
6484
|
-
}
|
|
6485
|
-
}
|
|
6486
|
-
function runStartCommand(cmd, cwd, onOutput) {
|
|
6487
|
-
const child = spawn("sh", ["-c", cmd], {
|
|
6488
|
-
cwd,
|
|
6489
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
6490
|
-
detached: true,
|
|
6491
|
-
env: { ...process.env }
|
|
6492
|
-
});
|
|
6493
|
-
child.stdout.on("data", (chunk) => {
|
|
6494
|
-
onOutput("stdout", chunk.toString());
|
|
6495
|
-
});
|
|
6496
|
-
child.stderr.on("data", (chunk) => {
|
|
6497
|
-
onOutput("stderr", chunk.toString());
|
|
6498
|
-
});
|
|
6499
|
-
child.unref();
|
|
6500
|
-
return child;
|
|
6501
|
-
}
|
|
6502
|
-
|
|
6503
6310
|
// src/runner/commit-watcher.ts
|
|
6504
|
-
import { execSync as
|
|
6311
|
+
import { execSync as execSync2 } from "child_process";
|
|
6505
6312
|
var logger5 = createServiceLogger("CommitWatcher");
|
|
6506
6313
|
var CommitWatcher = class {
|
|
6507
6314
|
constructor(config, callbacks) {
|
|
@@ -6535,7 +6342,7 @@ var CommitWatcher = class {
|
|
|
6535
6342
|
this.isSyncing = false;
|
|
6536
6343
|
}
|
|
6537
6344
|
getLocalHeadSha() {
|
|
6538
|
-
return
|
|
6345
|
+
return execSync2("git rev-parse HEAD", {
|
|
6539
6346
|
cwd: this.config.projectDir,
|
|
6540
6347
|
stdio: ["ignore", "pipe", "ignore"]
|
|
6541
6348
|
}).toString().trim();
|
|
@@ -6543,12 +6350,12 @@ var CommitWatcher = class {
|
|
|
6543
6350
|
poll() {
|
|
6544
6351
|
if (!this.branch || this.isSyncing) return;
|
|
6545
6352
|
try {
|
|
6546
|
-
|
|
6353
|
+
execSync2(`git fetch origin ${this.branch} --quiet`, {
|
|
6547
6354
|
cwd: this.config.projectDir,
|
|
6548
6355
|
stdio: "ignore",
|
|
6549
6356
|
timeout: 3e4
|
|
6550
6357
|
});
|
|
6551
|
-
const remoteSha =
|
|
6358
|
+
const remoteSha = execSync2(`git rev-parse origin/${this.branch}`, {
|
|
6552
6359
|
cwd: this.config.projectDir,
|
|
6553
6360
|
stdio: ["ignore", "pipe", "ignore"]
|
|
6554
6361
|
}).toString().trim();
|
|
@@ -6569,12 +6376,12 @@ var CommitWatcher = class {
|
|
|
6569
6376
|
let latestMessage = "";
|
|
6570
6377
|
let latestAuthor = "";
|
|
6571
6378
|
try {
|
|
6572
|
-
const countOutput =
|
|
6379
|
+
const countOutput = execSync2(`git rev-list --count ${previousSha}..origin/${this.branch}`, {
|
|
6573
6380
|
cwd: this.config.projectDir,
|
|
6574
6381
|
stdio: ["ignore", "pipe", "ignore"]
|
|
6575
6382
|
}).toString().trim();
|
|
6576
6383
|
commitCount = parseInt(countOutput, 10) || 1;
|
|
6577
|
-
const logOutput =
|
|
6384
|
+
const logOutput = execSync2(`git log -1 --format="%s|||%an" origin/${this.branch}`, {
|
|
6578
6385
|
cwd: this.config.projectDir,
|
|
6579
6386
|
stdio: ["ignore", "pipe", "ignore"]
|
|
6580
6387
|
}).toString().trim();
|
|
@@ -6609,7 +6416,7 @@ var CommitWatcher = class {
|
|
|
6609
6416
|
};
|
|
6610
6417
|
|
|
6611
6418
|
// src/runner/worktree.ts
|
|
6612
|
-
import { execSync as
|
|
6419
|
+
import { execSync as execSync3 } from "child_process";
|
|
6613
6420
|
import { existsSync as existsSync2 } from "fs";
|
|
6614
6421
|
import { join as join4 } from "path";
|
|
6615
6422
|
var WORKTREE_DIR = ".worktrees";
|
|
@@ -6624,7 +6431,7 @@ function ensureWorktree(projectDir, taskId, branch) {
|
|
|
6624
6431
|
return worktreePath;
|
|
6625
6432
|
}
|
|
6626
6433
|
try {
|
|
6627
|
-
|
|
6434
|
+
execSync3(`git checkout --detach origin/${branch}`, {
|
|
6628
6435
|
cwd: worktreePath,
|
|
6629
6436
|
stdio: "ignore"
|
|
6630
6437
|
});
|
|
@@ -6634,7 +6441,7 @@ function ensureWorktree(projectDir, taskId, branch) {
|
|
|
6634
6441
|
return worktreePath;
|
|
6635
6442
|
}
|
|
6636
6443
|
const ref = branch ? `origin/${branch}` : "HEAD";
|
|
6637
|
-
|
|
6444
|
+
execSync3(`git worktree add --detach "${worktreePath}" ${ref}`, {
|
|
6638
6445
|
cwd: projectDir,
|
|
6639
6446
|
stdio: "ignore"
|
|
6640
6447
|
});
|
|
@@ -6642,7 +6449,7 @@ function ensureWorktree(projectDir, taskId, branch) {
|
|
|
6642
6449
|
}
|
|
6643
6450
|
function detachWorktreeBranch(projectDir, branch) {
|
|
6644
6451
|
try {
|
|
6645
|
-
const output =
|
|
6452
|
+
const output = execSync3("git worktree list --porcelain", {
|
|
6646
6453
|
cwd: projectDir,
|
|
6647
6454
|
encoding: "utf-8"
|
|
6648
6455
|
});
|
|
@@ -6655,7 +6462,7 @@ function detachWorktreeBranch(projectDir, branch) {
|
|
|
6655
6462
|
const worktreePath = worktreeLine.replace("worktree ", "");
|
|
6656
6463
|
if (!worktreePath.includes(`/${WORKTREE_DIR}/`)) continue;
|
|
6657
6464
|
try {
|
|
6658
|
-
|
|
6465
|
+
execSync3("git checkout --detach", { cwd: worktreePath, stdio: "ignore" });
|
|
6659
6466
|
} catch {
|
|
6660
6467
|
}
|
|
6661
6468
|
}
|
|
@@ -6666,7 +6473,7 @@ function removeWorktree(projectDir, taskId) {
|
|
|
6666
6473
|
const worktreePath = join4(projectDir, WORKTREE_DIR, taskId);
|
|
6667
6474
|
if (!existsSync2(worktreePath)) return;
|
|
6668
6475
|
try {
|
|
6669
|
-
|
|
6476
|
+
execSync3(`git worktree remove "${worktreePath}" --force`, {
|
|
6670
6477
|
cwd: projectDir,
|
|
6671
6478
|
stdio: "ignore"
|
|
6672
6479
|
});
|
|
@@ -6674,14 +6481,67 @@ function removeWorktree(projectDir, taskId) {
|
|
|
6674
6481
|
}
|
|
6675
6482
|
}
|
|
6676
6483
|
|
|
6677
|
-
// src/
|
|
6678
|
-
import {
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6484
|
+
// src/setup/commands.ts
|
|
6485
|
+
import { spawn, execSync as execSync4 } from "child_process";
|
|
6486
|
+
function runSetupCommand(cmd, cwd, onOutput) {
|
|
6487
|
+
return new Promise((resolve2, reject) => {
|
|
6488
|
+
const child = spawn("sh", ["-c", cmd], {
|
|
6489
|
+
cwd,
|
|
6490
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
6491
|
+
env: { ...process.env }
|
|
6492
|
+
});
|
|
6493
|
+
child.stdout.on("data", (chunk) => {
|
|
6494
|
+
onOutput("stdout", chunk.toString());
|
|
6495
|
+
});
|
|
6496
|
+
child.stderr.on("data", (chunk) => {
|
|
6497
|
+
onOutput("stderr", chunk.toString());
|
|
6498
|
+
});
|
|
6499
|
+
child.on("close", (code) => {
|
|
6500
|
+
if (code === 0) {
|
|
6501
|
+
resolve2();
|
|
6502
|
+
} else {
|
|
6503
|
+
reject(new Error(`Setup command exited with code ${code}`));
|
|
6504
|
+
}
|
|
6505
|
+
});
|
|
6506
|
+
child.on("error", (err) => {
|
|
6507
|
+
reject(err);
|
|
6508
|
+
});
|
|
6509
|
+
});
|
|
6510
|
+
}
|
|
6511
|
+
var AUTH_TOKEN_TIMEOUT_MS = 3e4;
|
|
6512
|
+
function runAuthTokenCommand(cmd, userEmail, cwd) {
|
|
6513
|
+
try {
|
|
6514
|
+
const output = execSync4(`${cmd} ${JSON.stringify(userEmail)}`, {
|
|
6515
|
+
cwd,
|
|
6516
|
+
timeout: AUTH_TOKEN_TIMEOUT_MS,
|
|
6517
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
6518
|
+
env: { ...process.env }
|
|
6519
|
+
});
|
|
6520
|
+
const token = output.toString().trim();
|
|
6521
|
+
return token || null;
|
|
6522
|
+
} catch {
|
|
6523
|
+
return null;
|
|
6524
|
+
}
|
|
6525
|
+
}
|
|
6526
|
+
function runStartCommand(cmd, cwd, onOutput) {
|
|
6527
|
+
const child = spawn("sh", ["-c", cmd], {
|
|
6528
|
+
cwd,
|
|
6529
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
6530
|
+
detached: true,
|
|
6531
|
+
env: { ...process.env }
|
|
6532
|
+
});
|
|
6533
|
+
child.stdout.on("data", (chunk) => {
|
|
6534
|
+
onOutput("stdout", chunk.toString());
|
|
6535
|
+
});
|
|
6536
|
+
child.stderr.on("data", (chunk) => {
|
|
6537
|
+
onOutput("stderr", chunk.toString());
|
|
6538
|
+
});
|
|
6539
|
+
child.unref();
|
|
6540
|
+
return child;
|
|
6541
|
+
}
|
|
6682
6542
|
|
|
6683
6543
|
// src/tools/project-tools.ts
|
|
6684
|
-
import { z as
|
|
6544
|
+
import { z as z10 } from "zod";
|
|
6685
6545
|
function buildTaskListTools(connection) {
|
|
6686
6546
|
const projectId = connection.projectId;
|
|
6687
6547
|
return [
|
|
@@ -6689,9 +6549,9 @@ function buildTaskListTools(connection) {
|
|
|
6689
6549
|
"list_tasks",
|
|
6690
6550
|
"List tasks in the project. Optionally filter by status or assignee.",
|
|
6691
6551
|
{
|
|
6692
|
-
status:
|
|
6693
|
-
assigneeId:
|
|
6694
|
-
limit:
|
|
6552
|
+
status: z10.string().optional().describe("Filter by task status"),
|
|
6553
|
+
assigneeId: z10.string().optional().describe("Filter by assigned user ID"),
|
|
6554
|
+
limit: z10.number().optional().describe("Max number of tasks to return (default 50)")
|
|
6695
6555
|
},
|
|
6696
6556
|
async (params) => {
|
|
6697
6557
|
try {
|
|
@@ -6708,7 +6568,7 @@ function buildTaskListTools(connection) {
|
|
|
6708
6568
|
defineTool(
|
|
6709
6569
|
"get_task",
|
|
6710
6570
|
"Get detailed information about a task including chat messages, child tasks, and session.",
|
|
6711
|
-
{ task_id:
|
|
6571
|
+
{ task_id: z10.string().describe("The task ID to look up") },
|
|
6712
6572
|
async ({ task_id }) => {
|
|
6713
6573
|
try {
|
|
6714
6574
|
const task = await connection.call("getProjectTask", { projectId, taskId: task_id });
|
|
@@ -6725,10 +6585,10 @@ function buildTaskListTools(connection) {
|
|
|
6725
6585
|
"search_tasks",
|
|
6726
6586
|
"Search tasks by tags, text query, or status filters.",
|
|
6727
6587
|
{
|
|
6728
|
-
tagNames:
|
|
6729
|
-
searchQuery:
|
|
6730
|
-
statusFilters:
|
|
6731
|
-
limit:
|
|
6588
|
+
tagNames: z10.array(z10.string()).optional().describe("Filter by tag names"),
|
|
6589
|
+
searchQuery: z10.string().optional().describe("Text search in title/description"),
|
|
6590
|
+
statusFilters: z10.array(z10.string()).optional().describe("Filter by statuses"),
|
|
6591
|
+
limit: z10.number().optional().describe("Max results (default 20)")
|
|
6732
6592
|
},
|
|
6733
6593
|
async (params) => {
|
|
6734
6594
|
try {
|
|
@@ -6781,17 +6641,17 @@ function buildProjectInfoTools(connection) {
|
|
|
6781
6641
|
)
|
|
6782
6642
|
];
|
|
6783
6643
|
}
|
|
6784
|
-
function
|
|
6644
|
+
function buildMutationTools2(connection) {
|
|
6785
6645
|
const projectId = connection.projectId;
|
|
6786
6646
|
return [
|
|
6787
6647
|
defineTool(
|
|
6788
6648
|
"create_task",
|
|
6789
6649
|
"Create a new task in the project.",
|
|
6790
6650
|
{
|
|
6791
|
-
title:
|
|
6792
|
-
description:
|
|
6793
|
-
plan:
|
|
6794
|
-
status:
|
|
6651
|
+
title: z10.string().describe("Task title"),
|
|
6652
|
+
description: z10.string().optional().describe("Task description"),
|
|
6653
|
+
plan: z10.string().optional().describe("Implementation plan in markdown"),
|
|
6654
|
+
status: z10.string().optional().describe("Initial status (default: Planning)")
|
|
6795
6655
|
},
|
|
6796
6656
|
async (params) => {
|
|
6797
6657
|
try {
|
|
@@ -6808,12 +6668,12 @@ function buildMutationTools(connection) {
|
|
|
6808
6668
|
"update_task",
|
|
6809
6669
|
"Update an existing task's title, description, plan, status, or assignee.",
|
|
6810
6670
|
{
|
|
6811
|
-
task_id:
|
|
6812
|
-
title:
|
|
6813
|
-
description:
|
|
6814
|
-
plan:
|
|
6815
|
-
status:
|
|
6816
|
-
assignedUserId:
|
|
6671
|
+
task_id: z10.string().describe("The task ID to update"),
|
|
6672
|
+
title: z10.string().optional().describe("New title"),
|
|
6673
|
+
description: z10.string().optional().describe("New description"),
|
|
6674
|
+
plan: z10.string().optional().describe("New plan in markdown"),
|
|
6675
|
+
status: z10.string().optional().describe("New status"),
|
|
6676
|
+
assignedUserId: z10.string().nullable().optional().describe("Assign to user ID, or null to unassign")
|
|
6817
6677
|
},
|
|
6818
6678
|
async ({ task_id, ...fields }) => {
|
|
6819
6679
|
try {
|
|
@@ -6832,7 +6692,7 @@ function buildProjectTools(connection) {
|
|
|
6832
6692
|
return [
|
|
6833
6693
|
...buildTaskListTools(connection),
|
|
6834
6694
|
...buildProjectInfoTools(connection),
|
|
6835
|
-
...
|
|
6695
|
+
...buildMutationTools2(connection)
|
|
6836
6696
|
];
|
|
6837
6697
|
}
|
|
6838
6698
|
|
|
@@ -7027,14 +6887,204 @@ async function handleProjectChatMessage(message, connection, projectDir, session
|
|
|
7027
6887
|
}
|
|
7028
6888
|
}
|
|
7029
6889
|
|
|
7030
|
-
// src/runner/project-runner.ts
|
|
6890
|
+
// src/runner/project-runner-helpers.ts
|
|
6891
|
+
function parseErrorMessage(error) {
|
|
6892
|
+
return error instanceof Error ? error.message : String(error);
|
|
6893
|
+
}
|
|
6894
|
+
|
|
6895
|
+
// src/runner/project-runner-children.ts
|
|
6896
|
+
import { fork } from "child_process";
|
|
6897
|
+
import { execSync as execSync7 } from "child_process";
|
|
6898
|
+
import * as path from "path";
|
|
6899
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6900
|
+
|
|
6901
|
+
// src/runner/project-runner-git.ts
|
|
6902
|
+
import { execSync as execSync6 } from "child_process";
|
|
6903
|
+
|
|
6904
|
+
// src/runner/project-runner-sync.ts
|
|
6905
|
+
import { execSync as execSync5 } from "child_process";
|
|
7031
6906
|
var logger7 = createServiceLogger("ProjectRunner");
|
|
7032
|
-
var __filename = fileURLToPath2(import.meta.url);
|
|
7033
|
-
var __dirname = path.dirname(__filename);
|
|
7034
|
-
var HEARTBEAT_INTERVAL_MS = 3e4;
|
|
7035
|
-
var MAX_CONCURRENT = Math.max(1, parseInt(process.env.CONVEYOR_MAX_CONCURRENT ?? "10", 10) || 10);
|
|
7036
|
-
var STOP_TIMEOUT_MS = 3e4;
|
|
7037
6907
|
var START_CMD_KILL_TIMEOUT_MS = 5e3;
|
|
6908
|
+
async function executeSetupCommand(projectDir, connection) {
|
|
6909
|
+
const cmd = process.env.CONVEYOR_SETUP_COMMAND;
|
|
6910
|
+
if (!cmd) return;
|
|
6911
|
+
logger7.info("Running setup command", { command: cmd });
|
|
6912
|
+
try {
|
|
6913
|
+
await runSetupCommand(cmd, projectDir, (stream, data) => {
|
|
6914
|
+
connection.sendEvent({ type: "setup_output", stream, data });
|
|
6915
|
+
(stream === "stderr" ? process.stderr : process.stdout).write(data);
|
|
6916
|
+
});
|
|
6917
|
+
logger7.info("Setup command completed");
|
|
6918
|
+
} catch (error) {
|
|
6919
|
+
const msg = parseErrorMessage(error);
|
|
6920
|
+
logger7.error("Setup command failed", { error: msg });
|
|
6921
|
+
connection.sendEvent({ type: "setup_error", message: msg });
|
|
6922
|
+
throw error;
|
|
6923
|
+
}
|
|
6924
|
+
}
|
|
6925
|
+
function executeStartCommand(projectDir, state, connection) {
|
|
6926
|
+
const cmd = process.env.CONVEYOR_START_COMMAND;
|
|
6927
|
+
if (!cmd) return;
|
|
6928
|
+
logger7.info("Running start command", { command: cmd });
|
|
6929
|
+
const child = runStartCommand(cmd, projectDir, (stream, data) => {
|
|
6930
|
+
connection.sendEvent({ type: "start_command_output", stream, data });
|
|
6931
|
+
(stream === "stderr" ? process.stderr : process.stdout).write(data);
|
|
6932
|
+
});
|
|
6933
|
+
state.child = child;
|
|
6934
|
+
state.running = true;
|
|
6935
|
+
child.on("exit", (code, signal) => {
|
|
6936
|
+
state.running = false;
|
|
6937
|
+
state.child = null;
|
|
6938
|
+
logger7.info("Start command exited", { code, signal });
|
|
6939
|
+
connection.sendEvent({ type: "start_command_exited", code, signal });
|
|
6940
|
+
});
|
|
6941
|
+
child.on("error", (err) => {
|
|
6942
|
+
state.running = false;
|
|
6943
|
+
state.child = null;
|
|
6944
|
+
logger7.error("Start command error", { error: err.message });
|
|
6945
|
+
});
|
|
6946
|
+
}
|
|
6947
|
+
async function killStartCommand(state) {
|
|
6948
|
+
const child = state.child;
|
|
6949
|
+
if (!child || !state.running) return;
|
|
6950
|
+
try {
|
|
6951
|
+
if (child.pid) process.kill(-child.pid, "SIGTERM");
|
|
6952
|
+
} catch {
|
|
6953
|
+
child.kill("SIGTERM");
|
|
6954
|
+
}
|
|
6955
|
+
await new Promise((resolve2) => {
|
|
6956
|
+
const timer = setTimeout(() => {
|
|
6957
|
+
if (state.running && child.pid) {
|
|
6958
|
+
try {
|
|
6959
|
+
process.kill(-child.pid, "SIGKILL");
|
|
6960
|
+
} catch {
|
|
6961
|
+
child.kill("SIGKILL");
|
|
6962
|
+
}
|
|
6963
|
+
}
|
|
6964
|
+
resolve2();
|
|
6965
|
+
}, START_CMD_KILL_TIMEOUT_MS);
|
|
6966
|
+
child.on("exit", () => {
|
|
6967
|
+
clearTimeout(timer);
|
|
6968
|
+
resolve2();
|
|
6969
|
+
});
|
|
6970
|
+
});
|
|
6971
|
+
state.child = null;
|
|
6972
|
+
state.running = false;
|
|
6973
|
+
}
|
|
6974
|
+
async function restartStartCommand(projectDir, state, connection) {
|
|
6975
|
+
await killStartCommand(state);
|
|
6976
|
+
executeStartCommand(projectDir, state, connection);
|
|
6977
|
+
}
|
|
6978
|
+
async function handleSyncEnvironment(projectDir, branchSwitchCommand, state, connection, callback) {
|
|
6979
|
+
try {
|
|
6980
|
+
await killStartCommand(state);
|
|
6981
|
+
const cmd = branchSwitchCommand ?? process.env.CONVEYOR_BRANCH_SWITCH_COMMAND;
|
|
6982
|
+
if (cmd) {
|
|
6983
|
+
try {
|
|
6984
|
+
await runSetupCommand(cmd, projectDir, (stream, data) => {
|
|
6985
|
+
connection.sendEvent({ type: "sync_output", stream, data });
|
|
6986
|
+
});
|
|
6987
|
+
} catch (err) {
|
|
6988
|
+
const msg = parseErrorMessage(err);
|
|
6989
|
+
logger7.error("Branch switch sync command failed", { error: msg });
|
|
6990
|
+
}
|
|
6991
|
+
}
|
|
6992
|
+
executeStartCommand(projectDir, state, connection);
|
|
6993
|
+
callback?.({ ok: true });
|
|
6994
|
+
} catch (err) {
|
|
6995
|
+
const msg = parseErrorMessage(err);
|
|
6996
|
+
logger7.error("Environment sync failed", { error: msg });
|
|
6997
|
+
callback?.({ ok: false, error: msg });
|
|
6998
|
+
}
|
|
6999
|
+
}
|
|
7000
|
+
function getChangedFiles(projectDir, previousSha, newSha) {
|
|
7001
|
+
try {
|
|
7002
|
+
return execSync5(`git diff --name-only ${previousSha}..${newSha}`, {
|
|
7003
|
+
cwd: projectDir,
|
|
7004
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
7005
|
+
}).toString().trim().split("\n").filter(Boolean);
|
|
7006
|
+
} catch {
|
|
7007
|
+
return [];
|
|
7008
|
+
}
|
|
7009
|
+
}
|
|
7010
|
+
function runIndividualSyncSteps(projectDir, needsInstall, needsPrisma, stepsRun) {
|
|
7011
|
+
if (needsInstall) {
|
|
7012
|
+
try {
|
|
7013
|
+
execSync5("bun install", { cwd: projectDir, timeout: 12e4, stdio: "pipe" });
|
|
7014
|
+
stepsRun.push("install");
|
|
7015
|
+
} catch (err) {
|
|
7016
|
+
const msg = parseErrorMessage(err);
|
|
7017
|
+
logger7.error("bun install failed", { error: msg });
|
|
7018
|
+
}
|
|
7019
|
+
}
|
|
7020
|
+
if (needsPrisma) {
|
|
7021
|
+
try {
|
|
7022
|
+
execSync5("bunx prisma generate", { cwd: projectDir, timeout: 6e4, stdio: "pipe" });
|
|
7023
|
+
execSync5("bunx prisma db push --accept-data-loss", {
|
|
7024
|
+
cwd: projectDir,
|
|
7025
|
+
timeout: 6e4,
|
|
7026
|
+
stdio: "pipe"
|
|
7027
|
+
});
|
|
7028
|
+
stepsRun.push("prisma");
|
|
7029
|
+
} catch (err) {
|
|
7030
|
+
const msg = parseErrorMessage(err);
|
|
7031
|
+
logger7.error("Prisma sync failed", { error: msg });
|
|
7032
|
+
}
|
|
7033
|
+
}
|
|
7034
|
+
}
|
|
7035
|
+
async function syncDependencies(projectDir, branchSwitchCommand, connection, changedFiles, stepsRun) {
|
|
7036
|
+
const needsInstall = changedFiles.some(
|
|
7037
|
+
(f) => f === "package.json" || f === "bun.lockb" || f.endsWith("/package.json") || f.endsWith("/bun.lockb")
|
|
7038
|
+
);
|
|
7039
|
+
const needsPrisma = changedFiles.some(
|
|
7040
|
+
(f) => f.includes("prisma/schema.prisma") || f.includes("prisma/migrations/")
|
|
7041
|
+
);
|
|
7042
|
+
const cmd = branchSwitchCommand ?? process.env.CONVEYOR_BRANCH_SWITCH_COMMAND;
|
|
7043
|
+
if (cmd && (needsInstall || needsPrisma)) {
|
|
7044
|
+
try {
|
|
7045
|
+
await runSetupCommand(cmd, projectDir, (stream, data) => {
|
|
7046
|
+
connection.sendEvent({ type: "sync_output", stream, data });
|
|
7047
|
+
});
|
|
7048
|
+
stepsRun.push("branchSwitchCommand");
|
|
7049
|
+
} catch (err) {
|
|
7050
|
+
const msg = parseErrorMessage(err);
|
|
7051
|
+
logger7.error("Branch switch command failed", { error: msg });
|
|
7052
|
+
}
|
|
7053
|
+
} else if (!cmd) {
|
|
7054
|
+
runIndividualSyncSteps(projectDir, needsInstall, needsPrisma, stepsRun);
|
|
7055
|
+
}
|
|
7056
|
+
}
|
|
7057
|
+
async function smartSync(projectDir, branchSwitchCommand, state, connection, previousSha, newSha, branch) {
|
|
7058
|
+
if (hasUncommittedChanges(projectDir)) {
|
|
7059
|
+
connection.sendEvent({
|
|
7060
|
+
type: "commit_watch_warning",
|
|
7061
|
+
message: "Working tree has uncommitted changes. Auto-pull skipped."
|
|
7062
|
+
});
|
|
7063
|
+
return ["skipped:dirty_tree"];
|
|
7064
|
+
}
|
|
7065
|
+
await killStartCommand(state);
|
|
7066
|
+
try {
|
|
7067
|
+
execSync5(`git pull origin ${branch}`, {
|
|
7068
|
+
cwd: projectDir,
|
|
7069
|
+
stdio: "pipe",
|
|
7070
|
+
timeout: 6e4
|
|
7071
|
+
});
|
|
7072
|
+
} catch (err) {
|
|
7073
|
+
const msg = parseErrorMessage(err);
|
|
7074
|
+
logger7.error("Git pull failed during commit sync", { error: msg });
|
|
7075
|
+
executeStartCommand(projectDir, state, connection);
|
|
7076
|
+
return ["error:pull"];
|
|
7077
|
+
}
|
|
7078
|
+
const stepsRun = ["pull"];
|
|
7079
|
+
const changedFiles = getChangedFiles(projectDir, previousSha, newSha);
|
|
7080
|
+
await syncDependencies(projectDir, branchSwitchCommand, connection, changedFiles, stepsRun);
|
|
7081
|
+
executeStartCommand(projectDir, state, connection);
|
|
7082
|
+
stepsRun.push("startCommand");
|
|
7083
|
+
return stepsRun;
|
|
7084
|
+
}
|
|
7085
|
+
|
|
7086
|
+
// src/runner/project-runner-git.ts
|
|
7087
|
+
var logger8 = createServiceLogger("ProjectRunner");
|
|
7038
7088
|
function setupWorkDir(projectDir, assignment) {
|
|
7039
7089
|
const { taskId, branch, devBranch, useWorktree } = assignment;
|
|
7040
7090
|
const shortId = taskId.slice(0, 8);
|
|
@@ -7047,21 +7097,105 @@ function setupWorkDir(projectDir, assignment) {
|
|
|
7047
7097
|
}
|
|
7048
7098
|
if (branch && branch !== devBranch) {
|
|
7049
7099
|
if (hasUncommittedChanges(workDir)) {
|
|
7050
|
-
|
|
7100
|
+
logger8.warn("Uncommitted changes, skipping checkout", { taskId: shortId, branch });
|
|
7051
7101
|
} else {
|
|
7052
7102
|
try {
|
|
7053
|
-
|
|
7103
|
+
execSync6(`git checkout ${branch}`, { cwd: workDir, stdio: "ignore" });
|
|
7054
7104
|
} catch {
|
|
7055
7105
|
try {
|
|
7056
|
-
|
|
7106
|
+
execSync6(`git checkout -b ${branch}`, { cwd: workDir, stdio: "ignore" });
|
|
7057
7107
|
} catch {
|
|
7058
|
-
|
|
7108
|
+
logger8.warn("Could not checkout branch", { taskId: shortId, branch });
|
|
7059
7109
|
}
|
|
7060
7110
|
}
|
|
7061
7111
|
}
|
|
7062
7112
|
}
|
|
7063
7113
|
return { workDir, usesWorktree: shouldWorktree };
|
|
7064
7114
|
}
|
|
7115
|
+
function checkoutWorkspaceBranch(projectDir) {
|
|
7116
|
+
const workspaceBranch = process.env.CONVEYOR_WORKSPACE_BRANCH;
|
|
7117
|
+
if (!workspaceBranch) return;
|
|
7118
|
+
const currentBranch = getCurrentBranch(projectDir);
|
|
7119
|
+
if (currentBranch === workspaceBranch) return;
|
|
7120
|
+
if (hasUncommittedChanges(projectDir)) {
|
|
7121
|
+
logger8.warn("Uncommitted changes, skipping workspace branch checkout");
|
|
7122
|
+
return;
|
|
7123
|
+
}
|
|
7124
|
+
try {
|
|
7125
|
+
execSync6(`git fetch origin ${workspaceBranch}`, { cwd: projectDir, stdio: "pipe" });
|
|
7126
|
+
execSync6(`git checkout ${workspaceBranch}`, { cwd: projectDir, stdio: "pipe" });
|
|
7127
|
+
logger8.info("Checked out workspace branch", { workspaceBranch });
|
|
7128
|
+
} catch {
|
|
7129
|
+
logger8.warn("Failed to checkout workspace branch", { workspaceBranch });
|
|
7130
|
+
}
|
|
7131
|
+
}
|
|
7132
|
+
async function handleSwitchBranch(projectDir, branchSwitchCommand, startCmd, connection, commitWatcher, data, callback) {
|
|
7133
|
+
try {
|
|
7134
|
+
try {
|
|
7135
|
+
execSync6("git fetch origin", { cwd: projectDir, stdio: "pipe" });
|
|
7136
|
+
} catch {
|
|
7137
|
+
logger8.warn("Git fetch failed during branch switch");
|
|
7138
|
+
}
|
|
7139
|
+
detachWorktreeBranch(projectDir, data.branch);
|
|
7140
|
+
try {
|
|
7141
|
+
execSync6(`git checkout ${data.branch}`, { cwd: projectDir, stdio: "pipe" });
|
|
7142
|
+
} catch (err) {
|
|
7143
|
+
const msg = parseErrorMessage(err);
|
|
7144
|
+
callback({ ok: false, error: `Failed to checkout branch: ${msg}` });
|
|
7145
|
+
return;
|
|
7146
|
+
}
|
|
7147
|
+
try {
|
|
7148
|
+
execSync6(`git pull origin ${data.branch}`, { cwd: projectDir, stdio: "pipe" });
|
|
7149
|
+
} catch {
|
|
7150
|
+
logger8.warn("Git pull failed during branch switch");
|
|
7151
|
+
}
|
|
7152
|
+
if (data.syncAfter !== false) {
|
|
7153
|
+
await handleSyncEnvironment(projectDir, branchSwitchCommand, startCmd, connection);
|
|
7154
|
+
}
|
|
7155
|
+
commitWatcher.start(data.branch);
|
|
7156
|
+
callback({ ok: true });
|
|
7157
|
+
} catch (err) {
|
|
7158
|
+
const msg = parseErrorMessage(err);
|
|
7159
|
+
logger8.error("Branch switch failed", { error: msg });
|
|
7160
|
+
callback({ ok: false, error: msg });
|
|
7161
|
+
}
|
|
7162
|
+
}
|
|
7163
|
+
async function handleNewCommits(projectDir, branchSwitchCommand, startCmd, connection, setupComplete, data) {
|
|
7164
|
+
await connection.call("reportNewCommitsDetected", {
|
|
7165
|
+
projectId: connection.projectId,
|
|
7166
|
+
branch: data.branch,
|
|
7167
|
+
commits: [
|
|
7168
|
+
{
|
|
7169
|
+
sha: data.newCommitSha,
|
|
7170
|
+
message: data.latestMessage,
|
|
7171
|
+
author: data.latestAuthor
|
|
7172
|
+
}
|
|
7173
|
+
]
|
|
7174
|
+
});
|
|
7175
|
+
const stepsRun = await smartSync(
|
|
7176
|
+
projectDir,
|
|
7177
|
+
branchSwitchCommand,
|
|
7178
|
+
startCmd,
|
|
7179
|
+
connection,
|
|
7180
|
+
data.previousSha,
|
|
7181
|
+
data.newCommitSha,
|
|
7182
|
+
data.branch
|
|
7183
|
+
);
|
|
7184
|
+
await connection.call("reportEnvironmentReady", {
|
|
7185
|
+
projectId: connection.projectId,
|
|
7186
|
+
branch: data.branch,
|
|
7187
|
+
setupComplete,
|
|
7188
|
+
startCommandRunning: startCmd.running
|
|
7189
|
+
});
|
|
7190
|
+
logger8.info("Commit sync complete", { steps: stepsRun.join(", ") });
|
|
7191
|
+
}
|
|
7192
|
+
|
|
7193
|
+
// src/runner/project-runner-children.ts
|
|
7194
|
+
var logger9 = createServiceLogger("ProjectRunner");
|
|
7195
|
+
var __filename = fileURLToPath2(import.meta.url);
|
|
7196
|
+
var __dirname = path.dirname(__filename);
|
|
7197
|
+
var STOP_TIMEOUT_MS = 3e4;
|
|
7198
|
+
var MAX_CONCURRENT = Math.max(1, parseInt(process.env.CONVEYOR_MAX_CONCURRENT ?? "10", 10) || 10);
|
|
7065
7199
|
function spawnChildAgent(assignment, workDir) {
|
|
7066
7200
|
const { taskToken, apiUrl, taskId, sessionId, mode, isAuto, useSandbox, agentMode } = assignment;
|
|
7067
7201
|
const cliPath = path.resolve(__dirname, "cli.js");
|
|
@@ -7073,7 +7207,7 @@ function spawnChildAgent(assignment, workDir) {
|
|
|
7073
7207
|
const effectiveAgentMode = agentMode ?? (isAuto ? "auto" : "");
|
|
7074
7208
|
const effectiveApiUrl = apiUrl || process.env.CONVEYOR_API_URL || "";
|
|
7075
7209
|
if (!effectiveApiUrl) {
|
|
7076
|
-
|
|
7210
|
+
logger9.error("No API URL available for child agent", { taskId: taskId.slice(0, 8) });
|
|
7077
7211
|
}
|
|
7078
7212
|
const child = fork(cliPath, [], {
|
|
7079
7213
|
env: {
|
|
@@ -7101,16 +7235,112 @@ function spawnChildAgent(assignment, workDir) {
|
|
|
7101
7235
|
const shortId = taskId.slice(0, 8);
|
|
7102
7236
|
child.stdout?.on("data", (data) => {
|
|
7103
7237
|
for (const line of data.toString().trimEnd().split("\n")) {
|
|
7104
|
-
|
|
7238
|
+
logger9.info(line, { taskId: shortId });
|
|
7105
7239
|
}
|
|
7106
7240
|
});
|
|
7107
7241
|
child.stderr?.on("data", (data) => {
|
|
7108
7242
|
for (const line of data.toString().trimEnd().split("\n")) {
|
|
7109
|
-
|
|
7243
|
+
logger9.info(line, { taskId: shortId });
|
|
7110
7244
|
}
|
|
7111
7245
|
});
|
|
7112
7246
|
return child;
|
|
7113
7247
|
}
|
|
7248
|
+
async function killAgent(agent, taskId) {
|
|
7249
|
+
const shortId = taskId.slice(0, 8);
|
|
7250
|
+
if (agent.process.exitCode !== null) {
|
|
7251
|
+
logger9.info("Agent process already exited", { taskId: shortId });
|
|
7252
|
+
return;
|
|
7253
|
+
}
|
|
7254
|
+
logger9.info("Killing agent process", { taskId: shortId });
|
|
7255
|
+
agent.process.kill("SIGTERM");
|
|
7256
|
+
await new Promise((resolve2) => {
|
|
7257
|
+
const timer = setTimeout(() => {
|
|
7258
|
+
if (agent.process.exitCode === null) {
|
|
7259
|
+
logger9.warn("Agent did not exit after SIGTERM, sending SIGKILL", { taskId: shortId });
|
|
7260
|
+
agent.process.kill("SIGKILL");
|
|
7261
|
+
}
|
|
7262
|
+
resolve2();
|
|
7263
|
+
}, STOP_TIMEOUT_MS);
|
|
7264
|
+
agent.process.on("exit", () => {
|
|
7265
|
+
clearTimeout(timer);
|
|
7266
|
+
resolve2();
|
|
7267
|
+
});
|
|
7268
|
+
});
|
|
7269
|
+
}
|
|
7270
|
+
async function handleAssignment(assignment, activeAgents, projectDir, connection) {
|
|
7271
|
+
const { taskId, mode } = assignment;
|
|
7272
|
+
const shortId = taskId.slice(0, 8);
|
|
7273
|
+
const agentKey = assignment.agentMode === "code-review" ? `${taskId}:code-review` : taskId;
|
|
7274
|
+
const existing = activeAgents.get(agentKey);
|
|
7275
|
+
if (existing) {
|
|
7276
|
+
if (existing.process.exitCode === null) {
|
|
7277
|
+
logger9.info("Re-assignment received, killing existing agent", { taskId: shortId });
|
|
7278
|
+
await killAgent(existing, taskId);
|
|
7279
|
+
} else {
|
|
7280
|
+
logger9.info("Stale agent entry (process already exited), cleaning up", { taskId: shortId });
|
|
7281
|
+
}
|
|
7282
|
+
activeAgents.delete(agentKey);
|
|
7283
|
+
}
|
|
7284
|
+
if (activeAgents.size >= MAX_CONCURRENT) {
|
|
7285
|
+
logger9.warn("Max concurrent agents reached", { maxConcurrent: MAX_CONCURRENT });
|
|
7286
|
+
connection.emitTaskStopped(taskId, "max_concurrent_reached");
|
|
7287
|
+
return;
|
|
7288
|
+
}
|
|
7289
|
+
try {
|
|
7290
|
+
try {
|
|
7291
|
+
execSync7("git fetch origin", { cwd: projectDir, stdio: "ignore" });
|
|
7292
|
+
} catch {
|
|
7293
|
+
logger9.warn("Git fetch failed", { taskId: shortId });
|
|
7294
|
+
}
|
|
7295
|
+
const { workDir, usesWorktree } = setupWorkDir(projectDir, assignment);
|
|
7296
|
+
if (assignment.devBranch) {
|
|
7297
|
+
syncWithBaseBranch(workDir, assignment.devBranch);
|
|
7298
|
+
}
|
|
7299
|
+
const child = spawnChildAgent(assignment, workDir);
|
|
7300
|
+
activeAgents.set(agentKey, {
|
|
7301
|
+
process: child,
|
|
7302
|
+
worktreePath: workDir,
|
|
7303
|
+
mode,
|
|
7304
|
+
usesWorktree
|
|
7305
|
+
});
|
|
7306
|
+
connection.emitTaskStarted(taskId);
|
|
7307
|
+
logger9.info("Started task", { taskId: shortId, mode, workDir });
|
|
7308
|
+
child.on("exit", (code) => {
|
|
7309
|
+
activeAgents.delete(agentKey);
|
|
7310
|
+
const reason = code === 0 ? "completed" : `exited with code ${code}`;
|
|
7311
|
+
connection.emitTaskStopped(taskId, reason);
|
|
7312
|
+
logger9.info("Task exited", { taskId: shortId, reason });
|
|
7313
|
+
if (code === 0 && usesWorktree) {
|
|
7314
|
+
try {
|
|
7315
|
+
removeWorktree(projectDir, taskId);
|
|
7316
|
+
} catch {
|
|
7317
|
+
}
|
|
7318
|
+
}
|
|
7319
|
+
});
|
|
7320
|
+
} catch (error) {
|
|
7321
|
+
const msg = parseErrorMessage(error);
|
|
7322
|
+
logger9.error("Failed to start task", { taskId: shortId, error: msg });
|
|
7323
|
+
connection.emitTaskStopped(taskId, `start_failed: ${msg}`);
|
|
7324
|
+
}
|
|
7325
|
+
}
|
|
7326
|
+
function handleStopTask(taskId, activeAgents, projectDir) {
|
|
7327
|
+
const agentKey = activeAgents.has(taskId) ? taskId : `${taskId}:code-review`;
|
|
7328
|
+
const agent = activeAgents.get(agentKey);
|
|
7329
|
+
if (!agent) return;
|
|
7330
|
+
logger9.info("Stopping task", { taskId: taskId.slice(0, 8) });
|
|
7331
|
+
void killAgent(agent, taskId).then(() => {
|
|
7332
|
+
if (agent.usesWorktree) {
|
|
7333
|
+
try {
|
|
7334
|
+
removeWorktree(projectDir, taskId);
|
|
7335
|
+
} catch {
|
|
7336
|
+
}
|
|
7337
|
+
}
|
|
7338
|
+
});
|
|
7339
|
+
}
|
|
7340
|
+
|
|
7341
|
+
// src/runner/project-runner.ts
|
|
7342
|
+
var logger10 = createServiceLogger("ProjectRunner");
|
|
7343
|
+
var HEARTBEAT_INTERVAL_MS = 3e4;
|
|
7114
7344
|
var ProjectRunner = class {
|
|
7115
7345
|
connection;
|
|
7116
7346
|
projectDir;
|
|
@@ -7119,8 +7349,7 @@ var ProjectRunner = class {
|
|
|
7119
7349
|
stopping = false;
|
|
7120
7350
|
resolveLifecycle = null;
|
|
7121
7351
|
chatSessionIds = /* @__PURE__ */ new Map();
|
|
7122
|
-
|
|
7123
|
-
startCommandRunning = false;
|
|
7352
|
+
startCmd = { child: null, running: false };
|
|
7124
7353
|
setupComplete = false;
|
|
7125
7354
|
branchSwitchCommand;
|
|
7126
7355
|
commitWatcher;
|
|
@@ -7139,33 +7368,39 @@ var ProjectRunner = class {
|
|
|
7139
7368
|
},
|
|
7140
7369
|
{
|
|
7141
7370
|
onNewCommits: async (data) => {
|
|
7142
|
-
await
|
|
7371
|
+
await handleNewCommits(
|
|
7372
|
+
this.projectDir,
|
|
7373
|
+
this.branchSwitchCommand,
|
|
7374
|
+
this.startCmd,
|
|
7375
|
+
this.connection,
|
|
7376
|
+
this.setupComplete,
|
|
7377
|
+
data
|
|
7378
|
+
);
|
|
7143
7379
|
}
|
|
7144
7380
|
}
|
|
7145
7381
|
);
|
|
7146
7382
|
}
|
|
7147
7383
|
// ── Public lifecycle ───────────────────────────────────────────────────
|
|
7148
|
-
// oxlint-disable-next-line max-lines-per-function -- sequential lifecycle orchestration
|
|
7149
7384
|
async start() {
|
|
7150
|
-
this.
|
|
7385
|
+
checkoutWorkspaceBranch(this.projectDir);
|
|
7151
7386
|
await this.connection.connect();
|
|
7152
7387
|
const registration = await this.connection.call("registerProjectAgent", {
|
|
7153
7388
|
projectId: this.connection.projectId,
|
|
7154
7389
|
capabilities: ["task", "pm", "code-review", "audit"]
|
|
7155
7390
|
});
|
|
7156
7391
|
this.branchSwitchCommand = registration.branchSwitchCommand ?? process.env.CONVEYOR_BRANCH_SWITCH_COMMAND;
|
|
7157
|
-
|
|
7392
|
+
logger10.info("Registered as project agent", { agentName: registration.agentName });
|
|
7158
7393
|
try {
|
|
7159
|
-
await this.
|
|
7160
|
-
this.
|
|
7394
|
+
await executeSetupCommand(this.projectDir, this.connection);
|
|
7395
|
+
executeStartCommand(this.projectDir, this.startCmd, this.connection);
|
|
7161
7396
|
this.setupComplete = true;
|
|
7162
7397
|
this.connection.sendEvent({
|
|
7163
7398
|
type: "setup_complete",
|
|
7164
|
-
startCommandRunning: this.
|
|
7399
|
+
startCommandRunning: this.startCmd.running
|
|
7165
7400
|
});
|
|
7166
7401
|
} catch (error) {
|
|
7167
|
-
const msg =
|
|
7168
|
-
|
|
7402
|
+
const msg = parseErrorMessage(error);
|
|
7403
|
+
logger10.error("Environment setup failed", { error: msg });
|
|
7169
7404
|
this.setupComplete = false;
|
|
7170
7405
|
}
|
|
7171
7406
|
this.wireEventHandlers();
|
|
@@ -7177,7 +7412,7 @@ var ProjectRunner = class {
|
|
|
7177
7412
|
if (currentBranch) {
|
|
7178
7413
|
this.commitWatcher.start(currentBranch);
|
|
7179
7414
|
}
|
|
7180
|
-
|
|
7415
|
+
logger10.info("Connected, waiting for task assignments");
|
|
7181
7416
|
await new Promise((resolve2) => {
|
|
7182
7417
|
this.resolveLifecycle = resolve2;
|
|
7183
7418
|
});
|
|
@@ -7196,24 +7431,24 @@ var ProjectRunner = class {
|
|
|
7196
7431
|
wipMessage: "WIP: auto-commit on conveyor-agent shutdown"
|
|
7197
7432
|
});
|
|
7198
7433
|
if (result.hadWork) {
|
|
7199
|
-
|
|
7434
|
+
logger10.info("Shutdown git flush", {
|
|
7200
7435
|
dir,
|
|
7201
7436
|
committed: result.committed,
|
|
7202
7437
|
pushed: result.pushed
|
|
7203
7438
|
});
|
|
7204
7439
|
}
|
|
7205
7440
|
} catch (err) {
|
|
7206
|
-
const msg =
|
|
7207
|
-
|
|
7441
|
+
const msg = parseErrorMessage(err);
|
|
7442
|
+
logger10.warn("Shutdown git flush failed", { dir, error: msg });
|
|
7208
7443
|
}
|
|
7209
7444
|
}
|
|
7210
7445
|
}
|
|
7211
7446
|
async stop() {
|
|
7212
7447
|
if (this.stopping) return;
|
|
7213
7448
|
this.stopping = true;
|
|
7214
|
-
|
|
7449
|
+
logger10.info("Shutting down");
|
|
7215
7450
|
this.commitWatcher.stop();
|
|
7216
|
-
await this.
|
|
7451
|
+
await killStartCommand(this.startCmd);
|
|
7217
7452
|
if (this.heartbeatTimer) {
|
|
7218
7453
|
clearInterval(this.heartbeatTimer);
|
|
7219
7454
|
this.heartbeatTimer = null;
|
|
@@ -7226,7 +7461,7 @@ var ProjectRunner = class {
|
|
|
7226
7461
|
return;
|
|
7227
7462
|
}
|
|
7228
7463
|
agent.process.on("exit", () => resolve2());
|
|
7229
|
-
|
|
7464
|
+
handleStopTask(key, this.activeAgents, this.projectDir);
|
|
7230
7465
|
})
|
|
7231
7466
|
);
|
|
7232
7467
|
await Promise.race([
|
|
@@ -7243,7 +7478,7 @@ var ProjectRunner = class {
|
|
|
7243
7478
|
} catch {
|
|
7244
7479
|
}
|
|
7245
7480
|
this.connection.disconnect();
|
|
7246
|
-
|
|
7481
|
+
logger10.info("Shutdown complete");
|
|
7247
7482
|
if (this.resolveLifecycle) {
|
|
7248
7483
|
this.resolveLifecycle();
|
|
7249
7484
|
this.resolveLifecycle = null;
|
|
@@ -7251,8 +7486,12 @@ var ProjectRunner = class {
|
|
|
7251
7486
|
}
|
|
7252
7487
|
// ── Event wiring ───────────────────────────────────────────────────────
|
|
7253
7488
|
wireEventHandlers() {
|
|
7254
|
-
this.connection.onTaskAssignment(
|
|
7255
|
-
|
|
7489
|
+
this.connection.onTaskAssignment(
|
|
7490
|
+
(assignment) => void handleAssignment(assignment, this.activeAgents, this.projectDir, this.connection)
|
|
7491
|
+
);
|
|
7492
|
+
this.connection.onStopTask(
|
|
7493
|
+
(data) => handleStopTask(data.taskId, this.activeAgents, this.projectDir)
|
|
7494
|
+
);
|
|
7256
7495
|
this.connection.onShutdown(() => void this.stop());
|
|
7257
7496
|
this.connection.onChatMessage((msg) => {
|
|
7258
7497
|
const chatId = msg.chatId ?? "default";
|
|
@@ -7265,10 +7504,28 @@ var ProjectRunner = class {
|
|
|
7265
7504
|
});
|
|
7266
7505
|
this.connection.onAuditTags((request) => void this.handleAuditTags(request));
|
|
7267
7506
|
this.connection.onAuditTasks((request) => void this.handleAuditTasks(request));
|
|
7268
|
-
this.connection.onSwitchBranch(
|
|
7269
|
-
|
|
7507
|
+
this.connection.onSwitchBranch(
|
|
7508
|
+
(data, cb) => void handleSwitchBranch(
|
|
7509
|
+
this.projectDir,
|
|
7510
|
+
this.branchSwitchCommand,
|
|
7511
|
+
this.startCmd,
|
|
7512
|
+
this.connection,
|
|
7513
|
+
this.commitWatcher,
|
|
7514
|
+
data,
|
|
7515
|
+
cb
|
|
7516
|
+
)
|
|
7517
|
+
);
|
|
7518
|
+
this.connection.onSyncEnvironment(
|
|
7519
|
+
(cb) => void handleSyncEnvironment(
|
|
7520
|
+
this.projectDir,
|
|
7521
|
+
this.branchSwitchCommand,
|
|
7522
|
+
this.startCmd,
|
|
7523
|
+
this.connection,
|
|
7524
|
+
cb
|
|
7525
|
+
)
|
|
7526
|
+
);
|
|
7270
7527
|
this.connection.onRestartStartCommand((cb) => {
|
|
7271
|
-
void this.
|
|
7528
|
+
void restartStartCommand(this.projectDir, this.startCmd, this.connection).then(() => cb({ ok: true })).catch(
|
|
7272
7529
|
(err) => cb({ ok: false, error: err instanceof Error ? err.message : "Restart failed" })
|
|
7273
7530
|
);
|
|
7274
7531
|
});
|
|
@@ -7280,10 +7537,10 @@ var ProjectRunner = class {
|
|
|
7280
7537
|
capabilities: ["task", "pm", "code-review", "audit"]
|
|
7281
7538
|
});
|
|
7282
7539
|
this.connection.emitStatus(this.activeAgents.size > 0 ? "busy" : "idle");
|
|
7283
|
-
|
|
7540
|
+
logger10.info("Re-registered after reconnect");
|
|
7284
7541
|
} catch (error) {
|
|
7285
|
-
const msg =
|
|
7286
|
-
|
|
7542
|
+
const msg = parseErrorMessage(error);
|
|
7543
|
+
logger10.error("Failed to re-register after reconnect", { error: msg });
|
|
7287
7544
|
}
|
|
7288
7545
|
}
|
|
7289
7546
|
// ── Tag audit ──────────────────────────────────────────────────────────
|
|
@@ -7293,8 +7550,8 @@ var ProjectRunner = class {
|
|
|
7293
7550
|
const { handleTagAudit } = await import("./tag-audit-handler-I54W7ZD7.js");
|
|
7294
7551
|
await handleTagAudit(request, this.connection, this.projectDir);
|
|
7295
7552
|
} catch (error) {
|
|
7296
|
-
const msg =
|
|
7297
|
-
|
|
7553
|
+
const msg = parseErrorMessage(error);
|
|
7554
|
+
logger10.error("Tag audit failed", { error: msg, requestId: request.requestId });
|
|
7298
7555
|
try {
|
|
7299
7556
|
await this.connection.call("reportTagAuditResult", {
|
|
7300
7557
|
projectId: this.connection.projectId,
|
|
@@ -7313,11 +7570,11 @@ var ProjectRunner = class {
|
|
|
7313
7570
|
async handleAuditTasks(request) {
|
|
7314
7571
|
this.connection.emitStatus("busy");
|
|
7315
7572
|
try {
|
|
7316
|
-
const { handleTaskAudit } = await import("./task-audit-handler-
|
|
7573
|
+
const { handleTaskAudit } = await import("./task-audit-handler-TJOM5OJS.js");
|
|
7317
7574
|
await handleTaskAudit(request, this.connection, this.projectDir);
|
|
7318
7575
|
} catch (error) {
|
|
7319
|
-
const msg =
|
|
7320
|
-
|
|
7576
|
+
const msg = parseErrorMessage(error);
|
|
7577
|
+
logger10.error("Task audit failed", { error: msg, requestId: request.requestId });
|
|
7321
7578
|
for (const task of request.tasks) {
|
|
7322
7579
|
try {
|
|
7323
7580
|
await this.connection.call("reportTaskAuditResult", {
|
|
@@ -7355,353 +7612,9 @@ var ProjectRunner = class {
|
|
|
7355
7612
|
this.connection.emitStatus("idle");
|
|
7356
7613
|
}
|
|
7357
7614
|
}
|
|
7358
|
-
// ── Task management ────────────────────────────────────────────────────
|
|
7359
|
-
async killAgent(agent, taskId) {
|
|
7360
|
-
const shortId = taskId.slice(0, 8);
|
|
7361
|
-
if (agent.process.exitCode !== null) {
|
|
7362
|
-
logger7.info("Agent process already exited", { taskId: shortId });
|
|
7363
|
-
return;
|
|
7364
|
-
}
|
|
7365
|
-
logger7.info("Killing agent process", { taskId: shortId });
|
|
7366
|
-
agent.process.kill("SIGTERM");
|
|
7367
|
-
await new Promise((resolve2) => {
|
|
7368
|
-
const timer = setTimeout(() => {
|
|
7369
|
-
if (agent.process.exitCode === null) {
|
|
7370
|
-
logger7.warn("Agent did not exit after SIGTERM, sending SIGKILL", { taskId: shortId });
|
|
7371
|
-
agent.process.kill("SIGKILL");
|
|
7372
|
-
}
|
|
7373
|
-
resolve2();
|
|
7374
|
-
}, STOP_TIMEOUT_MS);
|
|
7375
|
-
agent.process.on("exit", () => {
|
|
7376
|
-
clearTimeout(timer);
|
|
7377
|
-
resolve2();
|
|
7378
|
-
});
|
|
7379
|
-
});
|
|
7380
|
-
}
|
|
7381
|
-
// oxlint-disable-next-line max-lines-per-function -- re-assignment logic requires sequential checks
|
|
7382
|
-
async handleAssignment(assignment) {
|
|
7383
|
-
const { taskId, mode } = assignment;
|
|
7384
|
-
const shortId = taskId.slice(0, 8);
|
|
7385
|
-
const agentKey = assignment.agentMode === "code-review" ? `${taskId}:code-review` : taskId;
|
|
7386
|
-
const existing = this.activeAgents.get(agentKey);
|
|
7387
|
-
if (existing) {
|
|
7388
|
-
if (existing.process.exitCode === null) {
|
|
7389
|
-
logger7.info("Re-assignment received, killing existing agent", { taskId: shortId });
|
|
7390
|
-
await this.killAgent(existing, taskId);
|
|
7391
|
-
} else {
|
|
7392
|
-
logger7.info("Stale agent entry (process already exited), cleaning up", { taskId: shortId });
|
|
7393
|
-
}
|
|
7394
|
-
this.activeAgents.delete(agentKey);
|
|
7395
|
-
}
|
|
7396
|
-
if (this.activeAgents.size >= MAX_CONCURRENT) {
|
|
7397
|
-
logger7.warn("Max concurrent agents reached", { maxConcurrent: MAX_CONCURRENT });
|
|
7398
|
-
this.connection.emitTaskStopped(taskId, "max_concurrent_reached");
|
|
7399
|
-
return;
|
|
7400
|
-
}
|
|
7401
|
-
try {
|
|
7402
|
-
try {
|
|
7403
|
-
execSync5("git fetch origin", { cwd: this.projectDir, stdio: "ignore" });
|
|
7404
|
-
} catch {
|
|
7405
|
-
logger7.warn("Git fetch failed", { taskId: shortId });
|
|
7406
|
-
}
|
|
7407
|
-
const { workDir, usesWorktree } = setupWorkDir(this.projectDir, assignment);
|
|
7408
|
-
if (assignment.devBranch) {
|
|
7409
|
-
syncWithBaseBranch(workDir, assignment.devBranch);
|
|
7410
|
-
}
|
|
7411
|
-
const child = spawnChildAgent(assignment, workDir);
|
|
7412
|
-
this.activeAgents.set(agentKey, {
|
|
7413
|
-
process: child,
|
|
7414
|
-
worktreePath: workDir,
|
|
7415
|
-
mode,
|
|
7416
|
-
usesWorktree
|
|
7417
|
-
});
|
|
7418
|
-
this.connection.emitTaskStarted(taskId);
|
|
7419
|
-
logger7.info("Started task", { taskId: shortId, mode, workDir });
|
|
7420
|
-
child.on("exit", (code) => {
|
|
7421
|
-
this.activeAgents.delete(agentKey);
|
|
7422
|
-
const reason = code === 0 ? "completed" : `exited with code ${code}`;
|
|
7423
|
-
this.connection.emitTaskStopped(taskId, reason);
|
|
7424
|
-
logger7.info("Task exited", { taskId: shortId, reason });
|
|
7425
|
-
if (code === 0 && usesWorktree) {
|
|
7426
|
-
try {
|
|
7427
|
-
removeWorktree(this.projectDir, taskId);
|
|
7428
|
-
} catch {
|
|
7429
|
-
}
|
|
7430
|
-
}
|
|
7431
|
-
});
|
|
7432
|
-
} catch (error) {
|
|
7433
|
-
const msg = error instanceof Error ? error.message : "Unknown";
|
|
7434
|
-
logger7.error("Failed to start task", { taskId: shortId, error: msg });
|
|
7435
|
-
this.connection.emitTaskStopped(taskId, `start_failed: ${msg}`);
|
|
7436
|
-
}
|
|
7437
|
-
}
|
|
7438
|
-
handleStopTask(taskId) {
|
|
7439
|
-
const agentKey = this.activeAgents.has(taskId) ? taskId : `${taskId}:code-review`;
|
|
7440
|
-
const agent = this.activeAgents.get(agentKey);
|
|
7441
|
-
if (!agent) return;
|
|
7442
|
-
logger7.info("Stopping task", { taskId: taskId.slice(0, 8) });
|
|
7443
|
-
void this.killAgent(agent, taskId).then(() => {
|
|
7444
|
-
if (agent.usesWorktree) {
|
|
7445
|
-
try {
|
|
7446
|
-
removeWorktree(this.projectDir, taskId);
|
|
7447
|
-
} catch {
|
|
7448
|
-
}
|
|
7449
|
-
}
|
|
7450
|
-
});
|
|
7451
|
-
}
|
|
7452
|
-
// ── Environment management ─────────────────────────────────────────────
|
|
7453
|
-
checkoutWorkspaceBranch() {
|
|
7454
|
-
const workspaceBranch = process.env.CONVEYOR_WORKSPACE_BRANCH;
|
|
7455
|
-
if (!workspaceBranch) return;
|
|
7456
|
-
const currentBranch = this.getCurrentBranch();
|
|
7457
|
-
if (currentBranch === workspaceBranch) return;
|
|
7458
|
-
if (hasUncommittedChanges(this.projectDir)) {
|
|
7459
|
-
logger7.warn("Uncommitted changes, skipping workspace branch checkout");
|
|
7460
|
-
return;
|
|
7461
|
-
}
|
|
7462
|
-
try {
|
|
7463
|
-
execSync5(`git fetch origin ${workspaceBranch}`, { cwd: this.projectDir, stdio: "pipe" });
|
|
7464
|
-
execSync5(`git checkout ${workspaceBranch}`, { cwd: this.projectDir, stdio: "pipe" });
|
|
7465
|
-
logger7.info("Checked out workspace branch", { workspaceBranch });
|
|
7466
|
-
} catch {
|
|
7467
|
-
logger7.warn("Failed to checkout workspace branch", { workspaceBranch });
|
|
7468
|
-
}
|
|
7469
|
-
}
|
|
7470
|
-
async executeSetupCommand() {
|
|
7471
|
-
const cmd = process.env.CONVEYOR_SETUP_COMMAND;
|
|
7472
|
-
if (!cmd) return;
|
|
7473
|
-
logger7.info("Running setup command", { command: cmd });
|
|
7474
|
-
try {
|
|
7475
|
-
await runSetupCommand(cmd, this.projectDir, (stream, data) => {
|
|
7476
|
-
this.connection.sendEvent({ type: "setup_output", stream, data });
|
|
7477
|
-
(stream === "stderr" ? process.stderr : process.stdout).write(data);
|
|
7478
|
-
});
|
|
7479
|
-
logger7.info("Setup command completed");
|
|
7480
|
-
} catch (error) {
|
|
7481
|
-
const msg = error instanceof Error ? error.message : "Setup command failed";
|
|
7482
|
-
logger7.error("Setup command failed", { error: msg });
|
|
7483
|
-
this.connection.sendEvent({ type: "setup_error", message: msg });
|
|
7484
|
-
throw error;
|
|
7485
|
-
}
|
|
7486
|
-
}
|
|
7487
|
-
executeStartCommand() {
|
|
7488
|
-
const cmd = process.env.CONVEYOR_START_COMMAND;
|
|
7489
|
-
if (!cmd) return;
|
|
7490
|
-
logger7.info("Running start command", { command: cmd });
|
|
7491
|
-
const child = runStartCommand(cmd, this.projectDir, (stream, data) => {
|
|
7492
|
-
this.connection.sendEvent({ type: "start_command_output", stream, data });
|
|
7493
|
-
(stream === "stderr" ? process.stderr : process.stdout).write(data);
|
|
7494
|
-
});
|
|
7495
|
-
this.startCommandChild = child;
|
|
7496
|
-
this.startCommandRunning = true;
|
|
7497
|
-
child.on("exit", (code, signal) => {
|
|
7498
|
-
this.startCommandRunning = false;
|
|
7499
|
-
this.startCommandChild = null;
|
|
7500
|
-
logger7.info("Start command exited", { code, signal });
|
|
7501
|
-
this.connection.sendEvent({ type: "start_command_exited", code, signal });
|
|
7502
|
-
});
|
|
7503
|
-
child.on("error", (err) => {
|
|
7504
|
-
this.startCommandRunning = false;
|
|
7505
|
-
this.startCommandChild = null;
|
|
7506
|
-
logger7.error("Start command error", { error: err.message });
|
|
7507
|
-
});
|
|
7508
|
-
}
|
|
7509
|
-
async killStartCommand() {
|
|
7510
|
-
const child = this.startCommandChild;
|
|
7511
|
-
if (!child || !this.startCommandRunning) return;
|
|
7512
|
-
try {
|
|
7513
|
-
if (child.pid) process.kill(-child.pid, "SIGTERM");
|
|
7514
|
-
} catch {
|
|
7515
|
-
child.kill("SIGTERM");
|
|
7516
|
-
}
|
|
7517
|
-
await new Promise((resolve2) => {
|
|
7518
|
-
const timer = setTimeout(() => {
|
|
7519
|
-
if (this.startCommandRunning && child.pid) {
|
|
7520
|
-
try {
|
|
7521
|
-
process.kill(-child.pid, "SIGKILL");
|
|
7522
|
-
} catch {
|
|
7523
|
-
child.kill("SIGKILL");
|
|
7524
|
-
}
|
|
7525
|
-
}
|
|
7526
|
-
resolve2();
|
|
7527
|
-
}, START_CMD_KILL_TIMEOUT_MS);
|
|
7528
|
-
child.on("exit", () => {
|
|
7529
|
-
clearTimeout(timer);
|
|
7530
|
-
resolve2();
|
|
7531
|
-
});
|
|
7532
|
-
});
|
|
7533
|
-
this.startCommandChild = null;
|
|
7534
|
-
this.startCommandRunning = false;
|
|
7535
|
-
}
|
|
7536
|
-
async restartStartCommand() {
|
|
7537
|
-
await this.killStartCommand();
|
|
7538
|
-
this.executeStartCommand();
|
|
7539
|
-
}
|
|
7540
7615
|
getCurrentBranch() {
|
|
7541
7616
|
return getCurrentBranch(this.projectDir);
|
|
7542
7617
|
}
|
|
7543
|
-
// ── Branch switching ───────────────────────────────────────────────────
|
|
7544
|
-
async handleSwitchBranch(data, callback) {
|
|
7545
|
-
try {
|
|
7546
|
-
try {
|
|
7547
|
-
execSync5("git fetch origin", { cwd: this.projectDir, stdio: "pipe" });
|
|
7548
|
-
} catch {
|
|
7549
|
-
logger7.warn("Git fetch failed during branch switch");
|
|
7550
|
-
}
|
|
7551
|
-
detachWorktreeBranch(this.projectDir, data.branch);
|
|
7552
|
-
try {
|
|
7553
|
-
execSync5(`git checkout ${data.branch}`, { cwd: this.projectDir, stdio: "pipe" });
|
|
7554
|
-
} catch (err) {
|
|
7555
|
-
const msg = err instanceof Error ? err.message : "Checkout failed";
|
|
7556
|
-
callback({ ok: false, error: `Failed to checkout branch: ${msg}` });
|
|
7557
|
-
return;
|
|
7558
|
-
}
|
|
7559
|
-
try {
|
|
7560
|
-
execSync5(`git pull origin ${data.branch}`, { cwd: this.projectDir, stdio: "pipe" });
|
|
7561
|
-
} catch {
|
|
7562
|
-
logger7.warn("Git pull failed during branch switch");
|
|
7563
|
-
}
|
|
7564
|
-
if (data.syncAfter !== false) {
|
|
7565
|
-
await this.handleSyncEnvironment();
|
|
7566
|
-
}
|
|
7567
|
-
this.commitWatcher.start(data.branch);
|
|
7568
|
-
callback({ ok: true });
|
|
7569
|
-
} catch (err) {
|
|
7570
|
-
const msg = err instanceof Error ? err.message : "Branch switch failed";
|
|
7571
|
-
logger7.error("Branch switch failed", { error: msg });
|
|
7572
|
-
callback({ ok: false, error: msg });
|
|
7573
|
-
}
|
|
7574
|
-
}
|
|
7575
|
-
async handleSyncEnvironment(callback) {
|
|
7576
|
-
try {
|
|
7577
|
-
await this.killStartCommand();
|
|
7578
|
-
const cmd = this.branchSwitchCommand ?? process.env.CONVEYOR_BRANCH_SWITCH_COMMAND;
|
|
7579
|
-
if (cmd) {
|
|
7580
|
-
try {
|
|
7581
|
-
await runSetupCommand(cmd, this.projectDir, (stream, data) => {
|
|
7582
|
-
this.connection.sendEvent({ type: "sync_output", stream, data });
|
|
7583
|
-
});
|
|
7584
|
-
} catch (err) {
|
|
7585
|
-
const msg = err instanceof Error ? err.message : "Sync command failed";
|
|
7586
|
-
logger7.error("Branch switch sync command failed", { error: msg });
|
|
7587
|
-
}
|
|
7588
|
-
}
|
|
7589
|
-
this.executeStartCommand();
|
|
7590
|
-
callback?.({ ok: true });
|
|
7591
|
-
} catch (err) {
|
|
7592
|
-
const msg = err instanceof Error ? err.message : "Sync failed";
|
|
7593
|
-
logger7.error("Environment sync failed", { error: msg });
|
|
7594
|
-
callback?.({ ok: false, error: msg });
|
|
7595
|
-
}
|
|
7596
|
-
}
|
|
7597
|
-
// ── Commit watching ────────────────────────────────────────────────────
|
|
7598
|
-
// oxlint-disable-next-line max-lines-per-function -- sequential sync steps
|
|
7599
|
-
async handleNewCommits(data) {
|
|
7600
|
-
await this.connection.call("reportNewCommitsDetected", {
|
|
7601
|
-
projectId: this.connection.projectId,
|
|
7602
|
-
branch: data.branch,
|
|
7603
|
-
commits: [
|
|
7604
|
-
{
|
|
7605
|
-
sha: data.newCommitSha,
|
|
7606
|
-
message: data.latestMessage,
|
|
7607
|
-
author: data.latestAuthor
|
|
7608
|
-
}
|
|
7609
|
-
]
|
|
7610
|
-
});
|
|
7611
|
-
const stepsRun = await this.smartSync(data.previousSha, data.newCommitSha, data.branch);
|
|
7612
|
-
await this.connection.call("reportEnvironmentReady", {
|
|
7613
|
-
projectId: this.connection.projectId,
|
|
7614
|
-
branch: data.branch,
|
|
7615
|
-
setupComplete: this.setupComplete,
|
|
7616
|
-
startCommandRunning: this.startCommandRunning
|
|
7617
|
-
});
|
|
7618
|
-
logger7.info("Commit sync complete", { steps: stepsRun.join(", ") });
|
|
7619
|
-
}
|
|
7620
|
-
async smartSync(previousSha, newSha, branch) {
|
|
7621
|
-
if (hasUncommittedChanges(this.projectDir)) {
|
|
7622
|
-
this.connection.sendEvent({
|
|
7623
|
-
type: "commit_watch_warning",
|
|
7624
|
-
message: "Working tree has uncommitted changes. Auto-pull skipped."
|
|
7625
|
-
});
|
|
7626
|
-
return ["skipped:dirty_tree"];
|
|
7627
|
-
}
|
|
7628
|
-
await this.killStartCommand();
|
|
7629
|
-
try {
|
|
7630
|
-
execSync5(`git pull origin ${branch}`, {
|
|
7631
|
-
cwd: this.projectDir,
|
|
7632
|
-
stdio: "pipe",
|
|
7633
|
-
timeout: 6e4
|
|
7634
|
-
});
|
|
7635
|
-
} catch (err) {
|
|
7636
|
-
const msg = err instanceof Error ? err.message : "Pull failed";
|
|
7637
|
-
logger7.error("Git pull failed during commit sync", { error: msg });
|
|
7638
|
-
this.executeStartCommand();
|
|
7639
|
-
return ["error:pull"];
|
|
7640
|
-
}
|
|
7641
|
-
const stepsRun = ["pull"];
|
|
7642
|
-
const changedFiles = this.getChangedFiles(previousSha, newSha);
|
|
7643
|
-
await this.syncDependencies(changedFiles, stepsRun);
|
|
7644
|
-
this.executeStartCommand();
|
|
7645
|
-
stepsRun.push("startCommand");
|
|
7646
|
-
return stepsRun;
|
|
7647
|
-
}
|
|
7648
|
-
async syncDependencies(changedFiles, stepsRun) {
|
|
7649
|
-
const needsInstall = changedFiles.some(
|
|
7650
|
-
(f) => f === "package.json" || f === "bun.lockb" || f.endsWith("/package.json") || f.endsWith("/bun.lockb")
|
|
7651
|
-
);
|
|
7652
|
-
const needsPrisma = changedFiles.some(
|
|
7653
|
-
(f) => f.includes("prisma/schema.prisma") || f.includes("prisma/migrations/")
|
|
7654
|
-
);
|
|
7655
|
-
const cmd = this.branchSwitchCommand ?? process.env.CONVEYOR_BRANCH_SWITCH_COMMAND;
|
|
7656
|
-
if (cmd && (needsInstall || needsPrisma)) {
|
|
7657
|
-
try {
|
|
7658
|
-
await runSetupCommand(cmd, this.projectDir, (stream, data) => {
|
|
7659
|
-
this.connection.sendEvent({ type: "sync_output", stream, data });
|
|
7660
|
-
});
|
|
7661
|
-
stepsRun.push("branchSwitchCommand");
|
|
7662
|
-
} catch (err) {
|
|
7663
|
-
const msg = err instanceof Error ? err.message : "Sync command failed";
|
|
7664
|
-
logger7.error("Branch switch command failed", { error: msg });
|
|
7665
|
-
}
|
|
7666
|
-
} else if (!cmd) {
|
|
7667
|
-
this.runIndividualSyncSteps(needsInstall, needsPrisma, stepsRun);
|
|
7668
|
-
}
|
|
7669
|
-
}
|
|
7670
|
-
runIndividualSyncSteps(needsInstall, needsPrisma, stepsRun) {
|
|
7671
|
-
if (needsInstall) {
|
|
7672
|
-
try {
|
|
7673
|
-
execSync5("bun install", { cwd: this.projectDir, timeout: 12e4, stdio: "pipe" });
|
|
7674
|
-
stepsRun.push("install");
|
|
7675
|
-
} catch (err) {
|
|
7676
|
-
const msg = err instanceof Error ? err.message : "Install failed";
|
|
7677
|
-
logger7.error("bun install failed", { error: msg });
|
|
7678
|
-
}
|
|
7679
|
-
}
|
|
7680
|
-
if (needsPrisma) {
|
|
7681
|
-
try {
|
|
7682
|
-
execSync5("bunx prisma generate", { cwd: this.projectDir, timeout: 6e4, stdio: "pipe" });
|
|
7683
|
-
execSync5("bunx prisma db push --accept-data-loss", {
|
|
7684
|
-
cwd: this.projectDir,
|
|
7685
|
-
timeout: 6e4,
|
|
7686
|
-
stdio: "pipe"
|
|
7687
|
-
});
|
|
7688
|
-
stepsRun.push("prisma");
|
|
7689
|
-
} catch (err) {
|
|
7690
|
-
const msg = err instanceof Error ? err.message : "Prisma sync failed";
|
|
7691
|
-
logger7.error("Prisma sync failed", { error: msg });
|
|
7692
|
-
}
|
|
7693
|
-
}
|
|
7694
|
-
}
|
|
7695
|
-
getChangedFiles(previousSha, newSha) {
|
|
7696
|
-
try {
|
|
7697
|
-
return execSync5(`git diff --name-only ${previousSha}..${newSha}`, {
|
|
7698
|
-
cwd: this.projectDir,
|
|
7699
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
7700
|
-
}).toString().trim().split("\n").filter(Boolean);
|
|
7701
|
-
} catch {
|
|
7702
|
-
return [];
|
|
7703
|
-
}
|
|
7704
|
-
}
|
|
7705
7618
|
};
|
|
7706
7619
|
|
|
7707
7620
|
// src/setup/config.ts
|
|
@@ -7747,15 +7660,15 @@ export {
|
|
|
7747
7660
|
PlanSync,
|
|
7748
7661
|
SessionRunner,
|
|
7749
7662
|
ProjectConnection,
|
|
7750
|
-
runSetupCommand,
|
|
7751
|
-
runAuthTokenCommand,
|
|
7752
|
-
runStartCommand,
|
|
7753
7663
|
CommitWatcher,
|
|
7754
7664
|
ensureWorktree,
|
|
7755
7665
|
detachWorktreeBranch,
|
|
7756
7666
|
removeWorktree,
|
|
7667
|
+
runSetupCommand,
|
|
7668
|
+
runAuthTokenCommand,
|
|
7669
|
+
runStartCommand,
|
|
7757
7670
|
ProjectRunner,
|
|
7758
7671
|
loadForwardPorts,
|
|
7759
7672
|
loadConveyorConfig
|
|
7760
7673
|
};
|
|
7761
|
-
//# sourceMappingURL=chunk-
|
|
7674
|
+
//# sourceMappingURL=chunk-5OMUMWQR.js.map
|