Skip to content

Commit 936cb2a

Browse files
Merge pull request #4 from Chintanpatel24/fix-installation-and-symlink
fix: resolve installation symlinks, support skip setup, and add relea…
2 parents a76abf4 + 9487723 commit 936cb2a

2 files changed

Lines changed: 90 additions & 30 deletions

File tree

src/config/load.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -88,27 +88,23 @@ export function saveEnv(vars: Record<string, string>): void {
8888
}
8989

9090
export function isConfigured(): boolean {
91-
// process env first (no home required)
91+
// Always load .env first to populate process.env!
92+
loadDotEnv();
93+
9294
const key =
9395
process.env.NVIDIA_API_KEY ||
9496
process.env.NIM_API_KEY ||
9597
process.env.OPENAI_API_KEY ||
9698
process.env.ANTHROPIC_API_KEY ||
9799
process.env.ARROWCODE_API_KEY;
98100
if (key) return true;
101+
99102
if (process.env.ARROWCODE_PROVIDER === "ollama") return true;
103+
100104
for (const p of ["ORCH", "FE", "BE", "QA"]) {
101105
if (process.env[`ARROW_${p}_API_KEY`]) return true;
102106
}
103-
// only then peek at user home files if they already exist
104-
loadDotEnv();
105-
if (existsSync(ENV_PATH)) {
106-
const again =
107-
process.env.NVIDIA_API_KEY ||
108-
process.env.OPENAI_API_KEY ||
109-
process.env.ARROWCODE_API_KEY;
110-
if (again) return true;
111-
}
107+
112108
if (existsSync(CONFIG_PATH)) {
113109
try {
114110
const c = parseYaml(readFileSync(CONFIG_PATH, "utf8")) as Partial<ArrowConfig>;

src/index.ts

Lines changed: 84 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -175,14 +175,14 @@ async function main() {
175175
const prompt = positionals.join(" ").trim();
176176
const harness = new Harness(cfg);
177177

178-
if (values["no-tui"]) {
179-
if (!prompt) {
180-
console.error("Headless mode requires a prompt.");
181-
process.exit(1);
182-
}
178+
// Fallback to non-TUI mode if terminal is not a TTY or if requested
179+
const isTTY = !!(process.stdout.isTTY && process.stdin.isTTY);
180+
const useNoTui = !!(values["no-tui"] || !isTTY);
181+
182+
if (useNoTui || prompt !== "") {
183183
harness.events.on((e) => {
184184
if (e.type === "phase")
185-
console.log(`[phase] ${e.phase} ${e.detail || ""}`);
185+
console.log(`\n[phase] ${e.phase.toUpperCase()} ${e.detail || ""}`);
186186
else if (e.type === "agent_log" && e.line.kind !== "say")
187187
console.log(`[${e.agent}] ${e.line.kind}: ${e.line.text}`);
188188
else if (e.type === "agent_log" && e.line.kind === "say")
@@ -192,30 +192,94 @@ async function main() {
192192
`[bus] ${e.message.from} -> ${e.message.to} [${e.message.kind}] ${e.message.title}`,
193193
);
194194
else if (e.type === "system") console.log(`[system] ${e.text}`);
195-
else if (e.type === "plan")
196-
console.log(`[plan] ${e.plan.title}\n${e.plan.summary}`);
197-
else if (e.type === "questions")
198-
console.log(
199-
`[questions]\n${e.questions.map((q, i) => `${i + 1}. ${q.question}`).join("\n")}`,
200-
);
201-
else if (e.type === "final") console.log("\n=== READY ===\n" + e.text);
195+
else if (e.type === "plan") {
196+
console.log(`\n=================== PLAN ===================`);
197+
console.log(`Title: ${e.plan.title}`);
198+
console.log(`Summary: ${e.plan.summary}`);
199+
console.log(`============================================\n`);
200+
} else if (e.type === "questions") {
201+
console.log(`\n================= QUESTIONS =================`);
202+
console.log(e.questions.map((q, i) => `${i + 1}. ${q.question}`).join("\n"));
203+
console.log(`=============================================\n`);
204+
} else if (e.type === "final") console.log("\n=== READY ===\n" + e.text);
202205
else if (e.type === "swarm")
203206
console.log(`[swarm] ${e.action} ${e.workerId} active=${e.active}`);
204207
else if (e.type === "approval_request") {
205208
if (cfg.autoApprove) harness.resolveApproval(e.id, true);
206209
else {
207-
console.log(`[approval] denied (use -y): ${e.agent} ${e.tool}`);
210+
console.log(`[approval] denied (use -y/--yolo or autoApprove): ${e.agent} ${e.tool}`);
208211
harness.resolveApproval(e.id, false);
209212
}
210213
}
211214
});
212-
await harness.startPlan(prompt);
213-
if (harness.plan) {
214-
console.log("\n[headless] auto /confirm");
215-
await harness.confirmAndExecute();
215+
216+
if (prompt) {
217+
await harness.startPlan(prompt);
218+
if (harness.plan) {
219+
console.log("\n[headless] auto /confirm");
220+
await harness.confirmAndExecute();
221+
}
222+
console.log("\n[metrics]", harness.metricsLine());
223+
process.exit(0);
224+
} else {
225+
// CLI Interactive Session (like Claude Code)
226+
const readline = await import("node:readline");
227+
const rl = readline.createInterface({
228+
input: process.stdin,
229+
output: process.stdout,
230+
});
231+
232+
console.log("\n================================================================");
233+
console.log(" /\\");
234+
console.log(" / \\ ARROWCODE Interactive CLI");
235+
console.log(" / /\\ \\ swarm coding harness");
236+
console.log(" /______\\ ORCH · FE · BE · QA");
237+
console.log("================================================================\n");
238+
console.log(" Welcome! You are in the interactive CLI mode (similar to Claude Code).");
239+
console.log(" - Type standard prompt to chat with the agents.");
240+
console.log(" - Use /plan <goal> to propose a new plan.");
241+
console.log(" - Use /confirm to approve and execute the current plan.");
242+
console.log(" - Use /accept to finish and save changes.");
243+
console.log(" - Use /help to see all available commands.");
244+
console.log(" - Type /exit or /quit to quit.\n");
245+
246+
const promptUser = () => {
247+
rl.question("arrowcode> ", async (answer) => {
248+
const line = answer.trim();
249+
if (!line) {
250+
promptUser();
251+
return;
252+
}
253+
if (line === "/exit" || line === "/quit") {
254+
rl.close();
255+
process.exit(0);
256+
}
257+
258+
if (line.startsWith("/")) {
259+
// Dispatch command
260+
const [cmd, ..._rest] = line.slice(1).split(/\s+/);
261+
const { dispatchCommand } = await import("./commands/registry");
262+
const res = await dispatchCommand(line, {
263+
harness,
264+
setYolo: () => {},
265+
getYolo: () => false,
266+
});
267+
if (res.type === "exit") {
268+
rl.close();
269+
process.exit(0);
270+
}
271+
if (res && "message" in res && res.message) {
272+
console.log(`[system] ${res.message}`);
273+
}
274+
} else {
275+
await harness.chat(line);
276+
}
277+
promptUser();
278+
});
279+
};
280+
promptUser();
281+
return;
216282
}
217-
console.log("\n[metrics]", harness.metricsLine());
218-
process.exit(0);
219283
}
220284

221285
const { waitUntilExit } = render(

0 commit comments

Comments
 (0)