autohand-cli 0.7.4 → 0.7.6

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/index.js CHANGED
@@ -2,9 +2,10 @@
2
2
  import {
3
3
  PermissionManager
4
4
  } from "./chunk-YMP7AGNT.js";
5
+ import "./chunk-27ISZOFA.js";
5
6
  import {
6
7
  SessionManager
7
- } from "./chunk-2FLBGPE3.js";
8
+ } from "./chunk-VO3JKFUH.js";
8
9
  import {
9
10
  MemoryManager
10
11
  } from "./chunk-4L5WYXHN.js";
@@ -28,7 +29,9 @@ import {
28
29
  import {
29
30
  metadata as metadata26
30
31
  } from "./chunk-CXZEPTRI.js";
31
- import "./chunk-27ISZOFA.js";
32
+ import {
33
+ metadata as metadata27
34
+ } from "./chunk-723DZKBU.js";
32
35
  import {
33
36
  metadata as metadata15
34
37
  } from "./chunk-2NUX2RAI.js";
@@ -39,9 +42,11 @@ import {
39
42
  metadata as metadata17
40
43
  } from "./chunk-OC5YDNFC.js";
41
44
  import {
42
- metadata as metadata18,
45
+ metadata as metadata18
46
+ } from "./chunk-4KZCGK7D.js";
47
+ import {
43
48
  package_default
44
- } from "./chunk-546JQ3ND.js";
49
+ } from "./chunk-5MCDN53U.js";
45
50
  import {
46
51
  metadata as metadata19
47
52
  } from "./chunk-2FSQPRPJ.js";
@@ -93,12 +98,6 @@ import {
93
98
  applyFormatter,
94
99
  metadata as metadata14
95
100
  } from "./chunk-XDVG3NM4.js";
96
- import {
97
- AUTOHAND_FILES,
98
- AUTOHAND_HOME,
99
- AUTOHAND_PATHS,
100
- PROJECT_DIR_NAME
101
- } from "./chunk-FUEL6BK7.js";
102
101
  import {
103
102
  metadata
104
103
  } from "./chunk-KZ7VMQTC.js";
@@ -114,7 +113,13 @@ import {
114
113
  } from "./chunk-Z3Q2AFDS.js";
115
114
  import {
116
115
  metadata as metadata5
117
- } from "./chunk-55DQY6B5.js";
116
+ } from "./chunk-R5KNHJ27.js";
117
+ import {
118
+ AUTOHAND_FILES,
119
+ AUTOHAND_HOME,
120
+ AUTOHAND_PATHS,
121
+ PROJECT_DIR_NAME
122
+ } from "./chunk-FUEL6BK7.js";
118
123
  import {
119
124
  metadata as metadata6
120
125
  } from "./chunk-QJ53OSGF.js";
@@ -122,8 +127,7 @@ import {
122
127
  // src/index.ts
123
128
  import "dotenv/config";
124
129
  import { Command } from "commander";
125
- import chalk16 from "chalk";
126
- import enquirer4 from "enquirer";
130
+ import chalk17 from "chalk";
127
131
  import { execSync as execSync3 } from "child_process";
128
132
 
129
133
  // src/startup/checks.ts
@@ -228,21 +232,67 @@ async function checkWorkspaceWritable(workspaceRoot) {
228
232
  };
229
233
  }
230
234
  }
231
- function checkGitRepo(workspaceRoot) {
235
+ function isEmptyDirectory(dir) {
236
+ try {
237
+ const entries = fs.readdirSync(dir);
238
+ const significantEntries = entries.filter((e) => !e.startsWith(".") || e === ".git");
239
+ return significantEntries.length === 0;
240
+ } catch {
241
+ return false;
242
+ }
243
+ }
244
+ function getGitBranch(workspaceRoot) {
232
245
  try {
233
246
  const result = spawnSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
234
247
  cwd: workspaceRoot,
235
248
  encoding: "utf8",
236
249
  timeout: 5e3
237
250
  });
238
- if (result.status === 0) {
239
- return {
240
- isGitRepo: true,
241
- branch: result.stdout.trim()
242
- };
251
+ if (result.status === 0 && result.stdout.trim()) {
252
+ return result.stdout.trim();
253
+ }
254
+ } catch {
255
+ }
256
+ try {
257
+ const result = spawnSync("git", ["symbolic-ref", "--short", "HEAD"], {
258
+ cwd: workspaceRoot,
259
+ encoding: "utf8",
260
+ timeout: 5e3
261
+ });
262
+ if (result.status === 0 && result.stdout.trim()) {
263
+ return result.stdout.trim();
243
264
  }
244
265
  } catch {
245
266
  }
267
+ return void 0;
268
+ }
269
+ function checkGitRepo(workspaceRoot) {
270
+ const gitDirExists = fs.existsSync(`${workspaceRoot}/.git`);
271
+ if (gitDirExists) {
272
+ const branch = getGitBranch(workspaceRoot);
273
+ return {
274
+ isGitRepo: true,
275
+ branch
276
+ };
277
+ }
278
+ if (isEmptyDirectory(workspaceRoot)) {
279
+ try {
280
+ const initResult = spawnSync("git", ["init"], {
281
+ cwd: workspaceRoot,
282
+ encoding: "utf8",
283
+ timeout: 5e3
284
+ });
285
+ if (initResult.status === 0) {
286
+ const branch = getGitBranch(workspaceRoot) || "main";
287
+ return {
288
+ isGitRepo: true,
289
+ branch,
290
+ initialized: true
291
+ };
292
+ }
293
+ } catch {
294
+ }
295
+ }
246
296
  return { isGitRepo: false };
247
297
  }
