reygent-code 0.1.0 → 1.0.1
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/cli.js +295 -135
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -584,6 +584,8 @@ var claudeAdapter = {
|
|
|
584
584
|
});
|
|
585
585
|
registerChildProcess(child);
|
|
586
586
|
let resultText = "";
|
|
587
|
+
let resultErrorMessage;
|
|
588
|
+
let resultApiErrorStatus;
|
|
587
589
|
let resultUsage;
|
|
588
590
|
const textChunks = [];
|
|
589
591
|
const name = options.agentName;
|
|
@@ -606,7 +608,13 @@ var claudeAdapter = {
|
|
|
606
608
|
if (stdoutEnded && stderrEnded && processExitCode !== null) {
|
|
607
609
|
clearTimeout(timeout);
|
|
608
610
|
const stdout = resultText || textChunks.join("\n");
|
|
609
|
-
resolve5({
|
|
611
|
+
resolve5({
|
|
612
|
+
stdout,
|
|
613
|
+
exitCode: processExitCode,
|
|
614
|
+
usage: resultUsage,
|
|
615
|
+
errorMessage: resultErrorMessage,
|
|
616
|
+
apiErrorStatus: resultApiErrorStatus
|
|
617
|
+
});
|
|
610
618
|
}
|
|
611
619
|
};
|
|
612
620
|
const stdoutRL = createInterface({ input: child.stdout });
|
|
@@ -641,6 +649,10 @@ var claudeAdapter = {
|
|
|
641
649
|
} else if (event.type === "result") {
|
|
642
650
|
const msg = event;
|
|
643
651
|
resultText = msg.result;
|
|
652
|
+
if (msg.is_error) {
|
|
653
|
+
resultErrorMessage = msg.result;
|
|
654
|
+
resultApiErrorStatus = msg.api_error_status;
|
|
655
|
+
}
|
|
644
656
|
const { inputTokens, outputTokens, cachedTokens, cacheWriteTokens } = extractTokenUsage(msg);
|
|
645
657
|
const hasUsage = msg.total_cost_usd !== void 0 || msg.duration_ms !== void 0 || msg.num_turns !== void 0 || inputTokens !== void 0 || outputTokens !== void 0;
|
|
646
658
|
if (hasUsage) {
|
|
@@ -809,14 +821,47 @@ var geminiAdapter = {
|
|
|
809
821
|
let inputTokens;
|
|
810
822
|
let outputTokens;
|
|
811
823
|
let cachedTokens;
|
|
824
|
+
let errorMessage;
|
|
825
|
+
let apiErrorStatus;
|
|
812
826
|
try {
|
|
813
827
|
const parsed = JSON.parse(stdout);
|
|
814
828
|
resultText = parsed.response ?? parsed.text ?? stdout;
|
|
815
829
|
inputTokens = parsed.usage_metadata?.prompt_token_count ?? parsed.input_tokens;
|
|
816
830
|
outputTokens = parsed.usage_metadata?.candidates_token_count ?? parsed.output_tokens;
|
|
817
831
|
cachedTokens = parsed.usage_metadata?.cached_content_token_count;
|
|
832
|
+
if (parsed.error) {
|
|
833
|
+
errorMessage = parsed.error.message;
|
|
834
|
+
let statusCode = parsed.error.status;
|
|
835
|
+
if (parsed.error.code) {
|
|
836
|
+
if (typeof parsed.error.code === "number") {
|
|
837
|
+
statusCode = parsed.error.code;
|
|
838
|
+
} else if (typeof parsed.error.code === "string") {
|
|
839
|
+
const code2 = parsed.error.code.toLowerCase();
|
|
840
|
+
if (code2 === "not_found" || code2 === "model_not_found") {
|
|
841
|
+
statusCode = 404;
|
|
842
|
+
} else if (code2 === "permission_denied" || code2 === "unauthenticated") {
|
|
843
|
+
statusCode = 403;
|
|
844
|
+
} else if (code2 === "invalid_api_key" || code2 === "invalid_authentication") {
|
|
845
|
+
statusCode = 401;
|
|
846
|
+
} else if (code2 === "resource_exhausted" || code2 === "rate_limit_exceeded") {
|
|
847
|
+
statusCode = 429;
|
|
848
|
+
} else if (code2 === "internal" || code2 === "server_error") {
|
|
849
|
+
statusCode = 500;
|
|
850
|
+
} else if (code2 === "invalid_argument") {
|
|
851
|
+
statusCode = 400;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
apiErrorStatus = statusCode;
|
|
856
|
+
}
|
|
818
857
|
} catch {
|
|
819
858
|
}
|
|
859
|
+
if (code !== 0 && !errorMessage && stderrChunks.length > 0) {
|
|
860
|
+
const stderr = stderrChunks.join("").trim();
|
|
861
|
+
if (stderr) {
|
|
862
|
+
errorMessage = stderr;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
820
865
|
resolve5({
|
|
821
866
|
stdout: resultText,
|
|
822
867
|
exitCode: code ?? 1,
|
|
@@ -826,7 +871,9 @@ var geminiAdapter = {
|
|
|
826
871
|
outputTokens,
|
|
827
872
|
cachedTokens,
|
|
828
873
|
provider: "gemini"
|
|
829
|
-
}
|
|
874
|
+
},
|
|
875
|
+
errorMessage,
|
|
876
|
+
apiErrorStatus
|
|
830
877
|
});
|
|
831
878
|
});
|
|
832
879
|
});
|
|
@@ -922,8 +969,10 @@ var codexAdapter = {
|
|
|
922
969
|
child.stdout.on("data", (chunk) => {
|
|
923
970
|
stdout += chunk.toString();
|
|
924
971
|
});
|
|
972
|
+
const stderrChunks = [];
|
|
925
973
|
child.stderr.on("data", (chunk) => {
|
|
926
974
|
const text = chunk.toString();
|
|
975
|
+
stderrChunks.push(text);
|
|
927
976
|
if (options.onActivity) {
|
|
928
977
|
const line = text.trim();
|
|
929
978
|
if (line) options.onActivity({ agent: name, detail: line.slice(0, 80) });
|
|
@@ -942,14 +991,44 @@ var codexAdapter = {
|
|
|
942
991
|
let inputTokens;
|
|
943
992
|
let outputTokens;
|
|
944
993
|
let cachedTokens;
|
|
994
|
+
let errorMessage;
|
|
995
|
+
let apiErrorStatus;
|
|
945
996
|
try {
|
|
946
997
|
const parsed = JSON.parse(stdout);
|
|
947
998
|
resultText = parsed.response ?? parsed.text ?? stdout;
|
|
948
999
|
inputTokens = parsed.usage?.prompt_tokens ?? parsed.input_tokens;
|
|
949
1000
|
outputTokens = parsed.usage?.completion_tokens ?? parsed.output_tokens;
|
|
950
1001
|
cachedTokens = parsed.usage?.prompt_tokens_details?.cached_tokens ?? parsed.cached_tokens;
|
|
1002
|
+
if (parsed.error) {
|
|
1003
|
+
errorMessage = parsed.error.message;
|
|
1004
|
+
if (typeof parsed.error.code === "string") {
|
|
1005
|
+
const code2 = parsed.error.code;
|
|
1006
|
+
if (code2 === "model_not_found" || code2 === "invalid_model") {
|
|
1007
|
+
apiErrorStatus = 404;
|
|
1008
|
+
} else if (code2 === "invalid_api_key" || code2 === "invalid_request_error") {
|
|
1009
|
+
apiErrorStatus = 401;
|
|
1010
|
+
} else if (code2 === "rate_limit_exceeded") {
|
|
1011
|
+
apiErrorStatus = 429;
|
|
1012
|
+
} else if (code2 === "insufficient_quota") {
|
|
1013
|
+
apiErrorStatus = 402;
|
|
1014
|
+
} else if (code2 === "server_error") {
|
|
1015
|
+
apiErrorStatus = 500;
|
|
1016
|
+
} else if (code2.includes("not_found")) {
|
|
1017
|
+
apiErrorStatus = 404;
|
|
1018
|
+
} else if (code2.includes("auth") || code2.includes("unauthorized")) {
|
|
1019
|
+
apiErrorStatus = 401;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
apiErrorStatus = apiErrorStatus ?? parsed.error.status;
|
|
1023
|
+
}
|
|
951
1024
|
} catch {
|
|
952
1025
|
}
|
|
1026
|
+
if (code !== 0 && !errorMessage && stderrChunks.length > 0) {
|
|
1027
|
+
const stderr = stderrChunks.join("").trim();
|
|
1028
|
+
if (stderr) {
|
|
1029
|
+
errorMessage = stderr;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
953
1032
|
resolve5({
|
|
954
1033
|
stdout: resultText,
|
|
955
1034
|
exitCode: code ?? 1,
|
|
@@ -960,7 +1039,9 @@ var codexAdapter = {
|
|
|
960
1039
|
cachedTokens,
|
|
961
1040
|
// Note: cacheWriteTokens not extracted — OpenAI doesn't currently expose this field
|
|
962
1041
|
provider: "codex"
|
|
963
|
-
}
|
|
1042
|
+
},
|
|
1043
|
+
errorMessage,
|
|
1044
|
+
apiErrorStatus
|
|
964
1045
|
});
|
|
965
1046
|
});
|
|
966
1047
|
});
|
|
@@ -2616,7 +2697,56 @@ function searchKnowledge(query) {
|
|
|
2616
2697
|
return results;
|
|
2617
2698
|
}
|
|
2618
2699
|
|
|
2700
|
+
// src/telemetry-helpers.ts
|
|
2701
|
+
function emitErrorTask(message, stage, options) {
|
|
2702
|
+
const chesstrace = getChesstrace();
|
|
2703
|
+
if (chesstrace) {
|
|
2704
|
+
try {
|
|
2705
|
+
chesstrace.emit(Events.ERROR_TASK, {
|
|
2706
|
+
type: "TaskError",
|
|
2707
|
+
message,
|
|
2708
|
+
stage,
|
|
2709
|
+
...options?.agent && { agent: options.agent },
|
|
2710
|
+
...options?.errorMessage && { errorMessage: options.errorMessage },
|
|
2711
|
+
...options?.apiErrorStatus && { apiErrorStatus: options.apiErrorStatus }
|
|
2712
|
+
});
|
|
2713
|
+
} catch (err) {
|
|
2714
|
+
if (isDebug()) {
|
|
2715
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
2716
|
+
console.error(`[DEBUG] Telemetry emit failed (ERROR_TASK): ${errMsg}`);
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2619
2722
|
// src/spawn.ts
|
|
2723
|
+
function looksLikeMalformedModel(model) {
|
|
2724
|
+
if (!model) return false;
|
|
2725
|
+
const trimmed = model.trim();
|
|
2726
|
+
if (trimmed.length === 0) return true;
|
|
2727
|
+
if (/\s/.test(trimmed)) return true;
|
|
2728
|
+
if (/^[^a-zA-Z0-9]|[^a-zA-Z0-9]$/.test(trimmed)) return true;
|
|
2729
|
+
if (trimmed.length < 3) return true;
|
|
2730
|
+
return false;
|
|
2731
|
+
}
|
|
2732
|
+
function formatExitDetail(result, model) {
|
|
2733
|
+
if (result.errorMessage) {
|
|
2734
|
+
const status = result.apiErrorStatus ? ` (HTTP ${result.apiErrorStatus})` : "";
|
|
2735
|
+
let detail = `
|
|
2736
|
+
${result.errorMessage}${status}`;
|
|
2737
|
+
if (result.apiErrorStatus === 404 && /not available/i.test(result.errorMessage) && looksLikeMalformedModel(model)) {
|
|
2738
|
+
detail += `
|
|
2739
|
+
Tip: edit .reygent/config.json "model" field, or run \`reygent config\` to pick a supported model.`;
|
|
2740
|
+
}
|
|
2741
|
+
return detail;
|
|
2742
|
+
}
|
|
2743
|
+
const trimmed = result.stdout.trim();
|
|
2744
|
+
if (!trimmed) return "";
|
|
2745
|
+
const truncated = trimmed.slice(0, 500);
|
|
2746
|
+
const suffix = trimmed.length > 500 ? "..." : "";
|
|
2747
|
+
return `
|
|
2748
|
+
${truncated}${suffix}`;
|
|
2749
|
+
}
|
|
2620
2750
|
async function spawnAgentStream(name, prompt2, timeoutMs, options) {
|
|
2621
2751
|
const providerName = options?.provider ?? resolveProvider();
|
|
2622
2752
|
const adapter = getProvider(providerName);
|
|
@@ -2628,13 +2758,12 @@ async function spawnAgentStream(name, prompt2, timeoutMs, options) {
|
|
|
2628
2758
|
provider: providerName,
|
|
2629
2759
|
reason
|
|
2630
2760
|
});
|
|
2631
|
-
chesstrace.emit(Events.ERROR_TASK, {
|
|
2632
|
-
type: "TaskError",
|
|
2633
|
-
message: `Provider "${providerName}" is not available: ${reason}`,
|
|
2634
|
-
stage: options?.stage ?? "spawn",
|
|
2635
|
-
agent: name
|
|
2636
|
-
});
|
|
2637
2761
|
}
|
|
2762
|
+
emitErrorTask(
|
|
2763
|
+
`Provider "${providerName}" is not available: ${reason}`,
|
|
2764
|
+
options?.stage ?? "spawn",
|
|
2765
|
+
{ agent: name }
|
|
2766
|
+
);
|
|
2638
2767
|
throw new TaskError(`Provider "${providerName}" is not available: ${reason}`);
|
|
2639
2768
|
}
|
|
2640
2769
|
const modelId = options?.model ?? await resolveModel(providerName);
|
|
@@ -2818,18 +2947,16 @@ async function runPlanner(spec, previousAnswers, options) {
|
|
|
2818
2947
|
const agents = getAgents();
|
|
2819
2948
|
const plannerAgent = agents.find((a) => a.name === "planner");
|
|
2820
2949
|
const prompt2 = buildPrompt(spec, previousAnswers, options);
|
|
2821
|
-
const
|
|
2950
|
+
const spawnResult = await spawnAgentStream("planner", prompt2, 3e5, { quiet: true, onActivity: options?.onActivity, provider: plannerAgent?.provider, model: plannerAgent?.model });
|
|
2951
|
+
const { stdout: raw, exitCode, usage, errorMessage, apiErrorStatus } = spawnResult;
|
|
2822
2952
|
if (exitCode !== 0) {
|
|
2823
|
-
const
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
});
|
|
2831
|
-
}
|
|
2832
|
-
throw new TaskError(`Planner: agent exited with code ${exitCode}`);
|
|
2953
|
+
const detail = formatExitDetail(spawnResult, plannerAgent?.model);
|
|
2954
|
+
emitErrorTask(
|
|
2955
|
+
`Planner: agent exited with code ${exitCode}${detail}`,
|
|
2956
|
+
"plan",
|
|
2957
|
+
{ agent: "planner", errorMessage, apiErrorStatus }
|
|
2958
|
+
);
|
|
2959
|
+
throw new TaskError(`Planner: agent exited with code ${exitCode}${detail}`);
|
|
2833
2960
|
}
|
|
2834
2961
|
let parsed;
|
|
2835
2962
|
try {
|
|
@@ -2857,31 +2984,23 @@ async function runPlanner(spec, previousAnswers, options) {
|
|
|
2857
2984
|
}
|
|
2858
2985
|
}
|
|
2859
2986
|
const errors = Array.isArray(obj.errors) ? obj.errors.join("\n - ") : "unknown validation error";
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
chesstrace2.emit(Events.ERROR_TASK, {
|
|
2863
|
-
type: "TaskError",
|
|
2864
|
-
message: `Planner: spec validation failed:
|
|
2987
|
+
emitErrorTask(
|
|
2988
|
+
`Planner: spec validation failed:
|
|
2865
2989
|
- ${errors}`,
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
}
|
|
2990
|
+
"plan",
|
|
2991
|
+
{ agent: "planner" }
|
|
2992
|
+
);
|
|
2870
2993
|
throw new TaskError(
|
|
2871
2994
|
`Planner: spec validation failed:
|
|
2872
2995
|
- ${errors}`
|
|
2873
2996
|
);
|
|
2874
2997
|
}
|
|
2875
2998
|
if (obj.valid !== true) {
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
stage: "plan",
|
|
2882
|
-
agent: "planner"
|
|
2883
|
-
});
|
|
2884
|
-
}
|
|
2999
|
+
emitErrorTask(
|
|
3000
|
+
"Planner: unexpected response \u2014 missing 'valid' field",
|
|
3001
|
+
"plan",
|
|
3002
|
+
{ agent: "planner" }
|
|
3003
|
+
);
|
|
2885
3004
|
throw new TaskError(
|
|
2886
3005
|
"Planner: unexpected response \u2014 missing 'valid' field"
|
|
2887
3006
|
);
|
|
@@ -2889,49 +3008,37 @@ async function runPlanner(spec, previousAnswers, options) {
|
|
|
2889
3008
|
const { goals, tasks, constraints, dod } = obj;
|
|
2890
3009
|
const chesstrace = getChesstrace();
|
|
2891
3010
|
if (!isNonEmptyStringArray(goals)) {
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
agent: "planner"
|
|
2898
|
-
});
|
|
2899
|
-
}
|
|
3011
|
+
emitErrorTask(
|
|
3012
|
+
"Planner: 'goals' must be a non-empty string array",
|
|
3013
|
+
"plan",
|
|
3014
|
+
{ agent: "planner" }
|
|
3015
|
+
);
|
|
2900
3016
|
throw new TaskError("Planner: 'goals' must be a non-empty string array");
|
|
2901
3017
|
}
|
|
2902
3018
|
if (!isNonEmptyStringArray(tasks)) {
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
agent: "planner"
|
|
2909
|
-
});
|
|
2910
|
-
}
|
|
3019
|
+
emitErrorTask(
|
|
3020
|
+
"Planner: 'tasks' must be a non-empty string array",
|
|
3021
|
+
"plan",
|
|
3022
|
+
{ agent: "planner" }
|
|
3023
|
+
);
|
|
2911
3024
|
throw new TaskError("Planner: 'tasks' must be a non-empty string array");
|
|
2912
3025
|
}
|
|
2913
3026
|
if (!isNonEmptyStringArray(constraints)) {
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
agent: "planner"
|
|
2920
|
-
});
|
|
2921
|
-
}
|
|
3027
|
+
emitErrorTask(
|
|
3028
|
+
"Planner: 'constraints' must be a non-empty string array",
|
|
3029
|
+
"plan",
|
|
3030
|
+
{ agent: "planner" }
|
|
3031
|
+
);
|
|
2922
3032
|
throw new TaskError(
|
|
2923
3033
|
"Planner: 'constraints' must be a non-empty string array"
|
|
2924
3034
|
);
|
|
2925
3035
|
}
|
|
2926
3036
|
if (!isNonEmptyStringArray(dod)) {
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
agent: "planner"
|
|
2933
|
-
});
|
|
2934
|
-
}
|
|
3037
|
+
emitErrorTask(
|
|
3038
|
+
"Planner: 'dod' must be a non-empty string array",
|
|
3039
|
+
"plan",
|
|
3040
|
+
{ agent: "planner" }
|
|
3041
|
+
);
|
|
2935
3042
|
throw new TaskError("Planner: 'dod' must be a non-empty string array");
|
|
2936
3043
|
}
|
|
2937
3044
|
return { result: { goals, tasks, constraints, dod }, usage };
|
|
@@ -3017,10 +3124,19 @@ Be specific and actionable. Expand the description into concrete requirements th
|
|
|
3017
3124
|
**Description:** ${description}${answersContext}`;
|
|
3018
3125
|
}
|
|
3019
3126
|
async function runClarification(description, previousAnswers, onActivity) {
|
|
3127
|
+
const agents = getAgents();
|
|
3128
|
+
const plannerAgent = agents.find((a) => a.name === "planner");
|
|
3020
3129
|
const prompt2 = buildClarificationPrompt(description, previousAnswers);
|
|
3021
|
-
const
|
|
3130
|
+
const clarifyResult = await spawnAgentStream("generate-spec", prompt2, 12e4, { quiet: true, onActivity });
|
|
3131
|
+
const { stdout: raw, exitCode, errorMessage, apiErrorStatus } = clarifyResult;
|
|
3022
3132
|
if (exitCode !== 0) {
|
|
3023
|
-
|
|
3133
|
+
const detail = formatExitDetail(clarifyResult, plannerAgent?.model);
|
|
3134
|
+
emitErrorTask(
|
|
3135
|
+
`generate-spec: agent exited with code ${exitCode}${detail}`,
|
|
3136
|
+
"clarification",
|
|
3137
|
+
{ agent: "generate-spec", errorMessage, apiErrorStatus }
|
|
3138
|
+
);
|
|
3139
|
+
throw new TaskError(`generate-spec: agent exited with code ${exitCode}${detail}`);
|
|
3024
3140
|
}
|
|
3025
3141
|
let parsed;
|
|
3026
3142
|
try {
|
|
@@ -3052,10 +3168,19 @@ async function runClarification(description, previousAnswers, onActivity) {
|
|
|
3052
3168
|
return { ready: true };
|
|
3053
3169
|
}
|
|
3054
3170
|
async function generateSpec(description, clarificationAnswers, onActivity) {
|
|
3171
|
+
const agents = getAgents();
|
|
3172
|
+
const plannerAgent = agents.find((a) => a.name === "planner");
|
|
3055
3173
|
const prompt2 = buildGeneratePrompt(description, clarificationAnswers);
|
|
3056
|
-
const
|
|
3174
|
+
const specResult = await spawnAgentStream("generate-spec", prompt2, 12e4, { onActivity });
|
|
3175
|
+
const { stdout, exitCode, errorMessage, apiErrorStatus } = specResult;
|
|
3057
3176
|
if (exitCode !== 0) {
|
|
3058
|
-
|
|
3177
|
+
const detail = formatExitDetail(specResult, plannerAgent?.model);
|
|
3178
|
+
emitErrorTask(
|
|
3179
|
+
`generate-spec: agent exited with code ${exitCode}${detail}`,
|
|
3180
|
+
"generate",
|
|
3181
|
+
{ agent: "generate-spec", errorMessage, apiErrorStatus }
|
|
3182
|
+
);
|
|
3183
|
+
throw new TaskError(`generate-spec: agent exited with code ${exitCode}${detail}`);
|
|
3059
3184
|
}
|
|
3060
3185
|
if (!stdout) {
|
|
3061
3186
|
throw new TaskError("generate-spec: empty result from agent");
|
|
@@ -3861,15 +3986,11 @@ async function runImplement(spec, plan, options, retryOptions) {
|
|
|
3861
3986
|
const devAgent = agents.find((a) => a.name === "dev");
|
|
3862
3987
|
const qeAgent = agents.find((a) => a.name === "qe");
|
|
3863
3988
|
if (!devAgent || !qeAgent) {
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
stage: options?.stage ?? "implement",
|
|
3870
|
-
agent: "implement"
|
|
3871
|
-
});
|
|
3872
|
-
}
|
|
3989
|
+
emitErrorTask(
|
|
3990
|
+
"Implement: missing dev or qe agent config",
|
|
3991
|
+
options?.stage ?? "implement",
|
|
3992
|
+
{ agent: "implement" }
|
|
3993
|
+
);
|
|
3873
3994
|
throw new TaskError("Implement: missing dev or qe agent config");
|
|
3874
3995
|
}
|
|
3875
3996
|
const agentsToRun = retryOptions?.agentsToRun ?? ["dev", "qe"];
|
|
@@ -3908,9 +4029,12 @@ async function runImplement(spec, plan, options, retryOptions) {
|
|
|
3908
4029
|
if (devResult.value.exitCode === 0) {
|
|
3909
4030
|
dev = extractDevOutput(devResult.value.stdout);
|
|
3910
4031
|
} else {
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
if (
|
|
4032
|
+
const detail = formatExitDetail(devResult.value, devAgent.model);
|
|
4033
|
+
console.log(chalk13.red("dev agent failed:"), `exit code ${devResult.value.exitCode}${detail}`);
|
|
4034
|
+
if (!devResult.value.errorMessage || devResult.value.errorMessage.length < 50) {
|
|
4035
|
+
const summary = getFailureSummary(devResult.value.stdout);
|
|
4036
|
+
if (summary) console.log(chalk13.gray(" \u21B3"), chalk13.gray(summary));
|
|
4037
|
+
}
|
|
3914
4038
|
}
|
|
3915
4039
|
} else {
|
|
3916
4040
|
console.log(chalk13.red("dev agent failed:"), devResult.reason);
|
|
@@ -3923,9 +4047,12 @@ async function runImplement(spec, plan, options, retryOptions) {
|
|
|
3923
4047
|
if (qeResult.value.exitCode === 0) {
|
|
3924
4048
|
qe = extractQEOutput(qeResult.value.stdout);
|
|
3925
4049
|
} else {
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
if (
|
|
4050
|
+
const detail = formatExitDetail(qeResult.value, qeAgent.model);
|
|
4051
|
+
console.log(chalk13.red("qe agent failed:"), `exit code ${qeResult.value.exitCode}${detail}`);
|
|
4052
|
+
if (!qeResult.value.errorMessage || qeResult.value.errorMessage.length < 50) {
|
|
4053
|
+
const summary = getFailureSummary(qeResult.value.stdout);
|
|
4054
|
+
if (summary) console.log(chalk13.gray(" \u21B3"), chalk13.gray(summary));
|
|
4055
|
+
}
|
|
3929
4056
|
}
|
|
3930
4057
|
} else {
|
|
3931
4058
|
console.log(chalk13.red("qe agent failed:"), qeResult.reason);
|
|
@@ -3941,9 +4068,12 @@ async function runImplement(spec, plan, options, retryOptions) {
|
|
|
3941
4068
|
}
|
|
3942
4069
|
usages.push({ agent: "dev", usage: devResult.usage });
|
|
3943
4070
|
if (devResult.exitCode !== 0) {
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
if (
|
|
4071
|
+
const detail = formatExitDetail(devResult);
|
|
4072
|
+
console.log(chalk13.red("dev agent failed:"), `exit code ${devResult.exitCode}${detail}`);
|
|
4073
|
+
if (!devResult.errorMessage) {
|
|
4074
|
+
const summary = getFailureSummary(devResult.stdout);
|
|
4075
|
+
if (summary) console.log(chalk13.gray(" \u21B3"), chalk13.gray(summary));
|
|
4076
|
+
}
|
|
3947
4077
|
}
|
|
3948
4078
|
} catch (err) {
|
|
3949
4079
|
console.log(chalk13.red("dev agent failed:"), err);
|
|
@@ -3958,9 +4088,12 @@ async function runImplement(spec, plan, options, retryOptions) {
|
|
|
3958
4088
|
}
|
|
3959
4089
|
usages.push({ agent: "qe", usage: qeResult.usage });
|
|
3960
4090
|
if (qeResult.exitCode !== 0) {
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
if (
|
|
4091
|
+
const detail = formatExitDetail(qeResult);
|
|
4092
|
+
console.log(chalk13.red("qe agent failed:"), `exit code ${qeResult.exitCode}${detail}`);
|
|
4093
|
+
if (!qeResult.errorMessage) {
|
|
4094
|
+
const summary = getFailureSummary(qeResult.stdout);
|
|
4095
|
+
if (summary) console.log(chalk13.gray(" \u21B3"), chalk13.gray(summary));
|
|
4096
|
+
}
|
|
3964
4097
|
}
|
|
3965
4098
|
} catch (err) {
|
|
3966
4099
|
console.log(chalk13.red("qe agent failed:"), err);
|
|
@@ -3969,36 +4102,27 @@ async function runImplement(spec, plan, options, retryOptions) {
|
|
|
3969
4102
|
}
|
|
3970
4103
|
const chesstrace = getChesstrace();
|
|
3971
4104
|
if (runDev && dev === null && (runQE && qe === null)) {
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
agent: "implement"
|
|
3978
|
-
});
|
|
3979
|
-
}
|
|
4105
|
+
emitErrorTask(
|
|
4106
|
+
"Implement: all requested agents failed",
|
|
4107
|
+
options?.stage ?? "implement",
|
|
4108
|
+
{ agent: "implement" }
|
|
4109
|
+
);
|
|
3980
4110
|
throw new TaskError("Implement: all requested agents failed");
|
|
3981
4111
|
}
|
|
3982
4112
|
if (runDev && !runQE && dev === null) {
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
agent: "dev"
|
|
3989
|
-
});
|
|
3990
|
-
}
|
|
4113
|
+
emitErrorTask(
|
|
4114
|
+
"Implement: dev agent failed",
|
|
4115
|
+
options?.stage ?? "implement",
|
|
4116
|
+
{ agent: "dev" }
|
|
4117
|
+
);
|
|
3991
4118
|
throw new TaskError("Implement: dev agent failed");
|
|
3992
4119
|
}
|
|
3993
4120
|
if (!runDev && runQE && qe === null) {
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
agent: "qe"
|
|
4000
|
-
});
|
|
4001
|
-
}
|
|
4121
|
+
emitErrorTask(
|
|
4122
|
+
"Implement: qe agent failed",
|
|
4123
|
+
options?.stage ?? "implement",
|
|
4124
|
+
{ agent: "qe" }
|
|
4125
|
+
);
|
|
4002
4126
|
throw new TaskError("Implement: qe agent failed");
|
|
4003
4127
|
}
|
|
4004
4128
|
return { implement: { dev, qe }, usages };
|
|
@@ -8518,21 +8642,15 @@ ${extra.trim()}` : extra.trim();
|
|
|
8518
8642
|
console.log();
|
|
8519
8643
|
const pushSpinner = createLiveStatus("committing and pushing...");
|
|
8520
8644
|
const trace = getChesstrace();
|
|
8645
|
+
const commitMessage = "fix: address PR review comments";
|
|
8646
|
+
const maxRetries = options.retryCommits ?? 3;
|
|
8647
|
+
let committed = false;
|
|
8521
8648
|
try {
|
|
8522
8649
|
await exec4("git", ["add", "-A"]);
|
|
8523
|
-
await exec4("git", ["commit", "-m",
|
|
8524
|
-
|
|
8525
|
-
|
|
8526
|
-
|
|
8527
|
-
}
|
|
8528
|
-
await exec4("git", ["push", "origin", branch]);
|
|
8529
|
-
try {
|
|
8530
|
-
trace.emit(Events.GIT_PUSH, { branch });
|
|
8531
|
-
} catch {
|
|
8532
|
-
}
|
|
8533
|
-
pushSpinner.succeed(chalk22.green("Changes committed and pushed."));
|
|
8534
|
-
} catch (pushErr) {
|
|
8535
|
-
const msg = pushErr instanceof Error ? pushErr.message : String(pushErr);
|
|
8650
|
+
await exec4("git", ["commit", "-m", commitMessage]);
|
|
8651
|
+
committed = true;
|
|
8652
|
+
} catch (commitErr) {
|
|
8653
|
+
const msg = commitErr instanceof Error ? commitErr.message : String(commitErr);
|
|
8536
8654
|
if (msg.includes("nothing to commit")) {
|
|
8537
8655
|
try {
|
|
8538
8656
|
const ahead = await exec4("git", [
|
|
@@ -8554,11 +8672,47 @@ ${extra.trim()}` : extra.trim();
|
|
|
8554
8672
|
pushSpinner.warn(chalk22.yellow("Nothing to commit or push."));
|
|
8555
8673
|
}
|
|
8556
8674
|
} else {
|
|
8675
|
+
let retryCount = 0;
|
|
8676
|
+
while (retryCount < maxRetries) {
|
|
8677
|
+
retryCount++;
|
|
8678
|
+
pushSpinner.text = `pre-commit hook failed, re-staging and retrying (${retryCount}/${maxRetries})...`;
|
|
8679
|
+
try {
|
|
8680
|
+
await exec4("git", ["add", "-A"]);
|
|
8681
|
+
await exec4("git", ["commit", "-m", commitMessage]);
|
|
8682
|
+
committed = true;
|
|
8683
|
+
break;
|
|
8684
|
+
} catch (retryErr) {
|
|
8685
|
+
const retryMsg = retryErr instanceof Error ? retryErr.message : String(retryErr);
|
|
8686
|
+
if (retryCount >= maxRetries) {
|
|
8687
|
+
try {
|
|
8688
|
+
trace.emit(Events.GIT_ERROR, { operation: "commit", error: retryMsg, retriesExhausted: maxRetries });
|
|
8689
|
+
} catch {
|
|
8690
|
+
}
|
|
8691
|
+
pushSpinner.fail(chalk22.red(`Commit failed after ${maxRetries} retries: ${retryMsg}`));
|
|
8692
|
+
}
|
|
8693
|
+
}
|
|
8694
|
+
}
|
|
8695
|
+
}
|
|
8696
|
+
}
|
|
8697
|
+
if (committed) {
|
|
8698
|
+
try {
|
|
8699
|
+
trace.emit(Events.GIT_COMMIT, { branch, messageSubject: commitMessage });
|
|
8700
|
+
} catch {
|
|
8701
|
+
}
|
|
8702
|
+
try {
|
|
8703
|
+
await exec4("git", ["push", "origin", branch]);
|
|
8557
8704
|
try {
|
|
8558
|
-
trace.emit(Events.
|
|
8705
|
+
trace.emit(Events.GIT_PUSH, { branch });
|
|
8559
8706
|
} catch {
|
|
8560
8707
|
}
|
|
8561
|
-
pushSpinner.
|
|
8708
|
+
pushSpinner.succeed(chalk22.green("Changes committed and pushed."));
|
|
8709
|
+
} catch (pushErr) {
|
|
8710
|
+
const pushMsg = pushErr instanceof Error ? pushErr.message : String(pushErr);
|
|
8711
|
+
try {
|
|
8712
|
+
trace.emit(Events.GIT_ERROR, { operation: "push", error: pushMsg });
|
|
8713
|
+
} catch {
|
|
8714
|
+
}
|
|
8715
|
+
pushSpinner.fail(chalk22.red(`Push failed: ${pushMsg}`));
|
|
8562
8716
|
}
|
|
8563
8717
|
}
|
|
8564
8718
|
console.log();
|
|
@@ -10517,7 +10671,13 @@ program.command("run").description("Run the reygent workflow from spec to review
|
|
|
10517
10671
|
}
|
|
10518
10672
|
}).action(runCommand);
|
|
10519
10673
|
program.command("review-work").description("Review current branch and post summary to PR/MR").option("--spec <source>", "Spec source with provider prefix (jira:KEY, linear:ID, markdown:FILE) \u2014 file paths auto-infer markdown:").option("--insecure", "Skip SSL certificate verification for API calls", false).action(reviewWorkCommand);
|
|
10520
|
-
program.command("review-comments").description("Fetch PR/MR review comments and address them with an agent").option("--insecure", "Skip SSL certificate verification for API calls", false).option("--auto-approve", "Auto-approve plan and execute without prompting", false).
|
|
10674
|
+
program.command("review-comments").description("Fetch PR/MR review comments and address them with an agent").option("--insecure", "Skip SSL certificate verification for API calls", false).option("--auto-approve", "Auto-approve plan and execute without prompting", false).option("--retry-commits <count>", "Max retries for pre-commit hook failures (default: 3)", (val) => {
|
|
10675
|
+
const num = parseInt(val, 10);
|
|
10676
|
+
if (isNaN(num) || num < 0 || num > 10) {
|
|
10677
|
+
throw new Error("--retry-commits must be between 0 and 10");
|
|
10678
|
+
}
|
|
10679
|
+
return num;
|
|
10680
|
+
}).action(reviewCommentsCommand);
|
|
10521
10681
|
program.command("config").description("Configure default provider, model, and per-agent overrides").action(configCommand);
|
|
10522
10682
|
registerTelemetryCommand(program);
|
|
10523
10683
|
registerAnalyzeCommand(program);
|