@rallycry/conveyor-agent 7.2.15 → 7.2.17
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-FDWECEDJ.js +33 -0
- package/dist/chunk-FDWECEDJ.js.map +1 -0
- package/dist/{chunk-TN24QXF5.js → chunk-WYIXEIRS.js} +704 -637
- package/dist/chunk-WYIXEIRS.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-C5YAMQJ2.js +0 -17
- package/dist/chunk-C5YAMQJ2.js.map +0 -1
- package/dist/chunk-TN24QXF5.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,13 +2394,26 @@ function buildGetTaskFileTool(connection) {
|
|
|
2408
2394
|
{ annotations: { readOnlyHint: true } }
|
|
2409
2395
|
);
|
|
2410
2396
|
}
|
|
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
|
+
];
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2408
|
+
// src/tools/incident-tools.ts
|
|
2409
|
+
import { z as z2 } from "zod";
|
|
2411
2410
|
function buildSearchIncidentsTool(connection) {
|
|
2412
2411
|
return defineTool(
|
|
2413
2412
|
"search_incidents",
|
|
2414
2413
|
"Search incidents in the current project. Optionally filter by status (Open, Acknowledged, Investigating, Resolved, Closed) or source.",
|
|
2415
2414
|
{
|
|
2416
|
-
status:
|
|
2417
|
-
source:
|
|
2415
|
+
status: z2.string().optional().describe("Filter by incident status"),
|
|
2416
|
+
source: z2.string().optional().describe("Filter by source (e.g., 'conveyor', 'agent', 'build')")
|
|
2418
2417
|
},
|
|
2419
2418
|
async ({ status, source }) => {
|
|
2420
2419
|
try {
|
|
@@ -2438,7 +2437,7 @@ function buildGetTaskIncidentsTool(connection) {
|
|
|
2438
2437
|
"get_task_incidents",
|
|
2439
2438
|
"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
2439
|
{
|
|
2441
|
-
task_id:
|
|
2440
|
+
task_id: z2.string().optional().describe("Task ID (defaults to current task)")
|
|
2442
2441
|
},
|
|
2443
2442
|
async ({ task_id }) => {
|
|
2444
2443
|
try {
|
|
@@ -2461,11 +2460,11 @@ function buildGetIncidentDetailTool(connection) {
|
|
|
2461
2460
|
"get_incident_detail",
|
|
2462
2461
|
"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
2462
|
{
|
|
2464
|
-
incident_id:
|
|
2465
|
-
section:
|
|
2466
|
-
search:
|
|
2467
|
-
offset:
|
|
2468
|
-
limit:
|
|
2463
|
+
incident_id: z2.string().describe("The incident ID to get details for"),
|
|
2464
|
+
section: z2.enum(["all", "user_report", "task_details", "subtasks", "messages", "logs"]).optional().default("all").describe("Which section to retrieve (default: all)"),
|
|
2465
|
+
search: z2.string().optional().describe("Search for specific text within the section (case-insensitive)"),
|
|
2466
|
+
offset: z2.number().optional().describe("Line offset for pagination (default 0)"),
|
|
2467
|
+
limit: z2.number().optional().describe("Max lines to return (default 100, max 500)")
|
|
2469
2468
|
},
|
|
2470
2469
|
async ({ incident_id, section, search, offset, limit }) => {
|
|
2471
2470
|
try {
|
|
@@ -2506,12 +2505,12 @@ function buildQueryGcpLogsTool(connection) {
|
|
|
2506
2505
|
"query_gcp_logs",
|
|
2507
2506
|
"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
2507
|
{
|
|
2509
|
-
filter:
|
|
2508
|
+
filter: z2.string().optional().describe(
|
|
2510
2509
|
`Cloud Logging filter expression (e.g., 'resource.type="gce_instance"'). Max 1000 chars.`
|
|
2511
2510
|
),
|
|
2512
|
-
start_time:
|
|
2513
|
-
end_time:
|
|
2514
|
-
severity:
|
|
2511
|
+
start_time: z2.string().optional().describe("Start time in ISO 8601 format (e.g., '2024-01-01T00:00:00Z')"),
|
|
2512
|
+
end_time: z2.string().optional().describe("End time in ISO 8601 format (e.g., '2024-01-02T00:00:00Z')"),
|
|
2513
|
+
severity: z2.enum([
|
|
2515
2514
|
"DEFAULT",
|
|
2516
2515
|
"DEBUG",
|
|
2517
2516
|
"INFO",
|
|
@@ -2522,7 +2521,7 @@ function buildQueryGcpLogsTool(connection) {
|
|
|
2522
2521
|
"ALERT",
|
|
2523
2522
|
"EMERGENCY"
|
|
2524
2523
|
]).optional().describe("Minimum severity level to filter by (default: all levels)"),
|
|
2525
|
-
page_size:
|
|
2524
|
+
page_size: z2.number().optional().describe("Number of log entries to return (default 100, max 500)")
|
|
2526
2525
|
},
|
|
2527
2526
|
async ({ filter, start_time, end_time, severity, page_size }) => {
|
|
2528
2527
|
try {
|
|
@@ -2551,6 +2550,17 @@ ${formatted}`);
|
|
|
2551
2550
|
{ annotations: { readOnlyHint: true } }
|
|
2552
2551
|
);
|
|
2553
2552
|
}
|
|
2553
|
+
function buildIncidentTools(connection) {
|
|
2554
|
+
return [
|
|
2555
|
+
buildSearchIncidentsTool(connection),
|
|
2556
|
+
buildGetTaskIncidentsTool(connection),
|
|
2557
|
+
buildGetIncidentDetailTool(connection),
|
|
2558
|
+
buildQueryGcpLogsTool(connection)
|
|
2559
|
+
];
|
|
2560
|
+
}
|
|
2561
|
+
|
|
2562
|
+
// src/tools/dependency-suggestion-tools.ts
|
|
2563
|
+
import { z as z3 } from "zod";
|
|
2554
2564
|
function buildGetDependenciesTool(connection) {
|
|
2555
2565
|
return defineTool(
|
|
2556
2566
|
"get_dependencies",
|
|
@@ -2576,8 +2586,8 @@ function buildGetSuggestionsTool(connection) {
|
|
|
2576
2586
|
"get_suggestions",
|
|
2577
2587
|
"List project suggestions sorted by vote score. Use this to see what the team thinks is important.",
|
|
2578
2588
|
{
|
|
2579
|
-
status:
|
|
2580
|
-
limit:
|
|
2589
|
+
status: z3.string().optional().describe("Filter by status: Open, Accepted, Rejected, Implemented"),
|
|
2590
|
+
limit: z3.number().optional().describe("Max results (default 20)")
|
|
2581
2591
|
},
|
|
2582
2592
|
async ({ status: _status, limit: _limit }) => {
|
|
2583
2593
|
try {
|
|
@@ -2597,13 +2607,16 @@ function buildGetSuggestionsTool(connection) {
|
|
|
2597
2607
|
{ annotations: { readOnlyHint: true } }
|
|
2598
2608
|
);
|
|
2599
2609
|
}
|
|
2610
|
+
|
|
2611
|
+
// src/tools/mutation-tools.ts
|
|
2612
|
+
import { z as z4 } from "zod";
|
|
2600
2613
|
function buildPostToChatTool(connection) {
|
|
2601
2614
|
return defineTool(
|
|
2602
2615
|
"post_to_chat",
|
|
2603
2616
|
"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
2617
|
{
|
|
2605
|
-
message:
|
|
2606
|
-
task_id:
|
|
2618
|
+
message: z4.string().describe("The message to post to the team"),
|
|
2619
|
+
task_id: z4.string().optional().describe("Child task ID to post to. Omit to post to the current task's chat.")
|
|
2607
2620
|
},
|
|
2608
2621
|
async ({ message, task_id }) => {
|
|
2609
2622
|
try {
|
|
@@ -2630,8 +2643,8 @@ function buildForceUpdateTaskStatusTool(connection) {
|
|
|
2630
2643
|
"force_update_task_status",
|
|
2631
2644
|
"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
2645
|
{
|
|
2633
|
-
status:
|
|
2634
|
-
task_id:
|
|
2646
|
+
status: z4.enum(["InProgress", "ReviewPR", "ReviewDev", "Complete"]).describe("The new status for the task"),
|
|
2647
|
+
task_id: z4.string().optional().describe("Child task ID to update. Omit to update the current task.")
|
|
2635
2648
|
},
|
|
2636
2649
|
async ({ status, task_id }) => {
|
|
2637
2650
|
try {
|
|
@@ -2662,15 +2675,15 @@ function buildCreatePullRequestTool(connection, config) {
|
|
|
2662
2675
|
"create_pull_request",
|
|
2663
2676
|
"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
2677
|
{
|
|
2665
|
-
title:
|
|
2666
|
-
body:
|
|
2667
|
-
branch:
|
|
2678
|
+
title: z4.string().describe("The PR title"),
|
|
2679
|
+
body: z4.string().describe("The PR description/body in markdown"),
|
|
2680
|
+
branch: z4.string().optional().describe(
|
|
2668
2681
|
"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
2682
|
),
|
|
2670
|
-
baseBranch:
|
|
2683
|
+
baseBranch: z4.string().optional().describe(
|
|
2671
2684
|
"The base branch to target for the PR (e.g. 'main', 'develop'). Defaults to the project's configured dev branch."
|
|
2672
2685
|
),
|
|
2673
|
-
commitMessage:
|
|
2686
|
+
commitMessage: z4.string().optional().describe(
|
|
2674
2687
|
"Commit message for staging uncommitted changes. If not provided, a default message based on the PR title will be used."
|
|
2675
2688
|
)
|
|
2676
2689
|
},
|
|
@@ -2748,7 +2761,7 @@ function buildAddDependencyTool(connection) {
|
|
|
2748
2761
|
"add_dependency",
|
|
2749
2762
|
"Add a dependency \u2014 this task cannot start until the specified task is merged to dev",
|
|
2750
2763
|
{
|
|
2751
|
-
depends_on_slug_or_id:
|
|
2764
|
+
depends_on_slug_or_id: z4.string().describe("Slug or ID of the task this task depends on")
|
|
2752
2765
|
},
|
|
2753
2766
|
async ({ depends_on_slug_or_id }) => {
|
|
2754
2767
|
try {
|
|
@@ -2770,7 +2783,7 @@ function buildRemoveDependencyTool(connection) {
|
|
|
2770
2783
|
"remove_dependency",
|
|
2771
2784
|
"Remove a dependency from this task",
|
|
2772
2785
|
{
|
|
2773
|
-
depends_on_slug_or_id:
|
|
2786
|
+
depends_on_slug_or_id: z4.string().describe("Slug or ID of the task to remove as dependency")
|
|
2774
2787
|
},
|
|
2775
2788
|
async ({ depends_on_slug_or_id }) => {
|
|
2776
2789
|
try {
|
|
@@ -2792,10 +2805,10 @@ function buildCreateFollowUpTaskTool(connection) {
|
|
|
2792
2805
|
"create_follow_up_task",
|
|
2793
2806
|
"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
2807
|
{
|
|
2795
|
-
title:
|
|
2796
|
-
description:
|
|
2797
|
-
plan:
|
|
2798
|
-
story_point_value:
|
|
2808
|
+
title: z4.string().describe("Follow-up task title"),
|
|
2809
|
+
description: z4.string().optional().describe("Brief description of the follow-up work"),
|
|
2810
|
+
plan: z4.string().optional().describe("Implementation plan if known"),
|
|
2811
|
+
story_point_value: z4.number().optional().describe("Story point estimate (1=Common, 2=Magic, 3=Rare, 5=Unique)")
|
|
2799
2812
|
},
|
|
2800
2813
|
async ({ title, description, plan, story_point_value }) => {
|
|
2801
2814
|
try {
|
|
@@ -2822,11 +2835,11 @@ function buildCreateSuggestionTool(connection) {
|
|
|
2822
2835
|
"create_suggestion",
|
|
2823
2836
|
"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
2837
|
{
|
|
2825
|
-
title:
|
|
2826
|
-
description:
|
|
2838
|
+
title: z4.string().describe("Short title for the suggestion"),
|
|
2839
|
+
description: z4.string().optional().describe(
|
|
2827
2840
|
"1-2 sentence description of what should change and why. Keep concise and project-focused."
|
|
2828
2841
|
),
|
|
2829
|
-
tag_names:
|
|
2842
|
+
tag_names: z4.array(z4.string()).optional().describe("Tag names to categorize the suggestion")
|
|
2830
2843
|
},
|
|
2831
2844
|
async ({ title, description, tag_names }) => {
|
|
2832
2845
|
try {
|
|
@@ -2855,8 +2868,8 @@ function buildVoteSuggestionTool(connection) {
|
|
|
2855
2868
|
"vote_suggestion",
|
|
2856
2869
|
"Vote on a project suggestion. Use +1 to upvote or -1 to downvote.",
|
|
2857
2870
|
{
|
|
2858
|
-
suggestion_id:
|
|
2859
|
-
value:
|
|
2871
|
+
suggestion_id: z4.string().describe("The suggestion ID to vote on"),
|
|
2872
|
+
value: z4.number().refine((v) => v === 1 || v === -1, { message: "Value must be 1 or -1" }).describe("+1 to upvote, -1 to downvote")
|
|
2860
2873
|
},
|
|
2861
2874
|
async ({ suggestion_id, value }) => {
|
|
2862
2875
|
try {
|
|
@@ -2874,41 +2887,39 @@ function buildVoteSuggestionTool(connection) {
|
|
|
2874
2887
|
}
|
|
2875
2888
|
);
|
|
2876
2889
|
}
|
|
2877
|
-
function
|
|
2878
|
-
|
|
2879
|
-
buildReadTaskChatTool(connection),
|
|
2890
|
+
function buildMutationTools(connection, config) {
|
|
2891
|
+
return [
|
|
2880
2892
|
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
2893
|
buildCreatePullRequestTool(connection, config),
|
|
2891
2894
|
buildAddDependencyTool(connection),
|
|
2892
2895
|
buildRemoveDependencyTool(connection),
|
|
2893
|
-
buildGetDependenciesTool(connection),
|
|
2894
2896
|
buildCreateFollowUpTaskTool(connection),
|
|
2895
2897
|
buildCreateSuggestionTool(connection),
|
|
2896
|
-
buildVoteSuggestionTool(connection)
|
|
2897
|
-
|
|
2898
|
+
buildVoteSuggestionTool(connection)
|
|
2899
|
+
];
|
|
2900
|
+
}
|
|
2901
|
+
|
|
2902
|
+
// src/tools/common-tools.ts
|
|
2903
|
+
function buildCommonTools(connection, config) {
|
|
2904
|
+
return [
|
|
2905
|
+
...buildTaskContextTools(connection),
|
|
2906
|
+
...buildIncidentTools(connection),
|
|
2907
|
+
buildGetDependenciesTool(connection),
|
|
2908
|
+
buildGetSuggestionsTool(connection),
|
|
2909
|
+
...buildMutationTools(connection, config)
|
|
2898
2910
|
];
|
|
2899
|
-
return tools;
|
|
2900
2911
|
}
|
|
2901
2912
|
|
|
2902
2913
|
// src/tools/pm-tools.ts
|
|
2903
|
-
import { z as
|
|
2914
|
+
import { z as z5 } from "zod";
|
|
2904
2915
|
var SP_DESCRIPTION = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique)";
|
|
2905
2916
|
function buildUpdateTaskTool(connection) {
|
|
2906
2917
|
return defineTool(
|
|
2907
2918
|
"update_task",
|
|
2908
2919
|
"Save the finalized task plan and/or description",
|
|
2909
2920
|
{
|
|
2910
|
-
plan:
|
|
2911
|
-
description:
|
|
2921
|
+
plan: z5.string().optional().describe("The task plan in markdown"),
|
|
2922
|
+
description: z5.string().optional().describe("Updated task description")
|
|
2912
2923
|
},
|
|
2913
2924
|
async ({ plan, description }) => {
|
|
2914
2925
|
try {
|
|
@@ -2929,11 +2940,11 @@ function buildCreateSubtaskTool(connection) {
|
|
|
2929
2940
|
"create_subtask",
|
|
2930
2941
|
"Create a subtask under the current parent task. Use for breaking complex tasks into smaller pieces.",
|
|
2931
2942
|
{
|
|
2932
|
-
title:
|
|
2933
|
-
description:
|
|
2934
|
-
plan:
|
|
2935
|
-
ordinal:
|
|
2936
|
-
storyPointValue:
|
|
2943
|
+
title: z5.string().describe("Subtask title"),
|
|
2944
|
+
description: z5.string().optional().describe("Brief description"),
|
|
2945
|
+
plan: z5.string().optional().describe("Implementation plan in markdown"),
|
|
2946
|
+
ordinal: z5.number().optional().describe("Step/order number (0-based)"),
|
|
2947
|
+
storyPointValue: z5.number().optional().describe(SP_DESCRIPTION)
|
|
2937
2948
|
},
|
|
2938
2949
|
async ({ title, description, plan, ordinal, storyPointValue }) => {
|
|
2939
2950
|
try {
|
|
@@ -2959,12 +2970,12 @@ function buildUpdateSubtaskTool(connection) {
|
|
|
2959
2970
|
"update_subtask",
|
|
2960
2971
|
"Update an existing subtask's fields",
|
|
2961
2972
|
{
|
|
2962
|
-
subtaskId:
|
|
2963
|
-
title:
|
|
2964
|
-
description:
|
|
2965
|
-
plan:
|
|
2966
|
-
ordinal:
|
|
2967
|
-
storyPointValue:
|
|
2973
|
+
subtaskId: z5.string().describe("The subtask ID to update"),
|
|
2974
|
+
title: z5.string().optional(),
|
|
2975
|
+
description: z5.string().optional(),
|
|
2976
|
+
plan: z5.string().optional(),
|
|
2977
|
+
ordinal: z5.number().optional(),
|
|
2978
|
+
storyPointValue: z5.number().optional().describe(SP_DESCRIPTION)
|
|
2968
2979
|
},
|
|
2969
2980
|
async ({ subtaskId, title, description, plan, storyPointValue }) => {
|
|
2970
2981
|
try {
|
|
@@ -2987,7 +2998,7 @@ function buildDeleteSubtaskTool(connection) {
|
|
|
2987
2998
|
return defineTool(
|
|
2988
2999
|
"delete_subtask",
|
|
2989
3000
|
"Delete a subtask",
|
|
2990
|
-
{ subtaskId:
|
|
3001
|
+
{ subtaskId: z5.string().describe("The subtask ID to delete") },
|
|
2991
3002
|
async ({ subtaskId }) => {
|
|
2992
3003
|
try {
|
|
2993
3004
|
await connection.call("deleteSubtask", {
|
|
@@ -3025,7 +3036,7 @@ function buildPackTools(connection) {
|
|
|
3025
3036
|
"start_child_cloud_build",
|
|
3026
3037
|
"Start a cloud build for a child task. The child must be in Open status with story points and an agent assigned.",
|
|
3027
3038
|
{
|
|
3028
|
-
childTaskId:
|
|
3039
|
+
childTaskId: z5.string().describe("The child task ID to start a cloud build for")
|
|
3029
3040
|
},
|
|
3030
3041
|
async ({ childTaskId }) => {
|
|
3031
3042
|
try {
|
|
@@ -3045,7 +3056,7 @@ function buildPackTools(connection) {
|
|
|
3045
3056
|
"stop_child_build",
|
|
3046
3057
|
"Stop a running cloud build for a child task. Sends a stop signal to the child agent.",
|
|
3047
3058
|
{
|
|
3048
|
-
childTaskId:
|
|
3059
|
+
childTaskId: z5.string().describe("The child task ID whose build should be stopped")
|
|
3049
3060
|
},
|
|
3050
3061
|
async ({ childTaskId }) => {
|
|
3051
3062
|
try {
|
|
@@ -3065,7 +3076,7 @@ function buildPackTools(connection) {
|
|
|
3065
3076
|
"approve_and_merge_pr",
|
|
3066
3077
|
"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
3078
|
{
|
|
3068
|
-
childTaskId:
|
|
3079
|
+
childTaskId: z5.string().describe("The child task ID whose PR should be approved and merged")
|
|
3069
3080
|
},
|
|
3070
3081
|
async ({ childTaskId }) => {
|
|
3071
3082
|
try {
|
|
@@ -3103,7 +3114,7 @@ function buildPmTools(connection, options) {
|
|
|
3103
3114
|
}
|
|
3104
3115
|
|
|
3105
3116
|
// src/tools/discovery-tools.ts
|
|
3106
|
-
import { z as
|
|
3117
|
+
import { z as z6 } from "zod";
|
|
3107
3118
|
var SP_DESCRIPTION2 = "Story point value (1=Common, 2=Magic, 3=Rare, 5=Unique)";
|
|
3108
3119
|
function buildDiscoveryTools(connection) {
|
|
3109
3120
|
return [
|
|
@@ -3111,11 +3122,11 @@ function buildDiscoveryTools(connection) {
|
|
|
3111
3122
|
"update_task_properties",
|
|
3112
3123
|
"Set one or more task properties in a single call. All fields are optional \u2014 only include the fields you want to update.",
|
|
3113
3124
|
{
|
|
3114
|
-
title:
|
|
3115
|
-
storyPointValue:
|
|
3116
|
-
tagNames:
|
|
3117
|
-
githubPRUrl:
|
|
3118
|
-
githubBranch:
|
|
3125
|
+
title: z6.string().optional().describe("The new task title"),
|
|
3126
|
+
storyPointValue: z6.number().optional().describe(SP_DESCRIPTION2),
|
|
3127
|
+
tagNames: z6.array(z6.string()).optional().describe("Array of tag names to assign"),
|
|
3128
|
+
githubPRUrl: z6.string().url().optional().describe("GitHub pull request URL to link to this task"),
|
|
3129
|
+
githubBranch: z6.string().optional().describe("Set the GitHub branch name for this task (e.g. 'conveyor/my-feature-abc123')")
|
|
3119
3130
|
},
|
|
3120
3131
|
async ({ title, storyPointValue, tagNames, githubPRUrl, githubBranch }) => {
|
|
3121
3132
|
try {
|
|
@@ -3146,14 +3157,14 @@ function buildDiscoveryTools(connection) {
|
|
|
3146
3157
|
}
|
|
3147
3158
|
|
|
3148
3159
|
// src/tools/code-review-tools.ts
|
|
3149
|
-
import { z as
|
|
3160
|
+
import { z as z7 } from "zod";
|
|
3150
3161
|
function buildCodeReviewTools(connection) {
|
|
3151
3162
|
return [
|
|
3152
3163
|
defineTool(
|
|
3153
3164
|
"approve_code_review",
|
|
3154
3165
|
"Approve the code review. Use this when the code passes all review criteria and is ready to merge.",
|
|
3155
3166
|
{
|
|
3156
|
-
summary:
|
|
3167
|
+
summary: z7.string().describe("Brief summary of what was reviewed and why it looks good")
|
|
3157
3168
|
},
|
|
3158
3169
|
async ({ summary }) => {
|
|
3159
3170
|
const content = `**Code Review: Approved** :white_check_mark:
|
|
@@ -3176,15 +3187,15 @@ ${summary}`;
|
|
|
3176
3187
|
"request_code_changes",
|
|
3177
3188
|
"Request changes during code review. Use this when substantive issues are found that need to be fixed before merge.",
|
|
3178
3189
|
{
|
|
3179
|
-
issues:
|
|
3180
|
-
|
|
3181
|
-
file:
|
|
3182
|
-
line:
|
|
3183
|
-
severity:
|
|
3184
|
-
description:
|
|
3190
|
+
issues: z7.array(
|
|
3191
|
+
z7.object({
|
|
3192
|
+
file: z7.string().describe("File path where the issue was found"),
|
|
3193
|
+
line: z7.number().optional().describe("Line number (if applicable)"),
|
|
3194
|
+
severity: z7.enum(["critical", "major", "minor"]).describe("Issue severity"),
|
|
3195
|
+
description: z7.string().describe("What is wrong and how to fix it")
|
|
3185
3196
|
})
|
|
3186
3197
|
).describe("List of issues found during review"),
|
|
3187
|
-
summary:
|
|
3198
|
+
summary: z7.string().describe("Brief overall summary of the review findings")
|
|
3188
3199
|
},
|
|
3189
3200
|
async ({ issues, summary }) => {
|
|
3190
3201
|
const issueLines = issues.map((issue) => {
|
|
@@ -3214,10 +3225,10 @@ ${issueLines}`;
|
|
|
3214
3225
|
}
|
|
3215
3226
|
|
|
3216
3227
|
// src/tools/debug-tools.ts
|
|
3217
|
-
import { z as
|
|
3228
|
+
import { z as z10 } from "zod";
|
|
3218
3229
|
|
|
3219
3230
|
// src/tools/telemetry-tools.ts
|
|
3220
|
-
import { z as
|
|
3231
|
+
import { z as z8 } from "zod";
|
|
3221
3232
|
|
|
3222
3233
|
// src/debug/telemetry-injector.ts
|
|
3223
3234
|
var BUFFER_SIZE = 200;
|
|
@@ -3612,12 +3623,12 @@ function buildGetTelemetryTool(manager) {
|
|
|
3612
3623
|
"debug_get_telemetry",
|
|
3613
3624
|
"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
3625
|
{
|
|
3615
|
-
type:
|
|
3616
|
-
urlPattern:
|
|
3617
|
-
minDuration:
|
|
3618
|
-
errorOnly:
|
|
3619
|
-
since:
|
|
3620
|
-
limit:
|
|
3626
|
+
type: z8.enum(["http", "db", "socket", "error"]).optional().describe("Filter by event type"),
|
|
3627
|
+
urlPattern: z8.string().optional().describe("Regex pattern to filter HTTP events by URL"),
|
|
3628
|
+
minDuration: z8.number().optional().describe("Minimum duration in ms \u2014 only return events slower than this"),
|
|
3629
|
+
errorOnly: z8.boolean().optional().describe("Only return error events and HTTP 4xx/5xx responses"),
|
|
3630
|
+
since: z8.number().optional().describe("Only return events after this timestamp (ms since epoch)"),
|
|
3631
|
+
limit: z8.number().optional().describe("Max events to return (default: 20, from most recent)")
|
|
3621
3632
|
},
|
|
3622
3633
|
async ({ type, urlPattern, minDuration, errorOnly, since, limit }) => {
|
|
3623
3634
|
const clientOrErr = requireDebugClient(manager);
|
|
@@ -3687,7 +3698,7 @@ function buildTelemetryTools(manager) {
|
|
|
3687
3698
|
}
|
|
3688
3699
|
|
|
3689
3700
|
// src/tools/client-debug-tools.ts
|
|
3690
|
-
import { z as
|
|
3701
|
+
import { z as z9 } from "zod";
|
|
3691
3702
|
function requirePlaywrightClient(manager) {
|
|
3692
3703
|
if (!manager.isClientDebugMode()) {
|
|
3693
3704
|
return "Client debug mode is not active. Use debug_enter_mode with clientSide: true first.";
|
|
@@ -3707,11 +3718,11 @@ function buildClientBreakpointTools(manager) {
|
|
|
3707
3718
|
"debug_set_client_breakpoint",
|
|
3708
3719
|
"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
3720
|
{
|
|
3710
|
-
file:
|
|
3721
|
+
file: z9.string().describe(
|
|
3711
3722
|
"Original source file path (e.g., src/components/App.tsx) \u2014 source maps resolve automatically"
|
|
3712
3723
|
),
|
|
3713
|
-
line:
|
|
3714
|
-
condition:
|
|
3724
|
+
line: z9.number().describe("Line number (1-based) in the original source file"),
|
|
3725
|
+
condition: z9.string().optional().describe("JavaScript condition expression \u2014 breakpoint only triggers when truthy")
|
|
3715
3726
|
},
|
|
3716
3727
|
async ({ file, line, condition }) => {
|
|
3717
3728
|
const clientOrErr = requirePlaywrightClient(manager);
|
|
@@ -3733,7 +3744,7 @@ Breakpoint ID: ${breakpointId}${sourceMapNote}`
|
|
|
3733
3744
|
"debug_remove_client_breakpoint",
|
|
3734
3745
|
"Remove a previously set client-side breakpoint by its ID.",
|
|
3735
3746
|
{
|
|
3736
|
-
breakpointId:
|
|
3747
|
+
breakpointId: z9.string().describe("The breakpoint ID returned by debug_set_client_breakpoint")
|
|
3737
3748
|
},
|
|
3738
3749
|
async ({ breakpointId }) => {
|
|
3739
3750
|
const clientOrErr = requirePlaywrightClient(manager);
|
|
@@ -3813,8 +3824,8 @@ ${JSON.stringify(queuedHits, null, 2)}`
|
|
|
3813
3824
|
"debug_evaluate_client",
|
|
3814
3825
|
"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
3826
|
{
|
|
3816
|
-
expression:
|
|
3817
|
-
frameIndex:
|
|
3827
|
+
expression: z9.string().describe("JavaScript expression to evaluate in the browser context"),
|
|
3828
|
+
frameIndex: z9.number().optional().describe("Call stack frame index (0 = top frame). Defaults to the top frame.")
|
|
3818
3829
|
},
|
|
3819
3830
|
async ({ expression, frameIndex }) => {
|
|
3820
3831
|
const clientOrErr = requirePlaywrightClient(manager);
|
|
@@ -3887,7 +3898,7 @@ function buildClientInteractionTools(manager) {
|
|
|
3887
3898
|
"debug_navigate_client",
|
|
3888
3899
|
"Navigate the headless browser to a specific URL. Use this to reproduce specific flows or visit different pages.",
|
|
3889
3900
|
{
|
|
3890
|
-
url:
|
|
3901
|
+
url: z9.string().describe("URL to navigate to (e.g., http://localhost:3000/dashboard)")
|
|
3891
3902
|
},
|
|
3892
3903
|
async ({ url }) => {
|
|
3893
3904
|
const clientOrErr = requirePlaywrightClient(manager);
|
|
@@ -3904,7 +3915,7 @@ function buildClientInteractionTools(manager) {
|
|
|
3904
3915
|
"debug_click_client",
|
|
3905
3916
|
"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
3917
|
{
|
|
3907
|
-
selector:
|
|
3918
|
+
selector: z9.string().describe(
|
|
3908
3919
|
"CSS selector of the element to click (e.g., 'button.submit', '#login-form input[type=submit]')"
|
|
3909
3920
|
)
|
|
3910
3921
|
},
|
|
@@ -3926,8 +3937,8 @@ function buildClientConsoleTool(manager) {
|
|
|
3926
3937
|
"debug_get_client_console",
|
|
3927
3938
|
"Get console messages captured from the headless browser. Includes console.log, warn, error, etc.",
|
|
3928
3939
|
{
|
|
3929
|
-
level:
|
|
3930
|
-
limit:
|
|
3940
|
+
level: z9.string().optional().describe("Filter by console level: log, warn, error, info, debug"),
|
|
3941
|
+
limit: z9.number().optional().describe("Maximum number of recent messages to return (default: all)")
|
|
3931
3942
|
},
|
|
3932
3943
|
// oxlint-disable-next-line require-await
|
|
3933
3944
|
async ({ level, limit }) => {
|
|
@@ -3954,8 +3965,8 @@ function buildClientNetworkTool(manager) {
|
|
|
3954
3965
|
"debug_get_client_network",
|
|
3955
3966
|
"Get network requests captured from the headless browser. Shows URLs, methods, status codes, and timing.",
|
|
3956
3967
|
{
|
|
3957
|
-
filter:
|
|
3958
|
-
limit:
|
|
3968
|
+
filter: z9.string().optional().describe("Regex pattern to filter requests by URL"),
|
|
3969
|
+
limit: z9.number().optional().describe("Maximum number of recent requests to return (default: all)")
|
|
3959
3970
|
},
|
|
3960
3971
|
// oxlint-disable-next-line require-await
|
|
3961
3972
|
async ({ filter, limit }) => {
|
|
@@ -3983,7 +3994,7 @@ function buildClientErrorsTool(manager) {
|
|
|
3983
3994
|
"debug_get_client_errors",
|
|
3984
3995
|
"Get uncaught errors captured from the headless browser. Includes error messages and source-mapped stack traces.",
|
|
3985
3996
|
{
|
|
3986
|
-
limit:
|
|
3997
|
+
limit: z9.number().optional().describe("Maximum number of recent errors to return (default: all)")
|
|
3987
3998
|
},
|
|
3988
3999
|
// oxlint-disable-next-line require-await
|
|
3989
4000
|
async ({ limit }) => {
|
|
@@ -4077,12 +4088,12 @@ function buildDebugLifecycleTools(manager) {
|
|
|
4077
4088
|
"debug_enter_mode",
|
|
4078
4089
|
"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
4090
|
{
|
|
4080
|
-
hypothesis:
|
|
4081
|
-
serverSide:
|
|
4091
|
+
hypothesis: z10.string().optional().describe("Your hypothesis about the bug \u2014 helps track debugging intent"),
|
|
4092
|
+
serverSide: z10.boolean().optional().describe(
|
|
4082
4093
|
"Enable server-side Node.js debugging (default: true if clientSide is not set)"
|
|
4083
4094
|
),
|
|
4084
|
-
clientSide:
|
|
4085
|
-
previewUrl:
|
|
4095
|
+
clientSide: z10.boolean().optional().describe("Enable client-side browser debugging via headless Chromium + Playwright"),
|
|
4096
|
+
previewUrl: z10.string().optional().describe(
|
|
4086
4097
|
"Preview URL for client-side debugging (e.g., http://localhost:3000). Required when clientSide is true."
|
|
4087
4098
|
)
|
|
4088
4099
|
},
|
|
@@ -4118,9 +4129,9 @@ function buildBreakpointTools(manager) {
|
|
|
4118
4129
|
"debug_set_breakpoint",
|
|
4119
4130
|
"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
4131
|
{
|
|
4121
|
-
file:
|
|
4122
|
-
line:
|
|
4123
|
-
condition:
|
|
4132
|
+
file: z10.string().describe("Absolute or relative file path to set the breakpoint in"),
|
|
4133
|
+
line: z10.number().describe("Line number (1-based) to set the breakpoint on"),
|
|
4134
|
+
condition: z10.string().optional().describe("JavaScript condition expression \u2014 breakpoint only triggers when truthy")
|
|
4124
4135
|
},
|
|
4125
4136
|
async ({ file, line, condition }) => {
|
|
4126
4137
|
const clientOrErr = requireDebugClient2(manager);
|
|
@@ -4142,7 +4153,7 @@ Breakpoint ID: ${breakpointId}`
|
|
|
4142
4153
|
"debug_remove_breakpoint",
|
|
4143
4154
|
"Remove a previously set breakpoint by its ID.",
|
|
4144
4155
|
{
|
|
4145
|
-
breakpointId:
|
|
4156
|
+
breakpointId: z10.string().describe("The breakpoint ID returned by debug_set_breakpoint")
|
|
4146
4157
|
},
|
|
4147
4158
|
async ({ breakpointId }) => {
|
|
4148
4159
|
const clientOrErr = requireDebugClient2(manager);
|
|
@@ -4223,8 +4234,8 @@ ${JSON.stringify(queuedHits, null, 2)}`
|
|
|
4223
4234
|
"debug_evaluate",
|
|
4224
4235
|
"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
4236
|
{
|
|
4226
|
-
expression:
|
|
4227
|
-
frameIndex:
|
|
4237
|
+
expression: z10.string().describe("The JavaScript expression to evaluate"),
|
|
4238
|
+
frameIndex: z10.number().optional().describe("Call stack frame index (0 = top frame). Defaults to the top frame.")
|
|
4228
4239
|
},
|
|
4229
4240
|
async ({ expression, frameIndex }) => {
|
|
4230
4241
|
const clientOrErr = requireDebugClient2(manager);
|
|
@@ -4252,12 +4263,12 @@ function buildProbeManagementTools(manager) {
|
|
|
4252
4263
|
"debug_add_probe",
|
|
4253
4264
|
"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
4265
|
{
|
|
4255
|
-
file:
|
|
4256
|
-
line:
|
|
4257
|
-
expressions:
|
|
4266
|
+
file: z10.string().describe("File path to probe"),
|
|
4267
|
+
line: z10.number().describe("Line number (1-based) to probe"),
|
|
4268
|
+
expressions: z10.array(z10.string()).describe(
|
|
4258
4269
|
'JavaScript expressions to capture when the line executes (e.g., ["req.params.id", "user.role"])'
|
|
4259
4270
|
),
|
|
4260
|
-
label:
|
|
4271
|
+
label: z10.string().optional().describe("Optional label for this probe (defaults to file:line)")
|
|
4261
4272
|
},
|
|
4262
4273
|
async ({ file, line, expressions, label }) => {
|
|
4263
4274
|
const clientOrErr = requireDebugClient2(manager);
|
|
@@ -4282,7 +4293,7 @@ Trigger the code path, then use debug_get_probe_results to see captured values.`
|
|
|
4282
4293
|
"debug_remove_probe",
|
|
4283
4294
|
"Remove a previously set debug probe by its ID.",
|
|
4284
4295
|
{
|
|
4285
|
-
probeId:
|
|
4296
|
+
probeId: z10.string().describe("The probe ID returned by debug_add_probe")
|
|
4286
4297
|
},
|
|
4287
4298
|
async ({ probeId }) => {
|
|
4288
4299
|
const clientOrErr = requireDebugClient2(manager);
|
|
@@ -4322,9 +4333,9 @@ function buildProbeResultTools(manager) {
|
|
|
4322
4333
|
"debug_get_probe_results",
|
|
4323
4334
|
"Fetch captured probe hit data. Returns expression values from each time a probed line executed.",
|
|
4324
4335
|
{
|
|
4325
|
-
probeId:
|
|
4326
|
-
label:
|
|
4327
|
-
limit:
|
|
4336
|
+
probeId: z10.string().optional().describe("Filter results by probe ID (resolves to its label)"),
|
|
4337
|
+
label: z10.string().optional().describe("Filter results by probe label"),
|
|
4338
|
+
limit: z10.number().optional().describe("Maximum number of recent hits to return (default: all)")
|
|
4328
4339
|
},
|
|
4329
4340
|
async ({ probeId, label, limit }) => {
|
|
4330
4341
|
const clientOrErr = requireDebugClient2(manager);
|
|
@@ -4795,10 +4806,20 @@ function stopTypingIfNeeded(host, isTyping) {
|
|
|
4795
4806
|
if (isTyping) host.connection.sendTypingStop();
|
|
4796
4807
|
}
|
|
4797
4808
|
function flushPendingToolCalls(host, turnToolCalls) {
|
|
4798
|
-
if (turnToolCalls.length === 0)
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4809
|
+
if (turnToolCalls.length === 0) {
|
|
4810
|
+
host.pendingToolOutputs.length = 0;
|
|
4811
|
+
return;
|
|
4812
|
+
}
|
|
4813
|
+
const outputsByTool = /* @__PURE__ */ new Map();
|
|
4814
|
+
for (const entry of host.pendingToolOutputs) {
|
|
4815
|
+
const list = outputsByTool.get(entry.tool) ?? [];
|
|
4816
|
+
list.push(entry.output);
|
|
4817
|
+
outputsByTool.set(entry.tool, list);
|
|
4818
|
+
}
|
|
4819
|
+
for (const call of turnToolCalls) {
|
|
4820
|
+
const list = outputsByTool.get(call.tool);
|
|
4821
|
+
if (list && list.length > 0) {
|
|
4822
|
+
call.output = list.shift();
|
|
4802
4823
|
}
|
|
4803
4824
|
}
|
|
4804
4825
|
host.connection.sendEvent({ type: "turn_end", toolCalls: [...turnToolCalls] });
|
|
@@ -5120,7 +5141,7 @@ function buildHooks(host) {
|
|
|
5120
5141
|
output,
|
|
5121
5142
|
isError: false
|
|
5122
5143
|
});
|
|
5123
|
-
host.pendingToolOutputs.push(output);
|
|
5144
|
+
host.pendingToolOutputs.push({ tool: input.tool_name, output });
|
|
5124
5145
|
if (input.tool_name === "mcp__conveyor__create_pull_request") {
|
|
5125
5146
|
try {
|
|
5126
5147
|
const props = await host.connection.getTaskProperties();
|
|
@@ -6441,67 +6462,8 @@ var ProjectConnection = class {
|
|
|
6441
6462
|
}
|
|
6442
6463
|
};
|
|
6443
6464
|
|
|
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
6465
|
// src/runner/commit-watcher.ts
|
|
6504
|
-
import { execSync as
|
|
6466
|
+
import { execSync as execSync2 } from "child_process";
|
|
6505
6467
|
var logger5 = createServiceLogger("CommitWatcher");
|
|
6506
6468
|
var CommitWatcher = class {
|
|
6507
6469
|
constructor(config, callbacks) {
|
|
@@ -6535,7 +6497,7 @@ var CommitWatcher = class {
|
|
|
6535
6497
|
this.isSyncing = false;
|
|
6536
6498
|
}
|
|
6537
6499
|
getLocalHeadSha() {
|
|
6538
|
-
return
|
|
6500
|
+
return execSync2("git rev-parse HEAD", {
|
|
6539
6501
|
cwd: this.config.projectDir,
|
|
6540
6502
|
stdio: ["ignore", "pipe", "ignore"]
|
|
6541
6503
|
}).toString().trim();
|
|
@@ -6543,12 +6505,12 @@ var CommitWatcher = class {
|
|
|
6543
6505
|
poll() {
|
|
6544
6506
|
if (!this.branch || this.isSyncing) return;
|
|
6545
6507
|
try {
|
|
6546
|
-
|
|
6508
|
+
execSync2(`git fetch origin ${this.branch} --quiet`, {
|
|
6547
6509
|
cwd: this.config.projectDir,
|
|
6548
6510
|
stdio: "ignore",
|
|
6549
6511
|
timeout: 3e4
|
|
6550
6512
|
});
|
|
6551
|
-
const remoteSha =
|
|
6513
|
+
const remoteSha = execSync2(`git rev-parse origin/${this.branch}`, {
|
|
6552
6514
|
cwd: this.config.projectDir,
|
|
6553
6515
|
stdio: ["ignore", "pipe", "ignore"]
|
|
6554
6516
|
}).toString().trim();
|
|
@@ -6569,12 +6531,12 @@ var CommitWatcher = class {
|
|
|
6569
6531
|
let latestMessage = "";
|
|
6570
6532
|
let latestAuthor = "";
|
|
6571
6533
|
try {
|
|
6572
|
-
const countOutput =
|
|
6534
|
+
const countOutput = execSync2(`git rev-list --count ${previousSha}..origin/${this.branch}`, {
|
|
6573
6535
|
cwd: this.config.projectDir,
|
|
6574
6536
|
stdio: ["ignore", "pipe", "ignore"]
|
|
6575
6537
|
}).toString().trim();
|
|
6576
6538
|
commitCount = parseInt(countOutput, 10) || 1;
|
|
6577
|
-
const logOutput =
|
|
6539
|
+
const logOutput = execSync2(`git log -1 --format="%s|||%an" origin/${this.branch}`, {
|
|
6578
6540
|
cwd: this.config.projectDir,
|
|
6579
6541
|
stdio: ["ignore", "pipe", "ignore"]
|
|
6580
6542
|
}).toString().trim();
|
|
@@ -6609,7 +6571,7 @@ var CommitWatcher = class {
|
|
|
6609
6571
|
};
|
|
6610
6572
|
|
|
6611
6573
|
// src/runner/worktree.ts
|
|
6612
|
-
import { execSync as
|
|
6574
|
+
import { execSync as execSync3 } from "child_process";
|
|
6613
6575
|
import { existsSync as existsSync2 } from "fs";
|
|
6614
6576
|
import { join as join4 } from "path";
|
|
6615
6577
|
var WORKTREE_DIR = ".worktrees";
|
|
@@ -6624,7 +6586,7 @@ function ensureWorktree(projectDir, taskId, branch) {
|
|
|
6624
6586
|
return worktreePath;
|
|
6625
6587
|
}
|
|
6626
6588
|
try {
|
|
6627
|
-
|
|
6589
|
+
execSync3(`git checkout --detach origin/${branch}`, {
|
|
6628
6590
|
cwd: worktreePath,
|
|
6629
6591
|
stdio: "ignore"
|
|
6630
6592
|
});
|
|
@@ -6634,7 +6596,7 @@ function ensureWorktree(projectDir, taskId, branch) {
|
|
|
6634
6596
|
return worktreePath;
|
|
6635
6597
|
}
|
|
6636
6598
|
const ref = branch ? `origin/${branch}` : "HEAD";
|
|
6637
|
-
|
|
6599
|
+
execSync3(`git worktree add --detach "${worktreePath}" ${ref}`, {
|
|
6638
6600
|
cwd: projectDir,
|
|
6639
6601
|
stdio: "ignore"
|
|
6640
6602
|
});
|
|
@@ -6642,7 +6604,7 @@ function ensureWorktree(projectDir, taskId, branch) {
|
|
|
6642
6604
|
}
|
|
6643
6605
|
function detachWorktreeBranch(projectDir, branch) {
|
|
6644
6606
|
try {
|
|
6645
|
-
const output =
|
|
6607
|
+
const output = execSync3("git worktree list --porcelain", {
|
|
6646
6608
|
cwd: projectDir,
|
|
6647
6609
|
encoding: "utf-8"
|
|
6648
6610
|
});
|
|
@@ -6655,7 +6617,7 @@ function detachWorktreeBranch(projectDir, branch) {
|
|
|
6655
6617
|
const worktreePath = worktreeLine.replace("worktree ", "");
|
|
6656
6618
|
if (!worktreePath.includes(`/${WORKTREE_DIR}/`)) continue;
|
|
6657
6619
|
try {
|
|
6658
|
-
|
|
6620
|
+
execSync3("git checkout --detach", { cwd: worktreePath, stdio: "ignore" });
|
|
6659
6621
|
} catch {
|
|
6660
6622
|
}
|
|
6661
6623
|
}
|
|
@@ -6666,7 +6628,7 @@ function removeWorktree(projectDir, taskId) {
|
|
|
6666
6628
|
const worktreePath = join4(projectDir, WORKTREE_DIR, taskId);
|
|
6667
6629
|
if (!existsSync2(worktreePath)) return;
|
|
6668
6630
|
try {
|
|
6669
|
-
|
|
6631
|
+
execSync3(`git worktree remove "${worktreePath}" --force`, {
|
|
6670
6632
|
cwd: projectDir,
|
|
6671
6633
|
stdio: "ignore"
|
|
6672
6634
|
});
|
|
@@ -6674,14 +6636,67 @@ function removeWorktree(projectDir, taskId) {
|
|
|
6674
6636
|
}
|
|
6675
6637
|
}
|
|
6676
6638
|
|
|
6677
|
-
// src/
|
|
6678
|
-
import {
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6639
|
+
// src/setup/commands.ts
|
|
6640
|
+
import { spawn, execSync as execSync4 } from "child_process";
|
|
6641
|
+
function runSetupCommand(cmd, cwd, onOutput) {
|
|
6642
|
+
return new Promise((resolve2, reject) => {
|
|
6643
|
+
const child = spawn("sh", ["-c", cmd], {
|
|
6644
|
+
cwd,
|
|
6645
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
6646
|
+
env: { ...process.env }
|
|
6647
|
+
});
|
|
6648
|
+
child.stdout.on("data", (chunk) => {
|
|
6649
|
+
onOutput("stdout", chunk.toString());
|
|
6650
|
+
});
|
|
6651
|
+
child.stderr.on("data", (chunk) => {
|
|
6652
|
+
onOutput("stderr", chunk.toString());
|
|
6653
|
+
});
|
|
6654
|
+
child.on("close", (code) => {
|
|
6655
|
+
if (code === 0) {
|
|
6656
|
+
resolve2();
|
|
6657
|
+
} else {
|
|
6658
|
+
reject(new Error(`Setup command exited with code ${code}`));
|
|
6659
|
+
}
|
|
6660
|
+
});
|
|
6661
|
+
child.on("error", (err) => {
|
|
6662
|
+
reject(err);
|
|
6663
|
+
});
|
|
6664
|
+
});
|
|
6665
|
+
}
|
|
6666
|
+
var AUTH_TOKEN_TIMEOUT_MS = 3e4;
|
|
6667
|
+
function runAuthTokenCommand(cmd, userEmail, cwd) {
|
|
6668
|
+
try {
|
|
6669
|
+
const output = execSync4(`${cmd} ${JSON.stringify(userEmail)}`, {
|
|
6670
|
+
cwd,
|
|
6671
|
+
timeout: AUTH_TOKEN_TIMEOUT_MS,
|
|
6672
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
6673
|
+
env: { ...process.env }
|
|
6674
|
+
});
|
|
6675
|
+
const token = output.toString().trim();
|
|
6676
|
+
return token || null;
|
|
6677
|
+
} catch {
|
|
6678
|
+
return null;
|
|
6679
|
+
}
|
|
6680
|
+
}
|
|
6681
|
+
function runStartCommand(cmd, cwd, onOutput) {
|
|
6682
|
+
const child = spawn("sh", ["-c", cmd], {
|
|
6683
|
+
cwd,
|
|
6684
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
6685
|
+
detached: true,
|
|
6686
|
+
env: { ...process.env }
|
|
6687
|
+
});
|
|
6688
|
+
child.stdout.on("data", (chunk) => {
|
|
6689
|
+
onOutput("stdout", chunk.toString());
|
|
6690
|
+
});
|
|
6691
|
+
child.stderr.on("data", (chunk) => {
|
|
6692
|
+
onOutput("stderr", chunk.toString());
|
|
6693
|
+
});
|
|
6694
|
+
child.unref();
|
|
6695
|
+
return child;
|
|
6696
|
+
}
|
|
6682
6697
|
|
|
6683
6698
|
// src/tools/project-tools.ts
|
|
6684
|
-
import { z as
|
|
6699
|
+
import { z as z11 } from "zod";
|
|
6685
6700
|
function buildTaskListTools(connection) {
|
|
6686
6701
|
const projectId = connection.projectId;
|
|
6687
6702
|
return [
|
|
@@ -6689,9 +6704,9 @@ function buildTaskListTools(connection) {
|
|
|
6689
6704
|
"list_tasks",
|
|
6690
6705
|
"List tasks in the project. Optionally filter by status or assignee.",
|
|
6691
6706
|
{
|
|
6692
|
-
status:
|
|
6693
|
-
assigneeId:
|
|
6694
|
-
limit:
|
|
6707
|
+
status: z11.string().optional().describe("Filter by task status"),
|
|
6708
|
+
assigneeId: z11.string().optional().describe("Filter by assigned user ID"),
|
|
6709
|
+
limit: z11.number().optional().describe("Max number of tasks to return (default 50)")
|
|
6695
6710
|
},
|
|
6696
6711
|
async (params) => {
|
|
6697
6712
|
try {
|
|
@@ -6708,7 +6723,7 @@ function buildTaskListTools(connection) {
|
|
|
6708
6723
|
defineTool(
|
|
6709
6724
|
"get_task",
|
|
6710
6725
|
"Get detailed information about a task including chat messages, child tasks, and session.",
|
|
6711
|
-
{ task_id:
|
|
6726
|
+
{ task_id: z11.string().describe("The task ID to look up") },
|
|
6712
6727
|
async ({ task_id }) => {
|
|
6713
6728
|
try {
|
|
6714
6729
|
const task = await connection.call("getProjectTask", { projectId, taskId: task_id });
|
|
@@ -6725,10 +6740,10 @@ function buildTaskListTools(connection) {
|
|
|
6725
6740
|
"search_tasks",
|
|
6726
6741
|
"Search tasks by tags, text query, or status filters.",
|
|
6727
6742
|
{
|
|
6728
|
-
tagNames:
|
|
6729
|
-
searchQuery:
|
|
6730
|
-
statusFilters:
|
|
6731
|
-
limit:
|
|
6743
|
+
tagNames: z11.array(z11.string()).optional().describe("Filter by tag names"),
|
|
6744
|
+
searchQuery: z11.string().optional().describe("Text search in title/description"),
|
|
6745
|
+
statusFilters: z11.array(z11.string()).optional().describe("Filter by statuses"),
|
|
6746
|
+
limit: z11.number().optional().describe("Max results (default 20)")
|
|
6732
6747
|
},
|
|
6733
6748
|
async (params) => {
|
|
6734
6749
|
try {
|
|
@@ -6781,18 +6796,17 @@ function buildProjectInfoTools(connection) {
|
|
|
6781
6796
|
)
|
|
6782
6797
|
];
|
|
6783
6798
|
}
|
|
6784
|
-
function
|
|
6799
|
+
function buildMutationTools2(connection) {
|
|
6785
6800
|
const projectId = connection.projectId;
|
|
6786
6801
|
return [
|
|
6787
6802
|
defineTool(
|
|
6788
6803
|
"create_task",
|
|
6789
6804
|
"Create a new task in the project.",
|
|
6790
6805
|
{
|
|
6791
|
-
title:
|
|
6792
|
-
description:
|
|
6793
|
-
plan:
|
|
6794
|
-
status:
|
|
6795
|
-
isBug: z8.boolean().optional().describe("Whether this is a bug report")
|
|
6806
|
+
title: z11.string().describe("Task title"),
|
|
6807
|
+
description: z11.string().optional().describe("Task description"),
|
|
6808
|
+
plan: z11.string().optional().describe("Implementation plan in markdown"),
|
|
6809
|
+
status: z11.string().optional().describe("Initial status (default: Planning)")
|
|
6796
6810
|
},
|
|
6797
6811
|
async (params) => {
|
|
6798
6812
|
try {
|
|
@@ -6809,12 +6823,12 @@ function buildMutationTools(connection) {
|
|
|
6809
6823
|
"update_task",
|
|
6810
6824
|
"Update an existing task's title, description, plan, status, or assignee.",
|
|
6811
6825
|
{
|
|
6812
|
-
task_id:
|
|
6813
|
-
title:
|
|
6814
|
-
description:
|
|
6815
|
-
plan:
|
|
6816
|
-
status:
|
|
6817
|
-
assignedUserId:
|
|
6826
|
+
task_id: z11.string().describe("The task ID to update"),
|
|
6827
|
+
title: z11.string().optional().describe("New title"),
|
|
6828
|
+
description: z11.string().optional().describe("New description"),
|
|
6829
|
+
plan: z11.string().optional().describe("New plan in markdown"),
|
|
6830
|
+
status: z11.string().optional().describe("New status"),
|
|
6831
|
+
assignedUserId: z11.string().nullable().optional().describe("Assign to user ID, or null to unassign")
|
|
6818
6832
|
},
|
|
6819
6833
|
async ({ task_id, ...fields }) => {
|
|
6820
6834
|
try {
|
|
@@ -6833,7 +6847,7 @@ function buildProjectTools(connection) {
|
|
|
6833
6847
|
return [
|
|
6834
6848
|
...buildTaskListTools(connection),
|
|
6835
6849
|
...buildProjectInfoTools(connection),
|
|
6836
|
-
...
|
|
6850
|
+
...buildMutationTools2(connection)
|
|
6837
6851
|
];
|
|
6838
6852
|
}
|
|
6839
6853
|
|
|
@@ -7028,14 +7042,204 @@ async function handleProjectChatMessage(message, connection, projectDir, session
|
|
|
7028
7042
|
}
|
|
7029
7043
|
}
|
|
7030
7044
|
|
|
7031
|
-
// src/runner/project-runner.ts
|
|
7045
|
+
// src/runner/project-runner-helpers.ts
|
|
7046
|
+
function parseErrorMessage(error) {
|
|
7047
|
+
return error instanceof Error ? error.message : String(error);
|
|
7048
|
+
}
|
|
7049
|
+
|
|
7050
|
+
// src/runner/project-runner-children.ts
|
|
7051
|
+
import { fork } from "child_process";
|
|
7052
|
+
import { execSync as execSync7 } from "child_process";
|
|
7053
|
+
import * as path from "path";
|
|
7054
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7055
|
+
|
|
7056
|
+
// src/runner/project-runner-git.ts
|
|
7057
|
+
import { execSync as execSync6 } from "child_process";
|
|
7058
|
+
|
|
7059
|
+
// src/runner/project-runner-sync.ts
|
|
7060
|
+
import { execSync as execSync5 } from "child_process";
|
|
7032
7061
|
var logger7 = createServiceLogger("ProjectRunner");
|
|
7033
|
-
var __filename = fileURLToPath2(import.meta.url);
|
|
7034
|
-
var __dirname = path.dirname(__filename);
|
|
7035
|
-
var HEARTBEAT_INTERVAL_MS = 3e4;
|
|
7036
|
-
var MAX_CONCURRENT = Math.max(1, parseInt(process.env.CONVEYOR_MAX_CONCURRENT ?? "10", 10) || 10);
|
|
7037
|
-
var STOP_TIMEOUT_MS = 3e4;
|
|
7038
7062
|
var START_CMD_KILL_TIMEOUT_MS = 5e3;
|
|
7063
|
+
async function executeSetupCommand(projectDir, connection) {
|
|
7064
|
+
const cmd = process.env.CONVEYOR_SETUP_COMMAND;
|
|
7065
|
+
if (!cmd) return;
|
|
7066
|
+
logger7.info("Running setup command", { command: cmd });
|
|
7067
|
+
try {
|
|
7068
|
+
await runSetupCommand(cmd, projectDir, (stream, data) => {
|
|
7069
|
+
connection.sendEvent({ type: "setup_output", stream, data });
|
|
7070
|
+
(stream === "stderr" ? process.stderr : process.stdout).write(data);
|
|
7071
|
+
});
|
|
7072
|
+
logger7.info("Setup command completed");
|
|
7073
|
+
} catch (error) {
|
|
7074
|
+
const msg = parseErrorMessage(error);
|
|
7075
|
+
logger7.error("Setup command failed", { error: msg });
|
|
7076
|
+
connection.sendEvent({ type: "setup_error", message: msg });
|
|
7077
|
+
throw error;
|
|
7078
|
+
}
|
|
7079
|
+
}
|
|
7080
|
+
function executeStartCommand(projectDir, state, connection) {
|
|
7081
|
+
const cmd = process.env.CONVEYOR_START_COMMAND;
|
|
7082
|
+
if (!cmd) return;
|
|
7083
|
+
logger7.info("Running start command", { command: cmd });
|
|
7084
|
+
const child = runStartCommand(cmd, projectDir, (stream, data) => {
|
|
7085
|
+
connection.sendEvent({ type: "start_command_output", stream, data });
|
|
7086
|
+
(stream === "stderr" ? process.stderr : process.stdout).write(data);
|
|
7087
|
+
});
|
|
7088
|
+
state.child = child;
|
|
7089
|
+
state.running = true;
|
|
7090
|
+
child.on("exit", (code, signal) => {
|
|
7091
|
+
state.running = false;
|
|
7092
|
+
state.child = null;
|
|
7093
|
+
logger7.info("Start command exited", { code, signal });
|
|
7094
|
+
connection.sendEvent({ type: "start_command_exited", code, signal });
|
|
7095
|
+
});
|
|
7096
|
+
child.on("error", (err) => {
|
|
7097
|
+
state.running = false;
|
|
7098
|
+
state.child = null;
|
|
7099
|
+
logger7.error("Start command error", { error: err.message });
|
|
7100
|
+
});
|
|
7101
|
+
}
|
|
7102
|
+
async function killStartCommand(state) {
|
|
7103
|
+
const child = state.child;
|
|
7104
|
+
if (!child || !state.running) return;
|
|
7105
|
+
try {
|
|
7106
|
+
if (child.pid) process.kill(-child.pid, "SIGTERM");
|
|
7107
|
+
} catch {
|
|
7108
|
+
child.kill("SIGTERM");
|
|
7109
|
+
}
|
|
7110
|
+
await new Promise((resolve2) => {
|
|
7111
|
+
const timer = setTimeout(() => {
|
|
7112
|
+
if (state.running && child.pid) {
|
|
7113
|
+
try {
|
|
7114
|
+
process.kill(-child.pid, "SIGKILL");
|
|
7115
|
+
} catch {
|
|
7116
|
+
child.kill("SIGKILL");
|
|
7117
|
+
}
|
|
7118
|
+
}
|
|
7119
|
+
resolve2();
|
|
7120
|
+
}, START_CMD_KILL_TIMEOUT_MS);
|
|
7121
|
+
child.on("exit", () => {
|
|
7122
|
+
clearTimeout(timer);
|
|
7123
|
+
resolve2();
|
|
7124
|
+
});
|
|
7125
|
+
});
|
|
7126
|
+
state.child = null;
|
|
7127
|
+
state.running = false;
|
|
7128
|
+
}
|
|
7129
|
+
async function restartStartCommand(projectDir, state, connection) {
|
|
7130
|
+
await killStartCommand(state);
|
|
7131
|
+
executeStartCommand(projectDir, state, connection);
|
|
7132
|
+
}
|
|
7133
|
+
async function handleSyncEnvironment(projectDir, branchSwitchCommand, state, connection, callback) {
|
|
7134
|
+
try {
|
|
7135
|
+
await killStartCommand(state);
|
|
7136
|
+
const cmd = branchSwitchCommand ?? process.env.CONVEYOR_BRANCH_SWITCH_COMMAND;
|
|
7137
|
+
if (cmd) {
|
|
7138
|
+
try {
|
|
7139
|
+
await runSetupCommand(cmd, projectDir, (stream, data) => {
|
|
7140
|
+
connection.sendEvent({ type: "sync_output", stream, data });
|
|
7141
|
+
});
|
|
7142
|
+
} catch (err) {
|
|
7143
|
+
const msg = parseErrorMessage(err);
|
|
7144
|
+
logger7.error("Branch switch sync command failed", { error: msg });
|
|
7145
|
+
}
|
|
7146
|
+
}
|
|
7147
|
+
executeStartCommand(projectDir, state, connection);
|
|
7148
|
+
callback?.({ ok: true });
|
|
7149
|
+
} catch (err) {
|
|
7150
|
+
const msg = parseErrorMessage(err);
|
|
7151
|
+
logger7.error("Environment sync failed", { error: msg });
|
|
7152
|
+
callback?.({ ok: false, error: msg });
|
|
7153
|
+
}
|
|
7154
|
+
}
|
|
7155
|
+
function getChangedFiles(projectDir, previousSha, newSha) {
|
|
7156
|
+
try {
|
|
7157
|
+
return execSync5(`git diff --name-only ${previousSha}..${newSha}`, {
|
|
7158
|
+
cwd: projectDir,
|
|
7159
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
7160
|
+
}).toString().trim().split("\n").filter(Boolean);
|
|
7161
|
+
} catch {
|
|
7162
|
+
return [];
|
|
7163
|
+
}
|
|
7164
|
+
}
|
|
7165
|
+
function runIndividualSyncSteps(projectDir, needsInstall, needsPrisma, stepsRun) {
|
|
7166
|
+
if (needsInstall) {
|
|
7167
|
+
try {
|
|
7168
|
+
execSync5("bun install", { cwd: projectDir, timeout: 12e4, stdio: "pipe" });
|
|
7169
|
+
stepsRun.push("install");
|
|
7170
|
+
} catch (err) {
|
|
7171
|
+
const msg = parseErrorMessage(err);
|
|
7172
|
+
logger7.error("bun install failed", { error: msg });
|
|
7173
|
+
}
|
|
7174
|
+
}
|
|
7175
|
+
if (needsPrisma) {
|
|
7176
|
+
try {
|
|
7177
|
+
execSync5("bunx prisma generate", { cwd: projectDir, timeout: 6e4, stdio: "pipe" });
|
|
7178
|
+
execSync5("bunx prisma db push --accept-data-loss", {
|
|
7179
|
+
cwd: projectDir,
|
|
7180
|
+
timeout: 6e4,
|
|
7181
|
+
stdio: "pipe"
|
|
7182
|
+
});
|
|
7183
|
+
stepsRun.push("prisma");
|
|
7184
|
+
} catch (err) {
|
|
7185
|
+
const msg = parseErrorMessage(err);
|
|
7186
|
+
logger7.error("Prisma sync failed", { error: msg });
|
|
7187
|
+
}
|
|
7188
|
+
}
|
|
7189
|
+
}
|
|
7190
|
+
async function syncDependencies(projectDir, branchSwitchCommand, connection, changedFiles, stepsRun) {
|
|
7191
|
+
const needsInstall = changedFiles.some(
|
|
7192
|
+
(f) => f === "package.json" || f === "bun.lockb" || f.endsWith("/package.json") || f.endsWith("/bun.lockb")
|
|
7193
|
+
);
|
|
7194
|
+
const needsPrisma = changedFiles.some(
|
|
7195
|
+
(f) => f.includes("prisma/schema.prisma") || f.includes("prisma/migrations/")
|
|
7196
|
+
);
|
|
7197
|
+
const cmd = branchSwitchCommand ?? process.env.CONVEYOR_BRANCH_SWITCH_COMMAND;
|
|
7198
|
+
if (cmd && (needsInstall || needsPrisma)) {
|
|
7199
|
+
try {
|
|
7200
|
+
await runSetupCommand(cmd, projectDir, (stream, data) => {
|
|
7201
|
+
connection.sendEvent({ type: "sync_output", stream, data });
|
|
7202
|
+
});
|
|
7203
|
+
stepsRun.push("branchSwitchCommand");
|
|
7204
|
+
} catch (err) {
|
|
7205
|
+
const msg = parseErrorMessage(err);
|
|
7206
|
+
logger7.error("Branch switch command failed", { error: msg });
|
|
7207
|
+
}
|
|
7208
|
+
} else if (!cmd) {
|
|
7209
|
+
runIndividualSyncSteps(projectDir, needsInstall, needsPrisma, stepsRun);
|
|
7210
|
+
}
|
|
7211
|
+
}
|
|
7212
|
+
async function smartSync(projectDir, branchSwitchCommand, state, connection, previousSha, newSha, branch) {
|
|
7213
|
+
if (hasUncommittedChanges(projectDir)) {
|
|
7214
|
+
connection.sendEvent({
|
|
7215
|
+
type: "commit_watch_warning",
|
|
7216
|
+
message: "Working tree has uncommitted changes. Auto-pull skipped."
|
|
7217
|
+
});
|
|
7218
|
+
return ["skipped:dirty_tree"];
|
|
7219
|
+
}
|
|
7220
|
+
await killStartCommand(state);
|
|
7221
|
+
try {
|
|
7222
|
+
execSync5(`git pull origin ${branch}`, {
|
|
7223
|
+
cwd: projectDir,
|
|
7224
|
+
stdio: "pipe",
|
|
7225
|
+
timeout: 6e4
|
|
7226
|
+
});
|
|
7227
|
+
} catch (err) {
|
|
7228
|
+
const msg = parseErrorMessage(err);
|
|
7229
|
+
logger7.error("Git pull failed during commit sync", { error: msg });
|
|
7230
|
+
executeStartCommand(projectDir, state, connection);
|
|
7231
|
+
return ["error:pull"];
|
|
7232
|
+
}
|
|
7233
|
+
const stepsRun = ["pull"];
|
|
7234
|
+
const changedFiles = getChangedFiles(projectDir, previousSha, newSha);
|
|
7235
|
+
await syncDependencies(projectDir, branchSwitchCommand, connection, changedFiles, stepsRun);
|
|
7236
|
+
executeStartCommand(projectDir, state, connection);
|
|
7237
|
+
stepsRun.push("startCommand");
|
|
7238
|
+
return stepsRun;
|
|
7239
|
+
}
|
|
7240
|
+
|
|
7241
|
+
// src/runner/project-runner-git.ts
|
|
7242
|
+
var logger8 = createServiceLogger("ProjectRunner");
|
|
7039
7243
|
function setupWorkDir(projectDir, assignment) {
|
|
7040
7244
|
const { taskId, branch, devBranch, useWorktree } = assignment;
|
|
7041
7245
|
const shortId = taskId.slice(0, 8);
|
|
@@ -7048,21 +7252,105 @@ function setupWorkDir(projectDir, assignment) {
|
|
|
7048
7252
|
}
|
|
7049
7253
|
if (branch && branch !== devBranch) {
|
|
7050
7254
|
if (hasUncommittedChanges(workDir)) {
|
|
7051
|
-
|
|
7255
|
+
logger8.warn("Uncommitted changes, skipping checkout", { taskId: shortId, branch });
|
|
7052
7256
|
} else {
|
|
7053
7257
|
try {
|
|
7054
|
-
|
|
7258
|
+
execSync6(`git checkout ${branch}`, { cwd: workDir, stdio: "ignore" });
|
|
7055
7259
|
} catch {
|
|
7056
7260
|
try {
|
|
7057
|
-
|
|
7261
|
+
execSync6(`git checkout -b ${branch}`, { cwd: workDir, stdio: "ignore" });
|
|
7058
7262
|
} catch {
|
|
7059
|
-
|
|
7263
|
+
logger8.warn("Could not checkout branch", { taskId: shortId, branch });
|
|
7060
7264
|
}
|
|
7061
7265
|
}
|
|
7062
7266
|
}
|
|
7063
7267
|
}
|
|
7064
7268
|
return { workDir, usesWorktree: shouldWorktree };
|
|
7065
7269
|
}
|
|
7270
|
+
function checkoutWorkspaceBranch(projectDir) {
|
|
7271
|
+
const workspaceBranch = process.env.CONVEYOR_WORKSPACE_BRANCH;
|
|
7272
|
+
if (!workspaceBranch) return;
|
|
7273
|
+
const currentBranch = getCurrentBranch(projectDir);
|
|
7274
|
+
if (currentBranch === workspaceBranch) return;
|
|
7275
|
+
if (hasUncommittedChanges(projectDir)) {
|
|
7276
|
+
logger8.warn("Uncommitted changes, skipping workspace branch checkout");
|
|
7277
|
+
return;
|
|
7278
|
+
}
|
|
7279
|
+
try {
|
|
7280
|
+
execSync6(`git fetch origin ${workspaceBranch}`, { cwd: projectDir, stdio: "pipe" });
|
|
7281
|
+
execSync6(`git checkout ${workspaceBranch}`, { cwd: projectDir, stdio: "pipe" });
|
|
7282
|
+
logger8.info("Checked out workspace branch", { workspaceBranch });
|
|
7283
|
+
} catch {
|
|
7284
|
+
logger8.warn("Failed to checkout workspace branch", { workspaceBranch });
|
|
7285
|
+
}
|
|
7286
|
+
}
|
|
7287
|
+
async function handleSwitchBranch(projectDir, branchSwitchCommand, startCmd, connection, commitWatcher, data, callback) {
|
|
7288
|
+
try {
|
|
7289
|
+
try {
|
|
7290
|
+
execSync6("git fetch origin", { cwd: projectDir, stdio: "pipe" });
|
|
7291
|
+
} catch {
|
|
7292
|
+
logger8.warn("Git fetch failed during branch switch");
|
|
7293
|
+
}
|
|
7294
|
+
detachWorktreeBranch(projectDir, data.branch);
|
|
7295
|
+
try {
|
|
7296
|
+
execSync6(`git checkout ${data.branch}`, { cwd: projectDir, stdio: "pipe" });
|
|
7297
|
+
} catch (err) {
|
|
7298
|
+
const msg = parseErrorMessage(err);
|
|
7299
|
+
callback({ ok: false, error: `Failed to checkout branch: ${msg}` });
|
|
7300
|
+
return;
|
|
7301
|
+
}
|
|
7302
|
+
try {
|
|
7303
|
+
execSync6(`git pull origin ${data.branch}`, { cwd: projectDir, stdio: "pipe" });
|
|
7304
|
+
} catch {
|
|
7305
|
+
logger8.warn("Git pull failed during branch switch");
|
|
7306
|
+
}
|
|
7307
|
+
if (data.syncAfter !== false) {
|
|
7308
|
+
await handleSyncEnvironment(projectDir, branchSwitchCommand, startCmd, connection);
|
|
7309
|
+
}
|
|
7310
|
+
commitWatcher.start(data.branch);
|
|
7311
|
+
callback({ ok: true });
|
|
7312
|
+
} catch (err) {
|
|
7313
|
+
const msg = parseErrorMessage(err);
|
|
7314
|
+
logger8.error("Branch switch failed", { error: msg });
|
|
7315
|
+
callback({ ok: false, error: msg });
|
|
7316
|
+
}
|
|
7317
|
+
}
|
|
7318
|
+
async function handleNewCommits(projectDir, branchSwitchCommand, startCmd, connection, setupComplete, data) {
|
|
7319
|
+
await connection.call("reportNewCommitsDetected", {
|
|
7320
|
+
projectId: connection.projectId,
|
|
7321
|
+
branch: data.branch,
|
|
7322
|
+
commits: [
|
|
7323
|
+
{
|
|
7324
|
+
sha: data.newCommitSha,
|
|
7325
|
+
message: data.latestMessage,
|
|
7326
|
+
author: data.latestAuthor
|
|
7327
|
+
}
|
|
7328
|
+
]
|
|
7329
|
+
});
|
|
7330
|
+
const stepsRun = await smartSync(
|
|
7331
|
+
projectDir,
|
|
7332
|
+
branchSwitchCommand,
|
|
7333
|
+
startCmd,
|
|
7334
|
+
connection,
|
|
7335
|
+
data.previousSha,
|
|
7336
|
+
data.newCommitSha,
|
|
7337
|
+
data.branch
|
|
7338
|
+
);
|
|
7339
|
+
await connection.call("reportEnvironmentReady", {
|
|
7340
|
+
projectId: connection.projectId,
|
|
7341
|
+
branch: data.branch,
|
|
7342
|
+
setupComplete,
|
|
7343
|
+
startCommandRunning: startCmd.running
|
|
7344
|
+
});
|
|
7345
|
+
logger8.info("Commit sync complete", { steps: stepsRun.join(", ") });
|
|
7346
|
+
}
|
|
7347
|
+
|
|
7348
|
+
// src/runner/project-runner-children.ts
|
|
7349
|
+
var logger9 = createServiceLogger("ProjectRunner");
|
|
7350
|
+
var __filename = fileURLToPath2(import.meta.url);
|
|
7351
|
+
var __dirname = path.dirname(__filename);
|
|
7352
|
+
var STOP_TIMEOUT_MS = 3e4;
|
|
7353
|
+
var MAX_CONCURRENT = Math.max(1, parseInt(process.env.CONVEYOR_MAX_CONCURRENT ?? "10", 10) || 10);
|
|
7066
7354
|
function spawnChildAgent(assignment, workDir) {
|
|
7067
7355
|
const { taskToken, apiUrl, taskId, sessionId, mode, isAuto, useSandbox, agentMode } = assignment;
|
|
7068
7356
|
const cliPath = path.resolve(__dirname, "cli.js");
|
|
@@ -7074,7 +7362,7 @@ function spawnChildAgent(assignment, workDir) {
|
|
|
7074
7362
|
const effectiveAgentMode = agentMode ?? (isAuto ? "auto" : "");
|
|
7075
7363
|
const effectiveApiUrl = apiUrl || process.env.CONVEYOR_API_URL || "";
|
|
7076
7364
|
if (!effectiveApiUrl) {
|
|
7077
|
-
|
|
7365
|
+
logger9.error("No API URL available for child agent", { taskId: taskId.slice(0, 8) });
|
|
7078
7366
|
}
|
|
7079
7367
|
const child = fork(cliPath, [], {
|
|
7080
7368
|
env: {
|
|
@@ -7102,16 +7390,112 @@ function spawnChildAgent(assignment, workDir) {
|
|
|
7102
7390
|
const shortId = taskId.slice(0, 8);
|
|
7103
7391
|
child.stdout?.on("data", (data) => {
|
|
7104
7392
|
for (const line of data.toString().trimEnd().split("\n")) {
|
|
7105
|
-
|
|
7393
|
+
logger9.info(line, { taskId: shortId });
|
|
7106
7394
|
}
|
|
7107
7395
|
});
|
|
7108
7396
|
child.stderr?.on("data", (data) => {
|
|
7109
7397
|
for (const line of data.toString().trimEnd().split("\n")) {
|
|
7110
|
-
|
|
7398
|
+
logger9.info(line, { taskId: shortId });
|
|
7111
7399
|
}
|
|
7112
7400
|
});
|
|
7113
7401
|
return child;
|
|
7114
7402
|
}
|
|
7403
|
+
async function killAgent(agent, taskId) {
|
|
7404
|
+
const shortId = taskId.slice(0, 8);
|
|
7405
|
+
if (agent.process.exitCode !== null) {
|
|
7406
|
+
logger9.info("Agent process already exited", { taskId: shortId });
|
|
7407
|
+
return;
|
|
7408
|
+
}
|
|
7409
|
+
logger9.info("Killing agent process", { taskId: shortId });
|
|
7410
|
+
agent.process.kill("SIGTERM");
|
|
7411
|
+
await new Promise((resolve2) => {
|
|
7412
|
+
const timer = setTimeout(() => {
|
|
7413
|
+
if (agent.process.exitCode === null) {
|
|
7414
|
+
logger9.warn("Agent did not exit after SIGTERM, sending SIGKILL", { taskId: shortId });
|
|
7415
|
+
agent.process.kill("SIGKILL");
|
|
7416
|
+
}
|
|
7417
|
+
resolve2();
|
|
7418
|
+
}, STOP_TIMEOUT_MS);
|
|
7419
|
+
agent.process.on("exit", () => {
|
|
7420
|
+
clearTimeout(timer);
|
|
7421
|
+
resolve2();
|
|
7422
|
+
});
|
|
7423
|
+
});
|
|
7424
|
+
}
|
|
7425
|
+
async function handleAssignment(assignment, activeAgents, projectDir, connection) {
|
|
7426
|
+
const { taskId, mode } = assignment;
|
|
7427
|
+
const shortId = taskId.slice(0, 8);
|
|
7428
|
+
const agentKey = assignment.agentMode === "code-review" ? `${taskId}:code-review` : taskId;
|
|
7429
|
+
const existing = activeAgents.get(agentKey);
|
|
7430
|
+
if (existing) {
|
|
7431
|
+
if (existing.process.exitCode === null) {
|
|
7432
|
+
logger9.info("Re-assignment received, killing existing agent", { taskId: shortId });
|
|
7433
|
+
await killAgent(existing, taskId);
|
|
7434
|
+
} else {
|
|
7435
|
+
logger9.info("Stale agent entry (process already exited), cleaning up", { taskId: shortId });
|
|
7436
|
+
}
|
|
7437
|
+
activeAgents.delete(agentKey);
|
|
7438
|
+
}
|
|
7439
|
+
if (activeAgents.size >= MAX_CONCURRENT) {
|
|
7440
|
+
logger9.warn("Max concurrent agents reached", { maxConcurrent: MAX_CONCURRENT });
|
|
7441
|
+
connection.emitTaskStopped(taskId, "max_concurrent_reached");
|
|
7442
|
+
return;
|
|
7443
|
+
}
|
|
7444
|
+
try {
|
|
7445
|
+
try {
|
|
7446
|
+
execSync7("git fetch origin", { cwd: projectDir, stdio: "ignore" });
|
|
7447
|
+
} catch {
|
|
7448
|
+
logger9.warn("Git fetch failed", { taskId: shortId });
|
|
7449
|
+
}
|
|
7450
|
+
const { workDir, usesWorktree } = setupWorkDir(projectDir, assignment);
|
|
7451
|
+
if (assignment.devBranch) {
|
|
7452
|
+
syncWithBaseBranch(workDir, assignment.devBranch);
|
|
7453
|
+
}
|
|
7454
|
+
const child = spawnChildAgent(assignment, workDir);
|
|
7455
|
+
activeAgents.set(agentKey, {
|
|
7456
|
+
process: child,
|
|
7457
|
+
worktreePath: workDir,
|
|
7458
|
+
mode,
|
|
7459
|
+
usesWorktree
|
|
7460
|
+
});
|
|
7461
|
+
connection.emitTaskStarted(taskId);
|
|
7462
|
+
logger9.info("Started task", { taskId: shortId, mode, workDir });
|
|
7463
|
+
child.on("exit", (code) => {
|
|
7464
|
+
activeAgents.delete(agentKey);
|
|
7465
|
+
const reason = code === 0 ? "completed" : `exited with code ${code}`;
|
|
7466
|
+
connection.emitTaskStopped(taskId, reason);
|
|
7467
|
+
logger9.info("Task exited", { taskId: shortId, reason });
|
|
7468
|
+
if (code === 0 && usesWorktree) {
|
|
7469
|
+
try {
|
|
7470
|
+
removeWorktree(projectDir, taskId);
|
|
7471
|
+
} catch {
|
|
7472
|
+
}
|
|
7473
|
+
}
|
|
7474
|
+
});
|
|
7475
|
+
} catch (error) {
|
|
7476
|
+
const msg = parseErrorMessage(error);
|
|
7477
|
+
logger9.error("Failed to start task", { taskId: shortId, error: msg });
|
|
7478
|
+
connection.emitTaskStopped(taskId, `start_failed: ${msg}`);
|
|
7479
|
+
}
|
|
7480
|
+
}
|
|
7481
|
+
function handleStopTask(taskId, activeAgents, projectDir) {
|
|
7482
|
+
const agentKey = activeAgents.has(taskId) ? taskId : `${taskId}:code-review`;
|
|
7483
|
+
const agent = activeAgents.get(agentKey);
|
|
7484
|
+
if (!agent) return;
|
|
7485
|
+
logger9.info("Stopping task", { taskId: taskId.slice(0, 8) });
|
|
7486
|
+
void killAgent(agent, taskId).then(() => {
|
|
7487
|
+
if (agent.usesWorktree) {
|
|
7488
|
+
try {
|
|
7489
|
+
removeWorktree(projectDir, taskId);
|
|
7490
|
+
} catch {
|
|
7491
|
+
}
|
|
7492
|
+
}
|
|
7493
|
+
});
|
|
7494
|
+
}
|
|
7495
|
+
|
|
7496
|
+
// src/runner/project-runner.ts
|
|
7497
|
+
var logger10 = createServiceLogger("ProjectRunner");
|
|
7498
|
+
var HEARTBEAT_INTERVAL_MS = 3e4;
|
|
7115
7499
|
var ProjectRunner = class {
|
|
7116
7500
|
connection;
|
|
7117
7501
|
projectDir;
|
|
@@ -7120,8 +7504,7 @@ var ProjectRunner = class {
|
|
|
7120
7504
|
stopping = false;
|
|
7121
7505
|
resolveLifecycle = null;
|
|
7122
7506
|
chatSessionIds = /* @__PURE__ */ new Map();
|
|
7123
|
-
|
|
7124
|
-
startCommandRunning = false;
|
|
7507
|
+
startCmd = { child: null, running: false };
|
|
7125
7508
|
setupComplete = false;
|
|
7126
7509
|
branchSwitchCommand;
|
|
7127
7510
|
commitWatcher;
|
|
@@ -7140,33 +7523,39 @@ var ProjectRunner = class {
|
|
|
7140
7523
|
},
|
|
7141
7524
|
{
|
|
7142
7525
|
onNewCommits: async (data) => {
|
|
7143
|
-
await
|
|
7526
|
+
await handleNewCommits(
|
|
7527
|
+
this.projectDir,
|
|
7528
|
+
this.branchSwitchCommand,
|
|
7529
|
+
this.startCmd,
|
|
7530
|
+
this.connection,
|
|
7531
|
+
this.setupComplete,
|
|
7532
|
+
data
|
|
7533
|
+
);
|
|
7144
7534
|
}
|
|
7145
7535
|
}
|
|
7146
7536
|
);
|
|
7147
7537
|
}
|
|
7148
7538
|
// ── Public lifecycle ───────────────────────────────────────────────────
|
|
7149
|
-
// oxlint-disable-next-line max-lines-per-function -- sequential lifecycle orchestration
|
|
7150
7539
|
async start() {
|
|
7151
|
-
this.
|
|
7540
|
+
checkoutWorkspaceBranch(this.projectDir);
|
|
7152
7541
|
await this.connection.connect();
|
|
7153
7542
|
const registration = await this.connection.call("registerProjectAgent", {
|
|
7154
7543
|
projectId: this.connection.projectId,
|
|
7155
7544
|
capabilities: ["task", "pm", "code-review", "audit"]
|
|
7156
7545
|
});
|
|
7157
7546
|
this.branchSwitchCommand = registration.branchSwitchCommand ?? process.env.CONVEYOR_BRANCH_SWITCH_COMMAND;
|
|
7158
|
-
|
|
7547
|
+
logger10.info("Registered as project agent", { agentName: registration.agentName });
|
|
7159
7548
|
try {
|
|
7160
|
-
await this.
|
|
7161
|
-
this.
|
|
7549
|
+
await executeSetupCommand(this.projectDir, this.connection);
|
|
7550
|
+
executeStartCommand(this.projectDir, this.startCmd, this.connection);
|
|
7162
7551
|
this.setupComplete = true;
|
|
7163
7552
|
this.connection.sendEvent({
|
|
7164
7553
|
type: "setup_complete",
|
|
7165
|
-
startCommandRunning: this.
|
|
7554
|
+
startCommandRunning: this.startCmd.running
|
|
7166
7555
|
});
|
|
7167
7556
|
} catch (error) {
|
|
7168
|
-
const msg =
|
|
7169
|
-
|
|
7557
|
+
const msg = parseErrorMessage(error);
|
|
7558
|
+
logger10.error("Environment setup failed", { error: msg });
|
|
7170
7559
|
this.setupComplete = false;
|
|
7171
7560
|
}
|
|
7172
7561
|
this.wireEventHandlers();
|
|
@@ -7178,7 +7567,7 @@ var ProjectRunner = class {
|
|
|
7178
7567
|
if (currentBranch) {
|
|
7179
7568
|
this.commitWatcher.start(currentBranch);
|
|
7180
7569
|
}
|
|
7181
|
-
|
|
7570
|
+
logger10.info("Connected, waiting for task assignments");
|
|
7182
7571
|
await new Promise((resolve2) => {
|
|
7183
7572
|
this.resolveLifecycle = resolve2;
|
|
7184
7573
|
});
|
|
@@ -7197,24 +7586,24 @@ var ProjectRunner = class {
|
|
|
7197
7586
|
wipMessage: "WIP: auto-commit on conveyor-agent shutdown"
|
|
7198
7587
|
});
|
|
7199
7588
|
if (result.hadWork) {
|
|
7200
|
-
|
|
7589
|
+
logger10.info("Shutdown git flush", {
|
|
7201
7590
|
dir,
|
|
7202
7591
|
committed: result.committed,
|
|
7203
7592
|
pushed: result.pushed
|
|
7204
7593
|
});
|
|
7205
7594
|
}
|
|
7206
7595
|
} catch (err) {
|
|
7207
|
-
const msg =
|
|
7208
|
-
|
|
7596
|
+
const msg = parseErrorMessage(err);
|
|
7597
|
+
logger10.warn("Shutdown git flush failed", { dir, error: msg });
|
|
7209
7598
|
}
|
|
7210
7599
|
}
|
|
7211
7600
|
}
|
|
7212
7601
|
async stop() {
|
|
7213
7602
|
if (this.stopping) return;
|
|
7214
7603
|
this.stopping = true;
|
|
7215
|
-
|
|
7604
|
+
logger10.info("Shutting down");
|
|
7216
7605
|
this.commitWatcher.stop();
|
|
7217
|
-
await this.
|
|
7606
|
+
await killStartCommand(this.startCmd);
|
|
7218
7607
|
if (this.heartbeatTimer) {
|
|
7219
7608
|
clearInterval(this.heartbeatTimer);
|
|
7220
7609
|
this.heartbeatTimer = null;
|
|
@@ -7227,7 +7616,7 @@ var ProjectRunner = class {
|
|
|
7227
7616
|
return;
|
|
7228
7617
|
}
|
|
7229
7618
|
agent.process.on("exit", () => resolve2());
|
|
7230
|
-
|
|
7619
|
+
handleStopTask(key, this.activeAgents, this.projectDir);
|
|
7231
7620
|
})
|
|
7232
7621
|
);
|
|
7233
7622
|
await Promise.race([
|
|
@@ -7244,7 +7633,7 @@ var ProjectRunner = class {
|
|
|
7244
7633
|
} catch {
|
|
7245
7634
|
}
|
|
7246
7635
|
this.connection.disconnect();
|
|
7247
|
-
|
|
7636
|
+
logger10.info("Shutdown complete");
|
|
7248
7637
|
if (this.resolveLifecycle) {
|
|
7249
7638
|
this.resolveLifecycle();
|
|
7250
7639
|
this.resolveLifecycle = null;
|
|
@@ -7252,8 +7641,12 @@ var ProjectRunner = class {
|
|
|
7252
7641
|
}
|
|
7253
7642
|
// ── Event wiring ───────────────────────────────────────────────────────
|
|
7254
7643
|
wireEventHandlers() {
|
|
7255
|
-
this.connection.onTaskAssignment(
|
|
7256
|
-
|
|
7644
|
+
this.connection.onTaskAssignment(
|
|
7645
|
+
(assignment) => void handleAssignment(assignment, this.activeAgents, this.projectDir, this.connection)
|
|
7646
|
+
);
|
|
7647
|
+
this.connection.onStopTask(
|
|
7648
|
+
(data) => handleStopTask(data.taskId, this.activeAgents, this.projectDir)
|
|
7649
|
+
);
|
|
7257
7650
|
this.connection.onShutdown(() => void this.stop());
|
|
7258
7651
|
this.connection.onChatMessage((msg) => {
|
|
7259
7652
|
const chatId = msg.chatId ?? "default";
|
|
@@ -7266,10 +7659,28 @@ var ProjectRunner = class {
|
|
|
7266
7659
|
});
|
|
7267
7660
|
this.connection.onAuditTags((request) => void this.handleAuditTags(request));
|
|
7268
7661
|
this.connection.onAuditTasks((request) => void this.handleAuditTasks(request));
|
|
7269
|
-
this.connection.onSwitchBranch(
|
|
7270
|
-
|
|
7662
|
+
this.connection.onSwitchBranch(
|
|
7663
|
+
(data, cb) => void handleSwitchBranch(
|
|
7664
|
+
this.projectDir,
|
|
7665
|
+
this.branchSwitchCommand,
|
|
7666
|
+
this.startCmd,
|
|
7667
|
+
this.connection,
|
|
7668
|
+
this.commitWatcher,
|
|
7669
|
+
data,
|
|
7670
|
+
cb
|
|
7671
|
+
)
|
|
7672
|
+
);
|
|
7673
|
+
this.connection.onSyncEnvironment(
|
|
7674
|
+
(cb) => void handleSyncEnvironment(
|
|
7675
|
+
this.projectDir,
|
|
7676
|
+
this.branchSwitchCommand,
|
|
7677
|
+
this.startCmd,
|
|
7678
|
+
this.connection,
|
|
7679
|
+
cb
|
|
7680
|
+
)
|
|
7681
|
+
);
|
|
7271
7682
|
this.connection.onRestartStartCommand((cb) => {
|
|
7272
|
-
void this.
|
|
7683
|
+
void restartStartCommand(this.projectDir, this.startCmd, this.connection).then(() => cb({ ok: true })).catch(
|
|
7273
7684
|
(err) => cb({ ok: false, error: err instanceof Error ? err.message : "Restart failed" })
|
|
7274
7685
|
);
|
|
7275
7686
|
});
|
|
@@ -7281,10 +7692,10 @@ var ProjectRunner = class {
|
|
|
7281
7692
|
capabilities: ["task", "pm", "code-review", "audit"]
|
|
7282
7693
|
});
|
|
7283
7694
|
this.connection.emitStatus(this.activeAgents.size > 0 ? "busy" : "idle");
|
|
7284
|
-
|
|
7695
|
+
logger10.info("Re-registered after reconnect");
|
|
7285
7696
|
} catch (error) {
|
|
7286
|
-
const msg =
|
|
7287
|
-
|
|
7697
|
+
const msg = parseErrorMessage(error);
|
|
7698
|
+
logger10.error("Failed to re-register after reconnect", { error: msg });
|
|
7288
7699
|
}
|
|
7289
7700
|
}
|
|
7290
7701
|
// ── Tag audit ──────────────────────────────────────────────────────────
|
|
@@ -7294,8 +7705,8 @@ var ProjectRunner = class {
|
|
|
7294
7705
|
const { handleTagAudit } = await import("./tag-audit-handler-I54W7ZD7.js");
|
|
7295
7706
|
await handleTagAudit(request, this.connection, this.projectDir);
|
|
7296
7707
|
} catch (error) {
|
|
7297
|
-
const msg =
|
|
7298
|
-
|
|
7708
|
+
const msg = parseErrorMessage(error);
|
|
7709
|
+
logger10.error("Tag audit failed", { error: msg, requestId: request.requestId });
|
|
7299
7710
|
try {
|
|
7300
7711
|
await this.connection.call("reportTagAuditResult", {
|
|
7301
7712
|
projectId: this.connection.projectId,
|
|
@@ -7314,11 +7725,11 @@ var ProjectRunner = class {
|
|
|
7314
7725
|
async handleAuditTasks(request) {
|
|
7315
7726
|
this.connection.emitStatus("busy");
|
|
7316
7727
|
try {
|
|
7317
|
-
const { handleTaskAudit } = await import("./task-audit-handler-
|
|
7728
|
+
const { handleTaskAudit } = await import("./task-audit-handler-TJOM5OJS.js");
|
|
7318
7729
|
await handleTaskAudit(request, this.connection, this.projectDir);
|
|
7319
7730
|
} catch (error) {
|
|
7320
|
-
const msg =
|
|
7321
|
-
|
|
7731
|
+
const msg = parseErrorMessage(error);
|
|
7732
|
+
logger10.error("Task audit failed", { error: msg, requestId: request.requestId });
|
|
7322
7733
|
for (const task of request.tasks) {
|
|
7323
7734
|
try {
|
|
7324
7735
|
await this.connection.call("reportTaskAuditResult", {
|
|
@@ -7356,353 +7767,9 @@ var ProjectRunner = class {
|
|
|
7356
7767
|
this.connection.emitStatus("idle");
|
|
7357
7768
|
}
|
|
7358
7769
|
}
|
|
7359
|
-
// ── Task management ────────────────────────────────────────────────────
|
|
7360
|
-
async killAgent(agent, taskId) {
|
|
7361
|
-
const shortId = taskId.slice(0, 8);
|
|
7362
|
-
if (agent.process.exitCode !== null) {
|
|
7363
|
-
logger7.info("Agent process already exited", { taskId: shortId });
|
|
7364
|
-
return;
|
|
7365
|
-
}
|
|
7366
|
-
logger7.info("Killing agent process", { taskId: shortId });
|
|
7367
|
-
agent.process.kill("SIGTERM");
|
|
7368
|
-
await new Promise((resolve2) => {
|
|
7369
|
-
const timer = setTimeout(() => {
|
|
7370
|
-
if (agent.process.exitCode === null) {
|
|
7371
|
-
logger7.warn("Agent did not exit after SIGTERM, sending SIGKILL", { taskId: shortId });
|
|
7372
|
-
agent.process.kill("SIGKILL");
|
|
7373
|
-
}
|
|
7374
|
-
resolve2();
|
|
7375
|
-
}, STOP_TIMEOUT_MS);
|
|
7376
|
-
agent.process.on("exit", () => {
|
|
7377
|
-
clearTimeout(timer);
|
|
7378
|
-
resolve2();
|
|
7379
|
-
});
|
|
7380
|
-
});
|
|
7381
|
-
}
|
|
7382
|
-
// oxlint-disable-next-line max-lines-per-function -- re-assignment logic requires sequential checks
|
|
7383
|
-
async handleAssignment(assignment) {
|
|
7384
|
-
const { taskId, mode } = assignment;
|
|
7385
|
-
const shortId = taskId.slice(0, 8);
|
|
7386
|
-
const agentKey = assignment.agentMode === "code-review" ? `${taskId}:code-review` : taskId;
|
|
7387
|
-
const existing = this.activeAgents.get(agentKey);
|
|
7388
|
-
if (existing) {
|
|
7389
|
-
if (existing.process.exitCode === null) {
|
|
7390
|
-
logger7.info("Re-assignment received, killing existing agent", { taskId: shortId });
|
|
7391
|
-
await this.killAgent(existing, taskId);
|
|
7392
|
-
} else {
|
|
7393
|
-
logger7.info("Stale agent entry (process already exited), cleaning up", { taskId: shortId });
|
|
7394
|
-
}
|
|
7395
|
-
this.activeAgents.delete(agentKey);
|
|
7396
|
-
}
|
|
7397
|
-
if (this.activeAgents.size >= MAX_CONCURRENT) {
|
|
7398
|
-
logger7.warn("Max concurrent agents reached", { maxConcurrent: MAX_CONCURRENT });
|
|
7399
|
-
this.connection.emitTaskStopped(taskId, "max_concurrent_reached");
|
|
7400
|
-
return;
|
|
7401
|
-
}
|
|
7402
|
-
try {
|
|
7403
|
-
try {
|
|
7404
|
-
execSync5("git fetch origin", { cwd: this.projectDir, stdio: "ignore" });
|
|
7405
|
-
} catch {
|
|
7406
|
-
logger7.warn("Git fetch failed", { taskId: shortId });
|
|
7407
|
-
}
|
|
7408
|
-
const { workDir, usesWorktree } = setupWorkDir(this.projectDir, assignment);
|
|
7409
|
-
if (assignment.devBranch) {
|
|
7410
|
-
syncWithBaseBranch(workDir, assignment.devBranch);
|
|
7411
|
-
}
|
|
7412
|
-
const child = spawnChildAgent(assignment, workDir);
|
|
7413
|
-
this.activeAgents.set(agentKey, {
|
|
7414
|
-
process: child,
|
|
7415
|
-
worktreePath: workDir,
|
|
7416
|
-
mode,
|
|
7417
|
-
usesWorktree
|
|
7418
|
-
});
|
|
7419
|
-
this.connection.emitTaskStarted(taskId);
|
|
7420
|
-
logger7.info("Started task", { taskId: shortId, mode, workDir });
|
|
7421
|
-
child.on("exit", (code) => {
|
|
7422
|
-
this.activeAgents.delete(agentKey);
|
|
7423
|
-
const reason = code === 0 ? "completed" : `exited with code ${code}`;
|
|
7424
|
-
this.connection.emitTaskStopped(taskId, reason);
|
|
7425
|
-
logger7.info("Task exited", { taskId: shortId, reason });
|
|
7426
|
-
if (code === 0 && usesWorktree) {
|
|
7427
|
-
try {
|
|
7428
|
-
removeWorktree(this.projectDir, taskId);
|
|
7429
|
-
} catch {
|
|
7430
|
-
}
|
|
7431
|
-
}
|
|
7432
|
-
});
|
|
7433
|
-
} catch (error) {
|
|
7434
|
-
const msg = error instanceof Error ? error.message : "Unknown";
|
|
7435
|
-
logger7.error("Failed to start task", { taskId: shortId, error: msg });
|
|
7436
|
-
this.connection.emitTaskStopped(taskId, `start_failed: ${msg}`);
|
|
7437
|
-
}
|
|
7438
|
-
}
|
|
7439
|
-
handleStopTask(taskId) {
|
|
7440
|
-
const agentKey = this.activeAgents.has(taskId) ? taskId : `${taskId}:code-review`;
|
|
7441
|
-
const agent = this.activeAgents.get(agentKey);
|
|
7442
|
-
if (!agent) return;
|
|
7443
|
-
logger7.info("Stopping task", { taskId: taskId.slice(0, 8) });
|
|
7444
|
-
void this.killAgent(agent, taskId).then(() => {
|
|
7445
|
-
if (agent.usesWorktree) {
|
|
7446
|
-
try {
|
|
7447
|
-
removeWorktree(this.projectDir, taskId);
|
|
7448
|
-
} catch {
|
|
7449
|
-
}
|
|
7450
|
-
}
|
|
7451
|
-
});
|
|
7452
|
-
}
|
|
7453
|
-
// ── Environment management ─────────────────────────────────────────────
|
|
7454
|
-
checkoutWorkspaceBranch() {
|
|
7455
|
-
const workspaceBranch = process.env.CONVEYOR_WORKSPACE_BRANCH;
|
|
7456
|
-
if (!workspaceBranch) return;
|
|
7457
|
-
const currentBranch = this.getCurrentBranch();
|
|
7458
|
-
if (currentBranch === workspaceBranch) return;
|
|
7459
|
-
if (hasUncommittedChanges(this.projectDir)) {
|
|
7460
|
-
logger7.warn("Uncommitted changes, skipping workspace branch checkout");
|
|
7461
|
-
return;
|
|
7462
|
-
}
|
|
7463
|
-
try {
|
|
7464
|
-
execSync5(`git fetch origin ${workspaceBranch}`, { cwd: this.projectDir, stdio: "pipe" });
|
|
7465
|
-
execSync5(`git checkout ${workspaceBranch}`, { cwd: this.projectDir, stdio: "pipe" });
|
|
7466
|
-
logger7.info("Checked out workspace branch", { workspaceBranch });
|
|
7467
|
-
} catch {
|
|
7468
|
-
logger7.warn("Failed to checkout workspace branch", { workspaceBranch });
|
|
7469
|
-
}
|
|
7470
|
-
}
|
|
7471
|
-
async executeSetupCommand() {
|
|
7472
|
-
const cmd = process.env.CONVEYOR_SETUP_COMMAND;
|
|
7473
|
-
if (!cmd) return;
|
|
7474
|
-
logger7.info("Running setup command", { command: cmd });
|
|
7475
|
-
try {
|
|
7476
|
-
await runSetupCommand(cmd, this.projectDir, (stream, data) => {
|
|
7477
|
-
this.connection.sendEvent({ type: "setup_output", stream, data });
|
|
7478
|
-
(stream === "stderr" ? process.stderr : process.stdout).write(data);
|
|
7479
|
-
});
|
|
7480
|
-
logger7.info("Setup command completed");
|
|
7481
|
-
} catch (error) {
|
|
7482
|
-
const msg = error instanceof Error ? error.message : "Setup command failed";
|
|
7483
|
-
logger7.error("Setup command failed", { error: msg });
|
|
7484
|
-
this.connection.sendEvent({ type: "setup_error", message: msg });
|
|
7485
|
-
throw error;
|
|
7486
|
-
}
|
|
7487
|
-
}
|
|
7488
|
-
executeStartCommand() {
|
|
7489
|
-
const cmd = process.env.CONVEYOR_START_COMMAND;
|
|
7490
|
-
if (!cmd) return;
|
|
7491
|
-
logger7.info("Running start command", { command: cmd });
|
|
7492
|
-
const child = runStartCommand(cmd, this.projectDir, (stream, data) => {
|
|
7493
|
-
this.connection.sendEvent({ type: "start_command_output", stream, data });
|
|
7494
|
-
(stream === "stderr" ? process.stderr : process.stdout).write(data);
|
|
7495
|
-
});
|
|
7496
|
-
this.startCommandChild = child;
|
|
7497
|
-
this.startCommandRunning = true;
|
|
7498
|
-
child.on("exit", (code, signal) => {
|
|
7499
|
-
this.startCommandRunning = false;
|
|
7500
|
-
this.startCommandChild = null;
|
|
7501
|
-
logger7.info("Start command exited", { code, signal });
|
|
7502
|
-
this.connection.sendEvent({ type: "start_command_exited", code, signal });
|
|
7503
|
-
});
|
|
7504
|
-
child.on("error", (err) => {
|
|
7505
|
-
this.startCommandRunning = false;
|
|
7506
|
-
this.startCommandChild = null;
|
|
7507
|
-
logger7.error("Start command error", { error: err.message });
|
|
7508
|
-
});
|
|
7509
|
-
}
|
|
7510
|
-
async killStartCommand() {
|
|
7511
|
-
const child = this.startCommandChild;
|
|
7512
|
-
if (!child || !this.startCommandRunning) return;
|
|
7513
|
-
try {
|
|
7514
|
-
if (child.pid) process.kill(-child.pid, "SIGTERM");
|
|
7515
|
-
} catch {
|
|
7516
|
-
child.kill("SIGTERM");
|
|
7517
|
-
}
|
|
7518
|
-
await new Promise((resolve2) => {
|
|
7519
|
-
const timer = setTimeout(() => {
|
|
7520
|
-
if (this.startCommandRunning && child.pid) {
|
|
7521
|
-
try {
|
|
7522
|
-
process.kill(-child.pid, "SIGKILL");
|
|
7523
|
-
} catch {
|
|
7524
|
-
child.kill("SIGKILL");
|
|
7525
|
-
}
|
|
7526
|
-
}
|
|
7527
|
-
resolve2();
|
|
7528
|
-
}, START_CMD_KILL_TIMEOUT_MS);
|
|
7529
|
-
child.on("exit", () => {
|
|
7530
|
-
clearTimeout(timer);
|
|
7531
|
-
resolve2();
|
|
7532
|
-
});
|
|
7533
|
-
});
|
|
7534
|
-
this.startCommandChild = null;
|
|
7535
|
-
this.startCommandRunning = false;
|
|
7536
|
-
}
|
|
7537
|
-
async restartStartCommand() {
|
|
7538
|
-
await this.killStartCommand();
|
|
7539
|
-
this.executeStartCommand();
|
|
7540
|
-
}
|
|
7541
7770
|
getCurrentBranch() {
|
|
7542
7771
|
return getCurrentBranch(this.projectDir);
|
|
7543
7772
|
}
|
|
7544
|
-
// ── Branch switching ───────────────────────────────────────────────────
|
|
7545
|
-
async handleSwitchBranch(data, callback) {
|
|
7546
|
-
try {
|
|
7547
|
-
try {
|
|
7548
|
-
execSync5("git fetch origin", { cwd: this.projectDir, stdio: "pipe" });
|
|
7549
|
-
} catch {
|
|
7550
|
-
logger7.warn("Git fetch failed during branch switch");
|
|
7551
|
-
}
|
|
7552
|
-
detachWorktreeBranch(this.projectDir, data.branch);
|
|
7553
|
-
try {
|
|
7554
|
-
execSync5(`git checkout ${data.branch}`, { cwd: this.projectDir, stdio: "pipe" });
|
|
7555
|
-
} catch (err) {
|
|
7556
|
-
const msg = err instanceof Error ? err.message : "Checkout failed";
|
|
7557
|
-
callback({ ok: false, error: `Failed to checkout branch: ${msg}` });
|
|
7558
|
-
return;
|
|
7559
|
-
}
|
|
7560
|
-
try {
|
|
7561
|
-
execSync5(`git pull origin ${data.branch}`, { cwd: this.projectDir, stdio: "pipe" });
|
|
7562
|
-
} catch {
|
|
7563
|
-
logger7.warn("Git pull failed during branch switch");
|
|
7564
|
-
}
|
|
7565
|
-
if (data.syncAfter !== false) {
|
|
7566
|
-
await this.handleSyncEnvironment();
|
|
7567
|
-
}
|
|
7568
|
-
this.commitWatcher.start(data.branch);
|
|
7569
|
-
callback({ ok: true });
|
|
7570
|
-
} catch (err) {
|
|
7571
|
-
const msg = err instanceof Error ? err.message : "Branch switch failed";
|
|
7572
|
-
logger7.error("Branch switch failed", { error: msg });
|
|
7573
|
-
callback({ ok: false, error: msg });
|
|
7574
|
-
}
|
|
7575
|
-
}
|
|
7576
|
-
async handleSyncEnvironment(callback) {
|
|
7577
|
-
try {
|
|
7578
|
-
await this.killStartCommand();
|
|
7579
|
-
const cmd = this.branchSwitchCommand ?? process.env.CONVEYOR_BRANCH_SWITCH_COMMAND;
|
|
7580
|
-
if (cmd) {
|
|
7581
|
-
try {
|
|
7582
|
-
await runSetupCommand(cmd, this.projectDir, (stream, data) => {
|
|
7583
|
-
this.connection.sendEvent({ type: "sync_output", stream, data });
|
|
7584
|
-
});
|
|
7585
|
-
} catch (err) {
|
|
7586
|
-
const msg = err instanceof Error ? err.message : "Sync command failed";
|
|
7587
|
-
logger7.error("Branch switch sync command failed", { error: msg });
|
|
7588
|
-
}
|
|
7589
|
-
}
|
|
7590
|
-
this.executeStartCommand();
|
|
7591
|
-
callback?.({ ok: true });
|
|
7592
|
-
} catch (err) {
|
|
7593
|
-
const msg = err instanceof Error ? err.message : "Sync failed";
|
|
7594
|
-
logger7.error("Environment sync failed", { error: msg });
|
|
7595
|
-
callback?.({ ok: false, error: msg });
|
|
7596
|
-
}
|
|
7597
|
-
}
|
|
7598
|
-
// ── Commit watching ────────────────────────────────────────────────────
|
|
7599
|
-
// oxlint-disable-next-line max-lines-per-function -- sequential sync steps
|
|
7600
|
-
async handleNewCommits(data) {
|
|
7601
|
-
await this.connection.call("reportNewCommitsDetected", {
|
|
7602
|
-
projectId: this.connection.projectId,
|
|
7603
|
-
branch: data.branch,
|
|
7604
|
-
commits: [
|
|
7605
|
-
{
|
|
7606
|
-
sha: data.newCommitSha,
|
|
7607
|
-
message: data.latestMessage,
|
|
7608
|
-
author: data.latestAuthor
|
|
7609
|
-
}
|
|
7610
|
-
]
|
|
7611
|
-
});
|
|
7612
|
-
const stepsRun = await this.smartSync(data.previousSha, data.newCommitSha, data.branch);
|
|
7613
|
-
await this.connection.call("reportEnvironmentReady", {
|
|
7614
|
-
projectId: this.connection.projectId,
|
|
7615
|
-
branch: data.branch,
|
|
7616
|
-
setupComplete: this.setupComplete,
|
|
7617
|
-
startCommandRunning: this.startCommandRunning
|
|
7618
|
-
});
|
|
7619
|
-
logger7.info("Commit sync complete", { steps: stepsRun.join(", ") });
|
|
7620
|
-
}
|
|
7621
|
-
async smartSync(previousSha, newSha, branch) {
|
|
7622
|
-
if (hasUncommittedChanges(this.projectDir)) {
|
|
7623
|
-
this.connection.sendEvent({
|
|
7624
|
-
type: "commit_watch_warning",
|
|
7625
|
-
message: "Working tree has uncommitted changes. Auto-pull skipped."
|
|
7626
|
-
});
|
|
7627
|
-
return ["skipped:dirty_tree"];
|
|
7628
|
-
}
|
|
7629
|
-
await this.killStartCommand();
|
|
7630
|
-
try {
|
|
7631
|
-
execSync5(`git pull origin ${branch}`, {
|
|
7632
|
-
cwd: this.projectDir,
|
|
7633
|
-
stdio: "pipe",
|
|
7634
|
-
timeout: 6e4
|
|
7635
|
-
});
|
|
7636
|
-
} catch (err) {
|
|
7637
|
-
const msg = err instanceof Error ? err.message : "Pull failed";
|
|
7638
|
-
logger7.error("Git pull failed during commit sync", { error: msg });
|
|
7639
|
-
this.executeStartCommand();
|
|
7640
|
-
return ["error:pull"];
|
|
7641
|
-
}
|
|
7642
|
-
const stepsRun = ["pull"];
|
|
7643
|
-
const changedFiles = this.getChangedFiles(previousSha, newSha);
|
|
7644
|
-
await this.syncDependencies(changedFiles, stepsRun);
|
|
7645
|
-
this.executeStartCommand();
|
|
7646
|
-
stepsRun.push("startCommand");
|
|
7647
|
-
return stepsRun;
|
|
7648
|
-
}
|
|
7649
|
-
async syncDependencies(changedFiles, stepsRun) {
|
|
7650
|
-
const needsInstall = changedFiles.some(
|
|
7651
|
-
(f) => f === "package.json" || f === "bun.lockb" || f.endsWith("/package.json") || f.endsWith("/bun.lockb")
|
|
7652
|
-
);
|
|
7653
|
-
const needsPrisma = changedFiles.some(
|
|
7654
|
-
(f) => f.includes("prisma/schema.prisma") || f.includes("prisma/migrations/")
|
|
7655
|
-
);
|
|
7656
|
-
const cmd = this.branchSwitchCommand ?? process.env.CONVEYOR_BRANCH_SWITCH_COMMAND;
|
|
7657
|
-
if (cmd && (needsInstall || needsPrisma)) {
|
|
7658
|
-
try {
|
|
7659
|
-
await runSetupCommand(cmd, this.projectDir, (stream, data) => {
|
|
7660
|
-
this.connection.sendEvent({ type: "sync_output", stream, data });
|
|
7661
|
-
});
|
|
7662
|
-
stepsRun.push("branchSwitchCommand");
|
|
7663
|
-
} catch (err) {
|
|
7664
|
-
const msg = err instanceof Error ? err.message : "Sync command failed";
|
|
7665
|
-
logger7.error("Branch switch command failed", { error: msg });
|
|
7666
|
-
}
|
|
7667
|
-
} else if (!cmd) {
|
|
7668
|
-
this.runIndividualSyncSteps(needsInstall, needsPrisma, stepsRun);
|
|
7669
|
-
}
|
|
7670
|
-
}
|
|
7671
|
-
runIndividualSyncSteps(needsInstall, needsPrisma, stepsRun) {
|
|
7672
|
-
if (needsInstall) {
|
|
7673
|
-
try {
|
|
7674
|
-
execSync5("bun install", { cwd: this.projectDir, timeout: 12e4, stdio: "pipe" });
|
|
7675
|
-
stepsRun.push("install");
|
|
7676
|
-
} catch (err) {
|
|
7677
|
-
const msg = err instanceof Error ? err.message : "Install failed";
|
|
7678
|
-
logger7.error("bun install failed", { error: msg });
|
|
7679
|
-
}
|
|
7680
|
-
}
|
|
7681
|
-
if (needsPrisma) {
|
|
7682
|
-
try {
|
|
7683
|
-
execSync5("bunx prisma generate", { cwd: this.projectDir, timeout: 6e4, stdio: "pipe" });
|
|
7684
|
-
execSync5("bunx prisma db push --accept-data-loss", {
|
|
7685
|
-
cwd: this.projectDir,
|
|
7686
|
-
timeout: 6e4,
|
|
7687
|
-
stdio: "pipe"
|
|
7688
|
-
});
|
|
7689
|
-
stepsRun.push("prisma");
|
|
7690
|
-
} catch (err) {
|
|
7691
|
-
const msg = err instanceof Error ? err.message : "Prisma sync failed";
|
|
7692
|
-
logger7.error("Prisma sync failed", { error: msg });
|
|
7693
|
-
}
|
|
7694
|
-
}
|
|
7695
|
-
}
|
|
7696
|
-
getChangedFiles(previousSha, newSha) {
|
|
7697
|
-
try {
|
|
7698
|
-
return execSync5(`git diff --name-only ${previousSha}..${newSha}`, {
|
|
7699
|
-
cwd: this.projectDir,
|
|
7700
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
7701
|
-
}).toString().trim().split("\n").filter(Boolean);
|
|
7702
|
-
} catch {
|
|
7703
|
-
return [];
|
|
7704
|
-
}
|
|
7705
|
-
}
|
|
7706
7773
|
};
|
|
7707
7774
|
|
|
7708
7775
|
// src/setup/config.ts
|
|
@@ -7748,15 +7815,15 @@ export {
|
|
|
7748
7815
|
PlanSync,
|
|
7749
7816
|
SessionRunner,
|
|
7750
7817
|
ProjectConnection,
|
|
7751
|
-
runSetupCommand,
|
|
7752
|
-
runAuthTokenCommand,
|
|
7753
|
-
runStartCommand,
|
|
7754
7818
|
CommitWatcher,
|
|
7755
7819
|
ensureWorktree,
|
|
7756
7820
|
detachWorktreeBranch,
|
|
7757
7821
|
removeWorktree,
|
|
7822
|
+
runSetupCommand,
|
|
7823
|
+
runAuthTokenCommand,
|
|
7824
|
+
runStartCommand,
|
|
7758
7825
|
ProjectRunner,
|
|
7759
7826
|
loadForwardPorts,
|
|
7760
7827
|
loadConveyorConfig
|
|
7761
7828
|
};
|
|
7762
|
-
//# sourceMappingURL=chunk-
|
|
7829
|
+
//# sourceMappingURL=chunk-WYIXEIRS.js.map
|