248
298
  function getPackageManagerHint() {
@@ -285,6 +335,7 @@ async function runStartupChecks(workspaceRoot) {
285
335
  writable: workspaceCheck.writable,
286
336
  isGitRepo: gitCheck.isGitRepo,
287
337
  branch: gitCheck.branch,
338
+ initialized: gitCheck.initialized,
288
339
  error: workspaceCheck.error
289
340
  },
290
341
  allRequiredMet,
@@ -294,6 +345,10 @@ async function runStartupChecks(workspaceRoot) {
294
345
  function printStartupCheckResults(results, verbose = false) {
295
346
  const missingRequired = results.tools.filter((t) => t.required && !t.installed);
296
347
  const missingOptional = results.tools.filter((t) => !t.required && !t.installed);
348
+ if (results.workspace.initialized) {
349
+ console.log(chalk.green("\u2713 Initialized git repository"));
350
+ console.log();
351
+ }
297
352
  if (missingRequired.length === 0 && !verbose) {
298
353
  return;
299
354
  }
@@ -1778,6 +1833,26 @@ var OpenRouterClient = class {
1778
1833
  payload.tool_choice = request.toolChoice;
1779
1834
  }
1780
1835
  }
1836
+ const model = (request.model ?? this.defaultModel).toLowerCase();
1837
+ if (request.thinkingLevel && request.thinkingLevel !== "normal") {
1838
+ if (model.includes("o1") || model.includes("o3")) {
1839
+ if (request.thinkingLevel === "extended") {
1840
+ payload.reasoning_effort = "high";
1841
+ } else if (request.thinkingLevel === "none") {
1842
+ payload.reasoning_effort = "low";
1843
+ }
1844
+ }
1845
+ if (model.includes("claude") && request.thinkingLevel === "extended") {
1846
+ payload.provider = {
1847
+ anthropic: {
1848
+ thinking: {
1849
+ type: "enabled",
1850
+ budget_tokens: 1e4
1851
+ }
1852
+ }
1853
+ };
1854
+ }
1855
+ }
1781
1856
  const headers = {
1782
1857
  "Content-Type": "application/json",
1783
1858
  "HTTP-Referer": "https://github.com/autohandai/code-cli",
@@ -2197,14 +2272,13 @@ var ProviderFactory = class {
2197
2272
  };
2198
2273
 
2199
2274
  // src/core/agent.ts
2200
- import chalk14 from "chalk";
2275
+ import chalk15 from "chalk";
2201
2276
  import fs19 from "fs-extra";
2202
2277
  import path18 from "path";
2203
2278
  import { randomUUID } from "crypto";
2204
2279
  import { spawnSync as spawnSync5 } from "child_process";
2205
2280
  import ora from "ora";
2206
- import enquirer3 from "enquirer";
2207
- import readline4 from "readline";
2281
+ import enquirer4 from "enquirer";
2208
2282
 
2209
2283
  // src/ui/inputPrompt.ts
2210
2284
  import chalk5 from "chalk";
@@ -2296,7 +2370,7 @@ var MentionPreview = class {
2296
2370
  this.disposed = false;
2297
2371
  this.lastSuggestions = [];
2298
2372
  const input = rl.input;
2299
- readline.emitKeypressEvents(input, rl);
2373
+ safeEmitKeypressEvents(input);
2300
2374
  this.statusLine = statusLine ? chalk3.gray(statusLine) : void 0;
2301
2375
  this.keypressHandler = this.handleKeypress.bind(this);
2302
2376
  input.prependListener("keypress", this.keypressHandler);
@@ -2664,6 +2738,13 @@ function drawInputBox(prompt, width) {
2664
2738
  // src/ui/inputPrompt.ts
2665
2739
  var NEWLINE_MARKER = " \u21B5 ";
2666
2740
  var MAX_NEWLINES = 2;
2741
+ var instrumentedStreams = /* @__PURE__ */ new WeakSet();
2742
+ function safeEmitKeypressEvents(stream) {
2743
+ if (!instrumentedStreams.has(stream)) {
2744
+ readline2.emitKeypressEvents(stream);
2745
+ instrumentedStreams.add(stream);
2746
+ }
2747
+ }
2667
2748
  function countNewlineMarkers(text) {
2668
2749
  return (text.match(new RegExp(NEWLINE_MARKER, "g")) || []).length;
2669
2750
  }
@@ -2677,6 +2758,7 @@ async function readInstruction(files, slashCommands, statusLine, io = {}, onImag
2677
2758
  }, 1e4);
2678
2759
  try {
2679
2760
  while (true) {
2761
+ await new Promise((resolve) => process.nextTick(resolve));
2680
2762
  const result = await promptOnce({
2681
2763
  files,
2682
2764
  slashCommands,
@@ -2695,8 +2777,11 @@ async function readInstruction(files, slashCommands, statusLine, io = {}, onImag
2695
2777
  }
2696
2778
  }
2697
2779
  function createReadline(stdInput, stdOutput) {
2698
- if (stdInput.isPaused && stdInput.isPaused()) {
2780
+ stdOutput.write("\r");
2781
+ safeEmitKeypressEvents(stdInput);
2782
+ try {
2699
2783
  stdInput.resume();
2784
+ } catch {
2700
2785
  }
2701
2786
  const rl = readline2.createInterface({
2702
2787
  input: stdInput,
@@ -2893,6 +2978,9 @@ ${chalk5.gray("Press Ctrl+C again to exit.")}
2893
2978
  input.on("keypress", handleKeypress);
2894
2979
  rl.setPrompt(`${chalk5.gray("\u203A")} `);
2895
2980
  rl.prompt(true);
2981
+ process.nextTick(() => {
2982
+ stdOutput.write(`\r${chalk5.gray("\u203A")} `);
2983
+ });
2896
2984
  rl.on("line", (value) => {
2897
2985
  let finalValue = convertNewlineMarkersToNewlines(value).trim();
2898
2986
  finalValue = processImagesInText(finalValue);
@@ -3964,7 +4052,8 @@ var SLASH_COMMANDS = [
3964
4052
  installMetadata,
3965
4053
  metadata24,
3966
4054
  metadata25,
3967
- metadata26
4055
+ metadata26,
4056
+ metadata27
3968
4057
  ];
3969
4058
 
3970
4059
  // src/core/conversationManager.ts
@@ -8534,7 +8623,7 @@ var SlashCommandHandler = class {
8534
8623
  return feedback(this.ctx);
8535
8624
  }
8536
8625
  case "/resume": {
8537
- const { resume } = await import("./resume-2NERFSTD.js");
8626
+ const { resume } = await import("./resume-CWYAK6XR.js");
8538
8627
  return resume({ sessionManager: this.ctx.sessionManager, args });
8539
8628
  }
8540
8629
  case "/sessions": {
@@ -8592,8 +8681,21 @@ var SlashCommandHandler = class {
8592
8681
  });
8593
8682
  return null;
8594
8683
  }
8684
+ case "/share": {
8685
+ const { execute } = await import("./share-3PSV53CQ.js");
8686
+ await execute(args.join(" "), {
8687
+ sessionManager: this.ctx.sessionManager,
8688
+ currentSession: this.ctx.currentSession,
8689
+ model: this.ctx.model,
8690
+ provider: this.ctx.provider,
8691
+ config: this.ctx.config,
8692
+ getTotalTokensUsed: this.ctx.getTotalTokensUsed,
8693
+ workspaceRoot: this.ctx.workspaceRoot
8694
+ });
8695
+ return null;
8696
+ }
8595
8697
  case "/status": {
8596
- const { status } = await import("./status-UT4UQN3H.js");
8698
+ const { status } = await import("./status-GPAZ67ZZ.js");
8597
8699
  return status(this.ctx);
8598
8700
  }
8599
8701
  case "/login": {
@@ -8885,184 +8987,1164 @@ var ProjectManager = class {
8885
8987
  }
8886
8988
  };
8887
8989
 
8888
- // src/core/agents/AgentDelegator.ts
8889
- import chalk10 from "chalk";
8890
-
8891
- // src/core/agents/SubAgent.ts
8892
- import chalk9 from "chalk";
8893
- var DELEGATION_TOOL_DEFINITIONS = [
8894
- {
8895
- name: "delegate_task",
8896
- description: "Delegate a task to another specialized sub-agent",
8897
- parameters: {
8898
- type: "object",
8899
- properties: {
8900
- agent_name: { type: "string", description: "Name of the agent to delegate to" },
8901
- task: { type: "string", description: "Task description for the sub-agent" }
8902
- },
8903
- required: ["agent_name", "task"]
8990
+ // src/onboarding/projectAnalyzer.ts
8991
+ import { pathExists, readJson, readFile } from "fs-extra";
8992
+ import { join as join2 } from "path";
8993
+ var ProjectAnalyzer = class {
8994
+ constructor(workspaceRoot) {
8995
+ this.workspaceRoot = workspaceRoot;
8996
+ }
8997
+ /**
8998
+ * Analyze the workspace and return project information
8999
+ */
9000
+ async analyze() {
9001
+ const info = {};
9002
+ await this.analyzeNodeProject(info);
9003
+ await this.analyzeRustProject(info);
9004
+ await this.analyzeGoProject(info);
9005
+ await this.analyzePythonProject(info);
9006
+ return info;
9007
+ }
9008
+ /**
9009
+ * Analyze Node.js/JavaScript/TypeScript project
9010
+ */
9011
+ async analyzeNodeProject(info) {
9012
+ const pkgPath = join2(this.workspaceRoot, "package.json");
9013
+ if (!await pathExists(pkgPath)) {
9014
+ return;
8904
9015
  }
8905
- },
8906
- {
8907
- name: "delegate_parallel",
8908
- description: "Run multiple sub-agents in parallel (max 5)",
8909
- parameters: {
8910
- type: "object",
8911
- properties: {
8912
- tasks: { type: "array", description: "Array of {agent_name, task} objects" }
8913
- },
8914
- required: ["tasks"]
9016
+ try {
9017
+ const pkg = await readJson(pkgPath);
9018
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
9019
+ if (deps.typescript || pkg.devDependencies?.typescript) {
9020
+ info.language = "TypeScript";
9021
+ } else {
9022
+ info.language = "JavaScript";
9023
+ }
9024
+ info.framework = this.detectNodeFramework(deps);
9025
+ info.packageManager = await this.detectNodePackageManager();
9026
+ info.testFramework = this.detectNodeTestFramework(deps);
9027
+ info.linter = this.detectNodeLinter(deps);
9028
+ info.formatter = this.detectNodeFormatter(deps);
9029
+ info.buildTool = this.detectNodeBuildTool(deps);
9030
+ } catch {
8915
9031
  }
8916
9032
  }
8917
- ];
8918
- var SubAgent = class {
8919
- constructor(config, llm, actionExecutor, options) {
8920
- this.config = config;
8921
- this.llm = llm;
8922
- this.actionExecutor = actionExecutor;
8923
- this.delegator = null;
8924
- this.name = config.name;
8925
- this.options = options;
8926
- const canDelegate = options.depth < options.maxDepth;
8927
- const allowedTools = new Set(config.tools);
8928
- let definitions = DEFAULT_TOOL_DEFINITIONS.filter((def) => allowedTools.has(def.name));
8929
- if (canDelegate) {
8930
- definitions = [...definitions, ...DELEGATION_TOOL_DEFINITIONS];
9033
+ /**
9034
+ * Detect Node.js framework from dependencies
9035
+ */
9036
+ detectNodeFramework(deps) {
9037
+ if (deps.next) return "Next.js";
9038
+ if (deps.nuxt) return "Nuxt";
9039
+ if (deps["@remix-run/node"] || deps["@remix-run/react"]) return "Remix";
9040
+ if (deps["@angular/core"]) return "Angular";
9041
+ if (deps.svelte) return "Svelte";
9042
+ if (deps.vue) return "Vue";
9043
+ if (deps.react) return "React";
9044
+ if (deps.solid) return "Solid";
9045
+ if (deps["@nestjs/core"]) return "NestJS";
9046
+ if (deps.fastify) return "Fastify";
9047
+ if (deps.hono) return "Hono";
9048
+ if (deps.koa) return "Koa";
9049
+ if (deps.express) return "Express";
9050
+ return void 0;
9051
+ }
9052
+ /**
9053
+ * Detect Node.js package manager from lockfiles
9054
+ */
9055
+ async detectNodePackageManager() {
9056
+ if (await pathExists(join2(this.workspaceRoot, "bun.lockb")) || await pathExists(join2(this.workspaceRoot, "bun.lock"))) {
9057
+ return "bun";
8931
9058
  }
8932
- const toolFilter = new ToolFilter(options.clientContext);
8933
- definitions = toolFilter.filterDefinitions(definitions);
8934
- if (canDelegate) {
8935
- this.delegator = new AgentDelegator(llm, actionExecutor, {
8936
- clientContext: options.clientContext,
8937
- currentDepth: options.depth,
8938
- maxDepth: options.maxDepth
8939
- });
9059
+ if (await pathExists(join2(this.workspaceRoot, "pnpm-lock.yaml"))) {
9060
+ return "pnpm";
8940
9061
  }
8941
- this.toolManager = new ToolManager({
8942
- executor: async (action, context) => {
8943
- if (action.type === "delegate_task" && this.delegator) {
8944
- return this.delegator.delegateTask(
8945
- action.agent_name,
8946
- action.task
8947
- );
8948
- }
8949
- if (action.type === "delegate_parallel" && this.delegator) {
8950
- return this.delegator.delegateParallel(action.tasks);
8951
- }
8952
- return this.actionExecutor.execute(action, context);
8953
- },
8954
- confirmApproval: async () => true,
8955
- // Sub-agents auto-approve (inherit from main agent in future)
8956
- definitions,
8957
- clientContext: options.clientContext
8958
- });
8959
- const enhancedSystemPrompt = this.buildSystemPrompt(config.systemPrompt, definitions);
8960
- this.conversation = new ConversationManager();
8961
- this.conversation.reset(enhancedSystemPrompt);
9062
+ if (await pathExists(join2(this.workspaceRoot, "yarn.lock"))) {
9063
+ return "yarn";
9064
+ }
9065
+ if (await pathExists(join2(this.workspaceRoot, "package-lock.json"))) {
9066
+ return "npm";
9067
+ }
9068
+ return "npm";
8962
9069
  }
8963
9070
  /**
8964
- * Build system prompt with tool signatures for the LLM
9071
+ * Detect Node.js test framework
8965
9072
  */
8966
- buildSystemPrompt(basePrompt, tools) {
8967
- const toolSignatures = tools.map((def) => this.formatToolSignature(def)).join("\n");
8968
- return [
8969
- basePrompt,
8970
- "",
8971
- "## Available Tools",
8972
- "You have access to the following tools. Use them when needed:",
8973
- "",
8974
- toolSignatures,
8975
- "",
8976
- "## Response Format",
8977
- "Always respond with structured JSON:",
8978
- "```json",
8979
- "{",
8980
- ' "thought": "Your reasoning about what to do next",',
8981
- ' "toolCalls": [{"tool": "tool_name", "args": {...}}],',
8982
- ' "finalResponse": "Your final answer when done (omit toolCalls if providing this)"',
8983
- "}",
8984
- "```",
8985
- "",
8986
- `Depth: ${this.options.depth}/${this.options.maxDepth} ${this.delegator ? "(can delegate further)" : "(max depth reached)"}`
8987
- ].join("\n");
9073
+ detectNodeTestFramework(deps) {
9074
+ if (deps.vitest) return "Vitest";
9075
+ if (deps.jest) return "Jest";
9076
+ if (deps.mocha) return "Mocha";
9077
+ if (deps.ava) return "AVA";
9078
+ if (deps["@playwright/test"]) return "Playwright";
9079
+ if (deps.cypress) return "Cypress";
9080
+ if (deps.puppeteer) return "Puppeteer";
9081
+ return void 0;
8988
9082
  }
8989
9083
  /**
8990
- * Format a tool definition as a signature string
9084
+ * Detect Node.js linter
8991
9085
  */
8992
- formatToolSignature(def) {
8993
- const params = def.parameters?.properties ? Object.entries(def.parameters.properties).map(([name, prop]) => {
8994
- const required = def.parameters?.required?.includes(name) ? "" : "?";
8995
- return `${name}${required}: ${prop.type}`;
8996
- }).join(", ") : "";
8997
- return `- ${def.name}(${params}): ${def.description}`;
8998
- }
8999
- async run(task) {
9000
- console.log(chalk9.cyan(`
9001
- \u{1F916} Sub-agent '${this.name}' starting task... (depth ${this.options.depth}/${this.options.maxDepth})`));
9002
- this.conversation.addMessage({ role: "user", content: task });
9003
- const tools = this.toolManager.toFunctionDefinitions();
9004
- const maxIterations = 10;
9005
- for (let i = 0; i < maxIterations; i++) {
9006
- const completion = await this.llm.complete({
9007
- messages: this.conversation.history(),
9008
- model: this.config.model,
9009
- temperature: 0.2,
9010
- tools: tools.length > 0 ? tools : void 0,
9011
- toolChoice: tools.length > 0 ? "auto" : void 0
9012
- });
9013
- const payload = this.parseResponse(completion);
9014
- if (completion.toolCalls?.length) {
9015
- this.conversation.addMessage({
9016
- role: "assistant",
9017
- content: completion.content || ""
9018
- });
9019
- } else {
9020
- this.conversation.addMessage({ role: "assistant", content: completion.content });
9021
- }
9022
- if (payload.thought) {
9023
- console.log(chalk9.gray(`[${this.name}] ${payload.thought}`));
9024
- }
9025
- if (payload.toolCalls && payload.toolCalls.length > 0) {
9026
- const results = await this.toolManager.execute(payload.toolCalls);
9027
- for (let j = 0; j < results.length; j++) {
9028
- const result = results[j];
9029
- const toolCall = completion.toolCalls?.[j];
9030
- const content = result.success ? result.output ?? "(no output)" : result.error ?? "Tool failed";
9031
- this.conversation.addMessage({
9032
- role: "tool",
9033
- name: result.tool,
9034
- content,
9035
- tool_call_id: toolCall?.id
9036
- });
9037
- if (!result.success) {
9038
- console.log(chalk9.red(`[${this.name}] Tool ${result.tool} failed: ${content}`));
9039
- }
9040
- }
9041
- continue;
9042
- }
9043
- const response = payload.finalResponse ?? payload.response ?? completion.content;
9044
- console.log(chalk9.cyan(`[${this.name}] Finished.`));
9045
- return response;
9046
- }
9047
- return `[${this.name}] Failed to complete task within ${maxIterations} iterations.`;
9086
+ detectNodeLinter(deps) {
9087
+ if (deps["@biomejs/biome"]) return "Biome";
9088
+ if (deps.eslint) return "ESLint";
9089
+ if (deps.oxlint) return "Oxlint";
9090
+ return void 0;
9048
9091
  }
9049
9092
  /**
9050
- * Parse LLM response, preferring native tool calls over JSON parsing
9093
+ * Detect Node.js formatter
9051
9094
  */
9052
- parseResponse(completion) {
9053
- if (completion.toolCalls && completion.toolCalls.length > 0) {
9054
- return {
9055
- thought: completion.content || void 0,
9056
- toolCalls: completion.toolCalls.map((tc) => ({
9057
- tool: tc.function.name,
9058
- args: this.safeParseJson(tc.function.arguments)
9059
- }))
9060
- };
9061
- }
9062
- return this.parsePayload(completion.content);
9095
+ detectNodeFormatter(deps) {
9096
+ if (deps.prettier) return "Prettier";
9097
+ if (deps["@biomejs/biome"]) return "Biome";
9098
+ if (deps.dprint) return "dprint";
9099
+ return void 0;
9063
9100
  }
9064
9101
  /**
9065
- * Safely parse JSON, returning empty object on failure
9102
+ * Detect Node.js build tool
9103
+ */
9104
+ detectNodeBuildTool(deps) {
9105
+ if (deps.vite) return "Vite";
9106
+ if (deps.tsup) return "tsup";
9107
+ if (deps.esbuild) return "esbuild";
9108
+ if (deps.rollup) return "Rollup";
9109
+ if (deps.webpack) return "Webpack";
9110
+ if (deps.parcel) return "Parcel";
9111
+ if (deps.turbopack || deps.turbo) return "Turbopack";
9112
+ return void 0;
9113
+ }
9114
+ /**
9115
+ * Analyze Rust project
9116
+ */
9117
+ async analyzeRustProject(info) {
9118
+ const cargoPath = join2(this.workspaceRoot, "Cargo.toml");
9119
+ if (!await pathExists(cargoPath)) {
9120
+ return;
9121
+ }
9122
+ info.language = "Rust";
9123
+ info.packageManager = "cargo";
9124
+ try {
9125
+ const cargoContent = await readFile(cargoPath, "utf-8");
9126
+ if (cargoContent.includes("actix-web")) {
9127
+ info.framework = "Actix";
9128
+ } else if (cargoContent.includes("axum")) {
9129
+ info.framework = "Axum";
9130
+ } else if (cargoContent.includes("rocket")) {
9131
+ info.framework = "Rocket";
9132
+ } else if (cargoContent.includes("warp")) {
9133
+ info.framework = "Warp";
9134
+ } else if (cargoContent.includes("tauri")) {
9135
+ info.framework = "Tauri";
9136
+ }
9137
+ } catch {
9138
+ }
9139
+ }
9140
+ /**
9141
+ * Analyze Go project
9142
+ */
9143
+ async analyzeGoProject(info) {
9144
+ const goModPath = join2(this.workspaceRoot, "go.mod");
9145
+ if (!await pathExists(goModPath)) {
9146
+ return;
9147
+ }
9148
+ info.language = "Go";
9149
+ info.packageManager = "go";
9150
+ try {
9151
+ const goModContent = await readFile(goModPath, "utf-8");
9152
+ if (goModContent.includes("github.com/gin-gonic/gin")) {
9153
+ info.framework = "Gin";
9154
+ } else if (goModContent.includes("github.com/gofiber/fiber")) {
9155
+ info.framework = "Fiber";
9156
+ } else if (goModContent.includes("github.com/labstack/echo")) {
9157
+ info.framework = "Echo";
9158
+ } else if (goModContent.includes("github.com/gorilla/mux")) {
9159
+ info.framework = "Gorilla";
9160
+ }
9161
+ } catch {
9162
+ }
9163
+ }
9164
+ /**
9165
+ * Analyze Python project
9166
+ */
9167
+ async analyzePythonProject(info) {
9168
+ const pyprojectPath = join2(this.workspaceRoot, "pyproject.toml");
9169
+ const requirementsPath = join2(this.workspaceRoot, "requirements.txt");
9170
+ const poetryLockPath = join2(this.workspaceRoot, "poetry.lock");
9171
+ const pipfilePath = join2(this.workspaceRoot, "Pipfile");
9172
+ const hasPyproject = await pathExists(pyprojectPath);
9173
+ const hasRequirements = await pathExists(requirementsPath);
9174
+ if (!hasPyproject && !hasRequirements) {
9175
+ return;
9176
+ }
9177
+ info.language = "Python";
9178
+ if (await pathExists(poetryLockPath)) {
9179
+ info.packageManager = "poetry";
9180
+ } else if (await pathExists(pipfilePath)) {
9181
+ info.packageManager = "pipenv";
9182
+ } else if (hasPyproject) {
9183
+ info.packageManager = "pip";
9184
+ } else {
9185
+ info.packageManager = "pip";
9186
+ }
9187
+ let depsContent = "";
9188
+ try {
9189
+ if (hasRequirements) {
9190
+ depsContent = await readFile(requirementsPath, "utf-8");
9191
+ } else if (hasPyproject) {
9192
+ depsContent = await readFile(pyprojectPath, "utf-8");
9193
+ }
9194
+ if (depsContent.includes("django")) {
9195
+ info.framework = "Django";
9196
+ } else if (depsContent.includes("fastapi")) {
9197
+ info.framework = "FastAPI";
9198
+ } else if (depsContent.includes("flask")) {
9199
+ info.framework = "Flask";
9200
+ } else if (depsContent.includes("starlette")) {
9201
+ info.framework = "Starlette";
9202
+ } else if (depsContent.includes("tornado")) {
9203
+ info.framework = "Tornado";
9204
+ }
9205
+ if (depsContent.includes("pytest")) {
9206
+ info.testFramework = "pytest";
9207
+ } else if (depsContent.includes("unittest")) {
9208
+ info.testFramework = "unittest";
9209
+ } else if (depsContent.includes("nose")) {
9210
+ info.testFramework = "nose";
9211
+ }
9212
+ if (depsContent.includes("ruff")) {
9213
+ info.linter = "Ruff";
9214
+ } else if (depsContent.includes("flake8")) {
9215
+ info.linter = "Flake8";
9216
+ } else if (depsContent.includes("pylint")) {
9217
+ info.linter = "Pylint";
9218
+ }
9219
+ if (depsContent.includes("black")) {
9220
+ info.formatter = "Black";
9221
+ } else if (depsContent.includes("ruff")) {
9222
+ info.formatter = "Ruff";
9223
+ } else if (depsContent.includes("autopep8")) {
9224
+ info.formatter = "autopep8";
9225
+ }
9226
+ } catch {
9227
+ }
9228
+ }
9229
+ };
9230
+
9231
+ // src/onboarding/agentsGenerator.ts
9232
+ var AgentsGenerator = class {
9233
+ /**
9234
+ * Generate AGENTS.md content
9235
+ */
9236
+ generateContent(info, options) {
9237
+ const sections = [];
9238
+ sections.push("# AGENTS.md");
9239
+ sections.push("");
9240
+ sections.push("This file helps Autohand understand how to work with this project.");
9241
+ sections.push("");
9242
+ sections.push(this.generateProjectOverview(info));
9243
+ if (info.packageManager) {
9244
+ sections.push(this.generateCommandsSection(info));
9245
+ }
9246
+ if (info.testFramework) {
9247
+ sections.push(this.generateTestingSection(info));
9248
+ }
9249
+ if (info.framework) {
9250
+ const frameworkSection = this.generateFrameworkSection(info);
9251
+ if (frameworkSection) {
9252
+ sections.push(frameworkSection);
9253
+ }
9254
+ }
9255
+ sections.push(this.generateCodeStyleSection(info));
9256
+ sections.push(this.generateConstraintsSection());
9257
+ if (options?.customSections) {
9258
+ for (const section of options.customSections) {
9259
+ sections.push(`## ${section.title}`);
9260
+ sections.push("");
9261
+ sections.push(section.content);
9262
+ sections.push("");
9263
+ }
9264
+ }
9265
+ return sections.join("\n");
9266
+ }
9267
+ /**
9268
+ * Generate project overview section
9269
+ */
9270
+ generateProjectOverview(info) {
9271
+ const lines = [];
9272
+ lines.push("## Project Overview");
9273
+ lines.push("");
9274
+ if (info.language) {
9275
+ lines.push(`- **Language**: ${info.language}`);
9276
+ }
9277
+ if (info.framework) {
9278
+ lines.push(`- **Framework**: ${info.framework}`);
9279
+ }
9280
+ if (info.packageManager) {
9281
+ lines.push(`- **Package Manager**: ${info.packageManager}`);
9282
+ }
9283
+ if (info.testFramework) {
9284
+ lines.push(`- **Test Framework**: ${info.testFramework}`);
9285
+ }
9286
+ if (info.buildTool) {
9287
+ lines.push(`- **Build Tool**: ${info.buildTool}`);
9288
+ }
9289
+ lines.push("");
9290
+ return lines.join("\n");
9291
+ }
9292
+ /**
9293
+ * Generate commands section based on package manager
9294
+ */
9295
+ generateCommandsSection(info) {
9296
+ const lines = [];
9297
+ lines.push("## Commands");
9298
+ lines.push("");
9299
+ const pm = info.packageManager;
9300
+ if (info.language === "Rust") {
9301
+ lines.push("- **Build**: `cargo build`");
9302
+ lines.push("- **Build (release)**: `cargo build --release`");
9303
+ lines.push("- **Run**: `cargo run`");
9304
+ lines.push("- **Test**: `cargo test`");
9305
+ lines.push("- **Check**: `cargo check`");
9306
+ lines.push("- **Format**: `cargo fmt`");
9307
+ lines.push("- **Lint**: `cargo clippy`");
9308
+ } else if (info.language === "Go") {
9309
+ lines.push("- **Build**: `go build`");
9310
+ lines.push("- **Run**: `go run .`");
9311
+ lines.push("- **Test**: `go test ./...`");
9312
+ lines.push("- **Format**: `go fmt ./...`");
9313
+ lines.push("- **Vet**: `go vet ./...`");
9314
+ } else if (info.language === "Python") {
9315
+ if (pm === "poetry") {
9316
+ lines.push("- **Install**: `poetry install`");
9317
+ lines.push("- **Run**: `poetry run python main.py`");
9318
+ lines.push("- **Add dependency**: `poetry add <package>`");
9319
+ if (info.testFramework) {
9320
+ lines.push(`- **Test**: \`poetry run pytest\``);
9321
+ }
9322
+ } else if (pm === "pipenv") {
9323
+ lines.push("- **Install**: `pipenv install`");
9324
+ lines.push("- **Run**: `pipenv run python main.py`");
9325
+ if (info.testFramework) {
9326
+ lines.push(`- **Test**: \`pipenv run pytest\``);
9327
+ }
9328
+ } else {
9329
+ lines.push("- **Install**: `pip install -r requirements.txt`");
9330
+ lines.push("- **Run**: `python main.py`");
9331
+ if (info.testFramework) {
9332
+ lines.push(`- **Test**: \`pytest\``);
9333
+ }
9334
+ }
9335
+ } else {
9336
+ const run = pm === "npm" ? "npm run" : pm;
9337
+ const install = pm === "npm" ? "npm install" : `${pm} install`;
9338
+ lines.push(`- **Install**: \`${install}\``);
9339
+ lines.push(`- **Dev**: \`${run} dev\``);
9340
+ lines.push(`- **Build**: \`${run} build\``);
9341
+ if (info.testFramework) {
9342
+ lines.push(`- **Test**: \`${run} test\``);
9343
+ }
9344
+ if (info.linter) {
9345
+ lines.push(`- **Lint**: \`${run} lint\``);
9346
+ }
9347
+ if (info.formatter) {
9348
+ lines.push(`- **Format**: \`${run} format\``);
9349
+ }
9350
+ }
9351
+ lines.push("");
9352
+ return lines.join("\n");
9353
+ }
9354
+ /**
9355
+ * Generate testing section
9356
+ */
9357
+ generateTestingSection(info) {
9358
+ const lines = [];
9359
+ lines.push("## Testing");
9360
+ lines.push("");
9361
+ lines.push(`This project uses **${info.testFramework}** for testing.`);
9362
+ lines.push("");
9363
+ lines.push("- Write tests for new features before implementation");
9364
+ lines.push("- Run tests before committing changes");
9365
+ lines.push("- Aim for good test coverage on critical paths");
9366
+ if (info.testFramework === "Vitest" || info.testFramework === "Jest") {
9367
+ lines.push("- Use `describe` and `it` blocks to organize tests");
9368
+ lines.push("- Mock external dependencies when appropriate");
9369
+ } else if (info.testFramework === "pytest") {
9370
+ lines.push("- Use fixtures for shared test setup");
9371
+ lines.push("- Use `pytest.mark` for test categorization");
9372
+ } else if (info.testFramework === "Playwright" || info.testFramework === "Cypress") {
9373
+ lines.push("- Write E2E tests for critical user flows");
9374
+ lines.push("- Keep selectors stable and meaningful");
9375
+ }
9376
+ lines.push("");
9377
+ return lines.join("\n");
9378
+ }
9379
+ /**
9380
+ * Generate framework-specific section
9381
+ */
9382
+ generateFrameworkSection(info) {
9383
+ const lines = [];
9384
+ switch (info.framework) {
9385
+ case "Next.js":
9386
+ lines.push("## Next.js Guidelines");
9387
+ lines.push("");
9388
+ lines.push("- Use the App Router for new pages (`app/` directory)");
9389
+ lines.push("- Prefer Server Components by default");
9390
+ lines.push('- Use `"use client"` only when needed for interactivity');
9391
+ lines.push("- Keep API routes in `app/api/`");
9392
+ lines.push("- Use `next/image` for optimized images");
9393
+ lines.push("- Use `next/link` for client-side navigation");
9394
+ break;
9395
+ case "React":
9396
+ lines.push("## React Guidelines");
9397
+ lines.push("");
9398
+ lines.push("- Use functional components with hooks");
9399
+ lines.push("- Keep components small and focused");
9400
+ lines.push("- Use custom hooks to share logic");
9401
+ lines.push("- Prefer composition over inheritance");
9402
+ lines.push("- Use TypeScript interfaces for props");
9403
+ break;
9404
+ case "Vue":
9405
+ lines.push("## Vue Guidelines");
9406
+ lines.push("");
9407
+ lines.push("- Use Composition API for new components");
9408
+ lines.push("- Keep components in single-file format (.vue)");
9409
+ lines.push("- Use composables to share logic");
9410
+ break;
9411
+ case "Express":
9412
+ lines.push("## Express Guidelines");
9413
+ lines.push("");
9414
+ lines.push("- Use middleware for cross-cutting concerns");
9415
+ lines.push("- Keep route handlers thin, delegate to services");
9416
+ lines.push("- Use async/await with proper error handling");
9417
+ lines.push("- Validate request input before processing");
9418
+ break;
9419
+ case "FastAPI":
9420
+ lines.push("## FastAPI Guidelines");
9421
+ lines.push("");
9422
+ lines.push("- Use Pydantic models for request/response validation");
9423
+ lines.push("- Use dependency injection for shared resources");
9424
+ lines.push("- Keep endpoints in organized routers");
9425
+ lines.push("- Use async functions for I/O operations");
9426
+ break;
9427
+ case "Django":
9428
+ lines.push("## Django Guidelines");
9429
+ lines.push("");
9430
+ lines.push("- Follow Django project structure conventions");
9431
+ lines.push("- Use class-based views where appropriate");
9432
+ lines.push("- Keep business logic in models or services");
9433
+ lines.push("- Use Django ORM for database operations");
9434
+ break;
9435
+ case "Flask":
9436
+ lines.push("## Flask Guidelines");
9437
+ lines.push("");
9438
+ lines.push("- Use blueprints to organize routes");
9439
+ lines.push("- Keep route handlers focused");
9440
+ lines.push("- Use Flask extensions for common functionality");
9441
+ break;
9442
+ case "NestJS":
9443
+ lines.push("## NestJS Guidelines");
9444
+ lines.push("");
9445
+ lines.push("- Follow module-based architecture");
9446
+ lines.push("- Use dependency injection");
9447
+ lines.push("- Use decorators for metadata");
9448
+ lines.push("- Keep controllers thin, services fat");
9449
+ break;
9450
+ default:
9451
+ return null;
9452
+ }
9453
+ lines.push("");
9454
+ return lines.join("\n");
9455
+ }
9456
+ /**
9457
+ * Generate code style section
9458
+ */
9459
+ generateCodeStyleSection(info) {
9460
+ const lines = [];
9461
+ lines.push("## Code Style");
9462
+ lines.push("");
9463
+ if (info.language === "TypeScript") {
9464
+ lines.push("- Use strict TypeScript settings");
9465
+ lines.push("- Define types/interfaces for data structures");
9466
+ lines.push("- Avoid `any` type - use `unknown` if type is truly unknown");
9467
+ lines.push("- Use type inference where obvious");
9468
+ } else if (info.language === "Python") {
9469
+ lines.push("- Follow PEP 8 style guidelines");
9470
+ lines.push("- Use type hints for function signatures");
9471
+ lines.push("- Use docstrings for public functions");
9472
+ } else if (info.language === "Rust") {
9473
+ lines.push("- Follow Rust naming conventions (snake_case for functions)");
9474
+ lines.push("- Use descriptive error types");
9475
+ lines.push("- Prefer `Result` over panicking");
9476
+ } else if (info.language === "Go") {
9477
+ lines.push("- Follow Go idioms and conventions");
9478
+ lines.push("- Use short variable names in small scopes");
9479
+ lines.push("- Handle errors explicitly");
9480
+ }
9481
+ lines.push("- Follow existing patterns in the codebase");
9482
+ lines.push("- Use meaningful variable and function names");
9483
+ lines.push("- Add comments for complex logic");
9484
+ lines.push("- Keep functions focused and small");
9485
+ if (info.linter) {
9486
+ lines.push(`- Run **${info.linter}** before committing`);
9487
+ }
9488
+ if (info.formatter) {
9489
+ lines.push(`- Format code with **${info.formatter}**`);
9490
+ }
9491
+ lines.push("");
9492
+ return lines.join("\n");
9493
+ }
9494
+ /**
9495
+ * Generate constraints section
9496
+ */
9497
+ generateConstraintsSection() {
9498
+ const lines = [];
9499
+ lines.push("## Constraints");
9500
+ lines.push("");
9501
+ lines.push("- Do not modify files outside the project directory");
9502
+ lines.push("- Ask before making breaking changes");
9503
+ lines.push("- Prefer editing existing files over creating new ones");
9504
+ lines.push("- Do not delete files without confirmation");
9505
+ lines.push("- Keep dependencies minimal - avoid adding new ones without good reason");
9506
+ lines.push("- Do not commit sensitive data (API keys, secrets, credentials)");
9507
+ lines.push("");
9508
+ return lines.join("\n");
9509
+ }
9510
+ };
9511
+
9512
+ // src/onboarding/setupWizard.ts
9513
+ import chalk9 from "chalk";
9514
+ import enquirer2 from "enquirer";
9515
+ import { pathExists as pathExists2, writeFile } from "fs-extra";
9516
+ import { join as join3 } from "path";
9517
+ var ASCII_FRIEND = [
9518
+ "\u2880\u2874\u281B\u281B\u283B\u28F7\u2844\u2800\u28E0\u2876\u281F\u281B\u283B\u28F6\u2844\u2880\u28F4\u287E\u281B\u281B\u28BF\u28E6\u2800\u2880\u28F4\u281E\u281B\u281B\u2836\u2840",
9519
+ "\u284E\u2800\u28B0\u28F6\u2846\u2808\u28FF\u28F4\u28FF\u2801\u28F4\u28F6\u2844\u2818\u28FF\u28FE\u284F\u2880\u28F6\u28E6\u2800\u28BB\u2847\u28FF\u2803\u28A0\u28F6\u2846\u2800\u28B9",
9520
+ "\u28A7\u2800\u2818\u281B\u2803\u28A0\u287F\u2819\u28FF\u2840\u2819\u281B\u2803\u28F0\u287F\u28BB\u28E7\u2808\u281B\u281B\u2880\u28FE\u2807\u28BB\u28C6\u2808\u281B\u280B\u2800\u287C",
9521
+ "\u2808\u283B\u28B6\u28F6\u287E\u281F\u2801\u2800\u2818\u283F\u28B6\u28F6\u287E\u281F\u2801\u2800\u2819\u2837\u28F6\u28F6\u283F\u280B\u2800\u2808\u283B\u2837\u28F6\u2876\u281A\u2801",
9522
+ "\u2880\u28F4\u283F\u283F\u2837\u28E6\u2840\u2800\u28E0\u28F6\u283F\u283B\u28B7\u28E6\u2840\u2800\u28E0\u287E\u281F\u283F\u28F6\u28C4\u2800\u2880\u28F4\u287E\u283F\u283F\u28F6\u28C4",
9523
+ "\u287E\u2803\u28A0\u28E4\u2844\u2818\u28FF\u28E0\u28FF\u2801\u28E0\u28E4\u2844\u2839\u28F7\u28FC\u284F\u2880\u28E4\u28E4\u2808\u28BF\u2846\u28FE\u280F\u2880\u28E4\u28C4\u2808\u28BF",
9524
+ "\u28A7\u2840\u2838\u283F\u2807\u2880\u28FF\u283A\u28FF\u2840\u283B\u283F\u2803\u28B0\u28FF\u28BF\u28C7\u2808\u283F\u283F\u2800\u28FC\u2847\u28BF\u28C7\u2818\u283F\u2807\u2800\u28F8",
9525
+ "\u2808\u28BF\u28E6\u28E4\u28F4\u287F\u2803\u2800\u2819\u28B7\u28E6\u28E4\u28F6\u287F\u2801\u2808\u283B\u28F7\u28E4\u28E4\u287E\u281B\u2800\u2808\u28BF\u28E6\u28E4\u28E4\u2834\u2801"
9526
+ ].join("\n");
9527
+ var SetupWizard = class {
9528
+ constructor(workspaceRoot, existingConfig) {
9529
+ this.workspaceRoot = workspaceRoot;
9530
+ this.existingConfig = existingConfig ?? null;
9531
+ this.state = {
9532
+ currentStep: "welcome",
9533
+ skipped: [],
9534
+ completed: false
9535
+ };
9536
+ }
9537
+ /**
9538
+ * Run the full onboarding wizard
9539
+ */
9540
+ async run(options) {
9541
+ if (!options?.force && this.isAlreadyConfigured()) {
9542
+ return {
9543
+ success: true,
9544
+ config: {},
9545
+ skippedSteps: ["welcome", "provider", "apiKey", "model", "telemetry", "preferences", "agentsFile"],
9546
+ cancelled: false
9547
+ };
9548
+ }
9549
+ try {
9550
+ if (!options?.skipWelcome) {
9551
+ await this.showWelcome();
9552
+ }
9553
+ const provider = await this.promptProvider();
9554
+ if (!provider) return this.cancelled();
9555
+ if (this.requiresApiKey(provider)) {
9556
+ const apiKey = await this.promptApiKey(provider);
9557
+ if (apiKey === null) return this.cancelled();
9558
+ }
9559
+ const model = await this.promptModel(provider);
9560
+ if (!model) return this.cancelled();
9561
+ await this.promptTelemetry();
9562
+ if (!options?.quickSetup) {
9563
+ await this.promptPreferences();
9564
+ } else {
9565
+ this.state.skipped.push("preferences");
9566
+ }
9567
+ await this.promptAgentsFile();
9568
+ return this.complete();
9569
+ } catch (error) {
9570
+ if (this.isCancellation(error)) {
9571
+ return this.cancelled();
9572
+ }
9573
+ throw error;
9574
+ }
9575
+ }
9576
+ /**
9577
+ * Check if configuration is already complete
9578
+ */
9579
+ isAlreadyConfigured() {
9580
+ if (!this.existingConfig) return false;
9581
+ const provider = this.existingConfig.provider;
9582
+ if (!provider) return false;
9583
+ const providerConfig = getProviderConfig(this.existingConfig, provider);
9584
+ return providerConfig !== null;
9585
+ }
9586
+ /**
9587
+ * Show welcome screen
9588
+ */
9589
+ async showWelcome() {
9590
+ console.clear();
9591
+ console.log(chalk9.gray(ASCII_FRIEND));
9592
+ console.log();
9593
+ console.log(chalk9.cyan.bold(" Welcome to Autohand!"));
9594
+ console.log(chalk9.gray(" Your super fast AI coding agent"));
9595
+ console.log();
9596
+ console.log(chalk9.white(" Let's get you set up in just a few steps."));
9597
+ console.log();
9598
+ await this.pressEnter();
9599
+ }
9600
+ /**
9601
+ * Prompt for provider selection
9602
+ */
9603
+ async promptProvider() {
9604
+ this.state.currentStep = "provider";
9605
+ const providers = ProviderFactory.getProviderNames();
9606
+ const choices = providers.map((p) => ({
9607
+ name: p,
9608
+ message: this.getProviderDisplayName(p),
9609
+ hint: this.getProviderHint(p)
9610
+ }));
9611
+ const result = await enquirer2.prompt({
9612
+ type: "select",
9613
+ name: "provider",
9614
+ message: "Which LLM provider would you like to use?",
9615
+ choices,
9616
+ initial: this.existingConfig?.provider ? providers.indexOf(this.existingConfig.provider) : 0
9617
+ });
9618
+ this.state.provider = result.provider;
9619
+ return result.provider;
9620
+ }
9621
+ /**
9622
+ * Prompt for API key (cloud providers)
9623
+ */
9624
+ async promptApiKey(provider) {
9625
+ this.state.currentStep = "apiKey";
9626
+ const existingKey = this.getExistingApiKey(provider);
9627
+ if (existingKey && existingKey !== "replace-me") {
9628
+ const { useExisting } = await enquirer2.prompt({
9629
+ type: "confirm",
9630
+ name: "useExisting",
9631
+ message: `Use existing ${this.getProviderDisplayName(provider)} API key? (ends with ...${existingKey.slice(-4)})`,
9632
+ initial: true
9633
+ });
9634
+ if (useExisting) {
9635
+ this.state.apiKey = existingKey;
9636
+ return existingKey;
9637
+ }
9638
+ }
9639
+ console.log(chalk9.gray(`
9640
+ Get your API key at: ${this.getApiKeyUrl(provider)}
9641
+ `));
9642
+ const result = await enquirer2.prompt({
9643
+ type: "password",
9644
+ name: "apiKey",
9645
+ message: `Enter your ${this.getProviderDisplayName(provider)} API key`,
9646
+ validate: (val) => {
9647
+ const v = val;
9648
+ if (!v?.trim()) return "API key is required";
9649
+ if (v.length < 10) return "API key seems too short";
9650
+ return true;
9651
+ }
9652
+ });
9653
+ this.state.apiKey = result.apiKey.trim();
9654
+ return this.state.apiKey;
9655
+ }
9656
+ /**
9657
+ * Prompt for model selection
9658
+ */
9659
+ async promptModel(provider) {
9660
+ this.state.currentStep = "model";
9661
+ const defaultModel = this.getDefaultModel(provider);
9662
+ const result = await enquirer2.prompt({
9663
+ type: "input",
9664
+ name: "model",
9665
+ message: "Enter model ID",
9666
+ initial: defaultModel,
9667
+ validate: (val) => {
9668
+ const v = val;
9669
+ return v?.trim() ? true : "Model is required";
9670
+ }
9671
+ });
9672
+ this.state.model = result.model.trim();
9673
+ return this.state.model;
9674
+ }
9675
+ /**
9676
+ * Prompt for telemetry preference
9677
+ */
9678
+ async promptTelemetry() {
9679
+ this.state.currentStep = "telemetry";
9680
+ console.log();
9681
+ console.log(chalk9.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
9682
+ console.log(chalk9.white.bold(" Help us improve Autohand"));
9683
+ console.log(chalk9.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
9684
+ console.log();
9685
+ console.log(chalk9.gray(" We collect anonymous usage data to understand how"));
9686
+ console.log(chalk9.gray(" Autohand is used and where we can make it better."));
9687
+ console.log();
9688
+ console.log(chalk9.gray(" What we collect:"));
9689
+ console.log(chalk9.gray(" - Command usage (which features are popular)"));
9690
+ console.log(chalk9.gray(" - Error rates (to fix bugs faster)"));
9691
+ console.log(chalk9.gray(" - Performance metrics (to speed things up)"));
9692
+ console.log();
9693
+ console.log(chalk9.gray(" What we never collect:"));
9694
+ console.log(chalk9.gray(" - Your code or file contents"));
9695
+ console.log(chalk9.gray(" - API keys or credentials"));
9696
+ console.log(chalk9.gray(" - Personal information"));
9697
+ console.log();
9698
+ const { telemetryEnabled } = await enquirer2.prompt({
9699
+ type: "confirm",
9700
+ name: "telemetryEnabled",
9701
+ message: "Share anonymous usage data to help improve Autohand?",
9702
+ initial: true
9703
+ });
9704
+ this.state.telemetryEnabled = telemetryEnabled;
9705
+ if (telemetryEnabled) {
9706
+ console.log(chalk9.green(" Thanks for helping us improve Autohand!"));
9707
+ } else {
9708
+ console.log(chalk9.gray(" No problem! You can change this anytime in config."));
9709
+ }
9710
+ }
9711
+ /**
9712
+ * Prompt for additional preferences
9713
+ */
9714
+ async promptPreferences() {
9715
+ this.state.currentStep = "preferences";
9716
+ const { configurePrefs } = await enquirer2.prompt({
9717
+ type: "confirm",
9718
+ name: "configurePrefs",
9719
+ message: "Would you like to configure additional preferences? (theme, auto-confirm)",
9720
+ initial: false
9721
+ });
9722
+ if (!configurePrefs) {
9723
+ this.state.skipped.push("preferences");
9724
+ return;
9725
+ }
9726
+ const themes = ["dark", "light", "dracula", "sandy", "tui"];
9727
+ const themeDescriptions = {
9728
+ dark: "Default dark theme",
9729
+ light: "Light theme for light backgrounds",
9730
+ dracula: "Popular Dracula color scheme",
9731
+ sandy: "Warm, earthy desert tones",
9732
+ tui: "New Zealand inspired colors"
9733
+ };
9734
+ const { theme } = await enquirer2.prompt({
9735
+ type: "select",
9736
+ name: "theme",
9737
+ message: "Select a theme",
9738
+ choices: themes.map((t) => ({ name: t, message: t, hint: themeDescriptions[t] })),
9739
+ initial: 0
9740
+ });
9741
+ const { autoConfirm } = await enquirer2.prompt({
9742
+ type: "confirm",
9743
+ name: "autoConfirm",
9744
+ message: "Auto-confirm non-destructive actions?",
9745
+ initial: false
9746
+ });
9747
+ const { checkForUpdates: checkForUpdates2 } = await enquirer2.prompt({
9748
+ type: "confirm",
9749
+ name: "checkForUpdates",
9750
+ message: "Check for updates on startup?",
9751
+ initial: true
9752
+ });
9753
+ this.state.preferences = { theme, autoConfirm, checkForUpdates: checkForUpdates2 };
9754
+ }
9755
+ /**
9756
+ * Prompt for AGENTS.md creation
9757
+ */
9758
+ async promptAgentsFile() {
9759
+ this.state.currentStep = "agentsFile";
9760
+ const agentsPath = join3(this.workspaceRoot, "AGENTS.md");
9761
+ const exists = await pathExists2(agentsPath);
9762
+ if (exists) {
9763
+ const { overwrite } = await enquirer2.prompt({
9764
+ type: "confirm",
9765
+ name: "overwrite",
9766
+ message: "AGENTS.md already exists. Would you like to regenerate it?",
9767
+ initial: false
9768
+ });
9769
+ if (!overwrite) {
9770
+ this.state.skipped.push("agentsFile");
9771
+ console.log(chalk9.gray(" Keeping existing AGENTS.md"));
9772
+ return;
9773
+ }
9774
+ } else {
9775
+ console.log();
9776
+ console.log(chalk9.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
9777
+ console.log(chalk9.white.bold(" Project Configuration"));
9778
+ console.log(chalk9.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
9779
+ console.log();
9780
+ console.log(chalk9.gray(" AGENTS.md helps Autohand understand your project better."));
9781
+ console.log(chalk9.gray(" It contains instructions specific to your codebase."));
9782
+ console.log();
9783
+ const { createAgents } = await enquirer2.prompt({
9784
+ type: "confirm",
9785
+ name: "createAgents",
9786
+ message: "Generate AGENTS.md based on your project?",
9787
+ initial: true
9788
+ });
9789
+ if (!createAgents) {
9790
+ this.state.skipped.push("agentsFile");
9791
+ console.log(chalk9.gray(" You can create it later with /init"));
9792
+ return;
9793
+ }
9794
+ }
9795
+ console.log();
9796
+ console.log(chalk9.gray(" Analyzing your project..."));
9797
+ const analyzer = new ProjectAnalyzer(this.workspaceRoot);
9798
+ const projectInfo = await analyzer.analyze();
9799
+ if (Object.keys(projectInfo).length > 0) {
9800
+ console.log();
9801
+ console.log(chalk9.gray(" Detected:"));
9802
+ if (projectInfo.language) {
9803
+ console.log(chalk9.white(` - Language: ${projectInfo.language}`));
9804
+ }
9805
+ if (projectInfo.framework) {
9806
+ console.log(chalk9.white(` - Framework: ${projectInfo.framework}`));
9807
+ }
9808
+ if (projectInfo.packageManager) {
9809
+ console.log(chalk9.white(` - Package manager: ${projectInfo.packageManager}`));
9810
+ }
9811
+ if (projectInfo.testFramework) {
9812
+ console.log(chalk9.white(` - Test framework: ${projectInfo.testFramework}`));
9813
+ }
9814
+ }
9815
+ const generator = new AgentsGenerator();
9816
+ const content = generator.generateContent(projectInfo);
9817
+ await writeFile(agentsPath, content);
9818
+ this.state.agentsFileCreated = true;
9819
+ console.log();
9820
+ console.log(chalk9.green(" Created AGENTS.md"));
9821
+ console.log(chalk9.gray(" You can customize it anytime to improve Autohand's understanding."));
9822
+ }
9823
+ /**
9824
+ * Build final config and return success
9825
+ */
9826
+ complete() {
9827
+ this.state.currentStep = "complete";
9828
+ this.state.completed = true;
9829
+ const config = {
9830
+ provider: this.state.provider
9831
+ };
9832
+ if (this.state.provider) {
9833
+ if (this.requiresApiKey(this.state.provider)) {
9834
+ config[this.state.provider] = {
9835
+ apiKey: this.state.apiKey,
9836
+ model: this.state.model,
9837
+ baseUrl: this.getDefaultBaseUrl(this.state.provider)
9838
+ };
9839
+ } else {
9840
+ config[this.state.provider] = {
9841
+ model: this.state.model,
9842
+ baseUrl: this.getDefaultBaseUrl(this.state.provider)
9843
+ };
9844
+ }
9845
+ }
9846
+ config.telemetry = {
9847
+ enabled: this.state.telemetryEnabled ?? true
9848
+ };
9849
+ if (this.state.preferences) {
9850
+ config.ui = {
9851
+ theme: this.state.preferences.theme,
9852
+ autoConfirm: this.state.preferences.autoConfirm,
9853
+ checkForUpdates: this.state.preferences.checkForUpdates
9854
+ };
9855
+ }
9856
+ this.showCompletionMessage();
9857
+ return {
9858
+ success: true,
9859
+ config,
9860
+ skippedSteps: this.state.skipped,
9861
+ cancelled: false,
9862
+ agentsFileCreated: this.state.agentsFileCreated
9863
+ };
9864
+ }
9865
+ /**
9866
+ * Show setup complete message
9867
+ */
9868
+ showCompletionMessage() {
9869
+ console.log();
9870
+ console.log();
9871
+ console.log(chalk9.green(" Setup complete!"));
9872
+ console.log();
9873
+ console.log(chalk9.gray(" What was created:"));
9874
+ console.log(chalk9.white(" - ~/.autohand/config.json (your settings)"));
9875
+ if (this.state.agentsFileCreated) {
9876
+ console.log(chalk9.white(" - AGENTS.md (project instructions for Autohand)"));
9877
+ }
9878
+ console.log();
9879
+ console.log(chalk9.gray(" Quick tips:"));
9880
+ console.log(chalk9.white(" - Type your request and press Enter to start"));
9881
+ console.log(chalk9.white(" - Use @filename to mention files"));
9882
+ console.log(chalk9.white(" - Type /help for all commands"));
9883
+ console.log(chalk9.white(" - Press Ctrl+C twice to exit"));
9884
+ console.log();
9885
+ }
9886
+ /**
9887
+ * Return cancelled result
9888
+ */
9889
+ cancelled() {
9890
+ return {
9891
+ success: false,
9892
+ config: {},
9893
+ skippedSteps: [],
9894
+ cancelled: true
9895
+ };
9896
+ }
9897
+ // Helper methods
9898
+ requiresApiKey(provider) {
9899
+ return provider === "openrouter" || provider === "openai";
9900
+ }
9901
+ getProviderDisplayName(provider) {
9902
+ const names = {
9903
+ openrouter: "OpenRouter",
9904
+ openai: "OpenAI",
9905
+ ollama: "Ollama",
9906
+ llamacpp: "llama.cpp",
9907
+ mlx: "MLX (Apple Silicon)"
9908
+ };
9909
+ return names[provider] || provider;
9910
+ }
9911
+ getProviderHint(provider) {
9912
+ const hints = {
9913
+ openrouter: "Cloud - Access to 100+ models (Claude, GPT-4, etc.)",
9914
+ openai: "Cloud - Official OpenAI models (GPT-4o, o1, etc.)",
9915
+ ollama: "Local - Run models on your machine (free)",
9916
+ llamacpp: "Local - Fast inference with GGUF models",
9917
+ mlx: "Local - Optimized for Apple Silicon Macs"
9918
+ };
9919
+ return hints[provider] || "";
9920
+ }
9921
+ getApiKeyUrl(provider) {
9922
+ const urls = {
9923
+ openrouter: "https://openrouter.ai/keys",
9924
+ openai: "https://platform.openai.com/api-keys"
9925
+ };
9926
+ return urls[provider] || "";
9927
+ }
9928
+ getDefaultModel(provider) {
9929
+ const defaults = {
9930
+ openrouter: "anthropic/claude-sonnet-4-20250514",
9931
+ openai: "gpt-4o",
9932
+ ollama: "llama3.2:latest",
9933
+ llamacpp: "default",
9934
+ mlx: "mlx-community/Llama-3.2-3B-Instruct-4bit"
9935
+ };
9936
+ return defaults[provider] || "";
9937
+ }
9938
+ getDefaultBaseUrl(provider) {
9939
+ const urls = {
9940
+ openrouter: "https://openrouter.ai/api/v1",
9941
+ openai: "https://api.openai.com/v1",
9942
+ ollama: "http://localhost:11434",
9943
+ llamacpp: "http://localhost:8080",
9944
+ mlx: "http://localhost:8080"
9945
+ };
9946
+ return urls[provider] || "";
9947
+ }
9948
+ getExistingApiKey(provider) {
9949
+ if (!this.existingConfig) return null;
9950
+ const config = this.existingConfig[provider];
9951
+ return config?.apiKey || null;
9952
+ }
9953
+ isCancellation(error) {
9954
+ if (error && typeof error === "object") {
9955
+ const e = error;
9956
+ return e.code === "ERR_USE_AFTER_CLOSE" || e.message?.includes("cancelled") || e.message?.includes("canceled");
9957
+ }
9958
+ return false;
9959
+ }
9960
+ async pressEnter() {
9961
+ console.log(chalk9.gray(" Press Enter to continue..."));
9962
+ await enquirer2.prompt({
9963
+ type: "invisible",
9964
+ name: "continue",
9965
+ message: ""
9966
+ });
9967
+ }
9968
+ };
9969
+
9970
+ // src/core/agents/AgentDelegator.ts
9971
+ import chalk11 from "chalk";
9972
+
9973
+ // src/core/agents/SubAgent.ts
9974
+ import chalk10 from "chalk";
9975
+ var DELEGATION_TOOL_DEFINITIONS = [
9976
+ {
9977
+ name: "delegate_task",
9978
+ description: "Delegate a task to another specialized sub-agent",
9979
+ parameters: {
9980
+ type: "object",
9981
+ properties: {
9982
+ agent_name: { type: "string", description: "Name of the agent to delegate to" },
9983
+ task: { type: "string", description: "Task description for the sub-agent" }
9984
+ },
9985
+ required: ["agent_name", "task"]
9986
+ }
9987
+ },
9988
+ {
9989
+ name: "delegate_parallel",
9990
+ description: "Run multiple sub-agents in parallel (max 5)",
9991
+ parameters: {
9992
+ type: "object",
9993
+ properties: {
9994
+ tasks: { type: "array", description: "Array of {agent_name, task} objects" }
9995
+ },
9996
+ required: ["tasks"]
9997
+ }
9998
+ }
9999
+ ];
10000
+ var SubAgent = class {
10001
+ constructor(config, llm, actionExecutor, options) {
10002
+ this.config = config;
10003
+ this.llm = llm;
10004
+ this.actionExecutor = actionExecutor;
10005
+ this.delegator = null;
10006
+ this.name = config.name;
10007
+ this.options = options;
10008
+ const canDelegate = options.depth < options.maxDepth;
10009
+ const allowedTools = new Set(config.tools);
10010
+ let definitions = DEFAULT_TOOL_DEFINITIONS.filter((def) => allowedTools.has(def.name));
10011
+ if (canDelegate) {
10012
+ definitions = [...definitions, ...DELEGATION_TOOL_DEFINITIONS];
10013
+ }
10014
+ const toolFilter = new ToolFilter(options.clientContext);
10015
+ definitions = toolFilter.filterDefinitions(definitions);
10016
+ if (canDelegate) {
10017
+ this.delegator = new AgentDelegator(llm, actionExecutor, {
10018
+ clientContext: options.clientContext,
10019
+ currentDepth: options.depth,
10020
+ maxDepth: options.maxDepth
10021
+ });
10022
+ }
10023
+ this.toolManager = new ToolManager({
10024
+ executor: async (action, context) => {
10025
+ if (action.type === "delegate_task" && this.delegator) {
10026
+ return this.delegator.delegateTask(
10027
+ action.agent_name,
10028
+ action.task
10029
+ );
10030
+ }
10031
+ if (action.type === "delegate_parallel" && this.delegator) {
10032
+ return this.delegator.delegateParallel(action.tasks);
10033
+ }
10034
+ return this.actionExecutor.execute(action, context);
10035
+ },
10036
+ confirmApproval: async () => true,
10037
+ // Sub-agents auto-approve (inherit from main agent in future)
10038
+ definitions,
10039
+ clientContext: options.clientContext
10040
+ });
10041
+ const enhancedSystemPrompt = this.buildSystemPrompt(config.systemPrompt, definitions);
10042
+ this.conversation = new ConversationManager();
10043
+ this.conversation.reset(enhancedSystemPrompt);
10044
+ }
10045
+ /**
10046
+ * Build system prompt with tool signatures for the LLM
10047
+ */
10048
+ buildSystemPrompt(basePrompt, tools) {
10049
+ const toolSignatures = tools.map((def) => this.formatToolSignature(def)).join("\n");
10050
+ return [
10051
+ basePrompt,
10052
+ "",
10053
+ "## Available Tools",
10054
+ "You have access to the following tools. Use them when needed:",
10055
+ "",
10056
+ toolSignatures,
10057
+ "",
10058
+ "## Response Format",
10059
+ "Always respond with structured JSON:",
10060
+ "```json",
10061
+ "{",
10062
+ ' "thought": "Your reasoning about what to do next",',
10063
+ ' "toolCalls": [{"tool": "tool_name", "args": {...}}],',
10064
+ ' "finalResponse": "Your final answer when done (omit toolCalls if providing this)"',
10065
+ "}",
10066
+ "```",
10067
+ "",
10068
+ `Depth: ${this.options.depth}/${this.options.maxDepth} ${this.delegator ? "(can delegate further)" : "(max depth reached)"}`
10069
+ ].join("\n");
10070
+ }
10071
+ /**
10072
+ * Format a tool definition as a signature string
10073
+ */
10074
+ formatToolSignature(def) {
10075
+ const params = def.parameters?.properties ? Object.entries(def.parameters.properties).map(([name, prop]) => {
10076
+ const required = def.parameters?.required?.includes(name) ? "" : "?";
10077
+ return `${name}${required}: ${prop.type}`;
10078
+ }).join(", ") : "";
10079
+ return `- ${def.name}(${params}): ${def.description}`;
10080
+ }
10081
+ async run(task) {
10082
+ console.log(chalk10.cyan(`
10083
+ \u{1F916} Sub-agent '${this.name}' starting task... (depth ${this.options.depth}/${this.options.maxDepth})`));
10084
+ this.conversation.addMessage({ role: "user", content: task });
10085
+ const tools = this.toolManager.toFunctionDefinitions();
10086
+ const maxIterations = 10;
10087
+ for (let i = 0; i < maxIterations; i++) {
10088
+ const completion = await this.llm.complete({
10089
+ messages: this.conversation.history(),
10090
+ model: this.config.model,
10091
+ temperature: 0.2,
10092
+ tools: tools.length > 0 ? tools : void 0,
10093
+ toolChoice: tools.length > 0 ? "auto" : void 0
10094
+ });
10095
+ const payload = this.parseResponse(completion);
10096
+ if (completion.toolCalls?.length) {
10097
+ this.conversation.addMessage({
10098
+ role: "assistant",
10099
+ content: completion.content || ""
10100
+ });
10101
+ } else {
10102
+ this.conversation.addMessage({ role: "assistant", content: completion.content });
10103
+ }
10104
+ if (payload.thought) {
10105
+ console.log(chalk10.gray(`[${this.name}] ${payload.thought}`));
10106
+ }
10107
+ if (payload.toolCalls && payload.toolCalls.length > 0) {
10108
+ const results = await this.toolManager.execute(payload.toolCalls);
10109
+ for (let j = 0; j < results.length; j++) {
10110
+ const result = results[j];
10111
+ const toolCall = completion.toolCalls?.[j];
10112
+ const content = result.success ? result.output ?? "(no output)" : result.error ?? "Tool failed";
10113
+ this.conversation.addMessage({
10114
+ role: "tool",
10115
+ name: result.tool,
10116
+ content,
10117
+ tool_call_id: toolCall?.id
10118
+ });
10119
+ if (!result.success) {
10120
+ console.log(chalk10.red(`[${this.name}] Tool ${result.tool} failed: ${content}`));
10121
+ }
10122
+ }
10123
+ continue;
10124
+ }
10125
+ const response = payload.finalResponse ?? payload.response ?? completion.content;
10126
+ console.log(chalk10.cyan(`[${this.name}] Finished.`));
10127
+ return response;
10128
+ }
10129
+ return `[${this.name}] Failed to complete task within ${maxIterations} iterations.`;
10130
+ }
10131
+ /**
10132
+ * Parse LLM response, preferring native tool calls over JSON parsing
10133
+ */
10134
+ parseResponse(completion) {
10135
+ if (completion.toolCalls && completion.toolCalls.length > 0) {
10136
+ return {
10137
+ thought: completion.content || void 0,
10138
+ toolCalls: completion.toolCalls.map((tc) => ({
10139
+ tool: tc.function.name,
10140
+ args: this.safeParseJson(tc.function.arguments)
10141
+ }))
10142
+ };
10143
+ }
10144
+ return this.parsePayload(completion.content);
10145
+ }
10146
+ /**
10147
+ * Safely parse JSON, returning empty object on failure
9066
10148
  */
9067
10149
  safeParseJson(json) {
9068
10150
  try {
@@ -9227,7 +10309,7 @@ ${result}`;
9227
10309
  }
9228
10310
  });
9229
10311
  const results = await Promise.all(promises);
9230
- return results.join("\n\n" + chalk10.gray("\u2500".repeat(40)) + "\n\n");
10312
+ return results.join("\n\n" + chalk11.gray("\u2500".repeat(40)) + "\n\n");
9231
10313
  }
9232
10314
  /**
9233
10315
  * Get the current delegation depth
@@ -9326,7 +10408,7 @@ var ErrorLogger = class {
9326
10408
  // src/feedback/FeedbackManager.ts
9327
10409
  import fs14 from "fs-extra";
9328
10410
  import path14 from "path";
9329
- import chalk11 from "chalk";
10411
+ import chalk12 from "chalk";
9330
10412
  import Enquirer from "enquirer";
9331
10413
 
9332
10414
  // src/feedback/FeedbackApiClient.ts
@@ -9616,11 +10698,13 @@ var FeedbackManager = class {
9616
10698
  };
9617
10699
  }
9618
10700
  saveState() {
9619
- try {
9620
- fs14.ensureDirSync(this.stateDir);
9621
- fs14.writeJsonSync(this.statePath, this.state, { spaces: 2 });
9622
- } catch {
9623
- }
10701
+ setImmediate(() => {
10702
+ try {
10703
+ fs14.ensureDirSync(this.stateDir);
10704
+ fs14.writeJsonSync(this.statePath, this.state, { spaces: 2 });
10705
+ } catch {
10706
+ }
10707
+ });
9624
10708
  }
9625
10709
  async saveFeedbackResponse(response) {
9626
10710
  try {
@@ -9724,10 +10808,10 @@ var FeedbackManager = class {
9724
10808
  this.state.lastPromptedAt = (/* @__PURE__ */ new Date()).toISOString();
9725
10809
  this.saveState();
9726
10810
  console.log();
9727
- console.log(chalk11.cyan("\u2501".repeat(50)));
9728
- console.log(chalk11.cyan.bold(" Quick Feedback"));
9729
- console.log(chalk11.gray(" Help us improve Autohand (takes 10 seconds)"));
9730
- console.log(chalk11.cyan("\u2501".repeat(50)));
10811
+ console.log(chalk12.cyan("\u2501".repeat(50)));
10812
+ console.log(chalk12.cyan.bold(" Quick Feedback"));
10813
+ console.log(chalk12.gray(" Help us improve Autohand (takes 10 seconds)"));
10814
+ console.log(chalk12.cyan("\u2501".repeat(50)));
9731
10815
  console.log();
9732
10816
  const safePrompt = async (config) => {
9733
10817
  try {
@@ -9740,19 +10824,56 @@ var FeedbackManager = class {
9740
10824
  }
9741
10825
  };
9742
10826
  try {
9743
- const npsResult = await safePrompt({
9744
- type: "select",
10827
+ const { Select } = Enquirer;
10828
+ const ratingChoices = [
10829
+ { name: "5", message: `${chalk12.green("5")} - Excellent` },
10830
+ { name: "4", message: `${chalk12.green("4")} - Good` },
10831
+ { name: "3", message: `${chalk12.yellow("3")} - Okay` },
10832
+ { name: "2", message: `${chalk12.red("2")} - Poor` },
10833
+ { name: "1", message: `${chalk12.red("1")} - Very Poor` },
10834
+ { name: "skip", message: `${chalk12.gray("s")} - Skip` }
10835
+ ];
10836
+ const ratingPrompt = new Select({
9745
10837
  name: "score",
9746
- message: "How would you rate your experience?",
9747
- choices: [
9748
- { name: "5", message: `${chalk11.green("5")} - Excellent` },
9749
- { name: "4", message: `${chalk11.green("4")} - Good` },
9750
- { name: "3", message: `${chalk11.yellow("3")} - Okay` },
9751
- { name: "2", message: `${chalk11.red("2")} - Poor` },
9752
- { name: "1", message: `${chalk11.red("1")} - Very Poor` },
9753
- { name: "skip", message: `${chalk11.gray("Skip")}` }
9754
- ]
10838
+ message: "How would you rate your experience? (press 1-5 or s to skip)",
10839
+ choices: ratingChoices
9755
10840
  });
10841
+ const originalKeypress = ratingPrompt.keypress.bind(ratingPrompt);
10842
+ ratingPrompt.keypress = async function(char, key) {
10843
+ if (char >= "1" && char <= "5") {
10844
+ const targetName = char;
10845
+ const index = ratingChoices.findIndex((c) => c.name === targetName);
10846
+ if (index !== -1) {
10847
+ this.index = index;
10848
+ this.selected = this.choices[index];
10849
+ this.value = this.choices[index].name;
10850
+ await this.render();
10851
+ return this.submit();
10852
+ }
10853
+ }
10854
+ if (char === "s" || char === "S") {
10855
+ const index = ratingChoices.findIndex((c) => c.name === "skip");
10856
+ if (index !== -1) {
10857
+ this.index = index;
10858
+ this.selected = this.choices[index];
10859
+ this.value = this.choices[index].name;
10860
+ await this.render();
10861
+ return this.submit();
10862
+ }
10863
+ }
10864
+ return originalKeypress(char, key);
10865
+ };
10866
+ let npsResult = null;
10867
+ try {
10868
+ const score = await ratingPrompt.run();
10869
+ npsResult = { score };
10870
+ } catch (error) {
10871
+ if (error?.code === "ERR_USE_AFTER_CLOSE") {
10872
+ npsResult = null;
10873
+ } else {
10874
+ throw error;
10875
+ }
10876
+ }
9756
10877
  if (!npsResult) {
9757
10878
  this.state.dismissed++;
9758
10879
  this.saveState();
@@ -9761,7 +10882,7 @@ var FeedbackManager = class {
9761
10882
  if (npsResult.score === "skip") {
9762
10883
  this.state.dismissed++;
9763
10884
  this.saveState();
9764
- console.log(chalk11.gray("\nNo problem! You can always use /feedback later.\n"));
10885
+ console.log(chalk12.gray("\nNo problem! You can always use /feedback later.\n"));
9765
10886
  return false;
9766
10887
  }
9767
10888
  const npsScore = parseInt(npsResult.score, 10);
@@ -9808,11 +10929,11 @@ var FeedbackManager = class {
9808
10929
  this.state.averageNps = this.state.npsScores.reduce((a, b) => a + b, 0) / this.state.npsScores.length;
9809
10930
  this.saveState();
9810
10931
  console.log();
9811
- console.log(chalk11.green("Thank you for your feedback!"));
10932
+ console.log(chalk12.green("Thank you for your feedback!"));
9812
10933
  if (npsScore >= 4) {
9813
- console.log(chalk11.gray("Your support helps us build a better tool."));
10934
+ console.log(chalk12.gray("Your support helps us build a better tool."));
9814
10935
  } else {
9815
- console.log(chalk11.gray("We'll work hard to improve your experience."));
10936
+ console.log(chalk12.gray("We'll work hard to improve your experience."));
9816
10937
  }
9817
10938
  console.log();
9818
10939
  return true;
@@ -9822,7 +10943,7 @@ var FeedbackManager = class {
9822
10943
  }
9823
10944
  this.state.dismissed++;
9824
10945
  this.saveState();
9825
- console.log(chalk11.gray("\nFeedback skipped.\n"));
10946
+ console.log(chalk12.gray("\nFeedback skipped.\n"));
9826
10947
  return false;
9827
10948
  }
9828
10949
  }
@@ -9834,10 +10955,10 @@ var FeedbackManager = class {
9834
10955
  async quickRating() {
9835
10956
  console.log();
9836
10957
  console.log(
9837
- chalk11.cyan("Quick rating: ") + chalk11.gray("Press ") + chalk11.bold("1-5") + chalk11.gray(" to rate your experience (or ") + chalk11.bold("Enter") + chalk11.gray(" to skip)")
10958
+ chalk12.cyan("Quick rating: ") + chalk12.gray("Press ") + chalk12.bold("1-5") + chalk12.gray(" to rate your experience (or ") + chalk12.bold("Enter") + chalk12.gray(" to skip)")
9838
10959
  );
9839
10960
  console.log(
9840
- chalk11.gray(" 1=Poor 2=Fair 3=Good 4=Great 5=Excellent")
10961
+ chalk12.gray(" 1=Poor 2=Fair 3=Good 4=Great 5=Excellent")
9841
10962
  );
9842
10963
  return new Promise((resolve) => {
9843
10964
  const stdin = process.stdin;
@@ -9869,7 +10990,7 @@ var FeedbackManager = class {
9869
10990
  const num = parseInt(key, 10);
9870
10991
  if (num >= 1 && num <= 5) {
9871
10992
  cleanup();
9872
- console.log(chalk11.green(` ${num}`));
10993
+ console.log(chalk12.green(` ${num}`));
9873
10994
  this.state.npsScores.push(num);
9874
10995
  this.state.feedbackCount++;
9875
10996
  this.state.lastFeedbackAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -9877,7 +10998,7 @@ var FeedbackManager = class {
9877
10998
  this.hasPromptedThisSession = true;
9878
10999
  this.state.lastPromptedAt = (/* @__PURE__ */ new Date()).toISOString();
9879
11000
  this.saveState();
9880
- console.log(chalk11.green("Thanks!"));
11001
+ console.log(chalk12.green("Thanks!"));
9881
11002
  console.log();
9882
11003
  resolve(num);
9883
11004
  }
@@ -10796,12 +11917,11 @@ var CommunitySkillsClient = class {
10796
11917
  };
10797
11918
 
10798
11919
  // src/ui/persistentInput.ts
10799
- import chalk13 from "chalk";
10800
- import readline3 from "readline";
11920
+ import chalk14 from "chalk";
10801
11921
  import EventEmitter from "events";
10802
11922
 
10803
11923
  // src/ui/terminalRegions.ts
10804
- import chalk12 from "chalk";
11924
+ import chalk13 from "chalk";
10805
11925
  var ESC = "\x1B";
10806
11926
  var CSI = `${ESC}[`;
10807
11927
  var TerminalRegions = class {
@@ -10865,15 +11985,15 @@ var TerminalRegions = class {
10865
11985
  this.output.write(`${CSI}s`);
10866
11986
  this.output.write(`${CSI}${height - 2};1H`);
10867
11987
  this.output.write(`${CSI}K`);
10868
- this.output.write(chalk12.gray("\u2500".repeat(width)));
11988
+ this.output.write(chalk13.gray("\u2500".repeat(width)));
10869
11989
  this.output.write(`${CSI}${height - 1};1H`);
10870
11990
  this.output.write(`${CSI}K`);
10871
- const queueStatus = queueCount > 0 ? chalk12.cyan(` [${queueCount} queued]`) : "";
11991
+ const queueStatus = queueCount > 0 ? chalk13.cyan(` [${queueCount} queued]`) : "";
10872
11992
  const statusText = status || "type to queue \xB7 Enter to submit";
10873
- this.output.write(chalk12.gray(statusText) + queueStatus);
11993
+ this.output.write(chalk13.gray(statusText) + queueStatus);
10874
11994
  this.output.write(`${CSI}${height};1H`);
10875
11995
  this.output.write(`${CSI}K`);
10876
- this.output.write(chalk12.gray("\u203A") + " " + input);
11996
+ this.output.write(chalk13.gray("\u203A") + " " + input);
10877
11997
  this.output.write(`${CSI}u`);
10878
11998
  }
10879
11999
  /**
@@ -10885,7 +12005,7 @@ var TerminalRegions = class {
10885
12005
  this.output.write(`${CSI}s`);
10886
12006
  this.output.write(`${CSI}${height};1H`);
10887
12007
  this.output.write(`${CSI}K`);
10888
- this.output.write(chalk12.gray("\u203A") + " " + input);
12008
+ this.output.write(chalk13.gray("\u203A") + " " + input);
10889
12009
  this.output.write(`${CSI}u`);
10890
12010
  }
10891
12011
  /**
@@ -10897,8 +12017,8 @@ var TerminalRegions = class {
10897
12017
  this.output.write(`${CSI}s`);
10898
12018
  this.output.write(`${CSI}${height - 1};1H`);
10899
12019
  this.output.write(`${CSI}K`);
10900
- const queueStatus = queueCount > 0 ? chalk12.cyan(` [${queueCount} queued]`) : "";
10901
- this.output.write(chalk12.gray(status) + queueStatus);
12020
+ const queueStatus = queueCount > 0 ? chalk13.cyan(` [${queueCount} queued]`) : "";
12021
+ this.output.write(chalk13.gray(status) + queueStatus);
10902
12022
  this.output.write(`${CSI}u`);
10903
12023
  }
10904
12024
  /**
@@ -11019,7 +12139,7 @@ var PersistentInput = class extends EventEmitter {
11019
12139
  this.currentInput = "";
11020
12140
  this.isPaused = false;
11021
12141
  if (this.silentMode) {
11022
- readline3.emitKeypressEvents(this.input);
12142
+ safeEmitKeypressEvents(this.input);
11023
12143
  const supportsRaw = typeof this.input.setRawMode === "function";
11024
12144
  const wasRaw = this.input.isRaw;
11025
12145
  if (!wasRaw && supportsRaw) {
@@ -11030,7 +12150,7 @@ var PersistentInput = class extends EventEmitter {
11030
12150
  this.input.on("keypress", this.handleKeypress);
11031
12151
  } else {
11032
12152
  this.regions.enable();
11033
- readline3.emitKeypressEvents(this.input);
12153
+ safeEmitKeypressEvents(this.input);
11034
12154
  const supportsRaw = typeof this.input.setRawMode === "function";
11035
12155
  if (supportsRaw) {
11036
12156
  this.input.setRawMode(true);
@@ -11140,7 +12260,7 @@ var PersistentInput = class extends EventEmitter {
11140
12260
  if (this.silentMode) {
11141
12261
  this.emit("queue-full", this.maxQueueSize);
11142
12262
  } else {
11143
- this.regions.writeAbove(chalk13.yellow(`
12263
+ this.regions.writeAbove(chalk14.yellow(`
11144
12264
  \u26A0 Queue full (max ${this.maxQueueSize})
11145
12265
  `));
11146
12266
  }
@@ -11152,7 +12272,7 @@ var PersistentInput = class extends EventEmitter {
11152
12272
  });
11153
12273
  const preview = text.length > 40 ? text.slice(0, 37) + "..." : text;
11154
12274
  if (!this.silentMode) {
11155
- this.regions.writeAbove(chalk13.cyan(`
12275
+ this.regions.writeAbove(chalk14.cyan(`
11156
12276
  \u2713 Queued: "${preview}" (${this.queue.length} pending)
11157
12277
  `));
11158
12278
  this.regions.updateStatus(this.statusLine, this.queue.length);
@@ -11255,7 +12375,7 @@ ${truncatedContent}` : outputLines > 0 ? `
11255
12375
  }
11256
12376
 
11257
12377
  // src/ui/promptCallback.ts
11258
- import enquirer2 from "enquirer";
12378
+ import enquirer3 from "enquirer";
11259
12379
  function isExternalCallbackEnabled() {
11260
12380
  return !!process.env.AUTOHAND_PERMISSION_CALLBACK_URL;
11261
12381
  }
@@ -11321,7 +12441,7 @@ async function confirm(message, context) {
11321
12441
  while (process.stdin.read() !== null) {
11322
12442
  }
11323
12443
  }
11324
- const { Select, Input } = enquirer2;
12444
+ const { Select, Input } = enquirer3;
11325
12445
  const choices = [
11326
12446
  { name: "yes", message: "1. Yes" },
11327
12447
  { name: "no", message: "2. No" },
@@ -11334,24 +12454,30 @@ async function confirm(message, context) {
11334
12454
  initial: 0
11335
12455
  });
11336
12456
  const originalKeypress = prompt.keypress.bind(prompt);
11337
- prompt.keypress = function(char, key) {
12457
+ prompt.keypress = async function(char, key) {
11338
12458
  if (char === "1" || char === "2" || char === "3") {
11339
12459
  const index = parseInt(char, 10) - 1;
11340
12460
  if (index >= 0 && index < choices.length) {
11341
12461
  this.index = index;
11342
- this.submit();
11343
- return;
12462
+ this.selected = this.choices[index];
12463
+ this.value = this.choices[index].name;
12464
+ await this.render();
12465
+ return this.submit();
11344
12466
  }
11345
12467
  }
11346
12468
  if (char === "y" || char === "Y") {
11347
12469
  this.index = 0;
11348
- this.submit();
11349
- return;
12470
+ this.selected = this.choices[0];
12471
+ this.value = this.choices[0].name;
12472
+ await this.render();
12473
+ return this.submit();
11350
12474
  }
11351
12475
  if (char === "n" || char === "N") {
11352
12476
  this.index = 1;
11353
- this.submit();
11354
- return;
12477
+ this.selected = this.choices[1];
12478
+ this.value = this.choices[1].name;
12479
+ await this.render();
12480
+ return this.submit();
11355
12481
  }
11356
12482
  return originalKeypress(char, key);
11357
12483
  };
@@ -11646,7 +12772,7 @@ var IntentDetector = class {
11646
12772
 
11647
12773
  // src/core/EnvironmentBootstrap.ts
11648
12774
  import fs17 from "fs-extra";
11649
- import { join as join2 } from "path";
12775
+ import { join as join4 } from "path";
11650
12776
  import { exec } from "child_process";
11651
12777
  import { promisify } from "util";
11652
12778
  var execAsync = promisify(exec);
@@ -11741,11 +12867,11 @@ var EnvironmentBootstrap = class {
11741
12867
  */
11742
12868
  async detectPackageManager(root) {
11743
12869
  for (const { file, pm } of this.lockfileChecks) {
11744
- if (await fs17.pathExists(join2(root, file))) {
12870
+ if (await fs17.pathExists(join4(root, file))) {
11745
12871
  return pm;
11746
12872
  }
11747
12873
  }
11748
- const pkgPath = join2(root, "package.json");
12874
+ const pkgPath = join4(root, "package.json");
11749
12875
  if (await fs17.pathExists(pkgPath)) {
11750
12876
  try {
11751
12877
  const pkg = await fs17.readJson(pkgPath);
@@ -11779,7 +12905,7 @@ var EnvironmentBootstrap = class {
11779
12905
  const step = { name: "Git Sync", status: "running" };
11780
12906
  const start = Date.now();
11781
12907
  try {
11782
- const isGit = await fs17.pathExists(join2(root, ".git"));
12908
+ const isGit = await fs17.pathExists(join4(root, ".git"));
11783
12909
  if (!isGit) {
11784
12910
  return {
11785
12911
  ...step,
@@ -11859,18 +12985,18 @@ var EnvironmentBootstrap = class {
11859
12985
  const start = Date.now();
11860
12986
  const details = [];
11861
12987
  try {
11862
- const nvmrcPath = join2(root, ".nvmrc");
12988
+ const nvmrcPath = join4(root, ".nvmrc");
11863
12989
  const nvmrcExists = await fs17.pathExists(nvmrcPath);
11864
- const nodeVersionPath = join2(root, ".node-version");
12990
+ const nodeVersionPath = join4(root, ".node-version");
11865
12991
  const nodeVersionExists = await fs17.pathExists(nodeVersionPath);
11866
12992
  if (nvmrcExists || nodeVersionExists) {
11867
12993
  details.push("Node version file found");
11868
12994
  }
11869
- const toolVersionsPath = join2(root, ".tool-versions");
12995
+ const toolVersionsPath = join4(root, ".tool-versions");
11870
12996
  if (await fs17.pathExists(toolVersionsPath)) {
11871
12997
  details.push(".tool-versions found");
11872
12998
  }
11873
- const rustToolchainPath = join2(root, "rust-toolchain.toml");
12999
+ const rustToolchainPath = join4(root, "rust-toolchain.toml");
11874
13000
  if (await fs17.pathExists(rustToolchainPath)) {
11875
13001
  details.push("Rust toolchain found");
11876
13002
  }
@@ -11949,7 +13075,7 @@ var EnvironmentBootstrap = class {
11949
13075
 
11950
13076
  // src/core/CodeQualityPipeline.ts
11951
13077
  import fs18 from "fs-extra";
11952
- import { join as join3 } from "path";
13078
+ import { join as join5 } from "path";
11953
13079
  import { exec as exec2 } from "child_process";
11954
13080
  import { promisify as promisify2 } from "util";
11955
13081
  var execAsync2 = promisify2(exec2);
@@ -12017,7 +13143,7 @@ var CodeQualityPipeline = class {
12017
13143
  * @returns Detected scripts
12018
13144
  */
12019
13145
  async detectScripts(root) {
12020
- const pkgPath = join3(root, "package.json");
13146
+ const pkgPath = join5(root, "package.json");
12021
13147
  if (!await fs18.pathExists(pkgPath)) {
12022
13148
  return {};
12023
13149
  }
@@ -12184,7 +13310,7 @@ var CodeQualityPipeline = class {
12184
13310
  };
12185
13311
 
12186
13312
  // src/core/agent.ts
12187
- var AutohandAgent = class {
13313
+ var AutohandAgent = class _AutohandAgent {
12188
13314
  constructor(llm, files, runtime) {
12189
13315
  this.llm = llm;
12190
13316
  this.files = files;
@@ -12193,6 +13319,8 @@ var AutohandAgent = class {
12193
13319
  this.contextPercentLeft = 100;
12194
13320
  this.toolOutputQueue = Promise.resolve();
12195
13321
  this.workspaceFiles = [];
13322
+ this.workspaceFilesCachedAt = 0;
13323
+ // 5 seconds
12196
13324
  this.isInstructionActive = false;
12197
13325
  this.hasPrintedExplorationHeader = false;
12198
13326
  this.taskStartedAt = null;
@@ -12240,10 +13368,10 @@ var AutohandAgent = class {
12240
13368
  },
12241
13369
  onHookOutput: (result) => {
12242
13370
  if (result.stdout && !result.response) {
12243
- console.log(chalk14.dim(`[hook:${result.hook.event}] ${result.stdout}`));
13371
+ console.log(chalk15.dim(`[hook:${result.hook.event}] ${result.stdout}`));
12244
13372
  }
12245
13373
  if (result.stderr && !result.blockingError) {
12246
- console.error(chalk14.yellow(`[hook:${result.hook.event}] ${result.stderr}`));
13374
+ console.error(chalk15.yellow(`[hook:${result.hook.event}] ${result.stderr}`));
12247
13375
  }
12248
13376
  }
12249
13377
  });
@@ -12412,7 +13540,7 @@ var AutohandAgent = class {
12412
13540
  this.inkRenderer.addQueuedInstruction(text);
12413
13541
  } else if (this.runtime.spinner) {
12414
13542
  const originalText = this.runtime.spinner.text;
12415
- this.runtime.spinner.text = chalk14.cyan(`\u2713 Queued: "${preview}" (${count} pending)`);
13543
+ this.runtime.spinner.text = chalk15.cyan(`\u2713 Queued: "${preview}" (${count} pending)`);
12416
13544
  setTimeout(() => {
12417
13545
  if (this.runtime.spinner) {
12418
13546
  this.runtime.spinner.text = originalText;
@@ -12420,7 +13548,8 @@ var AutohandAgent = class {
12420
13548
  }, 1500);
12421
13549
  }
12422
13550
  });
12423
- this.slashHandler = new SlashCommandHandler({
13551
+ const sessionMgr = this.sessionManager;
13552
+ const slashContext = {
12424
13553
  promptModelSelection: () => this.promptModelSelection(),
12425
13554
  createAgentsFile: () => this.createAgentsFile(),
12426
13555
  sessionManager: this.sessionManager,
@@ -12438,8 +13567,16 @@ var AutohandAgent = class {
12438
13567
  provider: this.activeProvider,
12439
13568
  config: runtime.config,
12440
13569
  getContextPercentLeft: () => this.contextPercentLeft,
12441
- getTotalTokensUsed: () => this.totalTokensUsed
12442
- }, SLASH_COMMANDS);
13570
+ getTotalTokensUsed: () => this.totalTokensUsed,
13571
+ // Share command needs current session - use getter for dynamic access
13572
+ get currentSession() {
13573
+ return sessionMgr.getCurrentSession() ?? void 0;
13574
+ }
13575
+ };
13576
+ this.slashHandler = new SlashCommandHandler(slashContext, SLASH_COMMANDS);
13577
+ }
13578
+ static {
13579
+ this.WORKSPACE_FILES_CACHE_TTL = 5e3;
12443
13580
  }
12444
13581
  async runInteractive() {
12445
13582
  await this.sessionManager.initialize();
@@ -12505,6 +13642,7 @@ var AutohandAgent = class {
12505
13642
  turnDuration,
12506
13643
  tokensUsed: this.sessionTokensUsed
12507
13644
  });
13645
+ this.ensureStdinReady();
12508
13646
  if (this.runtime.config.ui?.terminalBell !== false) {
12509
13647
  process.stdout.write("\x07");
12510
13648
  }
@@ -12516,6 +13654,7 @@ var AutohandAgent = class {
12516
13654
  sessionEndReason: "exit",
12517
13655
  duration: Date.now() - this.sessionStartedAt
12518
13656
  });
13657
+ this.ensureStdinReady();
12519
13658
  await this.telemetryManager.endSession("completed");
12520
13659
  }
12521
13660
  /**
@@ -12525,17 +13664,17 @@ var AutohandAgent = class {
12525
13664
  const info = getAutoCommitInfo(this.runtime.workspaceRoot);
12526
13665
  if (!info.canCommit) {
12527
13666
  if (info.error !== "No changes to commit") {
12528
- console.log(chalk14.yellow(`
13667
+ console.log(chalk15.yellow(`
12529
13668
  \u26A0 Cannot auto-commit: ${info.error}`));
12530
13669
  }
12531
13670
  return;
12532
13671
  }
12533
- console.log(chalk14.cyan("\n\u{1F9E0} Auto-commit: Changes detected"));
13672
+ console.log(chalk15.cyan("\n\u{1F9E0} Auto-commit: Changes detected"));
12534
13673
  info.filesChanged.slice(0, 5).forEach((file) => {
12535
- console.log(chalk14.gray(` ${file}`));
13674
+ console.log(chalk15.gray(` ${file}`));
12536
13675
  });
12537
13676
  if (info.filesChanged.length > 5) {
12538
- console.log(chalk14.gray(` ... and ${info.filesChanged.length - 5} more files`));
13677
+ console.log(chalk15.gray(` ... and ${info.filesChanged.length - 5} more files`));
12539
13678
  }
12540
13679
  const autoCommitPrompt = `You have uncommitted changes in the repository. Please perform the following steps:
12541
13680
 
@@ -12557,11 +13696,11 @@ Diff summary:
12557
13696
  ${info.diffSummary || "Use git diff to see changes"}
12558
13697
 
12559
13698
  If lint or tests fail, report the issues but do NOT commit.`;
12560
- console.log(chalk14.cyan("\n\u{1F504} Running lint, test, and generating commit message...\n"));
13699
+ console.log(chalk15.cyan("\n\u{1F504} Running lint, test, and generating commit message...\n"));
12561
13700
  try {
12562
13701
  await this.runInstruction(autoCommitPrompt);
12563
13702
  } catch (error) {
12564
- console.log(chalk14.red(`
13703
+ console.log(chalk15.red(`
12565
13704
  \u2717 Auto-commit failed: ${error.message}`));
12566
13705
  }
12567
13706
  }
@@ -12602,7 +13741,7 @@ If lint or tests fail, report the issues but do NOT commit.`;
12602
13741
  }
12603
13742
  await this.injectProjectKnowledge();
12604
13743
  this.updateContextUsage(this.conversation.history());
12605
- console.log(chalk14.cyan(`
13744
+ console.log(chalk15.cyan(`
12606
13745
  \u{1F4C2} Resumed session ${sessionId}`));
12607
13746
  await this.telemetryManager.startSession(
12608
13747
  sessionId,
@@ -12611,7 +13750,7 @@ If lint or tests fail, report the issues but do NOT commit.`;
12611
13750
  );
12612
13751
  await this.runInteractiveLoop();
12613
13752
  } catch (error) {
12614
- console.error(chalk14.red(`Failed to resume session: ${error.message}`));
13753
+ console.error(chalk15.red(`Failed to resume session: ${error.message}`));
12615
13754
  await this.telemetryManager.trackError({
12616
13755
  type: "session_resume_failed",
12617
13756
  message: error.message,
@@ -12630,31 +13769,31 @@ If lint or tests fail, report the issues but do NOT commit.`;
12630
13769
  if (this.pendingInkInstructions.length > 0) {
12631
13770
  instruction = this.pendingInkInstructions.shift() ?? null;
12632
13771
  if (instruction) {
12633
- console.log(chalk14.cyan(`
13772
+ console.log(chalk15.cyan(`
12634
13773
  \u25B6 Processing queued request: "${instruction.slice(0, 50)}${instruction.length > 50 ? "..." : ""}"`));
12635
13774
  const remaining = this.pendingInkInstructions.length;
12636
13775
  if (remaining > 0) {
12637
- console.log(chalk14.gray(` ${remaining} more request(s) queued`));
13776
+ console.log(chalk15.gray(` ${remaining} more request(s) queued`));
12638
13777
  }
12639
13778
  }
12640
13779
  } else if (this.inkRenderer?.hasQueuedInstructions()) {
12641
13780
  instruction = this.inkRenderer.dequeueInstruction() ?? null;
12642
13781
  if (instruction) {
12643
- console.log(chalk14.cyan(`
13782
+ console.log(chalk15.cyan(`
12644
13783
  \u25B6 Processing queued request: "${instruction.slice(0, 50)}${instruction.length > 50 ? "..." : ""}"`));
12645
13784
  const remaining = this.inkRenderer.getQueueCount();
12646
13785
  if (remaining > 0) {
12647
- console.log(chalk14.gray(` ${remaining} more request(s) queued`));
13786
+ console.log(chalk15.gray(` ${remaining} more request(s) queued`));
12648
13787
  }
12649
13788
  }
12650
13789
  } else if (this.persistentInput.hasQueued()) {
12651
13790
  const queued = this.persistentInput.dequeue();
12652
13791
  if (queued) {
12653
13792
  instruction = queued.text;
12654
- console.log(chalk14.cyan(`
13793
+ console.log(chalk15.cyan(`
12655
13794
  \u25B6 Processing queued request: "${instruction.slice(0, 50)}${instruction.length > 50 ? "..." : ""}"`));
12656
13795
  if (this.persistentInput.hasQueued()) {
12657
- console.log(chalk14.gray(` ${this.persistentInput.getQueueLength()} more request(s) queued`));
13796
+ console.log(chalk15.gray(` ${this.persistentInput.getQueueLength()} more request(s) queued`));
12658
13797
  }
12659
13798
  }
12660
13799
  }
@@ -12690,6 +13829,7 @@ If lint or tests fail, report the issues but do NOT commit.`;
12690
13829
  turnDuration,
12691
13830
  tokensUsed: this.sessionTokensUsed
12692
13831
  });
13832
+ this.ensureStdinReady();
12693
13833
  if (this.runtime.config.ui?.terminalBell !== false) {
12694
13834
  process.stdout.write("\x07");
12695
13835
  }
@@ -12730,9 +13870,9 @@ If lint or tests fail, report the issues but do NOT commit.`;
12730
13870
  await session.save();
12731
13871
  }
12732
13872
  const errorMessage = error.message || "Unknown error occurred";
12733
- console.error(chalk14.red("\n\u274C An error occurred:"));
12734
- console.error(chalk14.red(errorMessage));
12735
- console.error(chalk14.gray(`Error logged to: ${this.errorLogger.getLogPath()}
13873
+ console.error(chalk15.red("\n\u274C An error occurred:"));
13874
+ console.error(chalk15.red(errorMessage));
13875
+ console.error(chalk15.gray(`Error logged to: ${this.errorLogger.getLogPath()}
12736
13876
  `));
12737
13877
  continue;
12738
13878
  }
@@ -12760,7 +13900,7 @@ If lint or tests fail, report the issues but do NOT commit.`;
12760
13900
  return null;
12761
13901
  }
12762
13902
  if (normalized === "/") {
12763
- console.log(chalk14.gray("Type a slash command name (e.g. /diff) and press Enter."));
13903
+ console.log(chalk15.gray("Type a slash command name (e.g. /diff) and press Enter."));
12764
13904
  return null;
12765
13905
  }
12766
13906
  const looksLikeFilePath = (text) => {
@@ -12797,12 +13937,12 @@ If lint or tests fail, report the issues but do NOT commit.`;
12797
13937
  }
12798
13938
  async handleMemoryStore(content) {
12799
13939
  if (!content) {
12800
- console.log(chalk14.gray("Usage: # <text to remember>"));
12801
- console.log(chalk14.gray("Example: # Always use TypeScript strict mode"));
13940
+ console.log(chalk15.gray("Usage: # <text to remember>"));
13941
+ console.log(chalk15.gray("Example: # Always use TypeScript strict mode"));
12802
13942
  return;
12803
13943
  }
12804
13944
  try {
12805
- const { Select } = enquirer3;
13945
+ const { Select } = enquirer4;
12806
13946
  const prompt = new Select({
12807
13947
  name: "level",
12808
13948
  message: "Where should this memory be stored?",
@@ -12815,9 +13955,9 @@ If lint or tests fail, report the issues but do NOT commit.`;
12815
13955
  const similar = await this.memoryManager.findSimilar(content, level);
12816
13956
  if (similar && similar.score >= 0.6) {
12817
13957
  console.log();
12818
- console.log(chalk14.yellow("Found similar existing memory:"));
12819
- console.log(chalk14.gray(` "${similar.entry.content}"`));
12820
- const { Confirm } = enquirer3;
13958
+ console.log(chalk15.yellow("Found similar existing memory:"));
13959
+ console.log(chalk15.gray(` "${similar.entry.content}"`));
13960
+ const { Confirm } = enquirer4;
12821
13961
  const confirmPrompt = new Confirm({
12822
13962
  name: "update",
12823
13963
  message: "Update the existing memory instead of creating a new one?"
@@ -12825,27 +13965,31 @@ If lint or tests fail, report the issues but do NOT commit.`;
12825
13965
  const shouldUpdate = await confirmPrompt.run();
12826
13966
  if (shouldUpdate) {
12827
13967
  await this.memoryManager.updateMemory(similar.entry.id, content, level);
12828
- console.log(chalk14.green("Memory updated."));
13968
+ console.log(chalk15.green("Memory updated."));
12829
13969
  return;
12830
13970
  }
12831
13971
  }
12832
13972
  await this.memoryManager.store(content, level);
12833
- console.log(chalk14.green(`Memory saved to ${level} level.`));
13973
+ console.log(chalk15.green(`Memory saved to ${level} level.`));
12834
13974
  } catch (error) {
12835
13975
  if (error.isCanceled) {
12836
13976
  return;
12837
13977
  }
12838
- console.error(chalk14.red("Failed to store memory:"), error.message);
13978
+ console.error(chalk15.red("Failed to store memory:"), error.message);
12839
13979
  }
12840
13980
  }
12841
13981
  async listWorkspaceFiles() {
12842
13982
  const entries = await fs19.readdir(this.runtime.workspaceRoot);
12843
13983
  const sorted = entries.sort((a, b) => a.localeCompare(b));
12844
- console.log("\n" + chalk14.cyan("Workspace files:"));
13984
+ console.log("\n" + chalk15.cyan("Workspace files:"));
12845
13985
  console.log(sorted.map((entry) => ` - ${entry}`).join("\n"));
12846
13986
  console.log();
12847
13987
  }
12848
13988
  async collectWorkspaceFiles() {
13989
+ const now = Date.now();
13990
+ if (this.workspaceFiles.length > 0 && now - this.workspaceFilesCachedAt < _AutohandAgent.WORKSPACE_FILES_CACHE_TTL) {
13991
+ return this.workspaceFiles;
13992
+ }
12849
13993
  const git = spawnSync5("git", ["ls-files", "--cached", "--others", "--exclude-standard"], {
12850
13994
  cwd: this.runtime.workspaceRoot,
12851
13995
  encoding: "utf8"
@@ -12858,9 +14002,11 @@ If lint or tests fail, report the issues but do NOT commit.`;
12858
14002
  files.push(file);
12859
14003
  }
12860
14004
  });
14005
+ this.workspaceFilesCachedAt = now;
12861
14006
  return files;
12862
14007
  }
12863
14008
  await this.walkWorkspace(this.runtime.workspaceRoot, files);
14009
+ this.workspaceFilesCachedAt = now;
12864
14010
  return files;
12865
14011
  }
12866
14012
  async walkWorkspace(current, acc) {
@@ -12898,7 +14044,7 @@ If lint or tests fail, report the issues but do NOT commit.`;
12898
14044
  encoding: "utf8"
12899
14045
  });
12900
14046
  if (status.status === 0 && status.stdout) {
12901
- console.log("\n" + chalk14.cyan("Git status:"));
14047
+ console.log("\n" + chalk15.cyan("Git status:"));
12902
14048
  console.log(status.stdout.trim() + "\n");
12903
14049
  }
12904
14050
  const diff = spawnSync5("git", ["diff", "--color=always"], {
@@ -12906,18 +14052,18 @@ If lint or tests fail, report the issues but do NOT commit.`;
12906
14052
  encoding: "utf8"
12907
14053
  });
12908
14054
  if (diff.status === 0) {
12909
- console.log(chalk14.cyan("Git diff:"));
12910
- console.log(diff.stdout || chalk14.gray("No diff."));
14055
+ console.log(chalk15.cyan("Git diff:"));
14056
+ console.log(diff.stdout || chalk15.gray("No diff."));
12911
14057
  } else {
12912
- console.log(chalk14.yellow("Unable to compute git diff. Is this a git repository?"));
14058
+ console.log(chalk15.yellow("Unable to compute git diff. Is this a git repository?"));
12913
14059
  }
12914
14060
  }
12915
14061
  async undoLastMutation() {
12916
14062
  try {
12917
14063
  await this.files.undoLast();
12918
- console.log(chalk14.green("Reverted last mutation."));
14064
+ console.log(chalk15.green("Reverted last mutation."));
12919
14065
  } catch (error) {
12920
- console.log(chalk14.yellow(error.message));
14066
+ console.log(chalk15.yellow(error.message));
12921
14067
  }
12922
14068
  }
12923
14069
  async promptModelSelection() {
@@ -12925,16 +14071,16 @@ If lint or tests fail, report the issues but do NOT commit.`;
12925
14071
  const allProviders = ProviderFactory.getProviderNames();
12926
14072
  const providerChoices = allProviders.map((name) => {
12927
14073
  const isConfigured = this.isProviderConfigured(name);
12928
- const indicator = isConfigured ? chalk14.green("\u25CF") : chalk14.red("\u25CB");
12929
- const current = name === this.activeProvider ? chalk14.cyan(" (current)") : "";
12930
- const siliconNote = name === "mlx" ? chalk14.gray(" (Apple Silicon)") : "";
14074
+ const indicator = isConfigured ? chalk15.green("\u25CF") : chalk15.red("\u25CB");
14075
+ const current = name === this.activeProvider ? chalk15.cyan(" (current)") : "";
14076
+ const siliconNote = name === "mlx" ? chalk15.gray(" (Apple Silicon)") : "";
12931
14077
  return {
12932
14078
  name,
12933
14079
  message: `${indicator} ${name}${current}${siliconNote}`,
12934
14080
  value: name
12935
14081
  };
12936
14082
  });
12937
- const providerAnswer = await enquirer3.prompt([
14083
+ const providerAnswer = await enquirer4.prompt([
12938
14084
  {
12939
14085
  type: "select",
12940
14086
  name: "provider",
@@ -12944,7 +14090,7 @@ If lint or tests fail, report the issues but do NOT commit.`;
12944
14090
  ]);
12945
14091
  const selectedProvider = providerAnswer.provider;
12946
14092
  if (!this.isProviderConfigured(selectedProvider)) {
12947
- console.log(chalk14.yellow(`
14093
+ console.log(chalk15.yellow(`
12948
14094
  ${selectedProvider} is not configured yet. Let's set it up!
12949
14095
  `));
12950
14096
  await this.configureProvider(selectedProvider);
@@ -12953,7 +14099,7 @@ ${selectedProvider} is not configured yet. Let's set it up!
12953
14099
  await this.changeProviderModel(selectedProvider);
12954
14100
  } catch (error) {
12955
14101
  if (error.name === "ExitPromptError" || error.message?.includes("canceled")) {
12956
- console.log(chalk14.gray("\nConfiguration cancelled."));
14102
+ console.log(chalk15.gray("\nConfiguration cancelled."));
12957
14103
  return;
12958
14104
  }
12959
14105
  throw error;
@@ -12988,9 +14134,9 @@ ${selectedProvider} is not configured yet. Let's set it up!
12988
14134
  }
12989
14135
  async configureOpenRouter() {
12990
14136
  try {
12991
- console.log(chalk14.cyan("OpenRouter Configuration"));
12992
- console.log(chalk14.gray("Get your API key at: https://openrouter.ai/keys\n"));
12993
- const answers = await enquirer3.prompt([
14137
+ console.log(chalk15.cyan("OpenRouter Configuration"));
14138
+ console.log(chalk15.gray("Get your API key at: https://openrouter.ai/keys\n"));
14139
+ const answers = await enquirer4.prompt([
12994
14140
  {
12995
14141
  type: "password",
12996
14142
  name: "apiKey",
@@ -13012,10 +14158,10 @@ ${selectedProvider} is not configured yet. Let's set it up!
13012
14158
  this.runtime.options.model = answers.model;
13013
14159
  await saveConfig(this.runtime.config);
13014
14160
  this.resetLlmClient("openrouter", answers.model);
13015
- console.log(chalk14.green("\n\u2713 OpenRouter configured successfully!"));
14161
+ console.log(chalk15.green("\n\u2713 OpenRouter configured successfully!"));
13016
14162
  } catch (error) {
13017
14163
  if (error.name === "ExitPromptError" || error.message?.includes("canceled")) {
13018
- console.log(chalk14.gray("\nConfiguration cancelled."));
14164
+ console.log(chalk15.gray("\nConfiguration cancelled."));
13019
14165
  return;
13020
14166
  }
13021
14167
  throw error;
@@ -13023,8 +14169,8 @@ ${selectedProvider} is not configured yet. Let's set it up!
13023
14169
  }
13024
14170
  async configureOllama() {
13025
14171
  try {
13026
- console.log(chalk14.cyan("Ollama Configuration"));
13027
- console.log(chalk14.gray("Make sure Ollama is running: ollama serve\n"));
14172
+ console.log(chalk15.cyan("Ollama Configuration"));
14173
+ console.log(chalk15.gray("Make sure Ollama is running: ollama serve\n"));
13028
14174
  const ollamaUrl = "http://localhost:11434";
13029
14175
  let availableModels = [];
13030
14176
  try {
@@ -13034,13 +14180,13 @@ ${selectedProvider} is not configured yet. Let's set it up!
13034
14180
  availableModels = data.models?.map((m) => m.name) || [];
13035
14181
  }
13036
14182
  } catch {
13037
- console.log(chalk14.yellow("\u26A0 Could not connect to Ollama. Make sure it's running.\n"));
14183
+ console.log(chalk15.yellow("\u26A0 Could not connect to Ollama. Make sure it's running.\n"));
13038
14184
  }
13039
14185
  let modelAnswer;
13040
14186
  if (availableModels.length > 0) {
13041
- console.log(chalk14.green(`Found ${availableModels.length} model(s)
14187
+ console.log(chalk15.green(`Found ${availableModels.length} model(s)
13042
14188
  `));
13043
- modelAnswer = await enquirer3.prompt([
14189
+ modelAnswer = await enquirer4.prompt([
13044
14190
  {
13045
14191
  type: "select",
13046
14192
  name: "model",
@@ -13049,7 +14195,7 @@ ${selectedProvider} is not configured yet. Let's set it up!
13049
14195
  }
13050
14196
  ]);
13051
14197
  } else {
13052
- modelAnswer = await enquirer3.prompt([
14198
+ modelAnswer = await enquirer4.prompt([
13053
14199
  {
13054
14200
  type: "input",
13055
14201
  name: "model",
@@ -13066,10 +14212,10 @@ ${selectedProvider} is not configured yet. Let's set it up!
13066
14212
  this.runtime.options.model = modelAnswer.model;
13067
14213
  await saveConfig(this.runtime.config);
13068
14214
  this.resetLlmClient("ollama", modelAnswer.model);
13069
- console.log(chalk14.green("\n\u2713 Ollama configured successfully!"));
14215
+ console.log(chalk15.green("\n\u2713 Ollama configured successfully!"));
13070
14216
  } catch (error) {
13071
14217
  if (error.name === "ExitPromptError" || error.message?.includes("canceled")) {
13072
- console.log(chalk14.gray("\nConfiguration cancelled."));
14218
+ console.log(chalk15.gray("\nConfiguration cancelled."));
13073
14219
  return;
13074
14220
  }
13075
14221
  throw error;
@@ -13077,9 +14223,9 @@ ${selectedProvider} is not configured yet. Let's set it up!
13077
14223
  }
13078
14224
  async configureLlamaCpp() {
13079
14225
  try {
13080
- console.log(chalk14.cyan("llama.cpp Configuration"));
13081
- console.log(chalk14.gray("Make sure llama.cpp server is running: ./server -m model.gguf\n"));
13082
- const answers = await enquirer3.prompt([
14226
+ console.log(chalk15.cyan("llama.cpp Configuration"));
14227
+ console.log(chalk15.gray("Make sure llama.cpp server is running: ./server -m model.gguf\n"));
14228
+ const answers = await enquirer4.prompt([
13083
14229
  {
13084
14230
  type: "input",
13085
14231
  name: "port",
@@ -13102,10 +14248,10 @@ ${selectedProvider} is not configured yet. Let's set it up!
13102
14248
  this.runtime.options.model = answers.model;
13103
14249
  await saveConfig(this.runtime.config);
13104
14250
  this.resetLlmClient("llamacpp", answers.model);
13105
- console.log(chalk14.green("\n\u2713 llama.cpp configured successfully!"));
14251
+ console.log(chalk15.green("\n\u2713 llama.cpp configured successfully!"));
13106
14252
  } catch (error) {
13107
14253
  if (error.name === "ExitPromptError" || error.message?.includes("canceled")) {
13108
- console.log(chalk14.gray("\nConfiguration cancelled."));
14254
+ console.log(chalk15.gray("\nConfiguration cancelled."));
13109
14255
  return;
13110
14256
  }
13111
14257
  throw error;
@@ -13113,8 +14259,8 @@ ${selectedProvider} is not configured yet. Let's set it up!
13113
14259
  }
13114
14260
  async configureOpenAI() {
13115
14261
  try {
13116
- console.log(chalk14.cyan("OpenAI Configuration"));
13117
- console.log(chalk14.gray("Get your API key at: https://platform.openai.com/api-keys\n"));
14262
+ console.log(chalk15.cyan("OpenAI Configuration"));
14263
+ console.log(chalk15.gray("Get your API key at: https://platform.openai.com/api-keys\n"));
13118
14264
  const modelChoices = [
13119
14265
  "gpt-4o",
13120
14266
  "gpt-4o-mini",
@@ -13122,7 +14268,7 @@ ${selectedProvider} is not configured yet. Let's set it up!
13122
14268
  "gpt-4",
13123
14269
  "gpt-3.5-turbo"
13124
14270
  ];
13125
- const answers = await enquirer3.prompt([
14271
+ const answers = await enquirer4.prompt([
13126
14272
  {
13127
14273
  type: "password",
13128
14274
  name: "apiKey",
@@ -13144,10 +14290,10 @@ ${selectedProvider} is not configured yet. Let's set it up!
13144
14290
  this.runtime.options.model = answers.model;
13145
14291
  await saveConfig(this.runtime.config);
13146
14292
  this.resetLlmClient("openai", answers.model);
13147
- console.log(chalk14.green("\n\u2713 OpenAI configured successfully!"));
14293
+ console.log(chalk15.green("\n\u2713 OpenAI configured successfully!"));
13148
14294
  } catch (error) {
13149
14295
  if (error.name === "ExitPromptError" || error.message?.includes("canceled")) {
13150
- console.log(chalk14.gray("\nConfiguration cancelled."));
14296
+ console.log(chalk15.gray("\nConfiguration cancelled."));
13151
14297
  return;
13152
14298
  }
13153
14299
  throw error;
@@ -13155,9 +14301,9 @@ ${selectedProvider} is not configured yet. Let's set it up!
13155
14301
  }
13156
14302
  async configureMLX() {
13157
14303
  try {
13158
- console.log(chalk14.cyan("MLX Configuration (Apple Silicon)"));
13159
- console.log(chalk14.gray("MLX provides local LLM inference optimized for Apple Silicon."));
13160
- console.log(chalk14.gray("Make sure mlx-lm server is running: mlx_lm.server --model <model-name>\n"));
14304
+ console.log(chalk15.cyan("MLX Configuration (Apple Silicon)"));
14305
+ console.log(chalk15.gray("MLX provides local LLM inference optimized for Apple Silicon."));
14306
+ console.log(chalk15.gray("Make sure mlx-lm server is running: mlx_lm.server --model <model-name>\n"));
13161
14307
  const mlxUrl = "http://localhost:8080";
13162
14308
  let availableModels = [];
13163
14309
  try {
@@ -13167,11 +14313,11 @@ ${selectedProvider} is not configured yet. Let's set it up!
13167
14313
  availableModels = data.data?.map((m) => m.id) || [];
13168
14314
  }
13169
14315
  } catch {
13170
- console.log(chalk14.yellow("\u26A0 Could not connect to MLX server. Make sure it's running.\n"));
14316
+ console.log(chalk15.yellow("\u26A0 Could not connect to MLX server. Make sure it's running.\n"));
13171
14317
  }
13172
14318
  let modelAnswer;
13173
14319
  if (availableModels.length > 0) {
13174
- modelAnswer = await enquirer3.prompt([
14320
+ modelAnswer = await enquirer4.prompt([
13175
14321
  {
13176
14322
  type: "select",
13177
14323
  name: "model",
@@ -13180,7 +14326,7 @@ ${selectedProvider} is not configured yet. Let's set it up!
13180
14326
  }
13181
14327
  ]);
13182
14328
  } else {
13183
- modelAnswer = await enquirer3.prompt([
14329
+ modelAnswer = await enquirer4.prompt([
13184
14330
  {
13185
14331
  type: "input",
13186
14332
  name: "model",
@@ -13197,10 +14343,10 @@ ${selectedProvider} is not configured yet. Let's set it up!
13197
14343
  this.runtime.options.model = modelAnswer.model;
13198
14344
  await saveConfig(this.runtime.config);
13199
14345
  this.resetLlmClient("mlx", modelAnswer.model);
13200
- console.log(chalk14.green("\n\u2713 MLX configured successfully!"));
14346
+ console.log(chalk15.green("\n\u2713 MLX configured successfully!"));
13201
14347
  } catch (error) {
13202
14348
  if (error.name === "ExitPromptError" || error.message?.includes("canceled")) {
13203
- console.log(chalk14.gray("\nConfiguration cancelled."));
14349
+ console.log(chalk15.gray("\nConfiguration cancelled."));
13204
14350
  return;
13205
14351
  }
13206
14352
  throw error;
@@ -13210,6 +14356,10 @@ ${selectedProvider} is not configured yet. Let's set it up!
13210
14356
  try {
13211
14357
  const currentSettings = getProviderConfig(this.runtime.config, provider);
13212
14358
  const currentModel = this.runtime.options.model ?? currentSettings?.model ?? "";
14359
+ if (provider === "openai" || provider === "openrouter") {
14360
+ await this.changeCloudProviderSettings(provider, currentModel, currentSettings);
14361
+ return;
14362
+ }
13213
14363
  if (provider === "ollama" && currentSettings?.baseUrl) {
13214
14364
  try {
13215
14365
  const response = await fetch(`${currentSettings.baseUrl}/api/tags`);
@@ -13217,7 +14367,7 @@ ${selectedProvider} is not configured yet. Let's set it up!
13217
14367
  const data = await response.json();
13218
14368
  const models = data.models?.map((m) => m.name) || [];
13219
14369
  if (models.length > 0) {
13220
- const answer2 = await enquirer3.prompt([
14370
+ const answer2 = await enquirer4.prompt([
13221
14371
  {
13222
14372
  type: "select",
13223
14373
  name: "model",
@@ -13233,21 +14383,7 @@ ${selectedProvider} is not configured yet. Let's set it up!
13233
14383
  } catch {
13234
14384
  }
13235
14385
  }
13236
- if (provider === "openai") {
13237
- const models = ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "gpt-4", "gpt-3.5-turbo"];
13238
- const answer2 = await enquirer3.prompt([
13239
- {
13240
- type: "select",
13241
- name: "model",
13242
- message: "Select a model",
13243
- choices: models,
13244
- initial: Math.max(0, models.indexOf(currentModel))
13245
- }
13246
- ]);
13247
- await this.applyModelChange(provider, answer2.model, currentModel);
13248
- return;
13249
- }
13250
- const answer = await enquirer3.prompt([
14386
+ const answer = await enquirer4.prompt([
13251
14387
  {
13252
14388
  type: "input",
13253
14389
  name: "model",
@@ -13258,15 +14394,174 @@ ${selectedProvider} is not configured yet. Let's set it up!
13258
14394
  await this.applyModelChange(provider, answer.model?.trim(), currentModel);
13259
14395
  } catch (error) {
13260
14396
  if (error.name === "ExitPromptError" || error.message?.includes("canceled")) {
13261
- console.log(chalk14.gray("\nModel change cancelled."));
14397
+ console.log(chalk15.gray("\nModel change cancelled."));
13262
14398
  return;
13263
14399
  }
13264
14400
  throw error;
13265
14401
  }
13266
14402
  }
14403
+ async changeCloudProviderSettings(provider, currentModel, currentSettings) {
14404
+ const providerName = provider === "openai" ? "OpenAI" : "OpenRouter";
14405
+ const maskedKey = currentSettings?.apiKey ? `...${currentSettings.apiKey.slice(-4)}` : "not set";
14406
+ console.log(chalk15.cyan(`
14407
+ ${providerName} Settings`));
14408
+ console.log(chalk15.gray(`Current model: ${currentModel || "not set"}`));
14409
+ console.log(chalk15.gray(`Current API key: ${maskedKey}
14410
+ `));
14411
+ const { action } = await enquirer4.prompt([
14412
+ {
14413
+ type: "select",
14414
+ name: "action",
14415
+ message: "What would you like to change?",
14416
+ choices: [
14417
+ { name: "model", message: "Change model only" },
14418
+ { name: "apiKey", message: "Change API key only" },
14419
+ { name: "both", message: "Change both model and API key" }
14420
+ ]
14421
+ }
14422
+ ]);
14423
+ let newModel = currentModel;
14424
+ let newApiKey = currentSettings?.apiKey || "";
14425
+ if (action === "apiKey" || action === "both") {
14426
+ const keyUrl = provider === "openai" ? "https://platform.openai.com/api-keys" : "https://openrouter.ai/keys";
14427
+ console.log(chalk15.gray(`
14428
+ Get your API key at: ${keyUrl}
14429
+ `));
14430
+ const { apiKey } = await enquirer4.prompt([
14431
+ {
14432
+ type: "password",
14433
+ name: "apiKey",
14434
+ message: `Enter your ${providerName} API key`,
14435
+ validate: (val) => {
14436
+ const v = val;
14437
+ if (!v?.trim()) return "API key is required";
14438
+ if (v.length < 10) return "API key seems too short";
14439
+ return true;
14440
+ }
14441
+ }
14442
+ ]);
14443
+ console.log(chalk15.gray("\nValidating API key..."));
14444
+ const validationResult = await this.validateApiKey(provider, apiKey.trim());
14445
+ if (!validationResult.valid) {
14446
+ console.log(chalk15.red(`
14447
+ \u2717 ${validationResult.error}`));
14448
+ console.log(chalk15.gray(validationResult.hint || ""));
14449
+ return;
14450
+ }
14451
+ console.log(chalk15.green("\u2713 API key is valid\n"));
14452
+ newApiKey = apiKey.trim();
14453
+ }
14454
+ if (action === "model" || action === "both") {
14455
+ if (provider === "openai") {
14456
+ const models = ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "gpt-4", "gpt-3.5-turbo", "o1", "o1-mini"];
14457
+ const { model } = await enquirer4.prompt([
14458
+ {
14459
+ type: "select",
14460
+ name: "model",
14461
+ message: "Select a model",
14462
+ choices: models,
14463
+ initial: Math.max(0, models.indexOf(currentModel))
14464
+ }
14465
+ ]);
14466
+ newModel = model;
14467
+ } else {
14468
+ const { model } = await enquirer4.prompt([
14469
+ {
14470
+ type: "input",
14471
+ name: "model",
14472
+ message: "Enter the model ID",
14473
+ initial: currentModel || "anthropic/claude-sonnet-4-20250514"
14474
+ }
14475
+ ]);
14476
+ newModel = model.trim();
14477
+ }
14478
+ }
14479
+ const baseUrl = provider === "openai" ? "https://api.openai.com/v1" : "https://openrouter.ai/api/v1";
14480
+ this.runtime.config[provider] = {
14481
+ apiKey: newApiKey,
14482
+ baseUrl,
14483
+ model: newModel
14484
+ };
14485
+ this.runtime.config.provider = provider;
14486
+ this.runtime.options.model = newModel;
14487
+ await saveConfig(this.runtime.config);
14488
+ this.resetLlmClient(provider, newModel);
14489
+ this.contextWindow = getContextWindow(newModel);
14490
+ this.contextPercentLeft = 100;
14491
+ this.emitStatus();
14492
+ console.log(chalk15.green(`
14493
+ \u2713 ${providerName} settings updated successfully!`));
14494
+ console.log(chalk15.gray(` Provider: ${provider}`));
14495
+ console.log(chalk15.gray(` Model: ${newModel}`));
14496
+ }
14497
+ async validateApiKey(provider, apiKey) {
14498
+ try {
14499
+ const baseUrl = provider === "openai" ? "https://api.openai.com/v1" : "https://openrouter.ai/api/v1";
14500
+ const response = await fetch(`${baseUrl}/models`, {
14501
+ method: "GET",
14502
+ headers: {
14503
+ "Authorization": `Bearer ${apiKey}`,
14504
+ "Content-Type": "application/json",
14505
+ ...provider === "openrouter" && {
14506
+ "HTTP-Referer": "https://autohand.dev",
14507
+ "X-Title": "Autohand CLI"
14508
+ }
14509
+ }
14510
+ });
14511
+ if (response.ok) {
14512
+ return { valid: true };
14513
+ }
14514
+ const status = response.status;
14515
+ let errorData = {};
14516
+ try {
14517
+ errorData = await response.json();
14518
+ } catch {
14519
+ }
14520
+ if (status === 401) {
14521
+ return {
14522
+ valid: false,
14523
+ error: "Invalid API key",
14524
+ hint: provider === "openai" ? "Check that your API key is correct at https://platform.openai.com/api-keys" : "Check that your API key is correct at https://openrouter.ai/keys"
14525
+ };
14526
+ }
14527
+ if (status === 403) {
14528
+ return {
14529
+ valid: false,
14530
+ error: "API key does not have permission",
14531
+ hint: "Your API key may have restricted permissions or your account may need to add a payment method."
14532
+ };
14533
+ }
14534
+ if (status === 429) {
14535
+ return {
14536
+ valid: false,
14537
+ error: "Rate limited or quota exceeded",
14538
+ hint: "You may have exceeded your API quota. Check your usage and billing settings."
14539
+ };
14540
+ }
14541
+ return {
14542
+ valid: false,
14543
+ error: errorData?.error?.message || `API returned status ${status}`,
14544
+ hint: "Please verify your API key and try again."
14545
+ };
14546
+ } catch (error) {
14547
+ const err = error;
14548
+ if (err.message?.includes("fetch") || err.message?.includes("network")) {
14549
+ return {
14550
+ valid: false,
14551
+ error: "Network error - could not reach the API",
14552
+ hint: "Check your internet connection and try again."
14553
+ };
14554
+ }
14555
+ return {
14556
+ valid: false,
14557
+ error: `Validation failed: ${err.message}`,
14558
+ hint: "Please try again or check your network connection."
14559
+ };
14560
+ }
14561
+ }
13267
14562
  async applyModelChange(provider, newModel, currentModel) {
13268
14563
  if (!newModel || newModel === currentModel && provider === this.activeProvider) {
13269
- console.log(chalk14.gray("Model unchanged."));
14564
+ console.log(chalk15.gray("Model unchanged."));
13270
14565
  return;
13271
14566
  }
13272
14567
  const previousModel = this.runtime.options.model;
@@ -13283,10 +14578,10 @@ ${selectedProvider} is not configured yet. Let's set it up!
13283
14578
  toModel: newModel,
13284
14579
  provider
13285
14580
  });
13286
- console.log(chalk14.green(`\u2713 Using ${provider} model ${newModel}`));
14581
+ console.log(chalk15.green(`\u2713 Using ${provider} model ${newModel}`));
13287
14582
  }
13288
14583
  async promptApprovalMode() {
13289
- const answer = await enquirer3.prompt([
14584
+ const answer = await enquirer4.prompt([
13290
14585
  {
13291
14586
  type: "select",
13292
14587
  name: "mode",
@@ -13300,21 +14595,37 @@ ${selectedProvider} is not configured yet. Let's set it up!
13300
14595
  ]);
13301
14596
  this.runtime.options.yes = answer.mode === "prompt";
13302
14597
  console.log(
13303
- answer.mode === "prompt" ? chalk14.yellow("Auto-confirm enabled. Use responsibly.") : chalk14.green("Manual approvals required before risky writes.")
14598
+ answer.mode === "prompt" ? chalk15.yellow("Auto-confirm enabled. Use responsibly.") : chalk15.green("Manual approvals required before risky writes.")
13304
14599
  );
13305
14600
  }
13306
14601
  async createAgentsFile() {
13307
14602
  const target = path18.join(this.runtime.workspaceRoot, "AGENTS.md");
13308
14603
  if (await fs19.pathExists(target)) {
13309
- console.log(chalk14.gray("AGENTS.md already exists in this workspace."));
14604
+ console.log(chalk15.gray("AGENTS.md already exists in this workspace."));
13310
14605
  return;
13311
14606
  }
13312
- const template = `# Project Autopilot
13313
-
13314
- Describe how Autohand should work in this repo. Include framework commands, testing requirements, and any constraints.
13315
- `;
13316
- await fs19.writeFile(target, template, "utf8");
13317
- console.log(chalk14.green("Created AGENTS.md template. Customize it to guide the agent."));
14607
+ console.log(chalk15.gray("Analyzing project structure..."));
14608
+ const analyzer = new ProjectAnalyzer(this.runtime.workspaceRoot);
14609
+ const projectInfo = await analyzer.analyze();
14610
+ if (Object.keys(projectInfo).length > 0) {
14611
+ console.log(chalk15.gray("Detected:"));
14612
+ if (projectInfo.language) {
14613
+ console.log(chalk15.white(` - Language: ${projectInfo.language}`));
14614
+ }
14615
+ if (projectInfo.framework) {
14616
+ console.log(chalk15.white(` - Framework: ${projectInfo.framework}`));
14617
+ }
14618
+ if (projectInfo.packageManager) {
14619
+ console.log(chalk15.white(` - Package manager: ${projectInfo.packageManager}`));
14620
+ }
14621
+ if (projectInfo.testFramework) {
14622
+ console.log(chalk15.white(` - Test framework: ${projectInfo.testFramework}`));
14623
+ }
14624
+ }
14625
+ const generator = new AgentsGenerator();
14626
+ const content = generator.generateContent(projectInfo);
14627
+ await fs19.writeFile(target, content, "utf8");
14628
+ console.log(chalk15.green("Created AGENTS.md based on your project. Customize it to guide the agent."));
13318
14629
  }
13319
14630
  /**
13320
14631
  * Detect if instruction is simple chat that doesn't need tools
@@ -13356,7 +14667,7 @@ Describe how Autohand should work in this repo. Include framework commands, test
13356
14667
  return true;
13357
14668
  } catch (error) {
13358
14669
  if (error instanceof Error) {
13359
- console.error(chalk14.red(error.message));
14670
+ console.error(chalk15.red(error.message));
13360
14671
  }
13361
14672
  return false;
13362
14673
  } finally {
@@ -13375,7 +14686,7 @@ Describe how Autohand should work in this repo. Include framework commands, test
13375
14686
  if (intentResult.intent === "implementation") {
13376
14687
  const bootstrapResult = await this.runEnvironmentBootstrap();
13377
14688
  if (!bootstrapResult.success) {
13378
- console.log(chalk14.red("\n[BLOCKED] Environment setup failed. Fix issues before proceeding."));
14689
+ console.log(chalk15.red("\n[BLOCKED] Environment setup failed. Fix issues before proceeding."));
13379
14690
  this.isInstructionActive = false;
13380
14691
  return false;
13381
14692
  }
@@ -13388,7 +14699,7 @@ Describe how Autohand should work in this repo. Include framework commands, test
13388
14699
  canceledByUser = true;
13389
14700
  this.stopStatusUpdates();
13390
14701
  this.stopUI();
13391
- console.log("\n" + chalk14.yellow("Request canceled by user (ESC)."));
14702
+ console.log("\n" + chalk15.yellow("Request canceled by user (ESC)."));
13392
14703
  }
13393
14704
  });
13394
14705
  const cleanupEsc = this.useInkRenderer ? () => {
@@ -13397,7 +14708,7 @@ Describe how Autohand should work in this repo. Include framework commands, test
13397
14708
  canceledByUser = true;
13398
14709
  this.stopStatusUpdates();
13399
14710
  this.stopUI();
13400
- console.log("\n" + chalk14.yellow("Request canceled by user (ESC)."));
14711
+ console.log("\n" + chalk15.yellow("Request canceled by user (ESC)."));
13401
14712
  }
13402
14713
  }, true);
13403
14714
  const stopPreparation = this.startPreparationStatus(instruction);
@@ -13419,7 +14730,7 @@ Describe how Autohand should work in this repo. Include framework commands, test
13419
14730
  }
13420
14731
  if (error instanceof ProviderNotConfiguredError) {
13421
14732
  this.cleanupUI();
13422
- console.log(chalk14.yellow(`
14733
+ console.log(chalk15.yellow(`
13423
14734
  No provider is configured yet. Let's set one up!
13424
14735
  `));
13425
14736
  await this.promptModelSelection();
@@ -13431,9 +14742,9 @@ No provider is configured yet. Let's set one up!
13431
14742
  if (this.isRetryableSessionError(err) && this.sessionRetryCount < maxRetries) {
13432
14743
  this.sessionRetryCount++;
13433
14744
  await this.submitSessionFailureBugReport(err, this.sessionRetryCount, maxRetries);
13434
- console.log(chalk14.yellow(`
14745
+ console.log(chalk15.yellow(`
13435
14746
  \u26A0 Session encountered an error: ${err.message}`));
13436
- console.log(chalk14.cyan(` Attempting recovery (${this.sessionRetryCount}/${maxRetries})...`));
14747
+ console.log(chalk15.cyan(` Attempting recovery (${this.sessionRetryCount}/${maxRetries})...`));
13437
14748
  const delay = baseDelay * Math.pow(1.5, this.sessionRetryCount - 1);
13438
14749
  await this.sleep(delay);
13439
14750
  this.injectContinuationMessage(err, this.sessionRetryCount);
@@ -13456,7 +14767,7 @@ No provider is configured yet. Let's set one up!
13456
14767
  const errorMessage = error instanceof Error ? error.message : String(error);
13457
14768
  this.emitOutput({ type: "error", content: errorMessage });
13458
14769
  if (error instanceof Error) {
13459
- console.error(chalk14.red(error.message));
14770
+ console.error(chalk15.red(error.message));
13460
14771
  } else {
13461
14772
  console.error(error);
13462
14773
  }
@@ -13470,7 +14781,7 @@ No provider is configured yet. Let's set one up!
13470
14781
  const tokens = this.formatTokens(this.totalTokensUsed);
13471
14782
  const queueCount = this.pendingInkInstructions.length + (this.inkRenderer?.getQueueCount() ?? 0) + this.persistentInput.getQueueLength();
13472
14783
  const queueStatus = queueCount > 0 ? ` \xB7 ${queueCount} queued` : "";
13473
- console.log(chalk14.gray(`
14784
+ console.log(chalk15.gray(`
13474
14785
  Completed in ${elapsed} \xB7 ${tokens} used${queueStatus}`));
13475
14786
  }
13476
14787
  this.sessionTokensUsed += this.totalTokensUsed;
@@ -13546,7 +14857,7 @@ Completed in ${elapsed} \xB7 ${tokens} used${queueStatus}`));
13546
14857
  duration: sessionDuration
13547
14858
  });
13548
14859
  if (!session) {
13549
- console.log(chalk14.gray("Ending Autohand session."));
14860
+ console.log(chalk15.gray("Ending Autohand session."));
13550
14861
  await this.telemetryManager.shutdown();
13551
14862
  return;
13552
14863
  }
@@ -13566,12 +14877,12 @@ Completed in ${elapsed} \xB7 ${tokens} used${queueStatus}`));
13566
14877
  await this.telemetryManager.endSession("completed");
13567
14878
  await this.telemetryManager.shutdown();
13568
14879
  await this.sessionManager.closeSession(summary);
13569
- console.log(chalk14.gray("\nEnding Autohand session.\n"));
13570
- console.log(chalk14.cyan(`\u{1F4BE} Session saved: ${session.metadata.sessionId}`));
14880
+ console.log(chalk15.gray("\nEnding Autohand session.\n"));
14881
+ console.log(chalk15.cyan(`\u{1F4BE} Session saved: ${session.metadata.sessionId}`));
13571
14882
  if (syncResult.success) {
13572
- console.log(chalk14.gray(` Synced to cloud: ${syncResult.id}`));
14883
+ console.log(chalk15.gray(` Synced to cloud: ${syncResult.id}`));
13573
14884
  }
13574
- console.log(chalk14.gray(` Resume with: autohand resume ${session.metadata.sessionId}
14885
+ console.log(chalk15.gray(` Resume with: autohand resume ${session.metadata.sessionId}
13575
14886
  `));
13576
14887
  }
13577
14888
  async runReactLoop(abortController) {
@@ -13599,7 +14910,7 @@ Completed in ${elapsed} \xB7 ${tokens} used${queueStatus}`));
13599
14910
  );
13600
14911
  if (contextUsage.isCritical) {
13601
14912
  this.runtime.spinner?.stop();
13602
- console.log(chalk14.yellow("\n\u26A0 Context at critical level, auto-cropping old messages..."));
14913
+ console.log(chalk15.yellow("\n\u26A0 Context at critical level, auto-cropping old messages..."));
13603
14914
  const targetTokens = Math.floor(contextUsage.contextWindow * 0.7);
13604
14915
  const tokensToRemove = contextUsage.totalTokens - targetTokens;
13605
14916
  const avgMessageTokens = 200;
@@ -13612,12 +14923,12 @@ Completed in ${elapsed} \xB7 ${tokens} used${queueStatus}`));
13612
14923
  Summary of removed content:
13613
14924
  ${summary}`
13614
14925
  );
13615
- console.log(chalk14.gray(` Removed ${removed.length} messages to free up context space`));
13616
- console.log(chalk14.gray(` Summary preserved in context`));
14926
+ console.log(chalk15.gray(` Removed ${removed.length} messages to free up context space`));
14927
+ console.log(chalk15.gray(` Summary preserved in context`));
13617
14928
  }
13618
14929
  this.updateContextUsage(this.conversation.history(), tools);
13619
14930
  } else if (contextUsage.isWarning && iteration === 0) {
13620
- console.log(chalk14.yellow(`
14931
+ console.log(chalk15.yellow(`
13621
14932
  \u26A0 Context at ${Math.round(contextUsage.usagePercent * 100)}% - approaching limit`));
13622
14933
  }
13623
14934
  if (iteration >= 10 && iteration % 10 === 0) {
@@ -13630,6 +14941,7 @@ ${summary}`
13630
14941
  `);
13631
14942
  let completion;
13632
14943
  try {
14944
+ const thinkingLevel = process.env.AUTOHAND_THINKING_LEVEL || "normal";
13633
14945
  completion = await this.llm.complete({
13634
14946
  messages: messagesWithImages,
13635
14947
  temperature: this.runtime.options.temperature ?? 0.2,
@@ -13637,8 +14949,9 @@ ${summary}`
13637
14949
  signal: abortController.signal,
13638
14950
  tools: tools.length > 0 ? tools : void 0,
13639
14951
  toolChoice: tools.length > 0 ? "auto" : void 0,
13640
- maxTokens: 16e3
14952
+ maxTokens: 16e3,
13641
14953
  // Allow large outputs for file generation
14954
+ thinkingLevel
13642
14955
  });
13643
14956
  if (debugMode) process.stderr.write(`[AGENT DEBUG] LLM returned: content length=${completion.content?.length ?? 0}, toolCalls=${completion.toolCalls?.length ?? 0}
13644
14957
  `);
@@ -13666,12 +14979,12 @@ ${summary}`
13666
14979
  await this.saveAssistantMessage(completion.content, payload.toolCalls);
13667
14980
  this.updateContextUsage(this.conversation.history(), tools);
13668
14981
  if (debugMode) {
13669
- console.log(chalk14.yellow(`
14982
+ console.log(chalk15.yellow(`
13670
14983
  [DEBUG] Iteration ${iteration}:`));
13671
- console.log(chalk14.yellow(` - toolCalls: ${payload.toolCalls?.length ?? 0}`));
13672
- console.log(chalk14.yellow(` - thought: ${payload.thought?.slice(0, 100) || "(none)"}`));
13673
- console.log(chalk14.yellow(` - finalResponse: ${payload.finalResponse?.slice(0, 100) || "(none)"}`));
13674
- console.log(chalk14.yellow(` - raw content: ${completion.content?.slice(0, 200) || "(empty)"}`));
14984
+ console.log(chalk15.yellow(` - toolCalls: ${payload.toolCalls?.length ?? 0}`));
14985
+ console.log(chalk15.yellow(` - thought: ${payload.thought?.slice(0, 100) || "(none)"}`));
14986
+ console.log(chalk15.yellow(` - finalResponse: ${payload.finalResponse?.slice(0, 100) || "(none)"}`));
14987
+ console.log(chalk15.yellow(` - raw content: ${completion.content?.slice(0, 200) || "(empty)"}`));
13675
14988
  }
13676
14989
  const toolCount = payload.toolCalls?.length ?? 0;
13677
14990
  const hasResponse = Boolean(payload.finalResponse || payload.response || !toolCount && payload.thought);
@@ -13688,7 +15001,7 @@ ${summary}`
13688
15001
  } else {
13689
15002
  if (iteration > 0) {
13690
15003
  const status = toolCount > 0 ? `\u2192 Step ${iteration + 1}: calling ${toolCount} tool(s)` : hasResponse ? `\u2192 Step ${iteration + 1}: preparing response` : `\u2192 Step ${iteration + 1}: thinking...`;
13691
- console.log(chalk14.gray(status));
15004
+ console.log(chalk15.gray(status));
13692
15005
  }
13693
15006
  }
13694
15007
  if (payload.toolCalls && payload.toolCalls.length > 0) {
@@ -13707,8 +15020,8 @@ ${summary}`
13707
15020
  });
13708
15021
  await this.saveToolMessage("smart_context_cropper", content, call.id);
13709
15022
  this.updateContextUsage(this.conversation.history(), tools);
13710
- outputLines.push(`${chalk14.cyan("\u2702 smart_context_cropper")}`);
13711
- outputLines.push(chalk14.gray(content));
15023
+ outputLines.push(`${chalk15.cyan("\u2702 smart_context_cropper")}`);
15024
+ outputLines.push(chalk15.gray(content));
13712
15025
  outputLines.push("");
13713
15026
  }
13714
15027
  }
@@ -13826,6 +15139,7 @@ ${summary}`
13826
15139
  }
13827
15140
  this.stopStatusUpdates();
13828
15141
  let rawResponse;
15142
+ const usedThoughtAsResponse = Boolean(payload.thought) && !payload.finalResponse && !payload.response && !payload.toolCalls?.length;
13829
15143
  if (payload.finalResponse) {
13830
15144
  rawResponse = payload.finalResponse;
13831
15145
  } else if (payload.response) {
@@ -13836,7 +15150,10 @@ ${summary}`
13836
15150
  const cleanedContent = this.cleanupModelResponse(completion.content);
13837
15151
  rawResponse = cleanedContent.startsWith("{") ? "" : cleanedContent;
13838
15152
  }
13839
- const response = this.cleanupModelResponse(rawResponse.trim());
15153
+ let response = this.cleanupModelResponse(rawResponse.trim());
15154
+ if (!response && usedThoughtAsResponse && payload.thought) {
15155
+ response = payload.thought.trim();
15156
+ }
13840
15157
  if (!response && iteration > 0) {
13841
15158
  const consecutiveEmptyKey = "__consecutiveEmpty";
13842
15159
  const consecutiveEmpty = (this[consecutiveEmptyKey] ?? 0) + 1;
@@ -13844,7 +15161,7 @@ ${summary}`
13844
15161
  if (consecutiveEmpty >= 3) {
13845
15162
  if (debugMode) process.stderr.write(`[AGENT DEBUG] Exiting after 3 consecutive empty responses
13846
15163
  `);
13847
- console.log(chalk14.yellow("\n\u26A0 Model not providing response after multiple attempts. Showing available context."));
15164
+ console.log(chalk15.yellow("\n\u26A0 Model not providing response after multiple attempts. Showing available context."));
13848
15165
  const fallback = payload.thought || "The model did not provide a clear response. Please try rephrasing your question.";
13849
15166
  if (this.inkRenderer) {
13850
15167
  this.inkRenderer.setWorking(false);
@@ -13863,12 +15180,13 @@ ${summary}`
13863
15180
  continue;
13864
15181
  }
13865
15182
  this.__consecutiveEmpty = 0;
13866
- if (payload.thought) {
15183
+ const suppressThinking = usedThoughtAsResponse && response.length > 0;
15184
+ if (payload.thought && !suppressThinking) {
13867
15185
  this.emitOutput({ type: "thinking", thought: payload.thought });
13868
15186
  }
13869
15187
  this.emitOutput({ type: "message", content: response });
13870
15188
  if (this.inkRenderer) {
13871
- if (showThinking && payload.thought) {
15189
+ if (showThinking && payload.thought && !suppressThinking) {
13872
15190
  this.inkRenderer.setThinking(payload.thought);
13873
15191
  }
13874
15192
  this.inkRenderer.setElapsed(this.formatElapsedTime(this.sessionStartedAt));
@@ -13877,8 +15195,8 @@ ${summary}`
13877
15195
  this.inkRenderer.setFinalResponse(response);
13878
15196
  } else {
13879
15197
  this.runtime.spinner?.stop();
13880
- if (showThinking && payload.thought && !payload.thought.trim().startsWith("{")) {
13881
- console.log(chalk14.gray(`Thinking: ${payload.thought}`));
15198
+ if (showThinking && payload.thought && !suppressThinking && !payload.thought.trim().startsWith("{")) {
15199
+ console.log(chalk15.gray(`Thinking: ${payload.thought}`));
13882
15200
  console.log();
13883
15201
  }
13884
15202
  console.log(response);
@@ -13887,10 +15205,10 @@ ${summary}`
13887
15205
  }
13888
15206
  this.stopStatusUpdates();
13889
15207
  this.runtime.spinner?.stop();
13890
- console.log(chalk14.yellow(`
15208
+ console.log(chalk15.yellow(`
13891
15209
  \u26A0 Task exceeded ${maxIterations} tool iterations without completing.`));
13892
- console.log(chalk14.gray("This usually means the task is too complex for a single turn."));
13893
- console.log(chalk14.gray("Try breaking it into smaller steps or use /new to start fresh."));
15210
+ console.log(chalk15.gray("This usually means the task is too complex for a single turn."));
15211
+ console.log(chalk15.gray("Try breaking it into smaller steps or use /new to start fresh."));
13894
15212
  throw new Error(`Reached maximum iterations (${maxIterations}) while processing. Try a simpler request or break the task into smaller steps.`);
13895
15213
  }
13896
15214
  /**
@@ -13919,8 +15237,8 @@ ${summary}`
13919
15237
  toolCalls: completion.toolCalls.map((tc) => {
13920
15238
  const rawArgs = tc.function.arguments;
13921
15239
  if (!rawArgs || rawArgs === "{}" || rawArgs === "") {
13922
- console.error(chalk14.yellow(`\u26A0 Tool "${tc.function.name}" has empty/missing arguments`));
13923
- console.error(chalk14.gray(` Raw arguments: "${rawArgs}"`));
15240
+ console.error(chalk15.yellow(`\u26A0 Tool "${tc.function.name}" has empty/missing arguments`));
15241
+ console.error(chalk15.gray(` Raw arguments: "${rawArgs}"`));
13924
15242
  }
13925
15243
  return {
13926
15244
  id: tc.id,
@@ -13937,7 +15255,7 @@ ${summary}`
13937
15255
  */
13938
15256
  safeParseToolArgs(json) {
13939
15257
  if (!json || typeof json !== "string") {
13940
- console.error(chalk14.yellow("\u26A0 Tool arguments empty or not a string"));
15258
+ console.error(chalk15.yellow("\u26A0 Tool arguments empty or not a string"));
13941
15259
  return void 0;
13942
15260
  }
13943
15261
  try {
@@ -13945,11 +15263,11 @@ ${summary}`
13945
15263
  if (parsed && typeof parsed === "object") {
13946
15264
  return parsed;
13947
15265
  }
13948
- console.error(chalk14.yellow(`\u26A0 Tool arguments parsed but not an object: ${typeof parsed}`));
15266
+ console.error(chalk15.yellow(`\u26A0 Tool arguments parsed but not an object: ${typeof parsed}`));
13949
15267
  return void 0;
13950
15268
  } catch (err) {
13951
- console.error(chalk14.yellow(`\u26A0 Failed to parse tool arguments: ${err instanceof Error ? err.message : String(err)}`));
13952
- console.error(chalk14.gray(` Raw JSON: ${json.slice(0, 200)}${json.length > 200 ? "..." : ""}`));
15269
+ console.error(chalk15.yellow(`\u26A0 Failed to parse tool arguments: ${err instanceof Error ? err.message : String(err)}`));
15270
+ console.error(chalk15.gray(` Raw JSON: ${json.slice(0, 200)}${json.length > 200 ? "..." : ""}`));
13953
15271
  return void 0;
13954
15272
  }
13955
15273
  }
@@ -14465,7 +15783,7 @@ ${toolSignatures}
14465
15783
  const contents = await this.files.readFile(file);
14466
15784
  this.mentionContexts.push({ path: file, contents: this.trimContext(contents) });
14467
15785
  } catch (error) {
14468
- console.log(chalk14.yellow(`Unable to read ${file} for context: ${error.message}`));
15786
+ console.log(chalk15.yellow(`Unable to read ${file} for context: ${error.message}`));
14469
15787
  }
14470
15788
  }
14471
15789
  trimContext(content) {
@@ -14585,11 +15903,11 @@ ${ctx.contents}`).join("\n\n");
14585
15903
  return;
14586
15904
  }
14587
15905
  if (!this.hasPrintedExplorationHeader) {
14588
- console.log("\n" + chalk14.bold("* Explored"));
15906
+ console.log("\n" + chalk15.bold("* Explored"));
14589
15907
  this.hasPrintedExplorationHeader = true;
14590
15908
  }
14591
15909
  const label = this.formatExplorationLabel(event.kind);
14592
- console.log(` ${chalk14.cyan(label)} ${event.target}`);
15910
+ console.log(` ${chalk15.cyan(label)} ${event.target}`);
14593
15911
  }
14594
15912
  clearExplorationLog() {
14595
15913
  this.hasPrintedExplorationHeader = false;
@@ -14611,7 +15929,7 @@ ${ctx.contents}`).join("\n\n");
14611
15929
  formatToolResultsBatch(results, charLimit, toolCalls, thought) {
14612
15930
  const lines = [];
14613
15931
  if (thought && !thought.trim().startsWith("{")) {
14614
- lines.push(chalk14.white(thought));
15932
+ lines.push(chalk15.white(thought));
14615
15933
  lines.push("");
14616
15934
  }
14617
15935
  for (let i = 0; i < results.length; i++) {
@@ -14622,15 +15940,15 @@ ${ctx.contents}`).join("\n\n");
14622
15940
  const command = call?.args?.command;
14623
15941
  const commandArgs = call?.args?.args;
14624
15942
  const display = result.success ? formatToolOutputForDisplay({ tool: result.tool, content, charLimit, filePath, command, commandArgs }) : { output: content, truncated: false, totalChars: content.length };
14625
- const icon = result.success ? chalk14.green("\u2714") : chalk14.red("\u2716");
14626
- lines.push(`${icon} ${chalk14.bold(result.tool)}`);
15943
+ const icon = result.success ? chalk15.green("\u2714") : chalk15.red("\u2716");
15944
+ lines.push(`${icon} ${chalk15.bold(result.tool)}`);
14627
15945
  if (content) {
14628
15946
  if (result.success) {
14629
- lines.push(chalk14.gray(display.output));
15947
+ lines.push(chalk15.gray(display.output));
14630
15948
  } else {
14631
- lines.push(chalk14.red("\u250C\u2500 Error \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
14632
- lines.push(chalk14.red("\u2502 ") + chalk14.white(content));
14633
- lines.push(chalk14.red("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
15949
+ lines.push(chalk15.red("\u250C\u2500 Error \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
15950
+ lines.push(chalk15.red("\u2502 ") + chalk15.white(content));
15951
+ lines.push(chalk15.red("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
14634
15952
  }
14635
15953
  }
14636
15954
  lines.push("");
@@ -14819,7 +16137,7 @@ ${parts.join("\n")}`
14819
16137
  return () => {
14820
16138
  };
14821
16139
  }
14822
- readline4.emitKeypressEvents(input);
16140
+ safeEmitKeypressEvents(input);
14823
16141
  const supportsRaw = typeof input.setRawMode === "function";
14824
16142
  const wasRaw = input.isRaw;
14825
16143
  if (!wasRaw && supportsRaw) {
@@ -14843,7 +16161,7 @@ ${parts.join("\n")}`
14843
16161
  controller.abort();
14844
16162
  onCancel();
14845
16163
  } else {
14846
- console.log(chalk14.gray("Press Ctrl+C again to exit."));
16164
+ console.log(chalk15.gray("Press Ctrl+C again to exit."));
14847
16165
  }
14848
16166
  return;
14849
16167
  }
@@ -14859,7 +16177,7 @@ ${parts.join("\n")}`
14859
16177
  queue.push({ text, timestamp: Date.now() });
14860
16178
  const preview = text.length > 30 ? text.slice(0, 27) + "..." : text;
14861
16179
  if (this.runtime.spinner) {
14862
- this.runtime.spinner.text = chalk14.cyan(`\u2713 Queued: "${preview}" (${this.persistentInput.getQueueLength()} pending)`);
16180
+ this.runtime.spinner.text = chalk15.cyan(`\u2713 Queued: "${preview}" (${this.persistentInput.getQueueLength()} pending)`);
14863
16181
  }
14864
16182
  }
14865
16183
  return;
@@ -15018,7 +16336,7 @@ ${parts.join("\n")}`
15018
16336
  workspace: this.runtime.workspaceRoot
15019
16337
  });
15020
16338
  } catch (reportError) {
15021
- console.error(chalk14.gray(`[Debug] Failed to submit bug report: ${reportError.message}`));
16339
+ console.error(chalk15.gray(`[Debug] Failed to submit bug report: ${reportError.message}`));
15022
16340
  }
15023
16341
  }
15024
16342
  /**
@@ -15029,16 +16347,16 @@ ${parts.join("\n")}`
15029
16347
  return;
15030
16348
  }
15031
16349
  if (result.intent === "diagnostic") {
15032
- console.log(chalk14.blue("[DIAG] Mode: Diagnostic (read-only analysis)"));
16350
+ console.log(chalk15.blue("[DIAG] Mode: Diagnostic (read-only analysis)"));
15033
16351
  if (result.keywords.length > 0) {
15034
16352
  const kws = result.keywords.slice(0, 3).join('", "');
15035
- console.log(chalk14.gray(` Detected: "${kws}"`));
16353
+ console.log(chalk15.gray(` Detected: "${kws}"`));
15036
16354
  }
15037
16355
  } else {
15038
- console.log(chalk14.yellow("[IMPL] Mode: Implementation"));
16356
+ console.log(chalk15.yellow("[IMPL] Mode: Implementation"));
15039
16357
  if (result.keywords.length > 0) {
15040
16358
  const kws = result.keywords.slice(0, 3).join('", "');
15041
- console.log(chalk14.gray(` Detected: "${kws}"`));
16359
+ console.log(chalk15.gray(` Detected: "${kws}"`));
15042
16360
  }
15043
16361
  }
15044
16362
  console.log();
@@ -15049,22 +16367,22 @@ ${parts.join("\n")}`
15049
16367
  async runEnvironmentBootstrap() {
15050
16368
  const isDebug = process.env.AUTOHAND_DEBUG === "1";
15051
16369
  if (isDebug) {
15052
- console.log(chalk14.cyan("[BOOTSTRAP] Running environment setup..."));
16370
+ console.log(chalk15.cyan("[BOOTSTRAP] Running environment setup..."));
15053
16371
  }
15054
16372
  const result = await this.environmentBootstrap.run(this.runtime.workspaceRoot);
15055
16373
  for (const step of result.steps) {
15056
- const status = step.status === "success" ? chalk14.green("[OK]") : step.status === "failed" ? chalk14.red("[FAIL]") : step.status === "skipped" ? chalk14.gray("[SKIP]") : chalk14.gray("[...]");
15057
- const duration = step.duration ? chalk14.gray(`(${(step.duration / 1e3).toFixed(1)}s)`) : "";
15058
- const detail = step.detail ? chalk14.gray(` ${step.detail}`) : "";
16374
+ const status = step.status === "success" ? chalk15.green("[OK]") : step.status === "failed" ? chalk15.red("[FAIL]") : step.status === "skipped" ? chalk15.gray("[SKIP]") : chalk15.gray("[...]");
16375
+ const duration = step.duration ? chalk15.gray(`(${(step.duration / 1e3).toFixed(1)}s)`) : "";
16376
+ const detail = step.detail ? chalk15.gray(` ${step.detail}`) : "";
15059
16377
  if (step.status === "failed" || isDebug) {
15060
16378
  console.log(` ${status} ${step.name.padEnd(14)} ${duration}${detail}`);
15061
16379
  }
15062
16380
  if (step.error) {
15063
- console.log(chalk14.red(` Error: ${step.error}`));
16381
+ console.log(chalk15.red(` Error: ${step.error}`));
15064
16382
  }
15065
16383
  }
15066
16384
  if (result.success && isDebug) {
15067
- console.log(chalk14.green(`
16385
+ console.log(chalk15.green(`
15068
16386
  [READY] Environment ready (${(result.duration / 1e3).toFixed(1)}s)
15069
16387
  `));
15070
16388
  }
@@ -15074,26 +16392,26 @@ ${parts.join("\n")}`
15074
16392
  * Run code quality pipeline after file modifications
15075
16393
  */
15076
16394
  async runQualityPipeline() {
15077
- console.log(chalk14.cyan("\n[QUALITY] Running quality checks..."));
16395
+ console.log(chalk15.cyan("\n[QUALITY] Running quality checks..."));
15078
16396
  const result = await this.codeQualityPipeline.run(this.runtime.workspaceRoot);
15079
16397
  for (const check of result.checks) {
15080
- const status = check.status === "passed" ? chalk14.green("[OK]") : check.status === "failed" ? chalk14.red("[FAIL]") : check.status === "skipped" ? chalk14.gray("[SKIP]") : chalk14.gray("[...]");
15081
- const duration = check.duration ? chalk14.gray(`(${(check.duration / 1e3).toFixed(1)}s)`) : "";
16398
+ const status = check.status === "passed" ? chalk15.green("[OK]") : check.status === "failed" ? chalk15.red("[FAIL]") : check.status === "skipped" ? chalk15.gray("[SKIP]") : chalk15.gray("[...]");
16399
+ const duration = check.duration ? chalk15.gray(`(${(check.duration / 1e3).toFixed(1)}s)`) : "";
15082
16400
  console.log(` ${status} ${check.name.padEnd(8)} ${check.command.padEnd(20)} ${duration}`);
15083
16401
  if (check.status === "failed" && check.output) {
15084
16402
  const errorLines = check.output.split("\n").slice(0, 3);
15085
16403
  for (const line of errorLines) {
15086
16404
  if (line.trim()) {
15087
- console.log(chalk14.red(` ${line}`));
16405
+ console.log(chalk15.red(` ${line}`));
15088
16406
  }
15089
16407
  }
15090
16408
  }
15091
16409
  }
15092
16410
  if (result.passed) {
15093
- console.log(chalk14.green(`
16411
+ console.log(chalk15.green(`
15094
16412
  [PASS] ${result.summary} (${(result.duration / 1e3).toFixed(1)}s)`));
15095
16413
  } else {
15096
- console.log(chalk14.red(`
16414
+ console.log(chalk15.red(`
15097
16415
  [FAIL] ${result.summary}`));
15098
16416
  }
15099
16417
  }
@@ -15236,7 +16554,7 @@ ${parts.join("\n")}`
15236
16554
  if (!this.runtime.spinner) return;
15237
16555
  const inputPreview = this.queueInput.length > 40 ? "..." + this.queueInput.slice(-37) : this.queueInput;
15238
16556
  const inputLine = `
15239
- ${chalk14.gray("\u203A")} ${inputPreview}${chalk14.gray("\u258B")}`;
16557
+ ${chalk15.gray("\u203A")} ${inputPreview}${chalk15.gray("\u258B")}`;
15240
16558
  const fullText = statusLine + inputLine;
15241
16559
  const cacheKey = `${statusLine}|${inputPreview}`;
15242
16560
  if (cacheKey === this.lastRenderedStatus) return;
@@ -15278,6 +16596,25 @@ ${chalk14.gray("\u203A")} ${inputPreview}${chalk14.gray("\u258B")}`;
15278
16596
  }
15279
16597
  this.emitStatus();
15280
16598
  }
16599
+ /**
16600
+ * Ensure stdin is in a known good state for readline input.
16601
+ * This is called after operations that may interfere with stdin state,
16602
+ * such as hook execution with shell: true.
16603
+ */
16604
+ ensureStdinReady() {
16605
+ const stdin = process.stdin;
16606
+ if (!stdin.isTTY) return;
16607
+ try {
16608
+ if (typeof stdin.setRawMode === "function" && stdin.isRaw) {
16609
+ stdin.setRawMode(false);
16610
+ }
16611
+ if (stdin.isPaused()) {
16612
+ stdin.resume();
16613
+ }
16614
+ safeEmitKeypressEvents(stdin);
16615
+ } catch {
16616
+ }
16617
+ }
15281
16618
  formatStatusLine() {
15282
16619
  const percent = Number.isFinite(this.contextPercentLeft) ? Math.max(0, Math.min(100, this.contextPercentLeft)) : 100;
15283
16620
  const queueCount = this.inkRenderer?.getQueueCount() ?? this.persistentInput.getQueueLength();
@@ -15421,7 +16758,7 @@ ${chalk14.gray("\u203A")} ${inputPreview}${chalk14.gray("\u258B")}`;
15421
16758
  import fs20 from "fs-extra";
15422
16759
  import os8 from "os";
15423
16760
  import path19 from "path";
15424
- import chalk15 from "chalk";
16761
+ import chalk16 from "chalk";
15425
16762
  var AVAILABLE_TOOLS = {
15426
16763
  file: [
15427
16764
  "read_file",
@@ -15498,7 +16835,7 @@ var PYTHON_FRAMEWORKS = {
15498
16835
  tensorflow: ["tensorflow"],
15499
16836
  pytorch: ["torch"]
15500
16837
  };
15501
- var ProjectAnalyzer = class {
16838
+ var ProjectAnalyzer2 = class {
15502
16839
  constructor(projectRoot) {
15503
16840
  this.projectRoot = projectRoot;
15504
16841
  }
@@ -15852,8 +17189,8 @@ async function runAutoSkillGeneration(workspaceRoot, llm) {
15852
17189
  skillsGenerated: 0,
15853
17190
  skills: []
15854
17191
  };
15855
- console.log(chalk15.cyan("Analyzing project structure..."));
15856
- const analyzer = new ProjectAnalyzer(workspaceRoot);
17192
+ console.log(chalk16.cyan("Analyzing project structure..."));
17193
+ const analyzer = new ProjectAnalyzer2(workspaceRoot);
15857
17194
  let analysis;
15858
17195
  try {
15859
17196
  analysis = await analyzer.analyze();
@@ -15862,17 +17199,17 @@ async function runAutoSkillGeneration(workspaceRoot, llm) {
15862
17199
  return result;
15863
17200
  }
15864
17201
  if (analysis.languages.length === 0 && analysis.frameworks.length === 0) {
15865
- console.log(chalk15.yellow("Could not detect project type. No skills generated."));
17202
+ console.log(chalk16.yellow("Could not detect project type. No skills generated."));
15866
17203
  result.error = "Could not detect project type";
15867
17204
  return result;
15868
17205
  }
15869
17206
  const detected = [...analysis.languages, ...analysis.frameworks, ...analysis.patterns].join(", ");
15870
- console.log(chalk15.gray(`Detected: ${detected}`));
15871
- console.log(chalk15.gray(`Platform: ${analysis.platform}`));
15872
- console.log(chalk15.cyan("Generating skills..."));
17207
+ console.log(chalk16.gray(`Detected: ${detected}`));
17208
+ console.log(chalk16.gray(`Platform: ${analysis.platform}`));
17209
+ console.log(chalk16.cyan("Generating skills..."));
15873
17210
  const skills = await generateAutoSkills(analysis, llm);
15874
17211
  if (skills.length === 0) {
15875
- console.log(chalk15.yellow("No skills generated."));
17212
+ console.log(chalk16.yellow("No skills generated."));
15876
17213
  result.error = "No skills generated";
15877
17214
  return result;
15878
17215
  }
@@ -15882,17 +17219,17 @@ async function runAutoSkillGeneration(workspaceRoot, llm) {
15882
17219
  if (saved) {
15883
17220
  result.skillsGenerated++;
15884
17221
  result.skills.push(skill.name);
15885
- console.log(chalk15.green(` \u2713 ${skill.name}`));
17222
+ console.log(chalk16.green(` \u2713 ${skill.name}`));
15886
17223
  if (skill.allowedTools && skill.allowedTools.length > 0) {
15887
- console.log(chalk15.gray(` Tools: ${skill.allowedTools.slice(0, 5).join(", ")}${skill.allowedTools.length > 5 ? "..." : ""}`));
17224
+ console.log(chalk16.gray(` Tools: ${skill.allowedTools.slice(0, 5).join(", ")}${skill.allowedTools.length > 5 ? "..." : ""}`));
15888
17225
  }
15889
17226
  }
15890
17227
  }
15891
17228
  if (result.skillsGenerated > 0) {
15892
17229
  result.success = true;
15893
- console.log(chalk15.green(`
17230
+ console.log(chalk16.green(`
15894
17231
  \u2713 Generated ${result.skillsGenerated} skills in ${skillsDir}`));
15895
- console.log(chalk15.gray(` Use "/skills" to view and "/skills use <name>" to activate`));
17232
+ console.log(chalk16.gray(` Use "/skills" to view and "/skills use <name>" to activate`));
15896
17233
  }
15897
17234
  return result;
15898
17235
  }
@@ -16454,6 +17791,22 @@ ${sel.text}
16454
17791
  }
16455
17792
  process.stderr.write(`[RPC DEBUG] Instruction completed, success=${success}, content length=${this.currentMessageContent.length}
16456
17793
  `);
17794
+ const turnDuration = this.turnStartTime ? Date.now() - this.turnStartTime : 0;
17795
+ const hookManager = this.agent?.getHookManager?.();
17796
+ if (hookManager) {
17797
+ const snapshot2 = this.agent?.getStatusSnapshot();
17798
+ await hookManager.executeHooks("stop", {
17799
+ sessionId: this.sessionId || void 0,
17800
+ turnDuration,
17801
+ tokensUsed: snapshot2?.tokensUsed ?? 0
17802
+ });
17803
+ this.emitHookStop(
17804
+ snapshot2?.tokensUsed ?? 0,
17805
+ 0,
17806
+ // toolCallsCount - not tracked per turn currently
17807
+ turnDuration
17808
+ );
17809
+ }
16457
17810
  } catch (err) {
16458
17811
  const errorMessage = err instanceof Error ? err.message : String(err);
16459
17812
  const errorStack = err instanceof Error ? err.stack : "";
@@ -17504,7 +18857,7 @@ async function handleSingleRequest(request, adapter) {
17504
18857
  process.title = "autohand";
17505
18858
  function getGitCommit() {
17506
18859
  if (true) {
17507
- return "5392e8c";
18860
+ return "fc318c0";
17508
18861
  }
17509
18862
  try {
17510
18863
  return execSync3("git rev-parse --short HEAD", { encoding: "utf8", stdio: ["pipe", "pipe", "ignore"] }).trim();
@@ -17562,7 +18915,7 @@ process.on("unhandledRejection", (reason, promise) => {
17562
18915
  globalThis.__autohandLastError = reason;
17563
18916
  console.error("[DEBUG] Unhandled Rejection at:", promise, "reason:", reason);
17564
18917
  });
17565
- var ASCII_FRIEND = [
18918
+ var ASCII_FRIEND2 = [
17566
18919
  "\u2880\u2874\u281B\u281B\u283B\u28F7\u2844\u2800\u28E0\u2876\u281F\u281B\u283B\u28F6\u2844\u2880\u28F4\u287E\u281B\u281B\u28BF\u28E6\u2800\u2880\u28F4\u281E\u281B\u281B\u2836\u2840",
17567
18920
  "\u284E\u2800\u28B0\u28F6\u2846\u2808\u28FF\u28F4\u28FF\u2801\u28F4\u28F6\u2844\u2818\u28FF\u28FE\u284F\u2880\u28F6\u28E6\u2800\u28BB\u2847\u28FF\u2803\u28A0\u28F6\u2846\u2800\u28B9",
17568
18921
  "\u28A7\u2800\u2818\u281B\u2803\u28A0\u287F\u2819\u28FF\u2840\u2819\u281B\u2803\u28F0\u287F\u28BB\u28E7\u2808\u281B\u281B\u2880\u28FE\u2807\u28BB\u28C6\u2808\u281B\u280B\u2800\u287C",
@@ -17573,7 +18926,7 @@ var ASCII_FRIEND = [
17573
18926
  "\u2808\u28BF\u28E6\u28E4\u28F4\u287F\u2803\u2800\u2819\u28B7\u28E6\u28E4\u28F6\u287F\u2801\u2808\u283B\u28F7\u28E4\u28E4\u287E\u281B\u2800\u2808\u28BF\u28E6\u28E4\u28E4\u2834\u2801"
17574
18927
  ].join("\n");
17575
18928
  var program = new Command();
17576
- program.name("autohand").description("Autonomous LLM-powered coding agent CLI").version(getVersionString(), "-v, --version", "output the current version").option("-p, --prompt <text>", "Run a single instruction in command mode").option("--path <path>", "Workspace path to operate in").option("-y, --yes", "Auto-confirm risky actions", false).option("--dry-run", "Preview actions without applying mutations", false).option("-d, --debug", "Enable debug output (verbose logging)", false).option("--model <model>", "Override the configured LLM model").option("--config <path>", "Path to config file (default ~/.autohand/config.json)").option("--temperature <value>", "Sampling temperature", parseFloat).option("-c, --auto-commit", "Auto-commit with LLM-generated message (runs lint & test first)", false).option("--unrestricted", "Run without any approval prompts (use with caution)", false).option("--restricted", "Deny all dangerous operations automatically", false).option("--auto-skill", "Auto-generate skills based on project analysis", false).option("--skill-install [skill-name]", "Install a community skill (opens browser if no name)").option("--project", "Install skill to project level (with --skill-install)", false).option("--permissions", "Display current permission settings and exit", false).option("--patch", "Generate git patch without applying changes (requires --prompt)", false).option("--output <file>", "Output file for patch (default: stdout, used with --patch)").option("--mode <mode>", "Run mode: interactive (default) or rpc", "interactive").option("--auto-mode <prompt>", "Start autonomous development loop with the given task").option("--max-iterations <n>", "Max auto-mode iterations (default: 50)", parseInt).option("--completion-promise <text>", 'Completion marker text (default: "DONE")').option("--no-worktree", "Disable git worktree isolation in auto-mode").option("--checkpoint-interval <n>", "Git commit every N iterations (default: 5)", parseInt).option("--max-runtime <m>", "Max runtime in minutes (default: 120)", parseInt).option("--max-cost <d>", "Max API cost in dollars (default: 10)", parseFloat).action(async (opts) => {
18929
+ program.name("autohand").description("Autonomous LLM-powered coding agent CLI").version(getVersionString(), "-v, --version", "output the current version").option("-p, --prompt <text>", "Run a single instruction in command mode").option("--path <path>", "Workspace path to operate in").option("-y, --yes", "Auto-confirm risky actions", false).option("--dry-run", "Preview actions without applying mutations", false).option("-d, --debug", "Enable debug output (verbose logging)", false).option("--model <model>", "Override the configured LLM model").option("--config <path>", "Path to config file (default ~/.autohand/config.json)").option("--temperature <value>", "Sampling temperature", parseFloat).option("-c, --auto-commit", "Auto-commit with LLM-generated message (runs lint & test first)", false).option("--unrestricted", "Run without any approval prompts (use with caution)", false).option("--restricted", "Deny all dangerous operations automatically", false).option("--auto-skill", "Auto-generate skills based on project analysis", false).option("--skill-install [skill-name]", "Install a community skill (opens browser if no name)").option("--project", "Install skill to project level (with --skill-install)", false).option("--permissions", "Display current permission settings and exit", false).option("--login", "Sign in to your Autohand account", false).option("--logout", "Sign out of your Autohand account", false).option("--patch", "Generate git patch without applying changes (requires --prompt)", false).option("--output <file>", "Output file for patch (default: stdout, used with --patch)").option("--mode <mode>", "Run mode: interactive (default) or rpc", "interactive").option("--auto-mode <prompt>", "Start autonomous development loop with the given task").option("--max-iterations <n>", "Max auto-mode iterations (default: 50)", parseInt).option("--completion-promise <text>", 'Completion marker text (default: "DONE")').option("--no-worktree", "Disable git worktree isolation in auto-mode").option("--checkpoint-interval <n>", "Git commit every N iterations (default: 5)", parseInt).option("--max-runtime <m>", "Max runtime in minutes (default: 120)", parseInt).option("--max-cost <d>", "Max API cost in dollars (default: 10)", parseFloat).option("--setup", "Run the setup wizard to configure or reconfigure Autohand", false).action(async (opts) => {
17577
18930
  if (opts.skillInstall !== void 0) {
17578
18931
  await runSkillInstall(opts);
17579
18932
  return;
@@ -17582,6 +18935,34 @@ program.name("autohand").description("Autonomous LLM-powered coding agent CLI").
17582
18935
  await displayPermissions(opts);
17583
18936
  return;
17584
18937
  }
18938
+ if (opts.login) {
18939
+ const { login } = await import("./login-NYWZRZO5.js");
18940
+ const config = await loadConfig(opts.config);
18941
+ await login({ config });
18942
+ process.exit(0);
18943
+ }
18944
+ if (opts.logout) {
18945
+ const { logout } = await import("./logout-MBS7L3ZW.js");
18946
+ const config = await loadConfig(opts.config);
18947
+ await logout({ config });
18948
+ process.exit(0);
18949
+ }
18950
+ if (opts.setup) {
18951
+ const config = await loadConfig(opts.config);
18952
+ const workspaceRoot = resolveWorkspaceRoot(config, opts.path);
18953
+ const wizard = new SetupWizard(workspaceRoot, config);
18954
+ const result = await wizard.run({ skipWelcome: false });
18955
+ if (result.cancelled) {
18956
+ console.log(chalk17.gray("\nSetup cancelled."));
18957
+ process.exit(0);
18958
+ }
18959
+ if (result.success) {
18960
+ const newConfig = { ...config, ...result.config };
18961
+ await saveConfig(newConfig);
18962
+ console.log(chalk17.green("\nSetup complete! Run `autohand` to start."));
18963
+ }
18964
+ process.exit(0);
18965
+ }
17585
18966
  if (opts.patch) {
17586
18967
  await runPatchMode(opts);
17587
18968
  return;
@@ -17600,59 +18981,38 @@ program.name("autohand").description("Autonomous LLM-powered coding agent CLI").
17600
18981
  program.command("resume <sessionId>").description("Resume a previous session").option("--path <path>", "Workspace path to operate in").option("--model <model>", "Override the configured LLM model").action(async (sessionId, opts) => {
17601
18982
  await runCLI({ ...opts, resumeSessionId: sessionId });
17602
18983
  });
18984
+ program.command("login").description("Sign in to your Autohand account").action(async () => {
18985
+ const { login } = await import("./login-NYWZRZO5.js");
18986
+ const config = await loadConfig();
18987
+ await login({ config });
18988
+ process.exit(0);
18989
+ });
18990
+ program.command("logout").description("Sign out of your Autohand account").action(async () => {
18991
+ const { logout } = await import("./logout-MBS7L3ZW.js");
18992
+ const config = await loadConfig();
18993
+ await logout({ config });
18994
+ process.exit(0);
18995
+ });
17603
18996
  async function runCLI(options) {
17604
18997
  const statusPanel = null;
17605
18998
  try {
17606
18999
  let config = await loadConfig(options.config);
19000
+ const workspaceRoot = resolveWorkspaceRoot(config, options.path);
17607
19001
  const providerName = config.provider ?? "openrouter";
17608
19002
  const providerConfig = getProviderConfig(config, providerName);
17609
19003
  if (!providerConfig) {
17610
- if (config.isNewConfig) {
17611
- console.log(chalk16.cyan("\n\u2728 Welcome to Autohand!\n"));
17612
- console.log(chalk16.gray(`Config created at: ${config.configPath}
17613
- `));
17614
- }
17615
- console.log(chalk16.yellow(`No ${providerName} API key configured.
17616
- `));
17617
- let apiKey;
17618
- try {
17619
- const result = await enquirer4.prompt({
17620
- type: "password",
17621
- name: "apiKey",
17622
- message: `Enter your ${providerName === "openrouter" ? "OpenRouter" : providerName} API key`,
17623
- validate: (val) => {
17624
- if (typeof val !== "string" || !val.trim()) {
17625
- return "API key is required";
17626
- }
17627
- return true;
17628
- }
17629
- });
17630
- apiKey = result.apiKey;
17631
- } catch (error) {
17632
- if (error?.code === "ERR_USE_AFTER_CLOSE" || error?.message?.includes("cancelled")) {
17633
- console.log(chalk16.gray("\nSetup cancelled."));
17634
- process.exit(0);
17635
- }
17636
- throw error;
19004
+ const wizard = new SetupWizard(workspaceRoot, config);
19005
+ const result = await wizard.run({ skipWelcome: !config.isNewConfig });
19006
+ if (result.cancelled) {
19007
+ console.log(chalk17.gray("\nSetup cancelled."));
19008
+ process.exit(0);
17637
19009
  }
17638
- if (providerName === "openrouter") {
17639
- config.openrouter = {
17640
- ...config.openrouter,
17641
- apiKey: apiKey.trim(),
17642
- baseUrl: config.openrouter?.baseUrl || "https://openrouter.ai/api/v1",
17643
- model: config.openrouter?.model || "anthropic/claude-sonnet-4-20250514"
17644
- };
17645
- } else if (providerName === "openai") {
17646
- config.openai = {
17647
- ...config.openai,
17648
- apiKey: apiKey.trim(),
17649
- model: config.openai?.model || "gpt-4o"
17650
- };
19010
+ if (result.success) {
19011
+ config = { ...config, ...result.config };
19012
+ await saveConfig(config);
19013
+ console.log();
17651
19014
  }
17652
- await saveConfig(config);
17653
- console.log(chalk16.green("\u2713 API key saved to config\n"));
17654
19015
  }
17655
- const workspaceRoot = resolveWorkspaceRoot(config, options.path);
17656
19016
  const safetyCheck = checkWorkspaceSafety(workspaceRoot);
17657
19017
  if (!safetyCheck.safe) {
17658
19018
  printDangerousWorkspaceWarning(workspaceRoot, safetyCheck);
@@ -17672,7 +19032,7 @@ async function runCLI(options) {
17672
19032
  const checkResults = await runStartupChecks(workspaceRoot);
17673
19033
  printStartupCheckResults(checkResults);
17674
19034
  if (!checkResults.allRequiredMet) {
17675
- console.log(chalk16.yellow("Continuing anyway, but some features may not work correctly.\n"));
19035
+ console.log(chalk17.yellow("Continuing anyway, but some features may not work correctly.\n"));
17676
19036
  }
17677
19037
  if (options.model) {
17678
19038
  const providerName2 = config.provider ?? "openrouter";
@@ -17687,16 +19047,17 @@ async function runCLI(options) {
17687
19047
  const llmProvider = ProviderFactory.create(config);
17688
19048
  const files = new FileActionManager(workspaceRoot);
17689
19049
  if (options.autoSkill) {
17690
- console.log(chalk16.cyan("\nAuto-generating skills for this project...\n"));
19050
+ console.log(chalk17.cyan("\nAuto-generating skills for this project...\n"));
17691
19051
  const result = await runAutoSkillGeneration(workspaceRoot, llmProvider);
17692
19052
  if (!result.success) {
17693
- console.log(chalk16.yellow(result.error || "Failed to generate skills"));
19053
+ console.log(chalk17.yellow(result.error || "Failed to generate skills"));
17694
19054
  }
17695
19055
  return;
17696
19056
  }
17697
19057
  const agent = new AutohandAgent(llmProvider, files, runtime);
17698
19058
  if (options.prompt) {
17699
19059
  await agent.runCommandMode(options.prompt);
19060
+ process.exit(0);
17700
19061
  } else if (options.resumeSessionId) {
17701
19062
  await agent.resumeSession(options.resumeSessionId);
17702
19063
  } else {
@@ -17704,7 +19065,7 @@ async function runCLI(options) {
17704
19065
  }
17705
19066
  } catch (error) {
17706
19067
  if (error instanceof Error) {
17707
- console.error(chalk16.red(error.message));
19068
+ console.error(chalk17.red(error.message));
17708
19069
  } else {
17709
19070
  console.error(error);
17710
19071
  }
@@ -17716,7 +19077,7 @@ function printBanner() {
17716
19077
  return;
17717
19078
  }
17718
19079
  if (process.stdout.isTTY) {
17719
- console.log(chalk16.gray(ASCII_FRIEND));
19080
+ console.log(chalk17.gray(ASCII_FRIEND2));
17720
19081
  } else {
17721
19082
  console.log("autohand");
17722
19083
  }
@@ -17734,28 +19095,28 @@ function printWelcome(runtime, authUser, versionCheck) {
17734
19095
  }
17735
19096
  })();
17736
19097
  const dir = runtime.workspaceRoot;
17737
- let versionLine = `${chalk16.bold("> Autohand")} v${getVersionString()}`;
19098
+ let versionLine = `${chalk17.bold("> Autohand")} v${getVersionString()}`;
17738
19099
  if (versionCheck) {
17739
19100
  if (versionCheck.isUpToDate) {
17740
- versionLine += chalk16.green(" \u2713 Up to date");
19101
+ versionLine += chalk17.green(" \u2713 Up to date");
17741
19102
  } else if (versionCheck.updateAvailable && versionCheck.latestVersion) {
17742
- versionLine += chalk16.yellow(` \u2B06 Update available: v${versionCheck.latestVersion}`);
19103
+ versionLine += chalk17.yellow(` \u2B06 Update available: v${versionCheck.latestVersion}`);
17743
19104
  }
17744
19105
  }
17745
19106
  console.log(versionLine);
17746
19107
  if (versionCheck?.updateAvailable) {
17747
- console.log(chalk16.gray(" \u21B3 Run: ") + chalk16.cyan("curl -fsSL https://autohand.ai/install.sh | sh"));
19108
+ console.log(chalk17.gray(" \u21B3 Run: ") + chalk17.cyan("curl -fsSL https://autohand.ai/install.sh | sh"));
17748
19109
  }
17749
19110
  if (authUser) {
17750
- console.log(chalk16.green(`Welcome back, ${authUser.name || authUser.email}!`));
19111
+ console.log(chalk17.green(`Welcome back, ${authUser.name || authUser.email}!`));
17751
19112
  }
17752
- console.log(`${chalk16.gray("model:")} ${chalk16.cyan(model)} ${chalk16.gray("| directory:")} ${chalk16.cyan(dir)}`);
19113
+ console.log(`${chalk17.gray("model:")} ${chalk17.cyan(model)} ${chalk17.gray("| directory:")} ${chalk17.cyan(dir)}`);
17753
19114
  console.log();
17754
- console.log(chalk16.gray("To get started, describe a task or try one of these commands:"));
17755
- console.log(chalk16.cyan("/init ") + chalk16.gray("create an AGENTS.md file with instructions for Autohand"));
17756
- console.log(chalk16.cyan("/help ") + chalk16.gray("review my current changes and find issues"));
19115
+ console.log(chalk17.gray("To get started, describe a task or try one of these commands:"));
19116
+ console.log(chalk17.cyan("/init ") + chalk17.gray("create an AGENTS.md file with instructions for Autohand"));
19117
+ console.log(chalk17.cyan("/help ") + chalk17.gray("review my current changes and find issues"));
17757
19118
  if (!authUser) {
17758
- console.log(chalk16.cyan("/login ") + chalk16.gray("sign in to your Autohand account"));
19119
+ console.log(chalk17.cyan("/login ") + chalk17.gray("sign in to your Autohand account"));
17759
19120
  }
17760
19121
  console.log();
17761
19122
  }
@@ -17810,44 +19171,44 @@ async function displayPermissions(opts) {
17810
19171
  const blacklist = manager.getBlacklist();
17811
19172
  const settings = manager.getSettings();
17812
19173
  console.log();
17813
- console.log(chalk16.bold.cyan("Autohand Permissions"));
17814
- console.log(chalk16.gray("\u2500".repeat(60)));
19174
+ console.log(chalk17.bold.cyan("Autohand Permissions"));
19175
+ console.log(chalk17.gray("\u2500".repeat(60)));
17815
19176
  console.log();
17816
- console.log(chalk16.bold("Mode:"), chalk16.cyan(settings.mode || "interactive"));
19177
+ console.log(chalk17.bold("Mode:"), chalk17.cyan(settings.mode || "interactive"));
17817
19178
  console.log();
17818
- console.log(chalk16.bold("Workspace:"), chalk16.gray(workspaceRoot));
17819
- console.log(chalk16.bold("Config:"), chalk16.gray(config.configPath));
19179
+ console.log(chalk17.bold("Workspace:"), chalk17.gray(workspaceRoot));
19180
+ console.log(chalk17.bold("Config:"), chalk17.gray(config.configPath));
17820
19181
  console.log();
17821
- console.log(chalk16.bold.green("Approved (Whitelist)"));
19182
+ console.log(chalk17.bold.green("Approved (Whitelist)"));
17822
19183
  if (whitelist.length === 0) {
17823
- console.log(chalk16.gray(" No approved patterns"));
19184
+ console.log(chalk17.gray(" No approved patterns"));
17824
19185
  } else {
17825
19186
  whitelist.forEach((pattern, index) => {
17826
- console.log(chalk16.green(` ${index + 1}. ${pattern}`));
19187
+ console.log(chalk17.green(` ${index + 1}. ${pattern}`));
17827
19188
  });
17828
19189
  }
17829
19190
  console.log();
17830
- console.log(chalk16.bold.red("Denied (Blacklist)"));
19191
+ console.log(chalk17.bold.red("Denied (Blacklist)"));
17831
19192
  if (blacklist.length === 0) {
17832
- console.log(chalk16.gray(" No denied patterns"));
19193
+ console.log(chalk17.gray(" No denied patterns"));
17833
19194
  } else {
17834
19195
  blacklist.forEach((pattern, index) => {
17835
- console.log(chalk16.red(` ${index + 1}. ${pattern}`));
19196
+ console.log(chalk17.red(` ${index + 1}. ${pattern}`));
17836
19197
  });
17837
19198
  }
17838
19199
  console.log();
17839
- console.log(chalk16.gray("\u2500".repeat(60)));
17840
- console.log(chalk16.bold("Summary:"), `${whitelist.length} approved, ${blacklist.length} denied`);
19200
+ console.log(chalk17.gray("\u2500".repeat(60)));
19201
+ console.log(chalk17.bold("Summary:"), `${whitelist.length} approved, ${blacklist.length} denied`);
17841
19202
  console.log();
17842
- console.log(chalk16.gray("Use /permissions in interactive mode to manage permissions."));
17843
- console.log(chalk16.gray("Use --unrestricted to skip all approval prompts."));
17844
- console.log(chalk16.gray("Use --restricted to deny all dangerous operations."));
19203
+ console.log(chalk17.gray("Use /permissions in interactive mode to manage permissions."));
19204
+ console.log(chalk17.gray("Use --unrestricted to skip all approval prompts."));
19205
+ console.log(chalk17.gray("Use --restricted to deny all dangerous operations."));
17845
19206
  console.log();
17846
19207
  }
17847
19208
  async function runPatchMode(opts) {
17848
19209
  if (!opts.prompt) {
17849
- console.error(chalk16.red("Error: --patch requires --prompt to specify the instruction"));
17850
- console.error(chalk16.gray('Usage: autohand --prompt "your instruction" --patch'));
19210
+ console.error(chalk17.red("Error: --patch requires --prompt to specify the instruction"));
19211
+ console.error(chalk17.gray('Usage: autohand --prompt "your instruction" --patch'));
17851
19212
  process.exit(1);
17852
19213
  }
17853
19214
  const fs21 = await import("fs-extra");
@@ -17881,23 +19242,23 @@ async function runPatchMode(opts) {
17881
19242
  workspaceRoot,
17882
19243
  options: patchOptions
17883
19244
  };
17884
- console.error(chalk16.cyan("Patch Mode: Changes will be captured without modifying files\n"));
19245
+ console.error(chalk17.cyan("Patch Mode: Changes will be captured without modifying files\n"));
17885
19246
  try {
17886
19247
  const agent = new AutohandAgent(llmProvider, files, runtime);
17887
19248
  await agent.runCommandMode(opts.prompt);
17888
19249
  const changes = files.getPendingChanges();
17889
19250
  if (changes.length === 0) {
17890
- console.error(chalk16.yellow("\nNo changes were made."));
19251
+ console.error(chalk17.yellow("\nNo changes were made."));
17891
19252
  process.exit(0);
17892
19253
  }
17893
19254
  const patch = generateUnifiedPatch(changes);
17894
- console.error(chalk16.green(`
19255
+ console.error(chalk17.green(`
17895
19256
  \u2713 ${formatChangeSummary(changes)}`));
17896
19257
  if (opts.output) {
17897
19258
  await fs21.default.ensureDir((await import("path")).dirname(opts.output));
17898
19259
  await fs21.default.writeFile(opts.output, patch);
17899
- console.error(chalk16.green(`\u2713 Patch written to ${opts.output}`));
17900
- console.error(chalk16.gray("\nTo apply: git apply " + opts.output));
19260
+ console.error(chalk17.green(`\u2713 Patch written to ${opts.output}`));
19261
+ console.error(chalk17.gray("\nTo apply: git apply " + opts.output));
17901
19262
  } else {
17902
19263
  process.stdout.write(patch);
17903
19264
  }
@@ -17905,14 +19266,14 @@ async function runPatchMode(opts) {
17905
19266
  process.exit(0);
17906
19267
  } catch (error) {
17907
19268
  files.exitPreviewMode();
17908
- console.error(chalk16.red(`
19269
+ console.error(chalk17.red(`
17909
19270
  Error: ${error.message}`));
17910
19271
  process.exit(1);
17911
19272
  }
17912
19273
  }
17913
19274
  async function runAutoMode(opts) {
17914
19275
  if (!opts.autoMode) {
17915
- console.error(chalk16.red("Error: --auto-mode requires a task prompt"));
19276
+ console.error(chalk17.red("Error: --auto-mode requires a task prompt"));
17916
19277
  process.exit(1);
17917
19278
  }
17918
19279
  const config = await loadConfig(opts.config);
@@ -17934,9 +19295,9 @@ async function runAutoMode(opts) {
17934
19295
  }
17935
19296
  const { AutomodeManager, getAutomodeOptions } = await import("./AutomodeManager-WIMHLG4W.js");
17936
19297
  const { HookManager: HookManager2 } = await import("./HookManager-VIX56KFU.js");
17937
- const { SessionManager: SessionManager2 } = await import("./SessionManager-IMW2HGR3.js");
19298
+ const { SessionManager: SessionManager2 } = await import("./SessionManager-XDBEQUPG.js");
17938
19299
  const { MemoryManager: MemoryManager2 } = await import("./MemoryManager-UVHILGV5.js");
17939
- const readline5 = await import("readline");
19300
+ const readline3 = await import("readline");
17940
19301
  const llmProvider = ProviderFactory.create(config);
17941
19302
  const files = new FileActionManager(workspaceRoot);
17942
19303
  const providerName = config.provider ?? "openrouter";
@@ -17954,26 +19315,26 @@ async function runAutoMode(opts) {
17954
19315
  const automodeManager = new AutomodeManager(config, workspaceRoot, hookManager, session, memoryManager);
17955
19316
  const automodeOptions = getAutomodeOptions(opts, config);
17956
19317
  if (!automodeOptions) {
17957
- console.error(chalk16.red("Error: Failed to parse auto-mode options"));
19318
+ console.error(chalk17.red("Error: Failed to parse auto-mode options"));
17958
19319
  process.exit(1);
17959
19320
  }
17960
19321
  printBanner();
17961
- console.log(chalk16.bold.cyan("\n\u{1F504} Auto-Mode: Autonomous Development Loop\n"));
17962
- console.log(chalk16.gray("Task:"), chalk16.white(opts.autoMode));
17963
- console.log(chalk16.gray("Max Iterations:"), chalk16.cyan(automodeOptions.maxIterations ?? 50));
17964
- console.log(chalk16.gray("Completion Marker:"), chalk16.cyan(automodeOptions.completionPromise ?? "DONE"));
17965
- console.log(chalk16.gray("Worktree Isolation:"), chalk16.cyan(automodeOptions.useWorktree !== false ? "enabled" : "disabled"));
19322
+ console.log(chalk17.bold.cyan("\n\u{1F504} Auto-Mode: Autonomous Development Loop\n"));
19323
+ console.log(chalk17.gray("Task:"), chalk17.white(opts.autoMode));
19324
+ console.log(chalk17.gray("Max Iterations:"), chalk17.cyan(automodeOptions.maxIterations ?? 50));
19325
+ console.log(chalk17.gray("Completion Marker:"), chalk17.cyan(automodeOptions.completionPromise ?? "DONE"));
19326
+ console.log(chalk17.gray("Worktree Isolation:"), chalk17.cyan(automodeOptions.useWorktree !== false ? "enabled" : "disabled"));
17966
19327
  console.log();
17967
19328
  if (process.stdin.isTTY) {
17968
- readline5.emitKeypressEvents(process.stdin);
19329
+ readline3.emitKeypressEvents(process.stdin);
17969
19330
  process.stdin.setRawMode(true);
17970
19331
  process.stdin.on("keypress", (_str, key) => {
17971
19332
  if (key && key.name === "escape") {
17972
- console.log(chalk16.yellow("\n\u26A0\uFE0F Cancelling auto-mode..."));
19333
+ console.log(chalk17.yellow("\n\u26A0\uFE0F Cancelling auto-mode..."));
17973
19334
  automodeManager.cancel("user_escape");
17974
19335
  }
17975
19336
  if (key && key.ctrl && key.name === "c") {
17976
- console.log(chalk16.yellow("\n\u26A0\uFE0F Cancelling auto-mode..."));
19337
+ console.log(chalk17.yellow("\n\u26A0\uFE0F Cancelling auto-mode..."));
17977
19338
  automodeManager.cancel("user_escape");
17978
19339
  if (process.stdin.isTTY) {
17979
19340
  process.stdin.setRawMode(false);
@@ -18005,7 +19366,7 @@ async function runAutoMode(opts) {
18005
19366
  } catch (err) {
18006
19367
  success = false;
18007
19368
  error = err.message;
18008
- console.error(chalk16.red(`Iteration error: ${error}`));
19369
+ console.error(chalk17.red(`Iteration error: ${error}`));
18009
19370
  }
18010
19371
  return {
18011
19372
  success,
@@ -18022,17 +19383,50 @@ async function runAutoMode(opts) {
18022
19383
  if (finalState) {
18023
19384
  session.metadata.automodeIterations = finalState.currentIteration;
18024
19385
  const statusText = finalState.status === "completed" ? "completed" : `ended (${finalState.status})`;
18025
- await sessionManager.closeSession(`Auto-mode ${statusText} after ${finalState.currentIteration} iterations: ${opts.autoMode?.slice(0, 50)}...`);
18026
- console.log(chalk16.gray(`
19386
+ console.log(chalk17.gray(`
19387
+ \u{1F4CA} Auto-mode ${statusText} after ${finalState.currentIteration} iterations`));
19388
+ }
19389
+ console.log(chalk17.cyan("\n\u{1F504} Auto-mode finished. You can continue working interactively.\n"));
19390
+ console.log(chalk17.gray("Press Enter to continue in interactive mode, or Ctrl+C to exit.\n"));
19391
+ const continuePromise = new Promise((resolve) => {
19392
+ if (!process.stdin.isTTY) {
19393
+ resolve(false);
19394
+ return;
19395
+ }
19396
+ readline3.emitKeypressEvents(process.stdin);
19397
+ process.stdin.setRawMode(true);
19398
+ process.stdin.resume();
19399
+ const handleKey = (_str, key) => {
19400
+ process.stdin.off("keypress", handleKey);
19401
+ if (process.stdin.isTTY) {
19402
+ process.stdin.setRawMode(false);
19403
+ }
19404
+ if (key && key.ctrl && key.name === "c") {
19405
+ resolve(false);
19406
+ } else if (key && key.name === "return") {
19407
+ resolve(true);
19408
+ } else {
19409
+ resolve(true);
19410
+ }
19411
+ };
19412
+ process.stdin.on("keypress", handleKey);
19413
+ });
19414
+ const shouldContinue = await continuePromise;
19415
+ if (!shouldContinue) {
19416
+ const statusText = finalState?.status === "completed" ? "completed" : `ended (${finalState?.status})`;
19417
+ await sessionManager.closeSession(`Auto-mode ${statusText} after ${finalState?.currentIteration ?? 0} iterations: ${opts.autoMode?.slice(0, 50)}...`);
19418
+ console.log(chalk17.gray(`
18027
19419
  \u{1F4C1} Session saved: ${session.metadata.sessionId}`));
19420
+ process.exit(finalState?.status === "completed" ? 0 : 1);
18028
19421
  }
18029
- process.exit(finalState?.status === "completed" ? 0 : 1);
19422
+ console.log(chalk17.cyan("\n\u25B6\uFE0F Continuing in interactive mode...\n"));
19423
+ await agent.runInteractive();
18030
19424
  } catch (error) {
18031
19425
  if (process.stdin.isTTY) {
18032
19426
  process.stdin.setRawMode(false);
18033
19427
  }
18034
19428
  await sessionManager.closeSession(`Auto-mode failed: ${error.message}`);
18035
- console.error(chalk16.red(`
19429
+ console.error(chalk17.red(`
18036
19430
  Auto-mode error: ${error.message}`));
18037
19431
  process.exit(1);
18038
19432
  }