@hackerrank/astra-cli 0.1.23 → 0.1.24
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/README.md +1 -1
- package/package.json +1 -1
- package/src/prompts.js +24 -0
- package/src/repl.js +198 -0
package/README.md
CHANGED
|
@@ -80,7 +80,7 @@ astra -m claude-sonnet-5 # start chatting
|
|
|
80
80
|
astra -m claude-sonnet-5 -y # auto-run commands (no prompts)
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
In-REPL commands: `/help /exit /clear /history /tokens /yolo`.
|
|
83
|
+
In-REPL commands: `/help /plan <task> /exit /clear /history /tokens /yolo`.
|
|
84
84
|
|
|
85
85
|
### Autonomous task run
|
|
86
86
|
|
package/package.json
CHANGED
package/src/prompts.js
CHANGED
|
@@ -102,3 +102,27 @@ export function render(template, vars) {
|
|
|
102
102
|
key in vars && vars[key] != null ? String(vars[key]) : ""
|
|
103
103
|
);
|
|
104
104
|
}
|
|
105
|
+
|
|
106
|
+
export const PLAN_PROMPT_TEMPLATE = `Please explore the workspace and create a clear, actionable plan for this task:
|
|
107
|
+
|
|
108
|
+
Task:
|
|
109
|
+
{{task}}
|
|
110
|
+
|
|
111
|
+
Instructions for planning:
|
|
112
|
+
1. Run read-only commands (e.g. ls, find, grep, cat) to explore and understand the relevant files.
|
|
113
|
+
2. Present a structured plan with:
|
|
114
|
+
- **Objective & Scope**: Summary of what needs to be done.
|
|
115
|
+
- **Files to Modify / Create**: List of target files.
|
|
116
|
+
- **Self-Testing & Verification Strategy**: Exact test commands or verification scripts to write/run.
|
|
117
|
+
- **Self-Review Checklist**: Code quality, edge cases, and cleanliness checks.
|
|
118
|
+
3. Finish with a chat response presenting the complete plan so the user can review it before execution.`;
|
|
119
|
+
|
|
120
|
+
export const AUTONOMOUS_EXECUTION_PROMPT = `The plan has been approved. Execute the plan end-to-end now in autonomous mode without asking further questions.
|
|
121
|
+
|
|
122
|
+
Workflow requirements:
|
|
123
|
+
1. Implement the changes with clean, focused edits.
|
|
124
|
+
2. Self-verify by writing or running test scripts to prove the solution works.
|
|
125
|
+
3. Review your changes with \`git diff\` (or inspect modified files) to ensure no regressions, unintended files, or debug prints remain.
|
|
126
|
+
4. If any tests or checks fail, diagnose and fix the errors.
|
|
127
|
+
5. When completely finished, verified, and reviewed, run:
|
|
128
|
+
\`echo COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT\``;
|
package/src/repl.js
CHANGED
|
@@ -17,6 +17,7 @@ import readline from "node:readline";
|
|
|
17
17
|
import { fileURLToPath } from "node:url";
|
|
18
18
|
import { ask, saveAgentPrefs } from "./config.js";
|
|
19
19
|
import { AVAILABLE_MODELS, REASONING_LEVELS } from "./models.js";
|
|
20
|
+
import { PLAN_PROMPT_TEMPLATE, AUTONOMOUS_EXECUTION_PROMPT, render } from "./prompts.js";
|
|
20
21
|
|
|
21
22
|
const C = {
|
|
22
23
|
dim: (s) => `\x1b[2m${s}\x1b[0m`,
|
|
@@ -517,6 +518,7 @@ Interactive commands:
|
|
|
517
518
|
/help show this help
|
|
518
519
|
/exit, /quit leave (session is saved)
|
|
519
520
|
/clear start a fresh conversation (new context)
|
|
521
|
+
/plan <task> plan a task, review it, then execute autonomously with self-review
|
|
520
522
|
/history print the message history
|
|
521
523
|
/tokens show token usage so far
|
|
522
524
|
/yolo toggle auto-run of commands (no confirmation)
|
|
@@ -643,6 +645,25 @@ async function runStickyRepl(agent, model, yolo, intro = [], { runBench = null }
|
|
|
643
645
|
if (cmd === "history") { printHistory(agent, log); continue; }
|
|
644
646
|
if (cmd === "tokens") { log(tokensLine(model)); continue; }
|
|
645
647
|
if (cmd === "yolo") { yolo = !yolo; log(C.dim(`[astra] auto-run ${yolo ? "ON" : "OFF"}.`)); refresh(); continue; }
|
|
648
|
+
if (cmd === "plan") {
|
|
649
|
+
const taskText = line.slice(line.indexOf("plan") + 4).trim();
|
|
650
|
+
const prevYolo = yolo;
|
|
651
|
+
await handlePlanWorkflow({
|
|
652
|
+
taskText,
|
|
653
|
+
agent,
|
|
654
|
+
model,
|
|
655
|
+
log,
|
|
656
|
+
readInput: (promptText) => screen.readLine(promptText, { guardEnter: true, echo: true }),
|
|
657
|
+
startBusy: (label) => screen.startBusy(label),
|
|
658
|
+
stopBusy: () => screen.stopBusy(),
|
|
659
|
+
interrupt,
|
|
660
|
+
setYolo: (v) => { yolo = v; },
|
|
661
|
+
onProgress: () => refresh(),
|
|
662
|
+
});
|
|
663
|
+
yolo = prevYolo;
|
|
664
|
+
refresh();
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
646
667
|
log(C.dim(`[astra] unknown command: /${cmd} (try /help)`));
|
|
647
668
|
continue;
|
|
648
669
|
}
|
|
@@ -742,6 +763,25 @@ async function runPlainRepl(agent, model, yolo) {
|
|
|
742
763
|
if (cmd === "history") { printHistory(agent, log); continue; }
|
|
743
764
|
if (cmd === "tokens") { console.error(tokensLine(model)); continue; }
|
|
744
765
|
if (cmd === "yolo") { yolo = !yolo; console.error(C.dim(`[astra] auto-run ${yolo ? "ON" : "OFF"}.`)); continue; }
|
|
766
|
+
if (cmd === "plan") {
|
|
767
|
+
const taskText = line.slice(line.indexOf("plan") + 4).trim();
|
|
768
|
+
const prevYolo = yolo;
|
|
769
|
+
await handlePlanWorkflow({
|
|
770
|
+
taskText,
|
|
771
|
+
agent,
|
|
772
|
+
model,
|
|
773
|
+
log: (t) => console.error(t),
|
|
774
|
+
readInput: (promptText) => new Promise((res) => rl.question(promptText, res)),
|
|
775
|
+
startBusy: () => {},
|
|
776
|
+
stopBusy: () => {},
|
|
777
|
+
interrupt: { aborted: false, quit: false },
|
|
778
|
+
setYolo: (v) => { yolo = v; },
|
|
779
|
+
onProgress: () => printFooterInline(agent, model, yolo),
|
|
780
|
+
});
|
|
781
|
+
yolo = prevYolo;
|
|
782
|
+
printFooterInline(agent, model, yolo);
|
|
783
|
+
continue;
|
|
784
|
+
}
|
|
745
785
|
console.error(C.dim(`[astra] unknown command: /${cmd} (try /help)`));
|
|
746
786
|
continue;
|
|
747
787
|
}
|
|
@@ -859,3 +899,161 @@ function tokensLine(model) {
|
|
|
859
899
|
const dollars = usd < 0.01 ? "$" + usd.toFixed(5) : "$" + usd.toFixed(4);
|
|
860
900
|
return C.dim(`[astra] tokens: prompt=${pt} completion=${ct} total=${pt + ct} · cost=${dollars} (${src})`);
|
|
861
901
|
}
|
|
902
|
+
|
|
903
|
+
|
|
904
|
+
/**
|
|
905
|
+
* Run the plan-and-execute workflow:
|
|
906
|
+
* 1. Prompt model to explore workspace and draft a structured plan.
|
|
907
|
+
* 2. Pause and prompt user for approval / feedback.
|
|
908
|
+
* 3. On approval, execute autonomously to completion with auto-review and auto-fix.
|
|
909
|
+
*/
|
|
910
|
+
export async function handlePlanWorkflow({
|
|
911
|
+
taskText,
|
|
912
|
+
agent,
|
|
913
|
+
model,
|
|
914
|
+
log,
|
|
915
|
+
readInput,
|
|
916
|
+
startBusy,
|
|
917
|
+
stopBusy,
|
|
918
|
+
interrupt,
|
|
919
|
+
setYolo = () => {},
|
|
920
|
+
onProgress = () => {},
|
|
921
|
+
}) {
|
|
922
|
+
let task = taskText;
|
|
923
|
+
if (!task) {
|
|
924
|
+
task = (await readInput(C.yellow("Enter task to plan: "))).trim();
|
|
925
|
+
}
|
|
926
|
+
if (!task) {
|
|
927
|
+
log(C.dim("[astra] plan cancelled (empty task)."));
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
log(C.cyan(`[astra] exploring workspace and drafting plan for: "${task}"...`));
|
|
932
|
+
agent.addUserMessage(render(PLAN_PROMPT_TEMPLATE, { task }));
|
|
933
|
+
|
|
934
|
+
model._abort = new AbortController();
|
|
935
|
+
model.signal = model._abort.signal;
|
|
936
|
+
startBusy("planning");
|
|
937
|
+
try {
|
|
938
|
+
await driveUntilChat(agent, log, interrupt);
|
|
939
|
+
} finally {
|
|
940
|
+
stopBusy();
|
|
941
|
+
model.signal = null;
|
|
942
|
+
model._abort = null;
|
|
943
|
+
}
|
|
944
|
+
if (interrupt?.quit || interrupt?.aborted) return;
|
|
945
|
+
|
|
946
|
+
// Review gate loop
|
|
947
|
+
let approved = false;
|
|
948
|
+
while (!approved) {
|
|
949
|
+
const ans = (await readInput(
|
|
950
|
+
C.yellow("\nApprove plan and begin autonomous execution? [Y/n/feedback] ")
|
|
951
|
+
)).trim();
|
|
952
|
+
const lower = ans.toLowerCase();
|
|
953
|
+
|
|
954
|
+
if (lower === "n" || lower === "no" || lower === "cancel" || lower === "abort") {
|
|
955
|
+
log(C.dim("[astra] plan cancelled. Returning to interactive mode."));
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
if (ans === "" || lower === "y" || lower === "yes") {
|
|
959
|
+
approved = true;
|
|
960
|
+
break;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// Feedback provided: refine plan
|
|
964
|
+
log(C.cyan(`[astra] updating plan with feedback: "${ans}"...`));
|
|
965
|
+
agent.addUserMessage(
|
|
966
|
+
`Here is feedback on the plan: "${ans}". Please refine the plan accordingly and present the updated plan.`
|
|
967
|
+
);
|
|
968
|
+
model._abort = new AbortController();
|
|
969
|
+
model.signal = model._abort.signal;
|
|
970
|
+
startBusy("planning");
|
|
971
|
+
try {
|
|
972
|
+
await driveUntilChat(agent, log, interrupt);
|
|
973
|
+
} finally {
|
|
974
|
+
stopBusy();
|
|
975
|
+
model.signal = null;
|
|
976
|
+
model._abort = null;
|
|
977
|
+
}
|
|
978
|
+
if (interrupt?.quit || interrupt?.aborted) return;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// Execute autonomously
|
|
982
|
+
log(C.green("\n[astra] plan approved! Starting unattended autonomous execution with auto-review & auto-fix..."));
|
|
983
|
+
const prevMode = agent.mode;
|
|
984
|
+
setYolo(true);
|
|
985
|
+
agent.mode = "autonomous";
|
|
986
|
+
agent.addUserMessage(AUTONOMOUS_EXECUTION_PROMPT);
|
|
987
|
+
|
|
988
|
+
model._abort = new AbortController();
|
|
989
|
+
model.signal = model._abort.signal;
|
|
990
|
+
startBusy("executing plan");
|
|
991
|
+
let status = "Completed";
|
|
992
|
+
try {
|
|
993
|
+
status = await driveAutonomous(agent, log, interrupt);
|
|
994
|
+
} catch (err) {
|
|
995
|
+
log(C.red(`[astra] error during autonomous execution: ${err?.message || err}`));
|
|
996
|
+
status = "Error";
|
|
997
|
+
} finally {
|
|
998
|
+
stopBusy();
|
|
999
|
+
model.signal = null;
|
|
1000
|
+
model._abort = null;
|
|
1001
|
+
agent.mode = prevMode;
|
|
1002
|
+
agent.exitStatus = null;
|
|
1003
|
+
onProgress();
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
log(C.green(`\n[astra] plan execution finished (${status}). You are now back in interactive mode.`));
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* Drive turns autonomously until the model submits with the completion sentinel.
|
|
1011
|
+
*/
|
|
1012
|
+
export async function driveAutonomous(agent, log, interrupt = null) {
|
|
1013
|
+
while (true) {
|
|
1014
|
+
if (interrupt && interrupt.aborted) {
|
|
1015
|
+
log(C.yellow("[astra] autonomous execution interrupted — returning to prompt."));
|
|
1016
|
+
return "Interrupted";
|
|
1017
|
+
}
|
|
1018
|
+
let turn;
|
|
1019
|
+
try {
|
|
1020
|
+
turn = await agent.runTurn();
|
|
1021
|
+
} catch (err) {
|
|
1022
|
+
if (err && (err.name === "GatewayError" || err.name === "ContextWindowError")) {
|
|
1023
|
+
log(C.red(`[astra] ${err.message}`));
|
|
1024
|
+
return err.name;
|
|
1025
|
+
}
|
|
1026
|
+
throw err;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
if (interrupt && interrupt.aborted) {
|
|
1030
|
+
log(C.yellow("[astra] autonomous execution interrupted — returning to prompt."));
|
|
1031
|
+
return "Interrupted";
|
|
1032
|
+
}
|
|
1033
|
+
if (turn.kind === "exit") {
|
|
1034
|
+
log(C.dim(`[astra] autonomous run ended: ${turn.exit_status}`));
|
|
1035
|
+
return turn.exit_status;
|
|
1036
|
+
}
|
|
1037
|
+
if (turn.kind === "command") {
|
|
1038
|
+
const rc = turn.returncode;
|
|
1039
|
+
const tag = rc === 0 ? C.dim(`[rc ${rc}]`) : C.red(`[rc ${rc}]`);
|
|
1040
|
+
log(tag + "\n" + indent(turn.output));
|
|
1041
|
+
continue;
|
|
1042
|
+
}
|
|
1043
|
+
if (turn.kind === "declined") {
|
|
1044
|
+
log(C.dim("[astra] command skipped."));
|
|
1045
|
+
continue;
|
|
1046
|
+
}
|
|
1047
|
+
if (turn.kind === "format_error") {
|
|
1048
|
+
log(C.dim(`[astra] (reprompting: ${turn.error})`));
|
|
1049
|
+
continue;
|
|
1050
|
+
}
|
|
1051
|
+
if (turn.kind === "chat") {
|
|
1052
|
+
log("\n" + C.cyan("astra › ") + turn.content.trim());
|
|
1053
|
+
agent.addUserMessage(
|
|
1054
|
+
"Continue executing the approved plan in autonomous mode. When all changes are implemented, tested, and self-reviewed, run:\necho COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT"
|
|
1055
|
+
);
|
|
1056
|
+
continue;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|